Suninatas Game 20
challenges
Game 20
1 | reverseme: ELF 32-bit LSB executable, Intel i386, statically linked, not stripped |
Source
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
Analysis
限制条件:
- 文件必须重命名为
suninatas(strcmp(*argv, "./suninatas")) - Base64 解码后最多 12 字节,无法直接覆盖 return address(需要 12+4=16 字节)
Stack Pivot
auth 中 v4 位于
[ebp-8h],memcpy 拷入 12 字节时:
1 | [ebp-8h] v4 ← bytes 0-3 |
虽然无法直接控制 return address,但可以控制 saved ebp,利用
leave; ret 的链式效应实现栈迁移:
auth的leave恢复了被篡改的 ebp 给mainmain结束时执行leave(mov esp, ebp; pop ebp)— esp 被迁移到&inputmain执行ret— 从&input + 4弹出值作为 EIP
Payload 结构
12 字节 = 3 个 DWORD(little-endian):
| Offset | 值 | 作用 |
|---|---|---|
| 0-3 | 0xDEADBEEF |
main 的 pop ebp 读取,同时满足
correct() 中 input == 0xDEADBEEF |
| 4-7 | &correct (0x0804925f) |
main 的 ret 弹入 EIP |
| 8-11 | &input (0x0811c9ec) |
覆盖 auth 的 saved ebp,触发栈迁移 |
Exploit
获取地址(statically linked,地址固定):
1 | $ objdump -t ./suninatas | grep -E ' (correct|input)$' |
生成 payload:
1 | python -c "import base64, struct; print(base64.b64encode(struct.pack('<III', 0xDEADBEEF, 0x0804925f, 0x0811c9ec)).decode())" |
because
1 | memcpy(&input, v5, v7); // 拷贝到 BSS 段全局变量 input |
&input + 0 (前 4 字节): 0xDEADBEEF
&input + 4 (中 4 字节): 0x0804925f (&correct 的绝对地址)
&input + 8 (后 4 字节): 0x0811c9ec (&input 的绝对地址)
1 | memcpy(&v4, &input, a1); // 12 字节拷贝:8 字节填满 v4,4 字节溢出覆盖 saved ebp |
[ebp-8] 到 [ebp-5]: 填入 0xDEADBEEF
[ebp-4] 到 [ebp-1]: 填入 0x0804925f
[ebp+0] 到 [ebp+3]: 填入 0x0811c9ec (&input) <-saved ebp
when auth calls leave; ret to return to
main
mov esp, ebp: esp = ebp -> &input
pop ebp: esp -> ebp + 4 = &input + 4 and put 0xDEADBEEF into ebp
ret (pop eip): eip -> esp = &input + 4 = 0x0804925f = &correct
at &correct check input == 0xDEADBEEF
1 | $ ./suninatas |