R 3.4.4 中的基于栈的缓冲区溢出。在 x86 上实现完全利用,但在 x64 上由于程序约束,仅实现 RIP 控制并进行 gadget 分析。同一个漏洞存在于两种架构中,却导致不同的利用路径。
本仓库是我在讲授内存破坏利用时所使用材料的一部分(除了日常工作外,我还在多个网络安全课程中授课,帮助培训下一代逆向工程师)。
CVE-2019-25485 是我想让学生针对同一个漏洞在两种不同架构上分别进行完整研究,并亲眼看到其间差异时所用的案例。R 3.4.4 同时提供 x86 和 x64 版本,二者中存在完全相同的溢出:同一个 GUI 字段、同一个输入处理程序、同一个崩溃。这里将两者作为两个独立练习分别记录并完成利用:
R 3.4.4 是一个统计计算应用程序,不是网络服务或浏览器。该溢出通过桌面 GUI 字段触发,这意味着其攻击面与我讲授的其他所有案例都完全不同。这个案例的教学价值在于:
R 是一个可用于 Windows、macOS 和 Linux 的统计计算与图形环境。该漏洞位于 GUI 首选项对话框中,具体是“菜单和消息语言”字段,该字段会将用户输入复制到固定大小的栈缓冲区,而不验证其长度。
关键技术细节:
R 3.4.4 在处理“菜单和消息语言”字段时,会将所提供字符串复制到固定大小的栈缓冲区中,而不检查其长度。易受攻击逻辑的简化版本如下:
char language_buffer[256];
strcpy(language_buffer, user_input);
发送足够长的字符串会导致复制操作写入越过缓冲区末尾,破坏栈,直到保存的返回地址被覆盖。当函数返回时,CPU 会从栈中将攻击者控制的值加载到 RIP,并尝试跳转到该地址。
在 x64 上,Windows 会在任何跳转发生前强制执行规范地址验证。像 0x4141414141414141 这样的非规范地址会在 RIP 被加载前立即触发访问冲突,这意味着崩溃现象与 x86 不同,不会出现干净的 RIP = 4141414141414141。必须通过崩溃后从栈中读取循环模式来寻找偏移量,而不是直接从 RIP 读取。
可以通过向语言字段中粘贴长字符串来复现崩溃。无需认证。下面是用 Python 生成 payload 的示例:
import struct
payload = b'A' * 400
with open('payload.txt', 'wb') as f:
f.write(payload)
Open R 3.4.4 x64
Edit -> GUI Preferences
Paste contents of payload.txt into Language for menus and messages
Click OK
本仓库的目标不仅是演示崩溃,而是完整走一遍两种架构上的利用过程,记录 x86 上哪些方法有效、x64 上哪些方法失效,以及更重要的是,为什么。
为了让主 README 保持简洁,详细的利用笔记、脚本和调试器步骤都放在本仓库的 Vulnerability 📂 文件夹中,并分别组织在 x86 和 x64 子文件夹里。
在那里你可以找到针对两种架构的完整工作流程:
x86 - 完整利用:
x64 - RIP 控制与利用分析: