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
schrodingers-toctou — Detecta cargas de memoria inventadas por el compilador que convierten C seguro en vulnerabilidades TOCTOU. Incluye auditorías automatizadas del código fuente, análisis binario basado en Unicorn y barridos de compilador/arquitectura/banderas en más de 100 proyectos. | Kitploit
Herramientas/GitHubGitHub/xoreaxeaxeax/schrodingers-toctou
Análisis Dinámico (Sandboxing)Análisis Estático de Código (SAST)Análisis de VulnerabilidadesExplotaciónAnálisis de BinariosAprendizaje y Educación
GitHubxoreaxeaxeax/schrodingers-toctou

schrodingers-toctou

Detecta cargas de memoria inventadas por el compilador que convierten C seguro en vulnerabilidades TOCTOU. Incluye auditorías automatizadas del código fuente, análisis binario basado en Unicorn y barridos de compilador/arquitectura/banderas en más de 100 proyectos.

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 →
Ver Repositorio
94428hace 1 mesRevisado por Kitploit
Compartir

El TOCTOU de Schrödinger

"...la definición de 'compilador sensato' se vuelve cada vez más flexible."

El binario que ejecutas no es el programa que escribiste. El optimizador del compilador reescribe tu código fuente de maneras que nunca ves — y algunos de esos cambios pueden convertir silenciosa y legalmente código aparentemente seguro en binarios vulnerables. La misma línea puede ser segura con un compilador y explotable con otro, sin nada en el código fuente que te indique cuál: una vulnerabilidad mantenida en superposición, que solo se colapsa cuando compilas. Schrödinger's TOCTOU explora cargas inventadas por el compilador y sus amplias implicaciones para las vulnerabilidades de tiempo de verificación a tiempo de uso (TOCTOU) — encontradas en núcleos, hipervisores, enclaves, firmware, y bibliotecas de código abierto. En todas partes, el código aparentemente seguro queda expuesto a los caprichos del compilador. Pero esos son una muestra, no un límite; los mismos errores están muy probablemente también en tu código.

Desafío

Empieza con algo fácil.

¿Cuántas veces carga esta función *p?```c unsigned int g(unsigned short *p) { short t = p; / copy *p into a local for safekeeping */ return (unsigned short)t - t; }

Pista: la respuesta es 1 — el código fuente carga `*p` una sola vez en `t`.

Pégalo en [Compiler Explorer](https://godbolt.org/z/c5K9P4dPd) (`arm gcc 14.2.0`,
`-O2`) y cuenta las cargas desde `r0`, que contiene `p`:```asm
g:
        ldrh    r2, [r0]     # load *p, once
        ldrsh   r0, [r0]     # load *p, twice
        subs    r0, r2, r0
        bx      lr

Una carga en el código fuente, dos en el binario. La segunda es una carga inventada — una lectura que el compilador fabricó y que tú nunca escribiste. Es legal bajo la máquina abstracta de C, que asume que la memoria no puede cambiar entre dos lecturas. Pero cuando esa memoria puede ser escrita por un atacante, la suposición se convierte en una explotación: la carga inventada puede caer después de una comprobación de seguridad, reabriendo silenciosamente una ventana de tiempo de comprobación a tiempo de uso (TOCTOU) que el programador creía haber cerrado. El valor que validaste y el valor que usas ya no se garantiza que sean el mismo — aunque nunca escribiste código que lo volviera a leer.

Un desbordamiento de búfer de la nada

El desafío demuestra que la carga inventada existe; veamos cómo eso se convierte en corrupción de memoria.

En una vulnerabilidad TOCTOU, un programa comprueba que un valor es seguro, y luego usa el valor. Sin embargo, existe una ventana de explotación si un atacante puede cambiar el valor en la fracción de tiempo entre esas dos lecturas – el valor inofensivo pasa la comprobación mientras que el peligroso es el que se usa:```c if (shared->len <= 20) // CHECK reads shared->len // ** attacker modifies shared->len ** memcpy(out, shared->data, shared->len); // USE reads it again: buffer overflow

La solución clásica es **crear una instantánea primero**: copiar cualquier dato que el atacante podría manipular a un lugar local al que el atacante no pueda acceder, y luego no confiar en nada más que en ese local. Una vez que `len` está en una variable local queda congelado — un atacante que compita por la memoria compartida ya no puede tocarlo — por lo que la comprobación y la copia están garantizadas de ver el mismo valor. Así es como el código en `receive` a continuación corrige el TOCTOU: crea una instantánea del mensaje, valida la instantánea y publica la copia validada en `slot` para que un consumidor la reenvíe:```c
#include <string.h>

struct message {
    int  len;          /* payload length */
    char data[20];     /* payload        */
};

struct message slot;   /* the most recently validated message */
char out[20];          /* fixed 20-byte destination           */

void receive(struct message *shared) {
    struct message local = *shared;    /* 1. snapshot untrusted input   */
    if (local.len <= 20)               /* 2. validate the snapshot      */
        slot = local;                  /* 3. publish the validated copy */
}

void forward(void) {                   /* the time of use, later        */
    memcpy(out, slot.data, slot.len);  /* slot.len was checked <= 20 ... right? */
}

Según la fuente, esto es correcto. len se lee exactamente una vez — dentro de la instantánea — por lo que el valor que supera la comprobación <= 20 es el valor publicado en slot. La ventana TOCTOU queda cerrada y el código es seguro.

Excepto que no lo es. Bajo x86-64 gcc con -O2, receive lo lee de la memoria compartida original dos veces: una vez como escalar para la comprobación, y de nuevo como parte de la copia masiva que se publica en slot:```nasm receive: cmp DWORD PTR [rdi], 20 ; READ #1: the CHECK reads shared->len directly movdqu xmm0, XMMWORD PTR [rdi] ; READ #2: the bulk copy re-reads it (len is byte 0) mov rax, QWORD PTR [rdi+16] ; (the bulk copy's tail: struct bytes 16-23) jg .L1 ; len > 20? skip the publish mov QWORD PTR slot[rip+16], rax ; (publish that tail) movaps XMMWORD PTR slot[rip], xmm0 ; and publish the TOCTOU-vulnerable snapshot .L1: ret forward: movsx rdx, DWORD PTR slot[rip] ; copy size = slot.len, the unchecked READ #2 value mov esi, OFFSET FLAT:slot+4 ; src = slot.data mov edi, OFFSET FLAT:out ; dst = out[20] jmp memcpy ; copies slot.len bytes into out[20]

El control se aplica a la LECTURA #1; el valor que llega a `slot.len` es la LECTURA #2. Un atacante que altera `len` entre ambas pasa un valor seguro al control `<= 20` mientras que uno sobredimensionado se publica en `slot` — y `forward` copia entonces esa cantidad de bytes en `out[20]`, el desbordamiento exacto que la instantánea pretendía evitar, reintroducido por el optimizador.
Descargar herramienta