Structured Exception handlers are commonly exploited when building what’s known as a SEH based buffer overflow, this paper deals with a technique which encompasses DLL injection as a means to bypass a commonly found restriction within the exploitability of an SEH overflow. The practically of this technique is not the highest, as it will require the attacking end-user to have enough privileges to inject malicious additional code into another running process. But it’s highly practical in the sense of producing a proof of concept exploit for a SEH exploit that proves that the vulnerability is fully exploitable and won’t require any sort of additional techniques to bypass other means of restriction, e.g. utilizing an egghunter or a multi-jump to escape restricted space. On sites like exploit-db.com and other sites that revolve around posting exploit proof-of-concept code, researchers will find a large amount of “Local SEH overflows DOS” exploits, where an attacker found a vulnerability but could not bypass what this paper aims to bypass. Utilizing the technique that this paper provides, a researcher will never need to post “Local SEH overflow DOS” proofs-of-concept anymore. They will be able to post a fully weaponized exploit proof-of-concept. This technique can also apply to exploit a standard local buffer overflow if the attacker wants to create a proof of concept that proves the full exploitability capabilities of any type of local buffer overflow. They would just need to use this same sequence to implant a JMP ESP or something of the sort.
2. Understanding local SEH overflows
When exploiting a standard SEH overflow via a buffer overflow vulnerability, the goal is to overwrite and take control of the SEH and nSEH handler (the pointer to the next SEH handler on the SEH chain). This is the typical technique used to break out of the SEH chain and to restore execution in means of getting to your shellcode payload. It’s common to use a tool like mona.py to search the program for certain chunks and sequences of code that match up to the order of POP/POP/RETN, which pops the top frame off the stack twice and then returns back. Using a POP/POP/RET is the go-to standard when exploiting a SEH based overflow.
2.a Overwriting the SEH handler
By sending a large amount of data to the vulnerable input field, i.g. Sending 50,000 “A”’s, to the buffer will usually result in the programs SEH / nSEH handler being overwritten with the hex equivalent of A, which would turn the handlers into 41414141. This is how the majority of posted DOS SEH vulnerability are found on exploit-db. When these handlers get overwritten, the program will crash. This is how you identify the vulnerability.
2.b Taking control of the SEH & nSEH handlers
To calculate the size of the offset & the buffer size of the vulnerable input field, an attacker can use one of the tools provided by the Metasploit project, pattern_create.rb, and pattern_offset.rb to send a unique cyclic pattern and to then find where the pattern overwrites the SEH / nSEH handler. By calculating the exact size of the buffers, you can then send 8 bytes twice to take over both the SEH handler and the nSEH handler. If you properly calculate the buffer size, you can write over the handlers.
2.c Running into a restriction
A common restriction to prevent an attacker from fully exploiting a SEH based overflow is to not have any valid and able to be used POP/POP/RET sequence, simply by rendering them all useless by having them all include a null byte in them, that null byte makes it almost impossible to properly exploit the SEH overflow, and if you can’t use the POP/POP/RETs you won’t get any further. When analyzing a program for POP/POP/RETs with mona.py sometimes they will all be pretty much useless for having that null byte in them. As shown below.
And this is where this new technique comes into play, this null byte restriction is bypassable.
DLL injection There are a couple of types of DLL injection, this utilizes the “Runtime” injection method, which is a legitimate Win32API usage, using only Win32API functions we can inject a malicious DLL path into other running processes. DLL injection is a very common behavior of malware, with over 40% of malware having the capability to inject itself or other malicious code into running processes, usually to establish persistence on a system. DLL injection is also commonly found in video game cheating, where a user can inject a cheat menu into the game. There are four main steps to injecting a DLL payload into running processes. The steps are simply to first attach and set up a handler to a running process which will allow us to communicate with it. Then you will allocate space in memory in the host (victim) process that you are injecting. Then, you will inject and copy the malicious DLLs path into that host processes allocated space. And finally calling a function with the address of the LoadLibrary function which causes the injected DLL file’s path to be loaded into memory and executed via the infected process. OpenProcess() is responsible for setting up access to a process object and returning a handle to us as a means of communication with the process we select. VirtualAllocEx() is responsible for allocating memory space in the victim process for the malicious DLLs path to be injected. WriteProcessMemory() is responsible for writing the malicious DLL path to the area of memory that has been allocated with the previous step. LoadLibrary() is a kernel32.dll function which is used to load libraries and DLLs at runtime, We can have LoadLibraryA locate the address of kernel32.dll since kernel32.dll is mapped to the same address in almost every process. LoadLibraryA also happens to fit the thread start routine needed by CreateRemoteThread(). From a malware author’s perspective, LoadLibrary registers DLLs with the process, making LoadLibrary easily detected, so an alternative is to load the DLL from memory with something like a reflective DLL attack. Calling CreateRemoteThread() and passing it the address of LoadLibrary causes the injected path of the malicious DLL to be loaded into memory and executed. Alternatives to CreateRemoteThread are calling NtCreateThreadEx or RtlCreateUserThread. From a malware author’s point of view, CreateRemoteThread is highly tracked and flagged by common AV products. This function method also requires a malicious DLL on disk, which is much easier to detect than some alternate DLL injection methods where the DLL is loaded from memory.
4. Bypassing a null byte POP/POP/RET
We can bypass the null byte POP POP RET restriction by simple injecting our own POP POP RET from a module of our choice. And without any sort of memory restrictions, we can use this injected module as our module to get a POP POP RET sequence from. What you can do is load up the vulnerable process, inject our new and custom DLL into the process (THIS WILL REQUIRE INJECTION PERMISSIONS). And then use a POP POP RET from our new DLL.
And thus we can bypass the null byte instruction in the POP POP RET sequence.
7. Proof-Of-Concept exploit
A Proof of concept code can be found at: https://github.com/FULLSHADE/POPPOPRET-nullbyte-DLL-bypass/blob/master/ Nullbyte_PPR_DLL-injection_bypass.py. This exploit will pop a calculator through the vulnerable program. This exploit is taken advantage of a vulnerable input field in the SurfOffline Professional program. It’s vulnerable to a local SEH based overflow. And the injected DLL is “essfunc.dll” from the vulnserver exploit development series. The exploit also is using the alphanumeric encoder from msfvenom via the syntax ‘msfvenom -p windows/exec CMD=calc.exe -b “\x00\x0a\x0d\x0e” -e x86/alpha_mixed -f python -v shellcode EXITFUNC=seh’.
An output payload file is created since this is exploiting a local SEH based buffer overflow, within the output file, is a POC exploit for hijacking the SEH handler and using a (special) POP POP RET to escape the next SEH handler in the SEH chain. You need a process PID to inject a DLL into a process, the PID is automatically discovered via the process_injection() function using the psutil Python library for PID discovery. The drop_DLL_disk() function is called which decodes and drops a BASE64 encoded payload DLL, in this case, it’s originally essfunc.dll from vulnserver. After dropping the DLL payload to disk, the DLL is injected into the vulnerable process via the automatically discovered PID. After the new DLL is injected into the vulnerable running process (SurfOffline Professional), the attacker can exploit the vulnerable input field in the “New project” creation tool in SurfOffline Professional. File > New Program > Project Name > OK. This will use the new POP POP RET from the injected DLL to pop a calc.exe.
8. Conclusion & Sources
Conclusively, this whitepaper covers a new technique that utilizes DLL injection to inject a custom DLL into a running vulnerable process to add a POP POP RET sequence in the scenario that the vulnerable program doesn’t include any null byte free sequences. If the vulnerable program doesn’t have a POP POP RET that you can use, you can use DLL injection to add your own POP POP RET sequence via loading a module. Using this technique you won’t need to stop at posting DOS overflow POC’s on exploit-db, you can use DLL injection to add your own POP POP RET and then prove the full exploitability capabilities of the vulnerable program. Full exploitability meaning you can prove if the exploit would need to use something like an egghunter or a multi-staged jump to escape restricted buffer space after applying the POP POP RET sequence from your injected module. As a reminder, this will require the attacker to have enough privileges to conduct DLL injection.