0xsha/KindaRails2Shell
GitHub: 0xsha/KindaRails2Shell
针对 Rails Active Storage/libvips 任意文件读取漏洞(CVE-2026-66066)的零依赖单文件 PoC,集成文件读取、密钥恢复、条件性 RCE、扫描与本地实验环境。
Stars: 0 | Forks: 0
# KindaRails2Shell: CVE-2026-66066
一个零依赖、单文件的 PoC(概念验证)和仅限 loopback 的 Docker 实验环境,
用于验证 Rails Active Storage/libvips 的任意文件读取漏洞。它还
演示了安全公告中描述的、从恢复的 `secret_key_base` 到伪造图片变体及命令执行的条件性升级路径。
- 受影响版本:Rails 7.2.0–7.2.3.1, 8.0.0–8.0.5, 及 8.1.0–8.1.3
- 已修复版本:Rails 7.2.3.2, 8.0.5.1, 及 8.1.3.1
- 运行环境:Python 3.11+,仅使用标准库
- 适用范围:授权测试与本地研究
主要参考来源:
[Rails 安全公告](https://github.com/rails/rails/security/advisories/GHSA-xr9x-r78c-5hrm)、
[Rails 取证分析](https://github.com/rails/rails-forensics-CVE-2026-66066)、
[Ethiack 技术分析文章](https://ethiack.com/info-hub/research/kindarails2shell-how-a-matlab-file-reads-your-secrets-and-pops-a-shell-on-ruby-on-rails)、
以及 [8.1.3.1 版本发布说明](https://github.com/rails/rails/releases/tag/v8.1.3.1)。
## 演示
https://github.com/user-attachments/assets/fdcb5651-c1d1-4bc4-b322-8f1205b09635
## 为什么这个 PoC 很有用
`kr2s.py` 在单个文件中包含了完整的客户端程序:
- 内置可修改的 HDF5/MAT payload,无需依赖 `h5py` 或 `numpy`;
- 支持直接上传交付及自动处理 CSRF;
- 可从现有的 Active Storage 缩略图中提取变体密钥 (variation key);
- 具备严格的标准库 PNG 解码器,确保字节级精确恢复;
- 支持任意偏移量,且单次请求最多可读取 16 MiB;
- 包含 secret 发现机制及纯 Python 实现的 Rails 变体签名工具;
- 支持通过随机标记进行命令输出读取,并尽最大努力进行痕迹清理;
- 支持多线程的被动或显式主动扫描,并输出 JSON 报告;
- 支持 proxy、自定义路径、TLS、timeout 及 user-agent 控制。
该工具不会安装软件包、启动回调监听器、要求目标机器上存在 `curl`,也不依赖 `make`。RCE 阶段使用内置的 Active Storage 表现层路由 (representation route),不需要 Marshal gadget。
## 快速开始
启动包含漏洞的实验环境:
```
cd lab
RAILS_VERSION=8.1.3 docker compose up --build
```
Compose 端口绑定到 `127.0.0.1`,且容器以非特权用户身份运行,所有 Linux capabilities 均已被禁用。
在另一个终端中:
```
# Intrusive: 上传一个 blob,然后确认 /etc/hostname 是否可读。
python3 kr2s.py check http://127.0.0.1:3000
# 恢复固定大小的前缀。除非使用 -o,否则二进制输出将发送至 stdout。
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd --size 4096
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd -o passwd.bin
# 恢复选定的 environment secrets。默认情况下,值会被遮盖。
python3 kr2s.py secrets http://127.0.0.1:3000
# 显示匹配的完整 environment 值以供授权分析。
python3 kr2s.py secrets http://127.0.0.1:3000 --show-full
# 对一个目标或每行包含一个目标的文件进行 Passive 预检。
python3 kr2s.py scan http://127.0.0.1:3000
python3 kr2s.py scan targets.txt --threads 10 --json scan.json
# Intrusively 确认列表中的每个目标均可读取文件。
python3 kr2s.py scan targets.txt --active
# 探测选定的 libvips loaders。
python3 kr2s.py probe http://127.0.0.1:3000
# Conditional escalation。这将执行所提供的命令。
python3 kr2s.py rce http://127.0.0.1:3000 "id; uname -a"
```
运行 `python3 kr2s.py COMMAND --help` 查看所有可覆盖的参数。针对真实应用程序的常用控制参数包括 `--harvest-url`、`--show-path`、`--proxy`、`--insecure` 和 `--skb`。
`secrets --show-full` 会打印完整匹配到的环境变量值。如果不提供该参数,工具将仅打印前 12 个字符及省略号。
使用 `--also-read` 请求的文件(包括默认的 Rails 凭据路径)在可读的情况下,最多会打印 600 个字符。
被动 `scan` 仅报告页面是否暴露了可提取的 Active Storage 表现层;它无法识别目标已安装的 Rails 补丁级别。
`scan --active` 会上传一个恶意 blob 并尝试执行 canary 读取。失败的主动扫描结果会被标记为 `BLOCKED_OR_INCONCLUSIVE`,而不是 `SAFE`,因为从应用程序外部来看,WAF、身份验证、不支持的变体以及 Rails 的修复机制表现出的行为可能完全相同。
使用以下命令停止实验环境:
```
cd lab
docker compose down
```
## 漏洞利用原理
1. 客户端申请一个被声明为 `image/png` 的 Active Storage 直接上传。
2. 它会上传一个混合型的 MATLAB/HDF5 文件。其中的 HDF5 dataset 使用了外部存储,其文件名和偏移量指向 Rails 进程中的某个文件。
3. 从现有的缩略图中提取一个由服务器签名的变体密钥,并将其与恶意 blob 的签名 ID 结合使用。
4. 存在漏洞的 Rails 会将数据传递给 libvips 的 `matload`;目标文件的字节会转化为图像像素,并在生成的 PNG 中返回。
5. 客户端验证并解码该 PNG,随后反转 matio 的列主序布局,从而恢复原始字节。
该混合文件同时满足了两种解析器的要求:其文本以 `MATLAB 5.0` 开头,而位于偏移量 124 处的版本字则选择了由 HDF5 支撑的 MATLAB 7.3 处理路径。Rails 的修复方案会在攻击者控制的文件触及 `matload` 之前,拦截所有标记为不受信任的 libvips 操作。
## 条件性 RCE
除非提供 `--skb`,否则 `rce` 会首先从 `/proc/self/environ` 读取 `SECRET_KEY_BASE`。接着它会派生出 Active Storage 验证器密钥,并对包含 `instance_eval` 操作的 JSON 变体进行签名。在受影响的 vips 处理路径上,该操作会触达 `Vips::Image.public_send`。PoC 通过内置的表现层路由发送请求,利用 `/bin/sh` 执行提供的命令,并捕获两个输出流。
第二阶段的利用取决于能否成功恢复签名密钥,以及能否触达 Ethiack 在其对 [CVE-2025-24293](https://github.com/rails/rails/security/advisories/GHSA-r4mg-4433-c7g3) 的分析中所描述的、存在漏洞的 vips 转换行为。
CVE-2026-66066 的补丁关闭了文件读取的入口点;这并不是针对先前已泄露签名密钥的通用修复方案。如怀疑密钥已泄露,请按照 Rails 安全公告的建议轮换密钥。
命令输出会被写入一个随机的 `/tmp/.kr2s_*` 文件中,随后通过相同的文件读取漏洞原语进行恢复,最后通过第二个签名 payload 将其删除。
## 读取行为与限制
读取操作将返回所请求的固定大小区域。如果源文件较短,则其前缀后会跟随零字节。请使用 `--offset` 获取后续区域的内容。
默认的实验环境变体仅执行 PNG 转换,因此可以做到字节级精确恢复。真实应用程序可能会调整变体的大小。已提取密钥的模式使用单像素列以避免水平重采样,但如果变体的高度较低,仍然可能改变图像几何结构。在这种情况下,客户端会显式报错;请减小 `--size` 或提取一个不执行调整大小操作的表现层。
执行的每条命令都具有侵入性:它会创建持久化的 Active Storage blob,并可能生成处理后的变体。`rce` 还会额外执行所提供的命令。
## 版本矩阵
该矩阵脚本会构建每个发行版,对存在漏洞的版本执行真实的读取和 RCE 检查,断言已修复版本的读取行为被成功拦截,并在出现任何不匹配的情况时返回非零值:
```
./lab/matrix.sh
./lab/matrix.sh 8.1.3
./lab/matrix.sh 8.1.3.1:PATCHED
```
于 2026-07-31 使用 Ruby 3.4.10、image_processing 1.14.0、ruby-vips 2.3.0 和 libvips 8.16.1 进行了验证:
| Rails | 文件读取 | RCE 检查 | 结果 |
|---|---|---|---|
| 7.2.3.1 | `READ-OK` | `RCE-OK` | 通过 |
| 7.2.3.2 | 拦截 | 未运行 | 通过 |
| 8.0.5 | `READ-OK` | `RCE-OK` | 通过 |
| 8.0.5.1 | 拦截 | 未运行 | 通过 |
| 8.1.3 | `READ-OK` | `RCE-OK` | 通过 |
| 8.1.3.1 | 拦截 | 未运行 | 通过 |
对于已打补丁的版本行,我们特意没有使用预先提供的签名密钥来触发 RCE payload;该矩阵测试的是 CVE 文件读取的边界。
## 研究实用工具
`research/craft.py` 用于为加载器研究重新生成候选 payload,需要依赖 `h5py` 和 `numpy`。发布的 PoC 并不会导入该工具。
`lab/probe/probe.sh` 会在实验镜像内部使用 ruby-vips 检查这些候选项:
```
uv run --script research/craft.py all -o /tmp/kr2s-payloads
docker run --rm \
-v "$PWD/lab/probe:/probe:ro" \
-v /tmp/kr2s-payloads:/payloads:ro \
--entrypoint /probe/probe.sh kr2s-rails:8.1.3
```
使用以下命令运行无依赖项的单元测试:
```
python3 -m unittest discover -s tests -v
```
## 公开工作的出处
MAT/HDF5 payload 及无依赖的客户端是通过本地测试独立开发的。在公开的 PoC 出现后,此发布版本吸收了 [Zer0SumGam3 的 PoC](https://github.com/Zer0SumGam3/CVE-2026-66066-POC) 中记录的变体密钥提取技术及内置 Active Storage 路由。
此处的 Python 实现是原创的,且仅使用标准库。
早期版本演示了使用 `Puma::MiniSSL::Context` 和 ActiveSupport 代理通过签名的 Marshal payload 同样可以实现 RCE,这部分基于 [Behrad Taher 的公开 gadget 研究](https://behradtaher.dev/Hunting-for-Deserialization-Gadgets/)。
该路径避免了目标端的辅助二进制文件,但需要支持 Marshal 的签名 ID sink,以及已加载的 Puma 和 ActiveSupport gadget 类。在 Ethiack Research Team 发布了他们的 [KindaRails2Shell 技术分析文章](https://ethiack.com/info-hub/research/kindarails2shell-how-a-matlab-file-reads-your-secrets-and-pops-a-shell-on-ruby-on-rails) 后,该方法被替换。该文章记录了更简单的签名 JSON 变体技术,该技术使用原生路由并移除了上述先决条件。
漏洞发现归功于 André Baptista (0xacb)、Bruno Mendes (s3np41k1r1t0)、Rafael Castilho 和 Ethiack Research Team,以及 RyotaK (GMO Flatt Security)。Abdelmounaim Moulahcene (bl0rph) 在协调披露之前独立发布了底层的漏洞原语。有关维护者记录的完整时间线和致谢,请参阅 Rails 取证代码库。
## 负责任使用与许可证
请仅在您拥有或已获得明确授权测试的系统上使用。作者不对任何滥用行为承担责任。有关报告此研究产物问题的信息,请参阅 [SECURITY.md](SECURITY.md)。
基于 [MIT License](LICENSE) 发布。
标签:IP 地址批量处理, PoC, Python, Ruby on Rails, 内存转储, 子域名变形, 无后门, 暴力破解, 网络安全审计, 请求拦截, 逆向工具