Hoarder, HIPS bypasses, and Ambush

I gave an updated Ambush Presentation at Derbycon today. Reverse engineers can feel right at home stepping through the IDA-inspired slides. Hit the spacebar or the right arrow key to move forward, and z or the left arrow key to move backwards. If you are not familiar with Ambush, check out http://ambuships.com/. Recently supported in Ambush is a Windows standalone install method, and we have fixed a few bugs and some of the install gotchas so nothing should stop you from experiencing the awesome now.

On the attack side, I demonstrated Hoarder, which is a proof of concept to bypass standard hook-based host intrusion prevention systems by avoiding making any calls to OS DLLs at all, and only making raw syscalls to the kernel. It works in two steps. First, the getdlls program opens the target executable and recursively reads it and all of its required DLLs into C language byte arrays. It then identifies all of the required DLL aliases and generates source code for the loader to know how to connect the DLLs.

Output of getdlls

In the second step, the sources generated need to be compiled with the rest of the hoarder sources into the final product. These include a small bit of assembly to make a direct syscall to allocate memory and a modified version of Stephen Fewer's Reflective Loader to handle the DLL loading, such as section mapping, export forwarding, and relocating if necessary. These have been carefully written along with the required data structures to not make any external function calls. A lot of unusual compilation settings were also used to avoid any boilerplate compiler-inserted code or other standard libraries that would end up calling external DLLs. To do this with your own projects in MSVC, you will need to turn off C++ exceptions, turn off any optimization that will use functions like memset for example, and not use a WinMain or main method, but instead manually set the entry point to your own start function.

screenshot of hoarded helloworld.exe in IDA, showing no imported functions

You now effectively have a completely statically compiled executable on Windows.

[insert applause here]
But wait, don't get too excited just yet - Hoarder still won't invalidate Ambush or user-mode hooks in general:
First, right now this only works on toy examples, and many difficulties remain before using it on real malware. Any operation that relies on the Process Environment Block and associated data structures matching the DLLs that are loaded into memory will break, as well as any operation that relies on the modules actually mapping to real file handles, or other details that happen with a real loader. This includes most programs' functionality. In fact, I had to disable even calling the DllMain methods of the loaded DLLs to avoid crashing. This means that all the global variables and other initialization code was also messed up, breaking everything but the simplest functions. Even if those concerns were addressed, without a dramatic amount of work, the hoarded executable will be tied to a specific OS version, architecture, and service pack because otherwise the syscalls will be wrong. All this means that almost any real program will still have serious issues. Hoarder ended up being a learning exercise illustrating just how difficult it really is to avoid making DLL function calls. From an attack perspective, I am still interested in seeing whether the above can be addressed, but it will clearly be a major effort. So in the meantime, Ambush will remain effective against the entire spectrum of attacks and malware behavior. If we take into consideration the research done in "A Scientific (But Non Academic) Study of How Malware Employs Anti-Debugging, Anti-Disassembly and Anti-Virtualization Technologies," the fact that the overwhelming majority of malware out there does not even implement defenses against existing hooks, Ambush should remain effective against most malware long into the future.

Second, even if a complete hoarder is finished, Ambush will remain an effective defense against first-stage attacks, including shellcode. As I pointed out in the talk, we are slowly seeing the decline of memory corruption exploits in real attacks. For example, three out of four of the zero-days that Stuxnet used were non-memory corruption exploits, along with the vaunted Flame-dropping exploit and all of the recent Java 0days. Without directly executing shellcode, an attacker cannot simply make direct syscalls or avoid hooks. Even if an exploit is used that leads to direct native shellcode execution, although syscalls hypothetically could be used, the inherent complexity and platform-specificity of them makes direct-syscall shellcode unlikely. It is a lot of work to attempt to re-implement functionality such as downloading and executing a file with syscalls.

Third, Ambush will remain effective as a platform for distributing signatures to block exploits. If details of the vulnerability are known, Ambush can usually be used to prevent exploits themselves, or get an alert when one is attempted even if a patch has been applied. This once again lets you extend your ability to detect attempted compromises.

As always, send me a note if you are interested in using or working on any of the above.

Comments are closed.