Use-After-Free (UAF)¶
← Volver al Módulo 4¶
Tabla de Contenidos¶
- ¿Qué es Use-After-Free?
- El heap de Windows
- Dangling pointer — mecánica interna
- ¿Por qué es explotable?
- Heap Grooming y Heap Spraying
- UAF con objetos C++ y vtables
- Primitivas de explotación
- Técnicas avanzadas — UAF en navegadores
- Herramientas de detección
- Mitigaciones modernas
- Detección en reversing
- Ejemplo — CVE-2021-40444 (concepto)
- Resumen y Flujo de Explotación
1. ¿Qué es Use-After-Free?¶
Un Use-After-Free (UAF) es una vulnerabilidad de corrupción de memoria que ocurre cuando el código sigue usando un puntero después de que la memoria a la que apunta fue liberada. El puntero en cuestión se convierte en un dangling pointer (puntero colgante): sigue apuntando a la misma dirección, pero esa región de memoria ya no le pertenece al objeto original.
t=1 obj = new Foo(); // aloca en 0x1234, obj → 0x1234
t=2 delete obj; // 0x1234 liberada; obj sigue siendo 0x1234 (dangling)
t=3 bar = new Bar(); // el allocator puede reutilizar 0x1234 para Bar
t=4 obj->method(); // UAF: usa 0x1234 como si fuera Foo, pero contiene Bar
Consecuencias¶
| Consecuencia | Descripción |
|---|---|
| Crash (DoS) | Acceso a datos inválidos → excepción de acceso |
| Information leak | Leer datos del objeto sustituto (contraseñas, punteros, claves) |
| Arbitrary code execution (RCE) | Controlar qué objeto ocupa la memoria liberada y redirigir la ejecución |
Diferencia con otros bugs de heap¶
| Bug | Cuándo ocurre | Efecto principal |
|---|---|---|
| Heap overflow | Al escribir más allá del límite de un chunk | Corrompe el chunk siguiente |
| Double free | Al llamar free()/delete dos veces sobre el mismo puntero |
Corrompe los metadatos del allocator |
| Use-After-Free | Al usar un puntero después de liberar su memoria | Lee/escribe en un objeto sustituto |
2. El heap de Windows¶
Para entender por qué UAF es explotable hay que entender cómo el allocator reutiliza memoria.
NT Heap (heap clásico)¶
Windows provee el NT Heap (HeapAlloc / HeapFree), que organiza la memoria en chunks con cabeceras de metadatos. A grandes rasgos:
Chunk en memoria:
┌──────────────────┐
│ Header (8 B) │ → tamaño, flags, checksum, punteros de lista enlazada
├──────────────────┤
│ User data │ ← lo que recibe el caller de malloc/new
│ (N bytes) │
└──────────────────┘
Cuando se llama a free()/delete, el chunk no se borra — simplemente se inserta en una freelist (lista de chunks disponibles), marcándolo como libre. Los datos del usuario permanecen intactos hasta que otro malloc/new sobrescriba esa región.
Freelists y bins¶
El NT Heap mantiene diferentes listas según el tamaño del chunk:
| Estructura | Descripción |
|---|---|
| Lookaside list | Cache de chunks pequeños de tamaño fijo; altísima velocidad |
| FreeLists[0..127] | Chunks de 8 a 1016 bytes indexados por tamaño |
| FreeLists[0] | Chunks grandes (> 1016 bytes), ordenados por tamaño |
Cuando se pide un nuevo chunk del mismo tamaño que uno recientemente liberado, el allocator lo saca de la freelist correspondiente — la misma dirección vuelve a ser asignada.
Low Fragmentation Heap (LFH)¶
A partir de Windows Vista, el Low Fragmentation Heap (LFH) gestiona asignaciones pequeñas (≤ 16 KB) agrupando chunks del mismo tamaño en "subsegmentos":
- Un subsegmento contiene solo chunks del mismo tamaño (
UserBlocksSize) - Cuando se libera un chunk, el slot queda marcado como disponible dentro del subsegmento
- La reasignación del mismo tamaño muy probablemente reutilizará ese slot
Esto hace que UAF con LFH sea altamente predecible: si se controla cuándo y qué tamaño se asigna después del free, se puede colocar un objeto específico en la dirección exacta del dangling pointer.
Segment Heap (Windows 10+)¶
Windows 10 introdujo el Segment Heap como heap por defecto para algunos procesos (Edge, System processes). Tiene más complejidad y protecciones adicionales (guard pages, randomización de slots dentro de segmentos), pero el principio de reutilización de memoria sigue siendo explotable con el suficiente control.
3. Dangling pointer — mecánica interna¶
Código vulnerable mínimo¶
#include <iostream>
class Foo {
public:
int value;
void print() { std::cout << "Foo::value = " << value << "\n"; }
};
int main() {
Foo* p = new Foo(); // aloca chunk de sizeof(Foo) bytes
p->value = 42;
delete p; // libera el chunk — p sigue apuntando a la misma dirección
// ↓ UAF: el allocator puede haber reasignado ese chunk
p->print(); // comportamiento indefinido — puede crashear o imprimir basura
return 0;
}
Qué hay en memoria tras el delete¶
Justo después del delete p, la región de memoria tiene uno de estos estados:
- Intacta (datos del objeto aún presentes): en algunos allocators, los datos no se borran inmediatamente → leer
p->valuedevuelve42aunque el objeto ya no exista (falsa sensación de seguridad). - Sobreescrita por metadatos del allocator: el allocator puede usar los primeros bytes del user data para almacenar punteros de la freelist.
- Reasignada a otro objeto: si entre el
deletey el acceso UAF hubo otra asignación del mismo tamaño, esa región ahora contiene datos del objeto nuevo.
Antes del delete: Después del delete: Después de new Bar():
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ value = 42 │ │ fwd_ptr │ ← freelist │ Bar data │ ← nuevo objeto
│ ... │ delete() │ bck_ptr │ │ ... │
└─────────────┘ ───────→ └─────────────┘ new Bar() └─────────────┘
0x1234 0x1234 ─────────→ 0x1234
p → 0x1234 p → 0x1234 (dangling) p → 0x1234 (apunta a Bar!)
4. ¿Por qué es explotable?¶
La explotabilidad depende de si el atacante puede controlar:
- El tamaño del objeto que rellena la memoria liberada: para que el allocator reutilice exactamente la misma dirección, el nuevo objeto debe tener el mismo tamaño (o uno que el LFH mapee al mismo bin).
- El contenido del objeto sustituto: si el atacante controla los bytes del nuevo objeto, puede colocar datos arbitrarios donde el dangling pointer espera encontrar la estructura original.
- El timing: el atacante debe lograr que la reasignación ocurra entre el
free()y el acceso UAF.
El vector de ataque más poderoso: vtables¶
En objetos C++ con métodos virtuales, el primer campo del objeto en memoria es el __vptr — un puntero a la vtable de la clase. Cuando se llama a un método virtual, la CPU:
- Lee el
__vptrdel objeto (primeros 4/8 bytes). - Indexa la vtable en el offset correspondiente al método.
- Llama a la función encontrada.
Si el atacante puede controlar los bytes del objeto sustituto, puede colocar un puntero a una vtable falsa en los primeros bytes. La siguiente llamada a un método virtual ejecutará la función apuntada por la vtable falsa → RCE.
Objeto Foo en memoria (con vtable):
┌──────────────────┐
│ __vptr → vtableFoo │ ← 4/8 bytes
├──────────────────┤
│ value = 42 │
└──────────────────┘
Después del UAF (objeto sustituto controlado por atacante):
┌──────────────────┐
│ __vptr → fakeVtable │ ← apunta a datos del atacante
├──────────────────┤
│ ...datos Bar... │
└──────────────────┘
fakeVtable:
┌──────────────────┐
│ ptr → shellcode │ ← al llamar obj->method(), EIP/RIP → shellcode
└──────────────────┘
5. Heap Grooming y Heap Spraying¶
Heap Grooming (shaping)¶
Heap grooming es la técnica de manipular el estado del heap antes del UAF para crear condiciones favorables a la explotación. El objetivo es que la dirección del objeto liberado sea reutilizada exactamente por el objeto controlado por el atacante.
Técnica básica:
1. Realizar múltiples asignaciones del mismo tamaño que el objeto víctima
para "llenar" la freelist y forzar al allocator a asignar memoria contigua.
2. Liberar algunas asignaciones intermedias para crear "huecos" del tamaño correcto.
3. Liberar el objeto víctima (crear el dangling pointer).
4. Asignar el objeto controlado del mismo tamaño → aterriza en el hueco.
5. Usar el dangling pointer → accede al objeto controlado.
// Ejemplo conceptual de grooming
std::vector<Victim*> fillers;
for (int i = 0; i < 100; i++)
fillers.push_back(new Victim()); // llena la freelist con chunks del tamaño correcto
// Liberar algunos para crear huecos del tamaño exacto
delete fillers[50];
delete fillers[51];
Victim* vuln = new Victim(); // probablemente aterriza cerca de los huecos
delete vuln; // crea el dangling pointer
Controlled* ctrl = new Controlled(); // mismo tamaño → ocupa el hueco de vuln
// ctrl está en la misma dirección que vuln → UAF explotable
vuln->virtualMethod(); // usa vtable falsa de ctrl
Heap Spraying¶
Heap spraying es una técnica complementaria: en lugar de ser quirúrgico, se hacen miles de asignaciones del mismo tamaño con datos maliciosos (generalmente shellcode + NOP sled, o una vtable falsa). El objetivo es que la probabilidad de que el dangling pointer apunte a datos controlados sea muy alta.
Heap spraying:
┌────────────────────────────────────────────────┐
│ [payload][payload][payload][payload][payload]... │
│ [payload][payload][payload][payload][payload]... │
│ (miles de copias del mismo payload) │
└────────────────────────────────────────────────┘
↑ La dirección del dangling pointer probablemente cae en uno de estos bloques
Diferencia clave:
| Técnica | Precisión | Complejidad | Ruido |
|---|---|---|---|
| Heap grooming | Alta (quirúrgica) | Alta | Bajo |
| Heap spraying | Baja (probabilística) | Baja | Alto |
En exploits modernos ambas técnicas se combinan: spraying para asegurar que haya datos maliciosos en muchas posiciones, y grooming para aumentar la probabilidad de aterrizar en una de ellas.
6. UAF con objetos C++ y vtables¶
Este es el vector más común en bugs de navegadores, procesadores de documentos y aplicaciones GUI ricas en C++.
Ejemplo completo¶
#include <iostream>
#include <cstring>
class Animal {
public:
virtual void speak() {
std::cout << "Animal speaks\n";
}
virtual ~Animal() {}
char name[16];
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Woof! Name: " << name << "\n";
}
};
// Clase de tamaño similar para el objeto sustituto
class Payload {
public:
void* fake_vptr; // primer campo = __vptr falso
char data[24];
Payload(void* vptr) : fake_vptr(vptr) {
memset(data, 0x41, sizeof(data));
}
};
void uaf_example() {
// 1. Crear objeto víctima
Animal* animal = new Dog();
strncpy(animal->name, "Rex", 4);
// 2. Liberar → dangling pointer
delete animal;
// 3. Grooming: asignar objeto del mismo tamaño en la misma dirección
// (en un exploit real, se itearía hasta lograr la colisión de dirección)
void* fake_vtable[2] = { (void*)0xDEADBEEF, (void*)0xCAFEBABE };
Payload* p = new Payload((void*)fake_vtable);
(void)p; // en el exploit, p ocupa exactamente la dirección de animal
// 4. UAF: animal->speak() lee __vptr = fake_vtable[0] = 0xDEADBEEF → RCE
animal->speak();
}
Diagrama del estado de memoria¶
ANTES del delete:
animal → [__vptr → Dog::vtable | name: "Rex\0" | ...]
DESPUÉS del delete (dangling pointer):
animal → [fwd_ptr | bck_ptr | ...] ← metadatos de freelist
DESPUÉS de new Payload (grooming exitoso, misma dirección):
animal → [fake_vtable_ptr | 0x41414141 | ...] ← Payload ocupa el mismo slot
CUANDO animal->speak() se ejecuta:
1. Lee animal->__vptr → fake_vtable_ptr
2. Indexa vtable[0] → 0xDEADBEEF
3. call 0xDEADBEEF → RCE
7. Primitivas de explotación¶
A partir de un UAF, las primitivas que se pueden construir son:
7.1 Arbitrary Read (lectura arbitraria)¶
Si el objeto sustituto tiene un campo de tipo puntero que el código original desreferencia para leer datos, el atacante puede colocar una dirección arbitraria en ese campo.
// Código vulnerable que lee a través de un puntero interno:
struct Node {
int* data_ptr;
int count;
};
// El código hace: int val = node->data_ptr[0];
// Si node es dangling y el sustituto pone 0xDEAD0000 en data_ptr,
// el código leerá de 0xDEAD0000 → info leak arbitrario
7.2 Arbitrary Write (escritura arbitraria)¶
Similar, pero el código original escribe a través de un puntero del objeto. El atacante coloca una dirección target y un valor deseado en los campos del objeto sustituto.
// Código vulnerable que escribe a través de un puntero:
// node->data_ptr[0] = user_value;
// Si data_ptr apunta a una dirección controlada (ej. &return_address),
// el atacante puede escribir un valor arbitrario en esa dirección.
7.3 Virtual Call Hijacking (vtable hijack)¶
La primitiva más común: redirigir una llamada virtual hacia shellcode o un gadget ROP.
Flujo normal:
obj->method()
→ lee __vptr → vtable legítima
→ llama a Class::method()
Flujo con UAF + vtable hijack:
obj->method()
→ lee __vptr → vtable_falsa (controlada por atacante)
→ llama a attacker_function() → RCE
7.4 Encadenamiento con ROP¶
En entornos con DEP/NX activo, el vtable hijack no puede apuntar directamente a shellcode en el heap. En cambio, se apunta a un gadget ROP que inicia una ROP chain almacenada en el heap spray.
8. Técnicas avanzadas — UAF en navegadores¶
Los navegadores son el terreno más fértil para UAF porque: - Procesan contenido no confiable (HTML, JS, CSS, SVG) - Tienen una superficie de objetos C++ enorme (DOM, layout, JS engine) - Exponen APIs que permiten controlar el heap desde JavaScript
Type confusion via UAF en motores JS¶
Un patrón común en V8/SpiderMonkey:
// 1. Crear objeto y obtener una referencia
let arr = new Float64Array(8);
// 2. Trigger de la vulnerabilidad que libera el backing store de arr
// pero arr sigue siendo accesible desde JS
// 3. El GC o el JIT reutiliza esa memoria para otro objeto JS interno
// 4. Leer/escribir a través de arr ahora accede al objeto interno del engine
// → info leak de punteros heap/stack → rompe ASLR
// → arbitrary write → RCE
Técnica oob (Out-Of-Bounds) + UAF combinados¶
En muchos CVEs reales de Chromium/Firefox: 1. UAF → info leak: leer un puntero interno del engine para conocer la dirección base del heap. 2. UAF → arbitrary write: sobrescribir la longitud de un ArrayBuffer para crear un "super buffer" que puede leer/escribir fuera de sus límites. 3. OOB del super buffer → RCE: sobrescribir una función pointer o el code cache del JIT.
9. Herramientas de detección¶
AddressSanitizer (ASan)¶
Instrumentación a nivel de compilación (Clang/GCC) que detecta UAF, heap overflow, stack overflow, y use-after-return en tiempo de ejecución.
# Compilar con ASan
clang++ -fsanitize=address -g -o vuln vuln.cpp
# Al ejecutar, ASan intercepta cada malloc/free y verifica cada acceso
# Salida al detectar UAF:
# ==1234==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010
# READ of size 4 at 0x602000000010 thread T0
# #0 0x40123f in main vuln.cpp:15
# 0x602000000010 is located 0 bytes inside of 16-byte region
# freed by thread T0 here:
# #0 0x4a1234 in operator delete vuln.cpp:12
Valgrind (Linux)¶
valgrind --tool=memcheck --leak-check=full ./vuln
# Detecta UAF, lecturas/escrituras inválidas, y memory leaks
Application Verifier (Windows)¶
Herramienta de Microsoft que inyecta un heap de validación adicional. Detecta:
- UAF (el heap liberado se llena con bytes 0xEFEFEFEF)
- Double free
- Buffer overflows en el heap
# Habilitar para un ejecutable específico
appverif.exe -enable Heaps -for vuln.exe
# Ejecutar el programa — cualquier acceso a memoria liberada genera una excepción
# visible en WinDbg / x64dbg
WinDbg — PageHeap¶
!gflag +hpa activa el full page heap: cada asignación obtiene su propia página protegida. Al acceder a memoria liberada → excepción inmediata.
# En WinDbg
!gflag +hpa
g # continuar ejecución
# Al ocurrir UAF:
# Access violation - code c0000005
# Breakpoint exacto en la instrucción que accede al dangling pointer
Sanitizers adicionales¶
| Herramienta | Plataforma | Detecta |
|---|---|---|
| MemorySanitizer (MSan) | Linux/Clang | Uso de memoria no inicializada |
| LeakSanitizer (LSan) | Linux/Clang | Memory leaks |
| LLVM libFuzzer | Cross-platform | Fuzzing + ASan integrado |
| Dr. Memory | Windows/Linux | Similar a Valgrind, soporta Windows mejor |
10. Mitigaciones modernas¶
10.1 Puntero a nullptr tras delete¶
La mitigación más básica: anular el puntero inmediatamente después de liberar.
delete obj;
obj = nullptr; // cualquier acceso posterior genera null dereference (detectable)
// Mejor aún: usar un wrapper
template<typename T>
void safe_delete(T*& ptr) {
delete ptr;
ptr = nullptr;
}
Limitación: si hay copias del puntero en otras variables, esas copias siguen siendo dangling.
10.2 Smart pointers¶
std::unique_ptr y std::shared_ptr eliminan la gestión manual del ciclo de vida:
// Con unique_ptr: el objeto se destruye cuando sale de scope, imposible usar después
{
auto obj = std::make_unique<Dog>();
obj->speak();
} // obj destruido aquí, no hay dangling pointer posible
// Con shared_ptr: el objeto se destruye cuando el último shared_ptr es destruido
auto s1 = std::make_shared<Dog>();
auto s2 = s1; // dos referencias, refcount = 2
s1.reset(); // refcount = 1, objeto sigue vivo
s2.reset(); // refcount = 0 → objeto destruido
// No hay forma de acceder al objeto después de esto
10.3 Heap isolation¶
Técnica usada en navegadores modernos (Chrome's PartitionAlloc, Firefox's mozjemalloc): objetos de diferentes tipos se asignan en particiones separadas del heap. Aunque se libere un objeto de tipo A, la memoria no puede ser reutilizada para un objeto de tipo B.
Sin isolación: Con heap isolation:
Heap global: Partition A (Foo objects):
[Foo][Bar][Foo][Bar] [Foo][Foo][Foo][Foo]
Partition B (Bar objects):
Foo liberado → Bar lo ocupa [Bar][Bar][Bar][Bar]
→ UAF explotable Foo liberado → solo otro Foo lo ocupa
→ UAF de clase cruzada imposible
10.4 Delayed free / quarantine¶
El allocator no reutiliza inmediatamente la memoria liberada — la pone en cuarentena durante un tiempo o número de asignaciones. Hace que el grooming sea mucho más difícil.
Implementaciones: - PartitionAlloc (Chrome): quarantine list configurable - jemalloc: delay en la reutilización de páginas - MiMalloc (Microsoft): "secure" mode con randomización de asignaciones
10.5 Safe Unlinking y heap metadata protection¶
El NT Heap moderno (Windows 8+) incluye: - Heap cookie: valor aleatorio en el header del chunk; si se modifica → excepción - Encoding de punteros de freelist: los punteros fwd/bck se XOR con un valor aleatorio por heap → no se pueden predecir ni modificar sin conocer el secret - LFH randomización: el orden de asignación dentro de un subsegmento LFH es aleatorio (no determinista)
10.6 Control Flow Guard (CFG)¶
CFG (Windows 8.1+) valida que cada llamada indirecta (call [reg], call [mem]) apunte a un destino que fue marcado como válido en tiempo de compilación. Un vtable hijack que apunte a un gadget arbitrario fallará si CFG está activo:
; Con CFG, antes de cada call indirecto el compilador inserta:
mov ecx, <target>
call __guard_check_icall_fptr ; valida que target es un destino válido
call ecx ; solo se ejecuta si la validación pasó
Limitación de CFG: solo protege llamadas indirectas marcadas por el compilador. Gadgets dentro de funciones válidas (ROP) no son bloqueados por CFG.
11. Detección en reversing¶
Al analizar un binario buscando UAF en IDA Pro o Ghidra:
Patrones de código sospechoso¶
1. delete/free sin nulificación del puntero¶
; En IDA — patrón de delete seguido de uso posterior sin verificación de nullptr
call operator delete ; libera el objeto
mov eax, [ebp-4] ; carga el puntero (debería ser nullptr aquí)
mov [eax], ecx ; escribe en el puntero → UAF si delete ocurrió
2. Ciclo de vida de objetos no coincidentes¶
Buscás objetos que:
- Son liberados en una función/callback
- Son accedidos en otra función que puede ejecutarse concurrentemente o después
- No tienen ninguna verificación if (ptr != nullptr) antes de usarse
3. Callbacks y event handlers¶
Patrón clásico en aplicaciones GUI / navegadores:
// Objeto registra un callback
button->onClick = [&obj]() { obj.process(); };
// obj se destruye en otra ruta de código
delete &obj; // obj destruido, pero el callback sigue referenciándolo
// Al hacer click → onClick se ejecuta → accede a obj destruido → UAF
4. Shared ownership sin refcounting¶
Múltiples punteros al mismo objeto sin shared_ptr → uno puede hacer delete mientras el otro sigue usando el objeto.
Herramientas en el debugger¶
# En x64dbg: breakpoint en el destructor de la clase sospechosa
bp Foo::~Foo
# Al romperse, inspeccionar qué punteros siguen activos (stack, heap)
# y si alguno es accedido después de que el destructor retorne
# Con WinDbg + PageHeap: el acceso al dangling pointer generará una excepción
# inmediata con la dirección exacta de la instrucción infractora
Señales en el binario¶
- Presencia de
delete thisdentro de un método → el objeto puede ser usado después de su destrucción si el caller no verifica el retorno. - Iteradores sobre colecciones que se modifican durante la iteración → el elemento puede ser liberado mientras el iterador sigue apuntándolo.
- Callbacks registrados con
std::functiono punteros a función que capturanthis→ el objeto puede ser destruido antes de que el callback se ejecute.
12. Ejemplo — CVE-2021-40444 (concepto)¶
CVE-2021-40444 es una vulnerabilidad de ejecución remota de código en MSHTML (el motor de renderizado de Internet Explorer / Edge Legacy) explotada in-the-wild en septiembre de 2021, parcheada en MS21-Sep.
Nota: la vulnerabilidad real involucra una combinación de componentes. Se presenta aquí el concepto simplificado relevante para UAF.
El vector: ActiveX + MSHTML¶
Un archivo Office (.docx) puede contener un objeto OLE que fuerza la carga de una URL remota via MSHTML. El documento malicioso apuntaba a un control ActiveX que explotaba un UAF en el procesamiento de objetos del DOM de MSHTML.
Patrón conceptual del bug¶
// Objeto CDoc (documento MSHTML) con refcount
class CDoc {
int refcount;
void* parser_ctx; // puntero a contexto del parser
public:
void AddRef() { refcount++; }
void Release() { if (--refcount == 0) delete this; }
void Parse(const char* html);
};
// Escenario UAF conceptual:
CDoc* doc = new CDoc();
doc->AddRef(); // refcount = 1
// Durante parsing, una callback de script llama a doc->Release() prematuramente
// refcount → 0 → doc es destruido
// El parser continúa usando doc (dangling pointer) → UAF
doc->parser_ctx; // accede a memoria liberada
Técnica de explotación¶
- Heap spray via JavaScript: se crean miles de objetos del tamaño del
CDoccon una vtable falsa que apunta a shellcode. - La memoria del
CDocliberado es reutilizada por uno de los objetos del spray. - La siguiente llamada a un método virtual del
CDoc→ salta a la vtable falsa → shellcode.
Mitigaciones que hacen esto más difícil hoy¶
- CFG activo en MSHTML: los call indirectos se validan → la vtable falsa debe apuntar a un destino CFG-válido.
- ACG (Arbitrary Code Guard) en Edge: deshabilita la creación de páginas ejecutables en tiempo de ejecución → el shellcode en el heap no se puede ejecutar directamente → requiere ROP.
- Sandbox de procesos: el proceso renderer de Edge/IE está aislado → incluso con RCE en el renderer, se necesita un segundo escape de sandbox para afectar el sistema.
13. Resumen y Flujo de Explotación¶
Flujo completo de explotación de un UAF¶
┌──────────────────────────────────────────┐
│ 1. Identificar el UAF │
│ delete/free sin nulificar │
│ uso posterior del puntero │
└──────────────────┬───────────────────────┘
│
┌──────────────────▼───────────────────────┐
│ 2. Determinar el tamaño del objeto │
│ sizeof(Victim) → bin del LFH │
│ Misma partición de heap? │
└──────────────────┬───────────────────────┘
│
┌──────────────────▼───────────────────────┐
│ 3. Heap Grooming │
│ Llenar el bin con alocaciones │
│ Liberar el objeto víctima │
│ Alocar objeto controlado del mismo │
│ tamaño → ocupa la misma dirección │
└──────────────────┬───────────────────────┘
│
┌──────────────────▼───────────────────────┐
│ 4. Construir objeto sustituto │
│ Vtable falsa / punteros falsos │
│ Aprovechar la primitiva de escritura │
└──────────────────┬───────────────────────┘
│
┌─────────┴─────────┐
│ DEP activo │ DEP inactivo
▼ ▼
┌────────────────┐ ┌──────────────────────┐
│ 5a. Vtable → │ │ 5b. Vtable → │
│ gadget ROP │ │ shellcode directo │
│ → ROP chain │ │ en el heap │
│ → VirtualAlloc │ └──────────────────────┘
│ → shellcode │
└────────┬───────┘
└─────────┬─────────┘
│
┌──────────────────▼───────────────────────┐
│ 6. Activar el UAF │
│ Llamar al método virtual / función │
│ que usa el dangling pointer │
└──────────────────┬───────────────────────┘
│
┌──────────────────▼───────────────────────┐
│ 7. Ejecución de código arbitrario │
│ Shellcode / LoadLibrary / ROP chain │
└──────────────────────────────────────────┘
Checklist rápida al analizar un UAF¶
- ¿Qué clase/tipo es el objeto liberado?
- ¿Qué tamaño tiene el objeto? (para determinar el bin del LFH)
- ¿Tiene métodos virtuales? (vtable hijack posible)
- ¿Hay control sobre asignaciones del mismo tamaño entre el
freey el acceso? - ¿El acceso UAF es de lectura (info leak) o escritura (arbitrary write)?
- ¿Está CFG activo? (afecta la viabilidad del vtable hijack)
- ¿Está DEP activo? (requiere encadenar con ROP)
- ¿Hay heap isolation / PartitionAlloc? (puede requerir objetos del mismo tipo)
Comparativa de UAF vs. otras vulnerabilidades de heap¶
| Característica | UAF | Heap Overflow | Double Free |
|---|---|---|---|
| Origen | Ciclo de vida incorrecto del objeto | Copia sin bounds checking | Lógica incorrecta de free |
| Detectabilidad estática | Media | Alta | Alta |
| Controlabilidad | Alta (grooming) | Media | Baja |
| Primitiva natural | Vtable hijack / type confusion | Adjacent object overwrite | Allocator metadata corruption |
| Mitigación principal | Smart pointers, heap isolation | Bounds checking, safe functions | Nulificar tras free, allocator guards |
Apunte complementario al Módulo 4: Introducción a la Explotación de Vulnerabilidades — Next-Gen Reverse Engineering Training.