¿Qué establece fs: [0x28] (stack canary)?

11

De esta publicación se muestra que FS:[0x28]es un stack-canary. Estoy generando el mismo código usando GCC en esta función,

void foo () {
    char a[500] = {};
    printf("%s", a);
}

Específicamente, estoy obteniendo esta asamblea ...

    0x000006b5      64488b042528.  mov rax, qword fs:[0x28]                ; [0x28:8]=0x1978 ; '(' ; "x\x19"
    0x000006be      488945f8       mov qword [local_8h], rax
...stuff...
    0x00000700      488b45f8       mov rax, qword [local_8h]
    0x00000704      644833042528.  xor rax, qword fs:[0x28]
    0x0000070d      7405           je 0x714
    0x0000070f      e85cfeffff     call sym.imp.__stack_chk_fail           ; void __stack_chk_fail(void)
    ; CODE XREF from 0x0000070d (sym.foo)
    0x00000714      c9             leave
    0x00000715      c3             ret

¿Qué es establecer el valor de fs:[0x28]? El núcleo, o GCC está arrojando el código? ¿Puedes mostrar el código en el kernel o compilarlo en el binario que establece fs:[0x28]? ¿Se regenera el canario en el arranque o engendra proceso? ¿Dónde se documenta esto?

Evan Carroll
fuente

Respuestas:

17

Es fácil realizar un seguimiento de esta inicialización, ya que (casi) cada proceso stracemuestra una llamada sys muy sospechosa durante el comienzo del proceso:

arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0

Eso es lo que man 2 arch_prctldice:

   ARCH_SET_FS
          Set the 64-bit base for the FS register to addr.

Yay, parece que eso es lo que necesitamos. Para encontrar quién llama arch_prctl, busquemos un rastreo:

(gdb) catch syscall arch_prctl
Catchpoint 1 (syscall 'arch_prctl' [158])
(gdb) r
Starting program: <program path>

Catchpoint 1 (call to syscall arch_prctl), 0x00007ffff7dd9cad in init_tls () from /lib64/ld-linux-x86-64.so.2
(gdb) bt
#0  0x00007ffff7dd9cad in init_tls () from /lib64/ld-linux-x86-64.so.2
#1  0x00007ffff7ddd3e3 in dl_main () from /lib64/ld-linux-x86-64.so.2
#2  0x00007ffff7df04c0 in _dl_sysdep_start () from /lib64/ld-linux-x86-64.so.2
#3  0x00007ffff7dda028 in _dl_start () from /lib64/ld-linux-x86-64.so.2
#4  0x00007ffff7dd8fb8 in _start () from /lib64/ld-linux-x86-64.so.2
#5  0x0000000000000001 in ?? ()
#6  0x00007fffffffecef in ?? ()
#7  0x0000000000000000 in ?? ()

Por lo tanto, la base del segmento FS se establece mediante ld-linux, que es parte de glibc, durante la carga del programa (si el programa está vinculado estáticamente, este código se incrusta en el binario). Aquí es donde todo sucede.

Durante el inicio, el cargador inicializa TLS . Esto incluye la asignación de memoria y la configuración del valor base FS para que apunte al comienzo de TLS. Esto se hace a través de arch_prctl syscall . Después de llamar a la security_init función de inicialización TLS , que genera el valor de la protección de la pila y lo escribe en la ubicación de la memoria, que fs:[0x28]apunta a:

Y 0x28es el desplazamiento del stack_guardcampo en la estructura que se encuentra en el inicio de TLS.

Danila Kiver
fuente
zomfg, muy buena respuesta. Estaba tratando de desmontar un binario con radare. Esto tiene la forma y el contenido que estaba buscando. Gracias una tonelada.
Evan Carroll
¿Qué inicializa un proceso con arch_prctl(ARCH_SET_FS..)No veo eso en el ejecutable? ¿Es ese código del núcleo?
Evan Carroll
Vea el enlace "syscall" en la publicación. Lleva al sitio de llamada real ( git.launchpad.net/glibc/tree/sysdeps/x86_64/nptl/tls.h#n153 ) donde se ejecuta la llamada al sistema. Se ejecuta ld-linuxdurante la inicialización de TLS.
Danila Kiver
6

Lo que está viendo se llama (en GCC) Stack Smashing Protector (SSP) , que es una forma de protección de desbordamiento de búfer generada por el compilador. El valor es un número aleatorio generado por el programa al inicio y, como menciona el artículo de Wikipedia, se coloca en Thread Local Storage (TLS) . Otros compiladores pueden usar diferentes estrategias para implementar este tipo de protección.

¿Por qué almacenar el valor en TLS? Como el valor se encuentra allí, los registros CS, DS y SS no pueden acceder a su dirección, lo que hace que adivinar el valor almacenado sea muy difícil si está tratando de alterar la pila del código malicioso.

ErikF
fuente
Esto no es lo que estoy buscando, por lo que he aclarado un poco en un intento de ser claro. "número aleatorio generado por el programa al inicio" ¿puede mostrar en qué parte de un ejecutable se genera y qué pone el código para generarlo?
Evan Carroll