wiki上的一道经典例题,写wp的目的一方面是wiki上一些细节讲的有点不太清楚,一方面是自己巩固一下做过的题目,看是否是真的掌握。
首先提供题目二级制文件链接:books
预览:
拿到题目先看基本信息:
可以看到题目是64位文件,所有保护全开,一般这种题目是堆题,且看到full relro则大概率是篡改malloc_hook。
然后运行一下,找到程序运行入口,然后放入ida里看反汇编码:
准备功能分析:(不要忽视,做的题多了会发现有时候这里可能存在关键漏洞)
这里因为我已经做过了,所以函数名和一些变量名已经被我改成了对应的功能,可以看到程序开头先打印欢迎语,然后让我们输入作者(字符串储存在bss段,是可控的),这里的输入函数存在off-by-null漏洞,当输入32个字符时,既可以用来change,又可以用来leak(因为字符串后面没有‘\x00’,所以可以泄露authername这个字符串之后的内容直到’\x00’,这也是常见的套路。。。)
程序主功能分析:
程序有五个功能:1.create 2.delete 3.edit 4.print 5.change_authorname,大致的整体功能实现就是用户申请书本,然后书的名字和内容大小都由我们自己来定,并将它们的内容存放在堆上,然后创建一个大小固定的Book结构体来储存被创建的book的信息,其也放在堆上,创建一个book以后堆上的结构和bss段的联系大概如下图所示:
紧接着删除功能删除booklist中的Book指针,并且将指针归0,没有uaf漏洞
然后edit功能就是将Book的des重新输入,没有漏洞
然后是print功能,打印书的ID,name,des和书的作者,这个漏洞之前提过,分析bss段的结构就可以发现打印作者使可以泄露第一个book指针,这里需要注意,因为之后的所有chunk大小我们都可以自己控制,所以经过简单的计算,就等于我们泄露了所有的book指针和name,des指针!!!这是解题的一个关键之处。
再之后是change_authorname功能,就是更改作者的名字,当输入32个字节时依然存在off-by-null漏洞(因为是off-by-null,不是off-by-one,所以难度会有所加大)。
漏洞利用思路:
leak:
- 在前面已经提过,利用authorname最后一个字符’\x00’被book指针覆盖所以可以泄露book1指针的值,从而泄露之后所有的指针。
- 难点在于怎样泄露Libc,这里有一种新的方法,适用于chunk大小我们自己可控并且其地址可以泄露的情况,我们把book2的name和des申请的大于128KB(0x20000),则ptmalloc2将会用mmap来为我们分配内存,然而mmap的地址和libc的地址相对偏移不变(经过一次调试就能确定偏移),所以我们只要泄露了book2的name地址就等于泄露了libc,那再思考怎样泄露book2的name地址呢?现阶段我们只能利用程序自带的打印功能,他将打印所选的book的name指针和des指针所指向的内容,由book1指针已经由于off-by-null而最后一个字节被变为00,其之前肯定大于0,因为内存的分页分配机制,所以想让其被改过后的book1指针落入book1的des中,book1的des要相对的大一点。。。并且利用edit功能来重新编写book1的des,在book1指针指向的那个地方(这里的偏移不变,通过调试确定)伪造一个fake_book2_struct,其name指针为book2的name指针的地址(为addr_book1+56),然后利用打印功能来leak出book2的name指针的值,Libc泄露成功!!!
change:
利用程序自带的edit功能来改malloc_hook为one_gadget(one_gadget需要多次尝试),因为我们已经可以掌控book1的des指针的值了,所以也就实现了任意地址写入。之后再次申请的时候就会触发malloc_hook拿到shell。
exp如下(我当时做的时候是改free_hook,具体的一些偏移量需要自己调试来确定,exp只提供思路!!!):
#coding:utf-8
from pwn import *
context(os = 'linux',arch = 'amd64')
context.log_level = 'debug'
p=process('./b00ks')
P=ELF('./b00ks')
libc=ELF('./libc.so.6')
#先创建每个函数
def create(name_size