BROP - Blind Return Oriented Programming

Reading time: 7 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Unterstützen Sie HackTricks

Grundinformationen

Das Ziel dieses Angriffs ist es, einen ROP über einen Buffer Overflow auszunutzen, ohne Informationen über die verwundbare Binärdatei zu haben.
Dieser Angriff basiert auf folgendem Szenario:

  • Eine Stack-Sicherheitsanfälligkeit und Wissen darüber, wie man sie auslöst.
  • Eine Serveranwendung, die nach einem Absturz neu gestartet wird.

Angriff

1. Verwundbaren Offset finden durch das Senden eines weiteren Zeichens, bis ein Fehler des Servers erkannt wird

2. Brute-Force Canary um ihn zu leaken

3. Brute-Force gespeicherte RBP- und RIP-Adressen im Stack, um sie zu leaken

Weitere Informationen zu diesen Prozessen finden Sie hier (BF Forked & Threaded Stack Canaries) und hier (BF Adressen im Stack).

4. Stop-Gadget finden

Dieses Gadget ermöglicht es im Wesentlichen zu bestätigen, dass etwas Interessantes durch das ROP-Gadget ausgeführt wurde, da die Ausführung nicht abgestürzt ist. Normalerweise wird dieses Gadget etwas sein, das die Ausführung stoppt und es befindet sich am Ende der ROP-Kette, wenn nach ROP-Gadgets gesucht wird, um zu bestätigen, dass ein bestimmtes ROP-Gadget ausgeführt wurde.

5. BROP-Gadget finden

Diese Technik verwendet das ret2csu Gadget. Und das liegt daran, dass man, wenn man auf dieses Gadget in der Mitte einiger Anweisungen zugreift, Gadgets erhält, um rsi und rdi zu steuern:

https://www.scs.stanford.edu/brop/bittau-brop.pdf

Das wären die Gadgets:

  • pop rsi; pop r15; ret
  • pop rdi; ret

Beachten Sie, wie es mit diesen Gadgets möglich ist, 2 Argumente einer Funktion zu steuern.

Beachten Sie auch, dass das ret2csu-Gadget eine sehr einzigartige Signatur hat, da es 6 Register vom Stack poppt. Daher wird eine Kette wie folgt gesendet:

'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP

Wenn das STOP ausgeführt wird, bedeutet dies im Wesentlichen, dass eine Adresse, die 6 Register vom Stack poppt, verwendet wurde. Oder dass die verwendete Adresse auch eine STOP-Adresse war.

Um diese letzte Option zu entfernen, wird eine neue Kette wie die folgende ausgeführt, und sie darf das STOP-Gadget nicht ausführen, um zu bestätigen, dass das vorherige 6 Register gepoppt hat:

'A' * offset + canary + rbp + ADDR

Wenn die Adresse des ret2csu-Gadgets bekannt ist, ist es möglich, die Adresse der Gadgets zur Steuerung von rsi und rdi abzuleiten.

6. PLT finden

Die PLT-Tabelle kann von 0x400000 oder von der geleakten RIP-Adresse aus dem Stack (wenn PIE verwendet wird) durchsucht werden. Die Einträge der Tabelle sind durch 16B (0x10B) getrennt, und wenn eine Funktion aufgerufen wird, stürzt der Server nicht ab, selbst wenn die Argumente nicht korrekt sind. Außerdem stürzt das Überprüfen der Adresse eines Eintrags in der PLT + 6B ebenfalls nicht ab, da es der erste ausgeführte Code ist.

Daher ist es möglich, die PLT-Tabelle zu finden, indem man die folgenden Verhaltensweisen überprüft:

  • 'A' * offset + canary + rbp + ADDR + STOP -> kein Absturz
  • 'A' * offset + canary + rbp + (ADDR + 0x6) + STOP -> kein Absturz
  • 'A' * offset + canary + rbp + (ADDR + 0x10) + STOP -> kein Absturz

7. strcmp finden

Die strcmp Funktion setzt das Register rdx auf die Länge des zu vergleichenden Strings. Beachten Sie, dass rdx das dritte Argument ist und wir es größer als 0 haben müssen, um später write zu verwenden, um das Programm zu leaken.

Es ist möglich, den Standort von strcmp in der PLT basierend auf seinem Verhalten zu finden, indem man die Tatsache nutzt, dass wir jetzt die 2 ersten Argumente von Funktionen steuern können:

  • strcmp(<non read addr>, <non read addr>) -> Absturz
  • strcmp(<non read addr>, <read addr>) -> Absturz
  • strcmp(<read addr>, <non read addr>) -> Absturz
  • strcmp(<read addr>, <read addr>) -> kein Absturz

Es ist möglich, dies zu überprüfen, indem man jeden Eintrag der PLT-Tabelle aufruft oder den PLT-Slow-Path verwendet, der im Wesentlichen darin besteht, einen Eintrag in der PLT-Tabelle + 0xb (was zu dlresolve aufruft) gefolgt im Stack von der Eintragsnummer, die man testen möchte (beginnend bei null), um alle PLT-Einträge vom ersten an zu scannen:

  • strcmp(<non read addr>, <read addr>) -> Absturz
  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP -> Wird abstürzen
  • strcmp(<read addr>, <non read addr>) -> Absturz
  • b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
  • strcmp(<read addr>, <read addr>) -> kein Absturz
  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

Denken Sie daran, dass:

  • BROP + 0x7 auf pop RSI; pop R15; ret; zeigt
  • BROP + 0x9 auf pop RDI; ret; zeigt
  • PLT + 0xb auf einen Aufruf zu dl_resolve zeigt.

Nachdem strcmp gefunden wurde, ist es möglich, rdx auf einen Wert größer als 0 zu setzen.

tip

Beachten Sie, dass rdx normalerweise bereits einen Wert größer als 0 enthält, sodass dieser Schritt möglicherweise nicht erforderlich ist.

8. Write oder Äquivalentes finden

Schließlich wird ein Gadget benötigt, das Daten exfiltriert, um die Binärdatei zu exfiltrieren. Und zu diesem Zeitpunkt ist es möglich, 2 Argumente zu steuern und rdx größer als 0 zu setzen.

Es gibt 3 gängige Funktionen, die dafür ausgenutzt werden könnten:

  • puts(data)
  • dprintf(fd, data)
  • write(fd, data, len(data)

Das ursprüngliche Papier erwähnt jedoch nur die write-Funktion, also lassen Sie uns darüber sprechen:

Das aktuelle Problem ist, dass wir nicht wissen, wo sich die Write-Funktion innerhalb der PLT befindet und wir wissen nicht, eine fd-Nummer, um die Daten an unseren Socket zu senden.

Wir wissen jedoch, wo sich die PLT-Tabelle befindet, und es ist möglich, write basierend auf seinem Verhalten zu finden. Und wir können mehrere Verbindungen mit dem Server herstellen und eine hohe FD verwenden, in der Hoffnung, dass sie mit einigen unserer Verbindungen übereinstimmt.

Verhaltenssignaturen, um diese Funktionen zu finden:

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP -> Wenn Daten gedruckt werden, wurde puts gefunden
  • 'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> Wenn Daten gedruckt werden, wurde dprintf gefunden
  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> Wenn Daten gedruckt werden, wurde write gefunden

Automatische Ausnutzung

Referenzen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Unterstützen Sie HackTricks