.dtors semble accessible en écriture, mais tente d'écrire segfault

9

Il s'agit d'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
$

Pour les non initiés: gcc crée un segment destructeur,, .dtorsdans l'exécutable elf, qui est appelé après les main()sorties. Ce tableau est accessible en écriture depuis longtemps, et il semble qu'il devrait l'être dans mon cas (voir la readelfsortie). Mais tenter d'écrire dans la table provoque une erreur de segmentation.

Je me rends compte qu'il y a eu un mouvement vers les .dtors, plt, en lecture seule récemment, mais ce que je ne comprends pas, c'est l'inadéquation entre readelfet la faute de segmentation.

Fixee
la source
La vraie question est pourquoi voulez-vous qu'il soit accessible en écriture?
alex
1
J'enseigne un cours de sécurité qui consiste à interrompre une série de programmes vulnérables, mais un exercice consiste à écrire sur .dtors pour exécuter le shellcode. Cela ne fonctionne plus et j'essaie de retrouver le problème.
Fixee
La non-concordance est due au fait qu'il existe probablement des délocalisations de données (qui doivent être corrigées avant de marquer en lecture seule, et ne peuvent pas être paresseuses de toute façon, elles seront donc constantes une fois corrigées).
ninjalj

Réponses:

5

Ces sections sont marquées GNU_RELRO (relocations en lecture seule), ce qui signifie que dès que le chargeur dynamique a corrigé (au moment du chargement, il n'y a pas de relocalisation paresseuse) toutes les délocalisations, il marque ces sections en lecture seule. Notez que la plupart se .got.plttrouve sur une autre page, donc ne reçoit pas le traitement.

Vous pouvez voir le script de l'éditeur de liens avec ld --verbose, si vous recherchez RELRO, vous trouverez quelque chose de similaire à:

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

ce qui signifie que les sections RELRO se terminent sur 12 octets .got.plt(les pointeurs vers les fonctions de l'éditeur de liens dynamiques sont déjà résolus, ils peuvent donc être marqués en lecture seule).

Le projet Gentoo durci contient de la documentation sur RELRO à http://www.gentoo.at/proj/en/hardened/hardened-toolchain.xml#RELRO .

ninjalj
la source
5

Je peux dire pourquoi cela échoue, même si je ne sais pas vraiment quelle partie du système est responsable. Bien qu'il .dtorssoit marqué en écriture dans le binaire, il semble qu'il (avec .ctorsle GOT et quelques autres choses) soit mappé dans une page séparée et non inscriptible en mémoire. Sur mon système, .dtorsse met à 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 je lance l'exécutable et vérifie /proc/PID/maps, je vois:

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/ .bsssont toujours inscriptibles dans leur propre page, mais les autres 0x8049000-0x804a000ne le sont pas. Je suppose que c'est une fonctionnalité de sécurité dans le noyau (comme vous l'avez dit, "il y a eu un mouvement vers readonly .dtors, plt, got dernièrement"), mais je ne sais pas précisément comment il s'appelle (OpenBSD a quelque chose de très similaire appelé W ^ X ; Linux a PaX , mais n'est pas intégré à la plupart des noyaux)

Vous pouvez le contourner avec mprotect, ce qui vous permet de modifier les attributs en mémoire d'une page:

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

Avec cela, mon programme de test ne plante pas, mais si j'essaie d'écraser la sentinelle de fin de .dtors( 0x8049f18) avec l'adresse d'une autre fonction, cette fonction ne s'exécute toujours pas; cette partie que je ne peux pas comprendre.

J'espère que quelqu'un d'autre sait ce qui est responsable de rendre la page en lecture seule, et pourquoi la modification .dtorsne semble rien faire sur mon système

Michael Mrozek
la source
3
Si l'OP Linux avec PaX mprotectne peut pas rendre une page exécutable accessible en écriture ou rendre une page exécutable qui était accessible en écriture avant, sauf si cette fonctionnalité est désactivée avec paxctl -m.
stribika
@stribika Ah, bon à savoir
Michael Mrozek