0CTF 2025 Writeup
赛时做了一题, 总觉得自己越来越没有心力和时间投入 CTF 了...
easypwn
checksec

为了提高可读性, 在 IDA 中修改了一些变量和函数名.
根据程序逻辑, 或者观察输入输出可以发现这是一个大数计算器, 支持
+ - * / ( ) 和十进制数字的输入.
程序有一个操作是将栈指针放入堆中

且有对其的自增、自减操作:

很遗憾这里限制在了 DWORD, 所以并没有实际的栈溢出问题,
不过存在整数的上溢和下溢.
一开始我并没有找到可利用的漏洞点, 不过由于本题的输入非常结构化, 我们可以考虑对其 fuzzing.
大约 20 分钟后, 确实找到了一些可引起 crash 的输入:

使用 casr-cli, 得到了一些有趣的分析结果:

着重对 ReturnAv 的 case 进行分析.
先将这几组输入在 gdb 中复现一遍,
发现程序出现了返回地址非法引起的段错误

Vulnerabilities
调试发现,
这是由于在 main 中程序尝试将计算结果写回到栈中时,
对指针的自增操作缺乏边界检查, 导致了栈溢出:


Exploit
接下来的工作就是如何找到哪个输入影响到了返回地址, 已知如下的输入可以产生溢出
1 | 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111/*/1/*//1111111111 |

观察到这里只有 * 和 /,
我们猜测程序进行了乘除相关的运算. 为了方便验证,
可以把上面的大数换成相近的以 2 为底的指数.
1 | 1119872371088902105278721140284222139060822748617324767449994550481895935590080472690438746635803557888/*/1/*//1073741824 |

很明显这里的地址部分位也变得 2^n 对齐了,
接下来尝试增大第一个大数的位数.
1 | 2582249878086908589655919172003011874329705792829223512830659356540647622016841194629645353280137831435903171972747493376/*/1/*//1073741824 |

注意到第一个大数增大后, 溢出修改到的位置也更高了, 并且直接由我们的输入决定. 所以, 我们只需要找到溢出是从哪一位开始的.
例如, 令第一个大数为这样的随机十六进制数:
0x1beacaaeaaeebaecfecdccddfdfebbfecabaeaccfeadafcbabfaccfeaeafaadbabcfeacdfbaaaacdfbaebfbcaadfccbaaebab
从而得到输入:
1 | 4505566911037959143315498166383803793144684411847119861693784392667451435154625739231718365669889945682659227170528488363/*/1/*//1073741824 |

这样就确认了溢出是从哪里开始的.

同时也观察到高位被写入到了返回地址的后面, 也就是我们得到了进行栈溢出执行 ROP 的方法.
1 | from pwn import * |