.dtors parece escribible, pero intenta escribir segfault

9

Esto es Ubuntu 9.04, 2.6.28-11-server, 32bit x86


$ cat test.c
main() { int *dt = (int *)0x08049f18; *dt = 1; }
$ readelf -S ./test
...
  [18] .dtors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
...
$ ./test
Segmentation fault
$

Para los no iniciados: gcc crea un segmento destructor .dtors, en el ejecutable elf, que se llama después de las main()salidas. Esta tabla ha sido editable durante mucho tiempo, y parece que debería ser en mi caso (ver readelfsalida). Pero intentar escribir en la tabla causa una falla por defecto.

Me doy cuenta de que ha habido un movimiento hacia los .dtors de solo lectura, plt, que se obtuvieron últimamente, pero lo que no entiendo es la falta de coincidencia entre readelfy la falla predeterminada.

Fixee
fuente
La verdadera pregunta es ¿por qué quieres que se pueda escribir?
alex
1
Estoy enseñando una clase de seguridad que implica romper una serie de programas vulnerables, pero un ejercicio implica escribir en .dtors para ejecutar el código de shell. Ya no funciona y estoy tratando de localizar el problema.
Fixee
La falta de coincidencia se debe a que probablemente haya algunas reubicaciones de datos (que deben arreglarse antes de marcar solo lectura, y de todos modos no pueden ser perezosas, por lo que serán constantes una vez que se arreglen).
ninjalj

Respuestas:

5

Esas secciones están marcadas GNU_RELRO (reubicaciones de solo lectura), lo que significa que tan pronto como el cargador dinámico haya reparado (en el momento de la carga, no hay reubicaciones diferidas allí) todas las reubicaciones, marca esas secciones como de solo lectura. Tenga en cuenta que la mayor parte .got.pltestá en otra página, por lo que no recibe el tratamiento.

Puede ver el script del enlazador con ld --verbose, si busca RELRO encontrará algo similar a:

.got            : { *(.got) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt        : { *(.got.plt) }

lo que significa que las secciones RELRO terminan en 12 bytes .got.plt(los punteros a las funciones del enlazador dinámico ya están resueltos, por lo que pueden marcarse como de solo lectura).

El proyecto Gentoo endurecido tiene documentación sobre RELRO en http://www.gentoo.at/proj/en/hardened/hardened-toolchain.xml#RELRO .

ninjalj
fuente
5

Puedo decir por qué está fallando, aunque en realidad no sé qué parte del sistema es responsable. Si bien .dtorsestá marcado como editable en el binario, parece que (junto con .ctorsel GOT y algunas otras cosas) se están asignando a una página separada y no editable en la memoria. En mi sistema, .dtorsse está poniendo en 0x8049f14:

$ readelf -S test
  [17] .ctors            PROGBITS        08049f0c 000f0c 000008 00  WA  0   0  4
  [18] .dtors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
  [19] .jcr              PROGBITS        08049f1c 000f1c 000004 00  WA  0   0  4
  [20] .dynamic          DYNAMIC         08049f20 000f20 0000d0 08  WA  6   0  4
  [21] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4
  [22] .got.plt          PROGBITS        08049ff4 000ff4 00001c 04  WA  0   0  4
  [23] .data             PROGBITS        0804a010 001010 000008 00  WA  0   0  4
  [24] .bss              NOBITS          0804a018 001018 000008 00  WA  0   0  4

Si ejecuto el ejecutable y compruebo /proc/PID/maps, veo:

08048000-08049000 r-xp 00000000 08:02 163678     /tmp/test
08049000-0804a000 r--p 00000000 08:02 163678     /tmp/test
0804a000-0804b000 rw-p 00001000 08:02 163678     /tmp/test

.data/ .bsstodavía se pueden escribir en su propia página, pero los demás 0x8049000-0x804a000no. Supongo que esta es una característica de seguridad en el kernel (como dijiste, "ha habido un movimiento hacia solo .dtors, plt, últimamente"), pero no sé específicamente cómo se llama (OpenBSD tiene algo muy similar llamado W ^ X ; Linux tiene PaX , pero no está integrado en la mayoría de los núcleos)

Puede solucionarlo con mprotect, lo que le permite cambiar los atributos en memoria de una página:

mprotect((void*)0x8049000, 4096, PROT_WRITE);

Con eso, mi programa de prueba no se bloquea, pero si intento sobrescribir el centinela final de .dtors( 0x8049f18) con la dirección de otra función, esa función aún no se ejecuta; esa parte no puedo entenderlo.

Esperemos que alguien más sepa qué es responsable de hacer que la página sea de solo lectura y por qué la modificación .dtorsno parece hacer nada en mi sistema

Michael Mrozek
fuente
3
Si el OP Linux con PaX mprotectno puede hacer que una página ejecutable se pueda escribir o hacer que una página sea ejecutable antes, a menos que esa característica esté deshabilitada paxctl -m.
Stribika
@stribika Ah, es bueno saberlo
Michael Mrozek