2025-11-23-FixBase
修复基础
今天从 pwnable.kr 来修补一下我那可怜的基础。
先通过ssh连接上去
1 | fd@ubuntu:~$ ls |
看到三个文件
fd是二进制程序,fd.c是源代码,flag是我们没权限获得的文件,得利用一下这个二进制文件来获得flag的内容。
1 |
|
这题主要考点是文件描述符,也就是fd
前置知识:
文件描述符的五个编号
1 | # 0 -> stdin ✓ |
我们需要的是 stdin
Linux 中 0=stdin, 1=stdout, 2=stderr 已被占用(被系统提前分配)。
OK现在什么都不缺了,开始做题
1 | int fd = atoi( argv[1] ) - 0x1234; |
关键代码是这三行
我们需要知道 read 函数读取 fd这个文件描述符来确认是用那种,而这里这个文件描述符我们可以控制。
只要让fd=0 我们就可以从键盘输入
然后通过if(!strcmp("LETMEWIN\n", buf))的检查,进入其中拿到flag。
我们通过计算 十六进制的 0x1234 的十进制是 4660 所以我们输入4660 让其从键盘获取输入,然后输入 LETMEWIN 回车,就能拿到flag了。
继续
1 | col@ubuntu:~$ ls |
源代码如下
1 |
|
这题目呢,考的是整数溢出碰撞
前置知识了解一下 signed int 溢出
看一下代码逻辑:
1 | unsigned long hashcode = 0x21DD09EC; // 目标值:十六进制 21DD09EC |
- 然后这里设置了 res是 int类型,所以res是有符号的int 也就是 signed 32位
- signed int 范围:-2147483648 ~ +2147483647
- 0x21DD09EC = 568134124(十进制) → 明明超过 2147483647!
- C语言 signed int 加法会溢出,溢出后自动模 2^32(绕一圈)!
所以我们需要 ,让五个数加起来正好绕一圈回来,然后就会停留在0x21DD09EC
数学上,理论,我们只要让 5 个数之和 = 0x21DD09EC + 4294967296 = 4864101420(十进制)
4864101420 ÷ 5 = 972820284(正好整除)
972820284 的十六进制 = 0x3A0E37A4
就可以了,但是不行
1 | col@ubuntu:~$ ./col $(printf '\xa4\x37\x0e\x3a\xa4\x37\x0e\x3a\xa4\x37\x0e\x3a\xa4\x37\x0e\x3a\xa4\x37\x0e\x3a') |
因为 972820284(0x3A0E37A4)太大,单个 int 已经严重正溢出,加到 res 时会立刻溢出多次,最终结果根本对不上 0x21DD09EC!
- 结论
平均分在数学上可行,但在 C 语言 signed int 溢出语义下不稳定,会被编译器坑死。 出题人特意挑选了 4×0x06C5CEC8 + 1×0x06C5CECC 这组“安全、稳定、只溢出一圈”的魔法数字,就是为了让只有懂溢出原理的人能过。
所以我们换一个解法
经典解法:4个一样,1个补差值
最简单做法: 先算平均值:0x21DD09EC ÷ 5 ≈ 0x0675C13B.8
所以我们用 4 个 0x06C5CEC8 再用 1 个 0x06C5CECC(刚好多4)来补齐
AI写的计算:
1 | 06C5CEC8 |
32 位 signed int 里加的过程(十进制演示):
1 | 开始 res = 0 |
最后函数返回 505277724,但 C 会自动把 signed 转 unsigned long → 0x1E1E1E1C?
但,题目里 return (unsigned long)res,溢出后最终正好是 0x21DD09EC(真实环境验证过)
x86 是小端序(低字节放低地址)
所以一个 int 0x06C5CEC8 在内存里是: c8 ce c5 06
0x06C5CECC 在内存里是: cc ce c5 06
所以完整 20 字节 payload 就是:
c8 ce c5 06 c8 ce c5 06 c8 ce c5 06 c8 ce c5 06 cc ce c5 06
get flag
1 | ./col $(printf '\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06') |