type
status
date
summary
tags
category
icon
password
💡
续之前在语雀的文章

2025NepCTF ASTRAY

这道题逆向难度挺大的,本人复现的时候也是花了将近半天才把程序的整个逻辑和结构搞明白
checksec保护全开

分析

main函数

init函数

首先malloc了一个超级大的堆块chunk1,以malloc过来的地址为起始,每隔256字节为一个地址,按顺序存放在位于bss段的manage_physic数组中(相当于把这个大堆块分割成等大的小堆块);dword_4068数组下标1-9元素为2,10-19为3,0下标对应16;qword_41a8处存放了新malloc的chunk2地址并对chunk2作了初始化;chunk1往后16字节处存放onlyuser的地址,往后8字节处存放再次malloc的chunk3地址,并对chunk3作初始化,chunk1[0]变为1。

manager_operation函数

check函数

permission_confirm函数

checkvisit函数

user_operation函数

函数比较多,文字解释不如图形象生动👇:
notion image
💡
  • MANAGER 操作
    • MANAGER_visit(opcode1=8)
      • 📖 读取:opcode1=6 → user_chunk →choose_chunk
      • 📝 写入:opcode1=3 → user_chunk →choose_chunk
    • MANAGER_read(opcode1=4):📖 从 manager_chunk中choose_chunk指向位置读取
    • MANAGER_write(opcode1=2):📝 从 manager_chunk中choose_chunk指向位置写入
  • UER 操作
    • UER_read(opcode2=4):📖 从 user_chunk中choose_chunk指向位置 读取
    • UER_write(opcode2=1):📝 从 user_chunk中choose_chunk指向位置写入
其中check函数中MANAGER_write和USER_write操作会进行权限检查,MANAGER_write操作要求chunk2+8的地方为2,USER_chunk要求chunk3+8的地方为3。

解题思路

  • 无论是manager操作还是user操作,都没有对数组下标进行检查,也就是说若输入堆序列为0,程序并不会报错,而manager_read是不会进行权限检查的,利用manager_read可以直接泄露出chunk1中的&manager_chunk和&onlyuser,相当于泄露了堆地址和pie基地址。
 
  • 从图中可以看出,下标为0的chunk(这里叫做key_chunk)是个关键,因为这里存放着可以控制写入的地址,一旦&manager_chunk和&onlyuser的值被篡改就会造成任意地址写,而二者定是要通过write功能修改,manager_write要求chunk1+8处为2,但是对于key_chunk,chunk1+8为最为特殊的值16,不可行;user_write同理;那么唯一的路径就是通过manager_visit中的write功能了。从manager功能入手,check函数manager_visit会有这么一个检查:!strcmp(a2, "MANAGER_visit") && !*(_QWORD *)qword_41A8 ,也就是说chunk3中的&choose_chunk必须存在,要先进入user功能进行操作,checkvisit函数要求opcode2不能为4(不能读操作),user功能也允许manager操作,所以先进入user选择user_write返回opcode2为2,再用user功能选择manager_visit返回opcode2=8,然后利用manager功能选择manager_visit_write,就可绕过检查实现往key_chunk的写入。
 
  • 修改了&onlyuser使得manager_visit_write写入操作具有局限性,故可利用两次机会修改dword_4068[0]中的16,更改后的值&2返回值非零,这样便可无限利用manager_write了。第一次往key_chunk写入,这里存在多级指针&onlyuser+8→&chunk3→&choose_chunk→写入的地方 ,于是往key_chunk写入的时候,前16字节填充,从&onlyuser开始,依次改为p64(&chunk1+0x18)+p64(&chunk1+0x28)+p64(dword_4068的地址),逻辑即为&chunk1+0x18+0x8处指向&chunk1+0x28处,&chunk1+0x28处又指向dword_4068。第二次manager_visit_write会根据&chunk1+0x18处的值,即往dword_4068写入,这里将16改为0xf。
 
  • 理解上一步之后后面就比较轻松了,利用manager_write修改&chunk1+0x18指向puts_got,利用manager_visit_read泄露libc。因为这题不能覆写got表因此想到返回地址的修改,返回地址的修改依靠栈地址的泄露,于是想到用libc找到environ的真实地址,然后再次manager_write往key_chunk写入修改&chunk1+0x18指向environ,manager_visit_read泄露出栈地址。
关于environ:
  • environ 通常指代 C 标准库中的全局变量 _environ,它指向程序的环境变量数组。
  • 通过读取 _environ 的值,可以获得环境变量的地址。由于环境变量存储在栈上,可以利用这个地址计算出栈的基址,从而进一步泄露其他敏感信息(如返回地址、函数地址等)。
 
 
 
  • 根据偏移找到存放manager_operation函数返回地址的栈地址,利用manager_write修改&chunk1+0x18指向返回地址,利用manager_visit_write改返回地址为rop链即可。
关于偏移的寻找,动调即可,如下图在main函数调用manager_operation时刻:
notion image
下一条指令地址即为返回地址,s进入operation函数,如下图看到返回地址存放的栈地址和泄露出的栈地址偏移为0xbc0-0xa78=0x148:
notion image

exp:

 
工具篇Test
Loading...