vendredi 14 août 2009

Désactiver les protections sous Linux pour pratiquer le développement d'exploit


Je suis actuellement à parfaire mes connaissances sur le développement d'exploit pour utiliser des vulnérabilités de type buffer overflow. Armé de ma bible (En rétrospective The Shellcoder Handbook n'est pas un très bon livre pour apprendre l'exploitation, la progression est erratique et des techniques essentielles aujourd'hui comme ROP n'y sont pas présentées. Pour commener je recommande les tutoriels de Corelan Team, les outils de reversing sous Windows sont plus évoluées ce qui rend aussi l'apprentissage plus simple), je tente actuellement de mettre en pratique les différentes techniques (stack overflow, heap overflow, format strings bug, GOT rewriting etc.) en complétant les exercices Insecure Programming by example de gera.

Linux est la plateforme idéale pour pratiquer ces techniques puisque le système est bien documenté, le code source est accessible et de nombreux outils sont disponibles. Toutefois, les distributions récentes sont livrées avec plusieurs protections qui complexifient le développement d'exploit. Voici donc une liste de commandes pratiques pour désactiver ces protections.

Réglages du système
  • Activer les coredumps : ulimit -c unlimited
  • Désactiver l'ASLR : Ajouter kernel.randomize_va_space = 0 à /etc/sysctl.conf
  • Désactiver la détection de heap overflow : export MALLOC_CHECK_=0
Compiler avec les options suivantes :
  • -ggdb : activer le support pour le déboggeur gdb
  • -mpreferred-stack-boundary=2 : Évite que gcc aligne les variables différemment sur la stack
  • -fno-stack-protector : Désactive la protection de la stack avec ProPolice (activé sur Gentoo et Ubuntu)
Executables
  • Rendre la stack executable : execstack -s

Évidemment, je vous recommande fortement de pratiquer sur une machine virtuelle ou une machine dédiée puisque le fait de désactiver les protections rend l'ensemble des programmes vulnérables.

Merci à Phil pour la méthode pour désactiver ProPolice.

vendredi 7 août 2009

Exploitation via DTORS and mitigating factors in GCC 4.4

The rewriting of the DTORS section is a fairly known technique to exploit a program compiled with GCC. Described shortly, the DTORS section makes it possible to call function after the execution of a program. For example, adding the following code will have the effect of calling the stop() function after the execution of the main() function :

void stop() __attribute__ ((destructor));

void stop() {
printf("THE END\n");
}

The list of of functions to be called at the end of the program are written in the DTORS section, which is writable and thus can be used to redirect execution. For more details concerning this technique, consult the following article.

While trying to use this technique I found that it wasn't working if there where no destructor functions in the original code (contrarily to what is said in the first edition of The Art of Exploitation and in the article cited above). A look at the assembly code explains why :

First, the nm command gives us the location of the DTORS section.

$ nm no_dtors
080496a8 D __DTOR_END__
080496a4 d __DTOR_LIST__

Next, we look at the content of the __do_global_dtors which is handling the task of calling the destructors. We obtain the code with the objdump -d no_dtors command.

08048440 <__do_global_dtors_aux>:
8048440: 55 push %ebp
8048441: 89 e5 mov %esp,%ebp
8048443: 53 push %ebx
8048444: 83 ec 04 sub $0x4,%esp
8048447: 80 3d bc 97 04 08 00 cmpb $0x0,0x80497bc
804844e: 75 3f jne 804848f <__do_global_dtors_aux+0x4f>
8048450: a1 c0 97 04 08 mov 0x80497c0,%eax
8048455: bb a8 96 04 08 mov $0x80496a8,%ebx
804845a: 81 eb a4 96 04 08 sub $0x80496a4,%ebx

8048460: c1 fb 02 sar $0x2,%ebx
8048463: 83 eb 01 sub $0x1,%ebx
8048466: 39 d8 cmp %ebx,%eax
8048468: 73 1e jae 8048488 <__do_global_dtors_aux+0x48>
804846a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
8048470: 83 c0 01 add $0x1,%eax
8048473: a3 c0 97 04 08 mov %eax,0x80497c0
8048478: ff 14 85 a4 96 04 08 call *0x80496a4(,%eax,4)
804847f: a1 c0 97 04 08 mov 0x80497c0,%eax
8048484: 39 d8 cmp %ebx,%eax
8048486: 72 e8 jb 8048470 <__do_global_dtors_aux+0x30>
8048488: c6 05 bc 97 04 08 01 movb $0x1,0x80497bc
804848f: 83 c4 04 add $0x4,%esp
8048492: 5b pop %ebx
8048493: 5d pop %ebp
8048494: c3 ret
8048495: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
8048499: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi

The code excerpt is fairly long so I put the interesting part in blue. We can see that the adresses of the beginning and the end of the DTORS section (in red) are directly in the code. In fact, the beginning address is subtracted to the end address to determine the length of the DTORS section. The next instructions (sar and sub) are used to check if the length of the section is smaller or equal to 4 bytes. If it's the case then the execution jumps to the adress 0x08048488 and the content of the DTORS section is never read (the call instruction in red is never reached).

