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。

PWN | “网鼎杯”朱雀之战——魔法房间题解    其中learn magic就是新申请一个16字节的结构体,结构如下:

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

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

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

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

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

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

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

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

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

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

    我们首先申请两块内容为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 结构中的情况如下:

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

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

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

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

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

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

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

    此时,我们已经控制了程序的执行流,就可以编写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 = 0x400A0Dlearn(0x20, 'a')learn(0x20, 'b')forget(1)forget(0)payload = p64(0) + p64(system)learn(0x10, payload)use(1)r.interactive()

来源:白茶与清风,本文观点不代表自营销立场,网址:https://www.zyxiao.com/p/39604

发表评论

电子邮件地址不会被公开。 必填项已用*标注

侵权联系
分享本页
返回顶部