Expression Language injection (ELI)
Protect your applications against Expression Language injection
Java
What is Expression Language injection?
Expression Language injection (ELI) is a server-side code injection type of bug. ELI vulnerabilities occur when user input is not properly escaped before dynamically evaluating it using an expression language interpreter. Many languages and frameworks support expression language, giving developers an easy way to communicate with the application logic from within the presentation layer (web pages).
About this lesson
In this lesson, you will learn how Expression Language injection (ELI) works and how to protect your applications against it. We will begin by exploiting an ELI vulnerability in a simple application. Then we will analyze the vulnerable code and explore some options for remediation and prevention.
Meet Jane: a fashion fanatic by day and a hacker by night.
Jane decides to test her favorite fashion outlet against a list of vulnerability classes. She is currently testing the search functionality and already tested it against SQL injection (SQLi) and cross-site scripting (XSS). No luck on those, but that doesn’t mean it is safe yet.
Let's break down what happened in the story above.
Jane entered a payload that contained Java enclosed in ${...}
. In Java, ${...}
as well as #{...}
is seen as valid Expression Language. Everything inside this enclosure will be evaluated server-side. See the official Java documentation on this here.
The search engine of the fashion outlet website used Expression Language in a slick way that is similar to templating. The search results stored at the back end would be dynamic for easy maintainability. For example, if a user searches for “Namebrand T-shirt”, the search results returned from the search engine would be something like:
${garment(2932).manufacturer}: T-shirt${garment(2735).manufacturer}: Polo${garment(1987).manufacturer}: Short-Sleeve
By keeping these values dynamic, the outlet website does not have to modify all data entries when a garment property needs updating.
To fill in these values, the backend utilizes the Java Expression Language (Expression Factory) library to render all the expressions.
This is a valid and slick approach if there would have been no injection. But unfortunately in the case of the outlet search engine back-end, there was an injection point, where user input ended up in the search results that would be rendered.
Given the following (shortened for easy understanding) JSP code:
And the following (also shortened for easy understanding) Java back-end code:
The system.Search()
function returns the search results, but in cases where no results are found, it returns the error string “Sorry, no Search results for" + input
.
As you might have guessed already, this string is then evaluated by the expression language renderer and reflected onto the page.
Impacts of Expression Language injection
The impact of an Expression Language injection is practically remote code execution. An attacker can call system functions from within the expression language and utilize this to extract data or run commands.
Because unrestricted code execution is not always possible due to certain restrictions or complications. Expression Language injection can also result in other, more limited, injection attacks such as SQL injection or cross-site scripting, as demonstrated in the BigCorp Clothing example.
Preventing ELI vulnerabilities is as simple as not passing user input into an Expression Language interpreter. But this is easier said than done. There exist many frameworks that support Expression Language, and each has its own approach to handling expressions.
For example, the Spring framework has certain tags that, by default, double-interpret expression language. One reason for this is that Spring offers Expression Language (EL) support that is not reliant on the JSP/Servlet container. This was done to ensure compatibility with older versions, as expression language was not supported before JSP 2.0.
One of these “double interpret” locations is the code
attribute in the <spring:message>
tag. This means that any user input that ends up in that attribute, is evaluated a second time. More on this can be found in this excellent writeup.
In this case, to disable the double resolution functionality in versions 3.0.6 and above, you can place the following configuration in the applications web.xml:
Spring Expression Language Support springJspExpressionSupport false
This is a good example to showcase how user input should always be sanitized, even if you think no harm could be done.
To prevent the ELI vulnerability in the scenario of Jane and the Search Engine of the BigCorp Clothing, the steps are simple. Make sure that the system.Search()
does not return an error that is constructed using user input, or, only render the search results when no error is returned.
In this scenario, it is easier to not render the user input, rather than trying to sanitize or filter the user input. Sanitization can fail when other encodings or formats are supported.
At last, a general piece of advice is to disable Expression Language, and only enable it when it is needed, and safely implemented.
In Tomcat, this can be done by adding <el-ignored>true</el-ignored>
inside the <jsp-config>
tag within the web.xml
file, to explicitly disallow Expression Language. Then by configuring the @page
with isELIgnored=true
, this can be overwritten again to allow support for Expression Language.
Keep learning
To learn more about Expression Language injection, check out some other great content:
- More about Expression Language injection in the Spring Framework can be found here
- OWASP also has a dedicated resource page for ELI
- More about the CWE type (CWE-917) can be found here