I was pretty bored today and couldn’t think of an article to write, decided I’d come up with an example of escaping a sandbox. Most sandboxes use hooks placed within user-mode dlls in order to monitor process activity. If someone was able to remove or bypass these hooks, they would be able to escape the sandbox.
A common method used by advanced malware is to write a custom implementation of “LoadLibrary” / “LdrLoadDll”. Using this code they can manually map a new, clean, copy of a dll and use it to evade hooks. Because of the nature of PE files, it is generally quite complex to do this and required a good understanding of PE files and the PE loader. As it happens, there is currently no working, easy to find, code to do this on Google, so such methods are not seen is script-kiddie malware and I’d like to keep it that way. Instead I will be showing a nice little hack that works in a similar way, however will be fairly easy for sandbox developers to deal with.
How It Works
When i was thinking of which way to attack the sandbox, I thought it would be amusing (at least for me) to use hooks to facilitate the escape, I managed to do it using a single hook: “RtlEqualUnicodeString”.
First, if we look at the call path for “LoadLibrary” we will see it eventually ends up at “LdrLoadDll” which internally calls “LdrpLoadDll”. Inside “LdrpLoadDll” is a call to LdrpCheckForLoadedDll, this function is responsible for iterating through the list of currently loaded dlls (“PEB_LDR_DATA->InMemoryOrderModuleList”) and checking that the target dll isn’t already loaded.
|A snippet from LdrpCheckForLoadedDll
By hooking “RtlEqualUnicodeString” or “RtlCompareUnicodeString”, which is called internally by “RtlEqualUnicodeString”, we can trick LdrpLoadDll into loading an already loaded dll. Because of the way “GetModuleHandle” works, any subsequent calls will return a handle to the original dll and not our new one.
Now that we can trick the loader into loading new dlls, we can load a new copy of ntdll which will not be hooked. Using the address returned by “LdrLoadDll”, we can call “GetProcAddress” to get a pointer to “RtlCreateUserProcess” within the new dll. Now we can create a new process whilst bypassing any hooks to catch new process creation.
This method can easily be prevented by using a hook within LdrLoadDll. Each time LdrLoadDll is called, check if the name is that of a module we need to hook, if it is, call the original LdrLoadDll, then pass the address returned to the hooking function. Remember, we cannot use GetModuleHandle or equivalent.
|Process with 2 copies of ntdll loaded