ByRichard 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
-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.
-document-that-eluded-applocker-and-amsi/
_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.
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.
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.
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.
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)
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.
In one sentence, it is a script-based malware scanning API provided by Microsoft that can be integrated into any application to scan and detect the integrity of user input in order to safeguard the application and thus, consumers against malwares. For example, a messenger app may scan messages with AMSI for malware before sending it forward to the receiver.
AMSI is vendor-independent and provides open Win32 API and COM interfaces for the developer to use. Since Microsoft manages AMSI itself, the latest malware signatures are auto-updated in it. Hence, a developer can integrate AMSI quite easily to protect its consumers from dynamic, script-based malwares. You can read the developer guide here.
AMSI works on signature-based detection. This means that for every particular malicious keyword, URL, function or procedure, AMSI has a related signature in its database. So, if an attacker uses that same keyword in his code again, AMSI blocks the execution then and there.
Explanation: As you can see, the AMSI API is open, so any AV can read the data from its functions. Here, a windows script is being run. When it is passed through AMSI, amsi.dll is injected in the same virtual memory as that of our program. This amsi.dll has various functions that can evaluate code. These functions can be found here. However, the actual scanning task is conducted by these two functions:
These functions evaluate the code. If the code is clean, the results are finally passed to the AV provider class and from there to the AV service using RPC call. If the code is suspicious, it is blocked by the AMSI itself.
Now that we have discussed the basics of AMSI, we will be discussing some of the very well-known techniques to bypass AMSI. Bypassing AMSI is often necessary for red-teamers in order to execute arbitrary code for lateral movement/privilege escalation.
To cover all of the bypass methods extend beyond the scope of this article as there are new methods coming in each day. The prominent ones are discussed here and tested on Windows 10 version 1809. It is to be noted that the latest versions of Windows (beyond 1903) block almost all of the methods available on the internet as signatures keep getting updated.
Microsoft has integrated AMSI in the powershell terminal (powershell.exe application) which takes in input and parses it through the Powershell engine. If we open process hacker and search for amsi.dll we will see that amsi is running in the powershell terminal and any input will first be scanned by it.
However, this technique has its own demerits. A payload may trigger AMSI one or more times. It is virtually very time consuming and noise creating to keep obfuscating keyword by keyword after each run of a payload. Hence, we follow this manual obfuscation guide by @ShitSecure.
Ever since that time, many people have posted different variants of the same method. In some methods bytecode is used, in others, functions are replaced or strings are replaced but the logic prevails the same.
Daniel Duggan posted about memory hijacking techniques that can bypass AMSI in his blog here. The logic is to hook (read about hooking here) the function AmsiScanBuffer() so that it always returns the handle AMSI_RESULT_CLEAN indicating that AMSI has found no malware. The API responses could be monitored using the API monitor tool by Rohitab.
After the Rasta Mouse (Daniel Duggan) technique started getting detected, people made various changes in the code to make it FUD again. Fatrodzianko posted about one such technique in his blog here. He obfuscated the same code using opcodes and put the script on gist here.
We just have to download the script and run and the tool automatically will bypass AMSI using a valid method. For example, here WMF5 autologging bypass has worked. This method unloads AMSI from the current terminal and bypasses it.
In this article, we talked about the basics of AMSI, how to use them in a program, workflow and 7 ways to bypass them. It is to be noted that there are more ways than shown here but the aim of the article was to talk about most widely known 7 methods to bypass AMSI and how this AMSI evasion game has developed over time and how complexity has only increased. Hope you liked the article. Thanks for reading.
The message This script contains malicious content and has been blocked by your antivirus software. is very clear and shows us, that this script is flagged by AMSI. So, if we build our own custom AMSI bypass or just grab one payload from amsi.fail and load the script afterwards, this will not result in any error message:
Exception calling "Load" with "1" argument(s): "Could not load file or assembly '288768 bytes loaded from AnonymouslyHosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Anattempt was made to load a program with an incorrect format." - this message is not telling about any malicious software found but states that the binaries format is incorrect.
In this case, we successfully bypassed AMSI for the Powershell script-code itself, but [System.Reflection.Assembly]::Load($byteOutArray) triggers an AMSI-scan for the .NET binary which was base64 decoded and decompressed at runtime. But our bypass did not bypass the .NET AMSI-scan. Therefore the loading was blocked and [WireT4p.Program]::main() was not found. So lets take a look at how we can still execute the script.
They either disable Powershell Script-Logging or change subvalues of the System.Management.Automation namespace. The System.Management.Automation namespace basically is the root namespace for the Windows PowerShell. Both techniques are therefore Powershell specific and only affect the Anti Malware Scan-Interface for Powershell script-code.
3a8082e126