A Study in Exploit Development: Easychat SEH exploit
A typical penetration test involves automated compliance scanning to identify vulnerabilities, followed by a more manual testing process where the tester attempts to validate and exploit those vulnerabilities. Many times, we discover vulnerabilities with publicly available exploits. This can sometimes result in a complete domain compromise. Other times, we’ll find vulnerable software on a client’s network, but there won’t be any known exploits available. This can be frustrating because as a penetration tester, I know that with enough time and resources, a motivated attacker may be able to write their own exploit and take advantage of this vulnerability, but exploit development is not typically an easy or quick process. It’s a very different skill set than the typical skills used during a network penetration test. I’ve been wanting to learn and hone these skills to hopefully assist with future penetration tests where the pickings are slim and our only hope is to write an exploit from scratch.
How to learn exploit development for future penetration tests
Here are the steps covered outlining the steps I took for the EasyChat SEH exploit development:
- Set up the lab
- Verify the vulnerability
- Structured Exception Handling
- Proof of concept – using Python for exploit development
I know how basic stack overflows work, and I had to write one as part of getting the OSCP cert, but I wanted something a bit more challenging. I started looking for tutorials and exercises to learn more about this craft, but they all seemed boring to me. I wanted to write an exploit for real software. That’s when I turned to cve.mitre.org. This website lists all kinds of known vulnerabilities and includes helpful links for more information. I started with the most recent and scrolled backward. I was looking for an overflow vulnerability that could lead to remote code execution (RCE). I also wanted it to be a simple piece of software rather than something like a Windows service, so it would be easier to debug and understand. Eventually, I found a reported vulnerability for something called EasyChat.
Someone had done the hard part for me. They discovered that by registering a new user with an overly long username, you could overflow a buffer on the stack and obtain remote code execution. The bulletin also mentioned something called SEH, which I didn’t understand at the time. Someone had also already developed a working exploit, but I opted not to look at it, so I could learn on my own. I looked up the software and found that it was a simple HTTP server that functioned as a sort of chat room or forum. I figured this would be a perfect platform to learn on.
1. Setting up the lab
I decided I would try to get this exploit working on Windows Server 2012 R2. I set up a 2012 R2 virtual machine, and downloaded EasyChat from their main website. To my surprise, the current version offered on the website was the vulnerable version. It does not look like this has been patched, even though an exploit was released several months prior. I installed EasyChat and verified that it worked properly.
Next, I had to choose a debugger. Being somewhat new to this, I wanted to choose something that would be easier to work with. Many people like windbg but I did not want this project to be about me learning to use a debugger. I wanted to jump right in to the fun stuff. I therefore chose immunity debugger, as I had used it before and was somewhat familiar with its idiosyncrasies.
2. Verify the vulnerability
With the basic lab environment set up, I had to verify the vulnerability. When I loaded up EasyChat in my browser, I was presented with this home screen:
I clicked the “register” link and was presented with a new window:
The bulletin posted on mitre.org said that the overflow occurred when registering an overly long username. I just copied and pasted a whole bunch of A’s into the username field and registered an account to see what would happen. It actually registered the username successfully. I looked at the code for the form and found that it was limiting the username to 30 characters. That explained it.
I proxied my browser through Burp Suite and captured the request. Then I sent it to the repeater and modified it to include an extra-long username.
I submitted this to the website. Normally when you register a new account, the server replies with either a “congratulations” message, or a “username already exists” message. In this case, I received no response from the HTTP server. I went back to my 2012 machine and found this:
That was a good sign. I obviously had crashed the server. It looked like this finding was legitimate. Now it was time to start developing an actual exploit.
3. Structured Exception Handling
The security bulletin mentioned that this was a “stack-based buffer overflow (SEH)”. I didn’t know what SEH meant, so I had to do a bit of research. SEH stands for “structed exception handling,” and is specific to Windows operating systems. It is the method Windows uses to handle exceptions in programs.
When something unexpected happens in your software, you can program an exception handler to deal with it. If the handler cannot figure out what to do with it, it will get passed onto another handler. If that handler cannot figure it out, it gets passed on again. This process continues until the exception is passed to the last handler in the chain, which belongs to the Windows OS. At that point, the program just crashes. All these handlers form an SEH chain.
Each entry in the chain is written somewhere on the stack, and each entry contains two items. The first item is a pointer to the handler code. When an exception occurs, the CPU executes the code located at this pointer. The second item is a pointer to a location on the stack containing the next link in the SEH chain. If the first handler is unable to process the exception, it will follow that pointer (Next SEH, or NSEH), and continue down the chain. I learned pretty much everything about this process (and the exploitation of SEH) from this whitepaper.
Knowing how SEH works, how could I exploit it? I knew that if an exception occurs, the CPU will look at the SEH pointer and execute code in that location. Therefore, if I could overwrite the SEH pointer to point to my shellcode, then all I had to do was trigger an exception to get the shellcode to execute. Alternatively, I could overwrite NSEH to point to a pointer to my shellcode.
4. Proof of Concept: Using Python for exploit development
The first thing to do was write a proof of concept. The goal was to write some exploit code that would overwrite NSEH and SEH with a controlled value. That way I would know this exploit path is viable.
To do this, I wrote a fuzzer script in Python to make HTTP request for me and submit a bunch of junk data. That way I could more easily alter the request to include malicious code.
This is the fuzzer script I developed:
#!/usr/bin/python # coding: utf-8 import requests OVERFLOW = "A" * 2000 easychat_url="https://192.168.1.7/registresult.htm" payload = {'UserName': OVERFLOW, 'Password': 'AAAAA', 'Password1': 'AAAAA', 'Sex': '2', 'Email': '@', 'Icon': '0.gif', 'Resume': '', 'cw': '1', 'RoomID': '<!--$RoomID-->', 'RepUserName': '<!--$UserName-->', 'submit1': 'Register'} # Make the HTTP request r = requests.post(url=easychat_url, data=payload) # Print the HTTP response print r.text
This script is very simple. It creates a variable called OVERFLOW containing 2000 A’s. Then it submits the user registration request to EasyChat, putting the OVERFLOW value in the username parameter.
I fired up Immunity debugger on the Windows box and launched EasyChat. I let the program run normally within the debugger. I switched back to my attacking machine and launched the fuzzer script, then switched back to the debugger. Bingo.
At the bottom of the screen, there was an access violation “when reading [41414141]”. This is a good sign. 0x41 is the hexadecimal equivalent for the letter A. That means that the system likely attempted to read memory at location 0x41414141, which does not exist. I overwrote something important.
I pressed alt + s to inspect the SEH chain and I was greeted with this:
That is what I wanted to see. The memory address 0x072A6E34 contains a pointer to the SE handler. In this case, that pointer is 0x41414141 (AAAA). That means I was able to overwrite the handler address! Note that the next handler in the chain is supposedly at address 0x41414141 and Immunity says, “CORRUPT ENTRY.” That is because I also overwrote the NSEH pointer with AAAA.
Now I just needed to figure out how to overwrite the addresses with a specific value. I knew that by sending 2000 A’s, I could overwrite it with four A’s. But where in that long string of A’s is the actual handler address overwritten? Metasploit comes with a nice tool called pattern_create.rb to help figure this out.
I entered the following command:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2000
This generated a 2000-character string that had no repeating patterns. I modified the fuzzer.py script so that instead of sending 2000 A’s, it filled OVERFLOW with this special string instead. I restarted EasyChat in the debugger, and then ran the fuzzer script again. When the program crashed, I viewed the SEH chain again.
This time I could see that the SE handler was overwritten by 0x68413368. Now I just needed to figure out where that pattern was in that long string of 2000 characters. Metasploit comes with another tool to help figure that out.
# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 68413368 [*] Exact match at offset 221
Perfect. The script said that my pattern begins 221 bytes into my OVERFLOW buffer. That means NSEH would be located at 217 bytes into the buffer, since it comes right before the SEH address.
Time to adjust the fuzzer script to test this theory. I removed the long pattern and replaced it with the following code:
nseh = "NNNN" seh = "SSSS" OVERFLOW = ("A" * 217) + nseh + seh + ("B" * 500)
This should submit 217 A’s, followed by a nseh value of NNNN, followed up an SEH value of SSSS, and then 500 B’s. If all goes according to plan, I should see SSSS as the SEH address in the chain, with NNNN as the NSEH value.
Perfect! 0x53 is S and 0x4E is N. That means I overwrote exactly what I wanted to. I double-checked this by looking in the stack view at the address 0x07186E34:
This verified that NSEH and SEH were both overwritten correctly. There are A’s before and B’s after. This is exactly what I wanted. Now I just needed to figure out how to take advantage of this to execute some shellcode.
The easiest thing to do would be to overwrite the SEH address to an address containing my shellcode. In this case, that would have to be an address on the stack somewhere within my buffer of A’s. Unfortunately, this was not going to work in my case because the stack memory addresses change each time the EasyChat program is run. This means if I hardcoded an address into my buffer, it would not work more than the one time.
Luckily, there is a tricky method to salvage this exploit. Come back soon for Part 2 of this blog to see how I took this exploit to the next level! If you liked this blog, I bet you’d be interested in our blog post and webinar on how to think like a hacker. Check it out! Our SecOps Automation and Security services and FedRAMP compliance automation keeps your enterprise and cloud apps protected from exploits.