heap 0 题解
一、题目概况
heap 0 是 picoCTF 中用于引入堆溢出概念的入门题目。根据公开的 writeup 说明,本题会提供二进制文件、源代码以及远程访问实例。在源码中,存在input_data与safe_var两块堆上分配的数据。解题目标是将safe_var的值从初始的 “bico” 修改为其他任意值,从而触发程序进入打印 flag 的分支逻辑。
二、关键代码
公开 writeup 中展示的check_win函数逻辑非常直接:只要safe_var的值不等于 “bico”,程序就会打印 flag。
void check_win() { if (strcmp(safe_var, "bico") != 0) { printf("\nYOU WIN\n"); // read and print flag exit(0); } else { printf("Looks like everything is still secure!\n"); } } |
真正的漏洞出现在数据写入函数中。scanf("%s", input_data)没有对输入长度进行任何限制,这意味着用户可以输入远超input_data所在堆块容量的数据,从而造成堆缓冲区溢出。
void write_buffer() { printf("Data for buffer: "); fflush(stdout); scanf("%s", input_data); } |
heap 0 中覆盖 safe_var 的内存布局思路
三、解题过程
(1)确认目标变量。题目不需要劫持返回地址,也不需要写shellcode。目标只是改变safe_var的内容,使strcmp(safe_var, "bico") != 0成立。对于入门堆题来说,这种目标非常友好,因为我们只需要覆盖相邻数据。
(2)估算覆盖程度。公开writeup给出的内存布局示例中,input_data与safe_var的地址差为32字节。因此输入至少33字节,就能让数据越过input_data覆盖到safe_var。
Heap layout example: 0x...12b0 -> input_data: "pico" 0x...12d0 -> safe_var: "bico" 0x12d0 - 0x12b0 = 0x20 = 32 bytes |
(3)构造输入。payload可以非常朴素,只要长度超过边界即可。下面用Python生成33 个字符
payload = "A" * 33 print(payload) |
在交互菜单中选择写入buffer的功能,输入这段payload,再触发检查flag的功能即可。公开writeup给出的flag 为:
picoCTF{my_first_heap_overflow_c3935a08} |
四、复盘
heap 0的重点是把“溢出不只发生在栈上”这件事讲清楚。只要写入没有边界,堆上的相邻对象同样可能被覆盖;如果被覆盖的是权限位、状态变量或函数指针,影响就会迅速放大。
要点:scanf("%s", buf)不知道buf的容量,除非格式串显式限制宽度。
要点:相邻堆块之间的距离可以通过程序输出、调试器或源码推断。
要点:入门阶段先找“改变量即可赢”的题,再逐步过渡到tcache、UAF和函数指针劫持。