The program used as an example didn't have any destructor so it wasn't exploitable. If we add a destructor to the code, we obtain the following result :

8048455: bb ac 96 04 08 mov $0x80496ac,%ebx
804845a: 81 eb a4 96 04 08 sub $0x80496a4,%ebx

This time, the length of the DTORS section is 8 bytes and the addresses that it contains (in this case just one) are called.

I think that the behaviour generated by GCC was changed in the recent versions to limit the risks of DTORS exploitation, since most programs don't have destructors. I didn't look at GCC's source code though.

Exploitation via DTORS et facteur de mitigation dans gcc 4.4

La réécriture de la table DTORS est une technique connue pour exploiter un programme compilé avec GCC. En gros, DTORS permet d'appeler des fonctions à la fin du programme. Par exemple en ajoutant le code suivant, la fonction stop() sera appelée après l'exécution de la fonction main :

void stop() __attribute__ ((destructor));

void stop() {
printf("THE END\n");
}

La liste des fonctions à appeler après l'exécution du programme est placée dans la section DTORS, qui est accessible en écriture et qui peut donc être réécrite. Pour plus de détails sur cette technique, consulter cet article.

En tentant de mettre cette technique en œuvre, j'ai découvert que cela ne fonctionnait pas lorsqu'aucune fonction n'avait été déclarée comme destructor dans le code (contrairement à ce qui est dit dans la première édition de The Art of Exploitation et dans l'article cité précédemment). Une vérification du code assembleur nous explique pourquoi.

La commande nm nous permet de connaitre l'emplacement de la section DTORS :

$ nm no_dtors
080496a8 D __DTOR_END__
080496a4 d __DTOR_LIST__

Maintenant, examinons le contenu de la fonction __do_global_dtors_aux qui s'occupe d'appeler les destructeurs obtenu grâce à la commande objdump -d no_dtors.

08048440 <__do_global_dtors_aux>:
8048440: 55 push %ebp
8048441: 89 e5 mov %esp,%ebp
8048443: 53 push %ebx
8048444: 83 ec 04 sub $0x4,%esp
8048447: 80 3d bc 97 04 08 00 cmpb $0x0,0x80497bc
804844e: 75 3f jne 804848f <__do_global_dtors_aux+0x4f>
8048450: a1 c0 97 04 08 mov 0x80497c0,%eax
8048455: bb a8 96 04 08 mov $0x80496a8,%ebx
804845a: 81 eb a4 96 04 08 sub $0x80496a4,%ebx

8048460: c1 fb 02 sar $0x2,%ebx
8048463: 83 eb 01 sub $0x1,%ebx
8048466: 39 d8 cmp %ebx,%eax
8048468: 73 1e jae 8048488 <__do_global_dtors_aux+0x48>
804846a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
8048470: 83 c0 01 add $0x1,%eax
8048473: a3 c0 97 04 08 mov %eax,0x80497c0
8048478: ff 14 85 a4 96 04 08 call *0x80496a4(,%eax,4)
804847f: a1 c0 97 04 08 mov 0x80497c0,%eax
8048484: 39 d8 cmp %ebx,%eax
8048486: 72 e8 jb 8048470 <__do_global_dtors_aux+0x30>
8048488: c6 05 bc 97 04 08 01 movb $0x1,0x80497bc
804848f: 83 c4 04 add $0x4,%esp
8048492: 5b pop %ebx
8048493: 5d pop %ebp
8048494: c3 ret
8048495: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
8048499: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi

Le code est assez long, j'ai mis la section intéressante en bleu. On remarque que les adresses de début et de fin de DTORS sont manipulées directement. En fait, l'adresse de début est soustraite à l'adresse de fin pour déterminer la longueur de la section. Les opérations suivantes (sar et sub) servent en fait à déterminer si la longueur de la section est inférieure ou égale à 4 octets. Si tel est le cas, l'exécution saute à 0x08048488 ce qui fait que le contenu de la section n'est pas inspectée et il n'y a pas d'appel fonctions (via le call en rouge).

Le programme utilisé comme exemple n'avait pas de fonction destructor est n'est donc pas exploitable. Si nous ajoutons une fonction destructor au code, nous obtenons le résultat suivant :

8048455: bb ac 96 04 08 mov $0x80496ac,%ebx
804845a: 81 eb a4 96 04 08 sub $0x80496a4,%ebx

Dans ce cas, la longueur de la section est 8 octets ce qui fait que les adresses qui s'y trouve sont appelées.

J'ai l'impression que le comportement généré par GCC a été changée dans les dernières versions pour limiter les possibilités d'exploitation, la plupart des programmes ne faisant pas appel à des fonctions destructors. Je n'ai par contre pas vérifié l'historique du code source de GCC pour en être sûr.