用于回答关于 Rails 应用与 CVE-2026-66066(即 Active Storage 与 libvips 任意文件读取漏洞,被称为 KindaRails2Shell)的两个问题的工具与方法:
安全公告 GHSA-xr9x-r78c-5hrm 解释了该漏洞并指明了已修复的版本,但未披露足够细节以 评估上述问题。本项目旨在帮助解决这一问题。
这是一个文档与技能仓库,而非一个库。此处没有任何内容 会被安装到你的应用中。由智能体阅读它,并针对检出在别处的 你的应用进行操作,而唯一在生产环境中运行的脚本是一个自包含的 单文件,你将其复制到容器中。
Rails 通过读取 content_type 列来决定一个 blob 是否可以进行图像处理,
而直接上传允许客户端在不检查字节内容的情况下设置该列。
随后 libvips 通过嗅探魔数字节来决定文件实际是什么。一个前十个字节
声称是 MATLAB 5.0 的文件会被路由到 libvips 的 MATLAB 加载器,
后者将其交给 libmatio,而 libmatio 根据不同的字节范围进行分发,
发现 MAT 7.3,即 HDF5。HDF5 的 External File List 允许数据集的字节
存放在由路径和偏移量指定的另一个文件中,因此渲染这个“图像”会读取
攻击者选择的服务器上的文件,并将其内容作为像素返回。同样的混淆,
发生了两次,位于两个无法看到彼此字段的层级。
reference/the-attack.md 追踪了整个调用链,从入站请求
到 libmatio 打开目标文件的那一刻,并标记了四个关键节点。
reference/the-investigation.md 随后解释了为什么该攻击是可调查的:
它留下了三个强度递增的痕迹,其中最强的痕迹是渲染后的变体,
它将窃取的字节作为像素值保存在你自己的对象存储中。
这两个技能按顺序运行。第一个产生暴露窗口;第二个 在没有它的情况下无法开始。
| 技能 | 问题 | 产出 |
|---|---|---|
kr2s-was-i-vulnerable | 此应用是否曾经暴露? | 一个暴露窗口以及扫描所需的事实 |
kr2s-was-i-exploited | 是否有人利用过它,他们得到了什么? | 一份书面的取证分析 |
两者都是为智能体编写的,同时也适合想要手动完成
这项工作的人阅读。每个都有一个 SKILL.md 入口点和一个 references/guide.md
包含实际流程、其评估检查及其失败模式。
如果你已修复并想知道是否应该关注,请运行第一个。 它的交付物是一个时间窗口而非是或否,因为对于大多数 阅读本文的人来说,现在时的答案是“不,我们已修复”,而有用的答案是 调查需要覆盖哪些年份。
克隆此仓库并保留克隆。这两个技能共享 reference/ 中的参考
文档,它们的指南通过仓库相对路径读取这些文档,因此单独复制到某处的
技能目录将无法正常工作。
最简单的方法是在克隆内部启动你的智能体,并告诉它你的
应用在哪里。AGENTS.md 会从那里引导它,包括人们容易搞错的部分,
即此仓库是工具而非研究对象。
否则,指向文件并说明,这在任何能够读取本地文件的智能体中 都有效:
Read /path/to/rails-forensics-CVE-2026-66066/skills/kr2s-was-i-vulnerable/SKILL.md
and follow it. My application is at /path/to/my-app.
在 Claude Code 中,你可以改为通过符号链接而非复制的方式来安装它们,使其自行触发,这样它们会始终指向克隆目录:
ln -s "$PWD/skills/kr2s-was-i-vulnerable" ~/.claude/skills/
ln -s "$PWD/skills/kr2s-was-i-exploited" ~/.claude/skills/
如果希望将技能限定在单个应用范围内,请使用项目内的 .claude/skills/,而不是 ~/.claude/skills/。
无论采用哪种方式,都要告诉 agent 你的应用在哪里。这些技能明确规定,本仓库与被调查的应用是两个不同的根目录,它们可以读取第二个根目录,但绝不能写入其中。
AGENTS.md orientation for an agent started in this directory
reference/the-attack.md how the vulnerability works, traced call by call
reference/the-investigation.md why an investigation is possible, and where it runs out
skills/ the two skills
RUNBOOK.md operator's guide to the scanner: flags, verdicts, resuming
lib/crafted_mat_file.rb the detector
bin/ the scanner and three analysis utilities
test/ the test suite
这两份参考文档是本仓库中通俗易懂的部分,它们可以独立阅读。如果你想理解问题,请阅读它们;如果你想针对某个特定应用获得答案,请运行这些技能。
bin/kr2s_scan_active_storage_blobs.rb 由 rake build 从 lib/ 生成并提交,因此在生产环境中运行它不需要构建步骤,也不需要第二个文件。
lib/crafted_mat_file.rb 用于识别此攻击所需的构造文件。它从对象的前 128 字节中读取两个头部字段,除此之外不需要任何其他内容,这就是为什么扫描可以通过范围读取来对候选对象进行分类,而不必下载它。reference/the-attack.md 解释了是哪些字段,以及为什么没有任何合法写入器会产生这种组合。
本仓库有意不提供任何用于构建构造文件的代码,也不提供任何构造文件。检测器的测试用纯 Ruby 合成它们所需的 128 字节头部,而这正是检测器所读取的全部内容。reference/the-attack.md 完整描述了该结构,因此对于试图理解此攻击的读者来说,没有任何内容被隐瞒。被隐瞒的是一个可用的生成器,它可能通过更改一个参数而被重新指向任意路径。
覆盖范围: Active Storage,适用于使用 :vips 变体处理器的应用,且变体记录被跟踪的情况。
不覆盖范围: 应用将用户提供的文件交给 libvips 的任何其他位置。其他上传库、直接对象存储客户端以及定制的头像或徽标系统各自都需要单独排查,而此处的扫描器不会执行该排查。kr2s-was-i-vulnerable 会让你逐一列举它们,以便它们被明确命名,而不是在结论中被悄然省略。
来自此工具集的干净结果是强有力的证据,而非证明。两个技能都反复且具体地说明了这一点,并且分析模板中有一个不可省略的限制部分。
这源于 37signals 的一次真实调查,在漏洞存在的整个期间内扫描了一个大型生产 Active Storage 存储。该工具集正确识别了报告该缺陷的安全研究人员上传的测试文件,这是目前唯一可用的外部证据,证明检测器能够发现它应该发现的内容。
kr2s-was-i-exploited/references/analysis-template.md 中的完整分析是虚构的。它模仿了真实的分析,但其中的每个标识符、地址和日期都是虚构的。
请从安全公告中获取已修补的 Rails 版本号,而不是从本文中的任何内容获取。
mise exec -- rake # build and test
MIT。参见 LICENSE。