Plex2Shell: Plex Media Server Exploit Chain

CVE-2026-96652 · CVE-2026-96654 · CVE-2026-96655 · CVE-2026-96656 · detection, response and hardening
Threat
Plex2Shell (CVE-2026-96652, -96654, -96655, -96656)
Severity
EXPLOITED ITW · CISA KEV
Type
N-day exploit chain, cryptominer and proxy payloads
Access
Unauthenticated RCE via TCP 32400
Version
v0.1 Draft · 2026-10-22
Author
HuntPack
Confidence
High on chain and fix. Medium on campaign scale.
01

Executive Summary

Attacker objective: turn an internet-reachable Plex Media Server into a free compute and proxy node. The Plex2Shell chain (CVE-2026-96652, CVE-2026-96654, CVE-2026-96655, CVE-2026-96656) needs no valid login: a request with any junk token makes the server call itself, reach a loopback-only plugin API, leak the owner's admin token to an attacker-chosen host, then plant a shared object in the Codecs directory that the transcoder loads as code, running as the Plex service user. Researcher Zach Main published the write-up and proof of concept on 2026-10-16. A honeypot operator saw exploitation the next day, dropping an XMRig-based miner and a proxy tool named myawesomeproxy. Plex confirmed the flaws are "actively and widely exploited" on 2026-10-19 and CISA added all four CVEs to the KEV catalog on 2026-10-20. The fix is Plex Media Server 1.43.3.10861 or later.

Highest-value defensive angle: on a sensored host, a Plex process (especially Plex Transcoder) creating a shell, and an executable written into a Codecs directory by anything other than the server binary. Both are post-exploitation and survive payload renaming. Network indicators and file names are perishable.

Honest scope: Plex is prosumer software. In a managed fleet it mostly runs on NAS appliances, containers and home or unmanaged hosts that carry no EDR sensor. Those installs are outside endpoint visibility. Network egress controls, perimeter scanning for port 32400 and asset inventory carry that part. The CQL here covers only the sensored subset. Defender priority: (1) find every Plex install, sensored or not, and confirm it is on 1.43.3.10861 or later; (2) remove any internet exposure of port 32400; (3) on old builds that were reachable after 2026-10-17, assume compromise and check the Codecs directory, Preferences.xml and the process tree before trusting a patch alone.
02

Source Review & Web Hunter Notes

IDTierSourceKey findingCarry forward
S011Plex forum advisory (vendor), updated 2026-10-19Vendor lists the four CVEs, states they are being actively and widely exploited, names myawesomeproxy, mylovelyminer, x12345, .so/.dll files in Codecs and proxystream.st as signs of compromise. Fixed in 1.43.3.10861 and later.yes
S021Researcher write-up (zmain.info), 2026-10-16Primary description of the five-stage chain and the code-loading behavior. Used for defensive hunt logic only. Exploit code is not reproduced here.yes
S031BoredHackerBlog honeypot report, 2026-10-17 and updatesPrimary observation: three exploiting IPs, payload host, SHA-256 values for five files, myawesomeproxy, proxystream.st, first-request URI shape. Origin of nearly every atomic IOC in the press coverage.yes
S041CISA KEV alert, 2026-10-20All four Plex CVEs added to the KEV catalog.yes
S051/2Researcher detection script and YARA rules (code-zm repository)Defender-oriented triage logic: log patterns, Preferences.xml check, Codecs file shape, shell-under-Plex process check. Third-party content treated as data; not executed.partial (logic only)
S062SentinelOne vulnerability database, CVE-2026-96652Endpoint of the SSRF, affected range, egress-restriction workaround.partial
S072The Hacker News, 2026-10-19CVE roles, payload file names x12345, myawesomeproxy, mylovelyminer, pool host ota.proxystream.st, attacking IP list (one address printed twice).yes
S082SecurityOnline, Security Affairs, Cyber Security News, Daily Security Review, Cord Cutters News, Gizmodo, SafeguardCorroborating coverage: timeline, exposure counts (Shadowserver about 49,000 on 2026-10-18), residential-proxy risk, remediation. All trace back to S01 to S03 for exploitation facts.partial

Decisions and discrepancies

  • Independence: the press IOC lists all derive from one honeypot (S03). Indicators are therefore treated as single-sourced telemetry. IP addresses and hashes ship at detect. Only proxystream.st is also named independently by the vendor (S01), so it is the one indicator proposed at prevent.
  • CVE role mapping differs by source. The CVE records, SecurityOnline and The Hacker News describe CVE-2026-96654 as the URL-injection step and CVE-2026-96655 as the transcoder SSRF. Plex's 2026-10-19 table attaches the loopback agent access and the admin token disclosure to different IDs. The fixed version is identical everywhere. This pack uses the CVE-record wording.
  • CVSS differs by version. SecurityOnline mixes CVSS 3.1 and 4.0 values. Use: CVE-2026-96652 5.3 (v4) / 4.3 (v3.1); CVE-2026-96654 6.9 / 6.5; CVE-2026-96655 5.3 / 4.3; CVE-2026-96656 8.6 / 7.2. The researcher scores the chain 9.8.
  • Fix date conflict. Most sources say the fixed build shipped 2026-08-03 or 2026-08-04. One source says September. Plex's public warning was 2026-09-01 and the CVE records were published 2026-09-23.
  • Excluded: a VirusTotal sample the honeypot operator found by search as "similar" and the global.relay subdomain of proxystream.st tied by a third party to a different botnet. Both are related-cluster material and are not shipped. A site at zmain.com returned unrelated content and was discarded.
  • Not retrievable: the pcap repository and the Reddit thread. No unreviewed content is cited.
  • Attribution: none. Exploitation is opportunistic and the operator is unknown. The sources give the campaign no name beyond Plex2Shell, which names the exploit chain.
03

Hunt Brief & Attack Chain

Hypotheses (highest fidelity first)

#HypothesisMITREEvent typesExpected FPConf
H1A Plex process, above all Plex Transcoder, creates a shell, interpreter or downloader (Q1, Q2, Q3)T1059.004, T1105Process creation with lineageDVR post-processing scriptshigh
H2An executable is written into a Codecs directory by a process other than the server binary (Q8)T1129Executable file writtenCodec updates, package upgradesmedium
H3A loopback-prefixed wildcard-DNS name is resolved (Q7)T1528DNS lookupLocal dev testinghigh
H4A named payload, pool argument or published hash runs (Q4, Q5)T1496, T1090Process creation, file hashNone expectedhigh but perishable
H5Contact with published attacker addresses or proxystream.st (Q6)T1105, T1090Network connection, DNSAddress reusehigh but perishable
H6A Plex process talks to public hosts on unusual ports (Q9)T1090Network connectionRemote streaminglow (hunt)

Attack chain

StepBehaviorTelemetry (plain terms first)Hunt angle
1Unauthenticated SSRF, CVE-2026-96652: the player timeline endpoint checks only that a token header exists, so the server POSTs to a URL the attacker suppliesWeb or proxy access log, Plex application log, network sensor (outside endpoint telemetry)Native hunts (section 7)
2Loopback pivot, CVE-2026-96654: the forged callback reaches the loopback-only plugin API and an injected identifier calls a plugin functionPlex application log, network sensor (outside endpoint telemetry)Native hunts
3Owner token theft, CVE-2026-96655 path: a hostname beginning 127.0.0.1. resolves to an attacker address, so the server attaches its admin tokenDNS lookup with requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents, DnsQueryResponse)Q7
4Payload staging, CVE-2026-96656: a transcoder preference makes the encoder write attacker-controlled bytes into the Codecs directoryExecutable file written (CrowdStrike: ElfFileWritten or PeFileWritten. SentinelOne: File Creation. Defender: DeviceFileEvents, FileCreated)Q8
5Code execution: the transcoder loads every shared object in Codecs with no signature check, the constructor forks a shell as the Plex userProcess creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)Q1, Q2, Q3
6Payload fetch by shell script using curl or wget from the payload host, then miner and proxy launchedOutbound network connection with process (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents), process creationQ4, Q5, Q6
7Persistence by position: the planted file stays in Codecs and re-runs on the next transcode, so killing the miner does not evict the attackerExecutable file written, process creationQ1, Q8 and the containment runbook
04

Consolidated IOC Table

Every value below was re-derived from the saved snapshots in Plex2Shell-CVE-2026-96652-Hunt-sources/. All four addresses are public attacker or staging addresses from the honeypot report; none is a software version. Software versions in this pack (1.43.3.10861 and similar) are four-part strings that resemble an address but exceed the valid octet range and are never shipped as indicators.

TypeValueConfidenceActionContextExpiry
IPv445.194.92.222mediumdetectExploiting source, first request in the honeypot, Python-urllib user agent (S03)2027-01-22
IPv491.92.40.202mediumdetectExploiting source (S03, S07)2027-01-22
IPv4176.65.139.228mediumdetectExploiting source (S03, S07). Printed twice in one list; counted once.2027-01-22
IPv4217.60.194.116mediumdetectPayload source: serves the files named docker and x (S03, S07)2027-01-22
Domainproxystream.sthighpreventNamed by the vendor (S01) and the honeypot report (S03) as the proxy and miner infrastructure. Two sources.2027-04-22
Domainota.proxystream.stmediumdetectMining server named by The Hacker News (S07). Single-sourced. Covered by the parent entry if you block the parent.2027-04-22
SHA-2565712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441mediumdetectFile named docker, downloaded from the payload host (S03)2027-04-22
SHA-25668938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7mediumdetectFile named x, UPX packed (S03)2027-04-22
SHA-256b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49mediumdetectFile named x, unpacked, XMRig-based miner (S03)2027-04-22
SHA-256e546b10aee5edc754da4f9b0baace00392257121911f630145078eafad1e6bd9mediumdetectmyawesomeproxy binary (S03)2027-04-22
SHA-2562d3c618b21a7e2f8bcd735da188e44a7609ff3f204832dd0781cd09e4388c9dbmediumdetectXMRig sample from the honeypot (S03). XMRig is shared tooling; detect only.2027-04-22
Filenamex12345mediumhuntMiner process name listed by the vendor and The Hacker News (S01, S07). Attacker-chosen.2027-01-22
FilenamemyawesomeproxymediumhuntProxy tool name (S01, S03, S07). Prints a consent notice about external use of internet traffic.2027-01-22
FilenamemylovelyminermediumhuntMiner process name (S01, S07). Reddit reports of the same name are unconfirmed as Plex2Shell.2027-01-22
Not shipped: the miner wallet address and the full pool host and port are not in any retrievable source, so no value was invented. The behavioral substitutes are the command-line tokens in Q4. The generic names docker and x are too common to ship as filename indicators; they appear only as context. The related-cluster subdomain tied to another botnet is excluded.
05

Affected Surface & Telemetry Matrix

SurfaceTelemetry needed (plain terms first)PriorityGap risk
Linux server or Docker host running Plex, with an EDR sensorProcess creation, DNS lookup, network connection, executable file written (CrowdStrike: ProcessRollup2, DnsRequest, NetworkConnectIP4, ElfFileWritten. SentinelOne: Process Creation, DNS Resolved, IP Connect, File Creation. Defender: DeviceProcessEvents, DeviceEvents, DeviceNetworkEvents, DeviceFileEvents)highContainer-internal processes depend on sensor container support. Validate lineage fields.
Windows or macOS host running Plex, with a sensorSame telemetry (PeFileWritten for the DLL case)mediumAuto-update defaults to prompting, so old builds persist.
NAS appliance, router-hosted or vendor-app-store PlexNone. These devices usually cannot run a sensor.highOutside endpoint telemetry. Network flow, perimeter scanning and asset inventory only.
Internet-facing port 32400 and Remote Access forwardsWeb or proxy logs, network flow, external attack-surface scanhighOutside endpoint telemetry. Shadowserver reported about 49,000 exposed vulnerable hosts on 2026-10-18.
Plex application logs on the serverPlex Media Server log files, Preferences.xmlmediumOnly a few log rotations are kept. Preserve them before cleanup.
06

ATT&CK Mapping

TacticTechniqueObserved behaviorQuery or control
Initial AccessT1190 Exploit Public-Facing ApplicationFour chained CVEs against port 32400, no valid loginPatch, exposure removal, native hunts
Credential AccessT1528 Steal Application Access TokenOwner admin token leaked through a loopback-prefixed wildcard-DNS hostnameQ7
ExecutionT1129 Shared ModulesTranscoder loads an attacker-written shared object from CodecsQ8, Q1
ExecutionT1059.004 Unix ShellConstructor forks a shell as the Plex userQ1, Q2, Q3
Command and ControlT1105 Ingress Tool TransferShell script fetches the miner and proxy with wget or curlQ1, Q6
ImpactT1496 Resource HijackingXMRig-based miner, process name mylovelyminer or x12345Q4, Q5
Command and ControlT1090 Proxymyawesomeproxy routes third-party traffic through the victim's addressQ4, Q5, Q6, Q9
07

Native Audit-Log Hunts

Most of the chain is visible only in server and network logs, not endpoint telemetry. Run these on the Plex host, its reverse proxy and its network sensor.

WhereCheckSignal
Plex Media Server logSearch requests to the player timeline subscribe path where the protocol parameter holds a full URL, and search-agent requests whose identifier carries a function pathExploit request shapes published by the researcher (S05). First honeypot hit was a request of this shape from 45.194.92.222 with user agent Python-urllib/3.10 (S03). The user agent is a weak signal because the attacker can change it.
Reverse proxy, Zeek or SuricataSame URI shapes; a transcode request whose path parameter points to a non-loopback http hostNetwork-side view for hosts with no sensor. Researcher pcaps exist for signature work (S03).
Plex Media Server logAny lookup or request containing sslip.io or nip.ioWildcard-DNS token theft step
Preferences.xmlLook for a transcoder H.264 options value you did not setPlex lists this as a check (S01). The proof of concept clears the value after use, so an empty value is a weak signal, not proof of safety (S05).
Codecs directoryList files newer than 2026-10-16, with hashes. Unexpected shared objects or DLLs, or binaries whose last 64 KiB is uniform, are suspectPlex check (S01). Uniform-tail shape comes from the researcher's script (S05).
Live process tableShell, interpreter or netcat child of a Plex process; a shell running as the Plex user with an empty environment; a process mapping a deleted file from CodecsResearcher triage logic (S05)
Host performanceSustained high CPU while nothing is playingPlex and Safeguard list this (S01, S09). No endpoint event carries CPU, so this is a native check.

Read-only triage commands (Linux, adjust the data path for Docker and NAS)

# Version
curl -s http://127.0.0.1:32400/identity
# Process tree and CPU
pid=$(pgrep -f "Plex Media Server" | head -1); pstree -ap "$pid"
ps -eo pid,ppid,user,%cpu,cmd --sort=-%cpu | head -15
# Codecs files newer than the public PoC
PMS="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server"
find "$PMS/Codecs" -type f \( -name '*.so' -o -name '*.dll' \) -newermt 2026-10-16 -ls
# Transcoder options
grep -o 'Transcoder[A-Za-z0-9]*Options="[^"]*"' "$PMS/Preferences.xml"
# Outbound connections from Plex
ss -tnp | grep -i plex
08

Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL

Show all queries in
Pick your tenant's cloud first. Every "Open in Falcon" button below uses this selection.
Scope: these queries only see Plex hosts that run a Falcon sensor. NAS appliances and unmanaged home hosts return nothing here, which is not evidence of safety. Run Q1 and Q8 first. The "Open in Falcon" link uses a 7 day window; widen it to 14 days to reach 2026-10-17.
Q1 · Plex service process spawns a shell, interpreter or downloader
CONF HIGHFP MEDIUMCOST LOW

Looks for: A Plex-family process (server, transcoder, script host) creating sh, bash, cmd, PowerShell, python, curl, wget or a netcat-style tool. After CVE-2026-96656 the transcoder loads the planted shared object and its constructor forks a shell, so the shell's parent is Plex Transcoder. This is the highest-fidelity endpoint signal on a sensored host. FP: User-configured DVR post-processing scripts and some container wrapper scripts launched by the Plex service.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A shell started by a process the agent did not observe starting is not captured.
  • PowerQuery: The SHA256 of the new image is not projected here; the hash pivot is the separate hash card.
  • PowerQuery assumes: src.process.name holds the creator name as the agent reports it, possibly truncated to 15 characters on Linux.
  • PowerQuery assumes: src.process.user is the creating account; a created-process user field is not attested.
  • KQL – Defender XDR: A shell started by a process the sensor did not observe starting is not captured.
  • KQL – Defender XDR assumes: InitiatingProcessFileName holds the full creator name as the sensor reports it.
  • KQL – Defender XDR assumes: The regex is case-insensitive through the (?i) prefix.
// HUNT: Plex service process spawns a shell, interpreter or downloader
// MITRE: T1190, T1059.004, T1105
// CONF: high  FP: medium  COST: low
// REQUIRES: Process creation with command line on a sensored Plex host (Linux, Windows or macOS). Parent names are truncated to 15 characters on some Linux sensors, so the pattern is prefix-anchored.
// FALSE POSITIVES: user-configured DVR post-processing or media scripts launched by Plex; wrapper shells in some container images
// TUNING: after a 30 day baseline, exclude approved script names with | CommandLine!=/approved_script_name/i. Never exclude Plex Transcoder as the parent.
// LOOKBACK: 14d (exploitation began 2026-10-17)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(plex (media serv|transcoder|script hos|tuner serv|commercial)|plexmediaserver)/i
| FileName=/^(sh|bash|dash|zsh|ash|ksh|busybox|cmd|powershell|pwsh|wscript|cscript|mshta|python[0-9.]*|perl|curl|wget|nc|ncat|socat|script|setsid|nohup)(\.exe)?$/i
| table([@timestamp, ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine, ImageFileName, SHA256HashData])
Q2 · Grandchild of a Plex process launched through a shell
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: Anything started by a shell whose own parent is a Plex-family process. Catches the miner or proxy binary one step after the shell, including names the attacker changed. Complements Q1, which sees only the shell itself. FP: The same user-configured scripts as Q1; any wrapper that runs Plex helpers through a shell.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Chains where the shell was started by a process the agent did not observe are not captured.
  • PowerQuery assumes: src.process.parent.name is populated for the creating process on this agent version.
  • PowerQuery assumes: The Plex name is possibly truncated to 15 characters on Linux and is matched by prefix.
  • KQL – Defender XDR: Chains where the parent shell was not observed starting are not captured.
  • KQL – Defender XDR assumes: InitiatingProcessParentFileName is populated on the Linux and Windows sensors for this tenant.
// HUNT: Process started by a shell that Plex launched
// MITRE: T1059.004, T1105, T1496
// CONF: medium  FP: medium  COST: low
// REQUIRES: Process creation with parent and grandparent lineage (validate GrandparentBaseFileName is populated by your Linux sensor version)
// FALSE POSITIVES: approved DVR post-processing chains; container entrypoint wrappers
// TUNING: exclude approved chains with | FileName!=/^(ffmpeg|comskip|mediainfo)$/i after baselining
// LOOKBACK: 14d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| GrandparentBaseFileName=/^(plex (media serv|transcoder|script hos|tuner serv|commercial)|plexmediaserver)/i
| ParentBaseFileName=/^(sh|bash|dash|zsh|ash|busybox|cmd|powershell|pwsh|python[0-9.]*|perl)(\.exe)?$/i
| table([@timestamp, ComputerName, aid, UserName, GrandparentBaseFileName, ParentBaseFileName, FileName, CommandLine, ImageFileName, SHA256HashData])
Q3 · Reverse-shell and PTY-upgrade command-line shapes
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: Command lines carrying a /dev/tcp or /dev/udp redirect or a script-based PTY upgrade, the shapes the researcher's own hunt script flags for this chain. Scoped to any parent so it still fires if the shell is re-parented. FP: Admin port-check one-liners that use /dev/tcp; remote-support tooling.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A reverse shell built without these command-line shapes is not captured.
  • PowerQuery assumes: tgt.process.cmdline holds the full command line of the new process.
  • KQL – Defender XDR: A reverse shell built without these command-line shapes is not captured.
  • KQL – Defender XDR assumes: ProcessCommandLine holds the full command line of the created process.
// HUNT: Reverse-shell and PTY-upgrade command-line shapes
// MITRE: T1059.004, T1071
// CONF: medium  FP: medium  COST: medium
// REQUIRES: Process creation with command line on Linux or macOS
// FALSE POSITIVES: administrators using /dev/tcp for port checks; monitoring scripts
// TUNING: exclude known monitoring hosts with | ComputerName!=/^monitor-/i and review hits whose user is the Plex service account first
// LOOKBACK: 14d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| (CommandLine="*/dev/tcp/*" or CommandLine="*/dev/udp/*" or CommandLine=/script +-qc +\S+sh/i)
| table([@timestamp, ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine, ImageFileName])
Q4 · Named payloads and miner or proxy command-line arguments
CONF HIGHFP LOWCOST LOW

Looks for: Process names the Plex advisory and the honeypot report list (myawesomeproxy, mylovelyminer, x12345), plus command lines with proxystream.st, a stratum URL, a donate-level flag or a rig identifier. Names and pool strings are attacker-chosen and perishable, so a miss proves nothing. FP: Only a deliberately run miner in a lab.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A renamed payload run with none of these arguments is not captured; the hash card covers published samples.
  • PowerQuery assumes: tgt.process.name holds the base name of the new image.
  • KQL – Defender XDR: A renamed payload run with none of these arguments is not captured; the hash card covers published samples.
  • KQL – Defender XDR assumes: FileName holds the base name of the created image.
// HUNT: Named Plex2Shell payloads and miner or proxy arguments
// MITRE: T1496, T1090, T1105
// CONF: high  FP: low  COST: low
// REQUIRES: Process creation with command line (any platform)
// FALSE POSITIVES: sanctioned mining or proxy tooling in a lab
// LOOKBACK: 14d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| (FileName=/^(myawesomeproxy|mylovelyminer|x12345)(\.exe)?$/i or CommandLine=/(proxystream\.st|stratum\+(tcp|ssl)|--donate-level|--rig-id)/i)
| table([@timestamp, ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine, ImageFileName, SHA256HashData])
Q5 · Published sample hashes executed
CONF HIGHFP LOWCOST LOW

Looks for: Execution of any of the five SHA-256 values published by the honeypot operator for the downloaded dropper files, the XMRig-based miner (packed and unpacked) and the proxy tool. Single-sourced, so a hit is high confidence but a miss is weak, because the operator can recompile. FP: None expected for these values.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptions, telemetry-gapProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A rebuilt or repacked payload with a different hash is not captured.
  • PowerQuery assumes: tgt.process.image.sha256 is populated on process-creation events for this tenant; the field is mapped by an independent backend but population is worth a readiness check.
  • KQL – Defender XDR: SHA256 is usually unpopulated in DeviceProcessEvents; the source has no SHA1 or MD5 values to pivot on instead.
  • KQL – Defender XDR assumes: SHA256 is populated for the created process on this tenant.
// HUNT: Published Plex2Shell sample hashes executed
// MITRE: T1496, T1090, T1105
// CONF: high  FP: low  COST: low
// REQUIRES: Process creation with file hash (any platform)
// FALSE POSITIVES: none expected; hashes are single-sourced so ship as detect only
// LOOKBACK: 14d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| in(SHA256HashData, values=[
  "5712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441",
  "68938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7",
  "b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49",
  "e546b10aee5edc754da4f9b0baace00392257121911f630145078eafad1e6bd9",
  "2d3c618b21a7e2f8bcd735da188e44a7609ff3f204832dd0781cd09e4388c9db"
])
| table([@timestamp, ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine, ImageFileName, SHA256HashData])
Q6 · Connections to the published attacker addresses and proxystream.st lookups
CONF HIGHFP LOWCOST LOW

Looks for: Outbound connections to the three exploitation source addresses and the payload host named by the honeypot report, and DNS lookups of proxystream.st. The addresses are likely short-lived, so treat a hit as confirmed contact and a miss as inconclusive. FP: Shared hosting reuse of an address after the campaign moves on; check the connection date against 2026-10-17 onward.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analytic, equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A DNS lookup answered from a resolver cache the agent did not observe is not captured.
  • PowerQuery assumes: event.dns.request is the populated DNS name field; some schema versions populate dns.request instead.
  • PowerQuery assumes: dst.ip.address holds the remote address on IP Connect events.
  • KQL – Defender XDR: A DNS lookup with no following connection is not captured; DeviceNetworkEvents is not a DNS log.
  • KQL – Defender XDR assumes: RemoteUrl is populated for the connection when a host name was resolved.
// HUNT: Contact with published attacker addresses or proxystream.st
// MITRE: T1105, T1090, T1496
// CONF: high  FP: low  COST: low
// REQUIRES: Outbound network connection with process and DNS lookup (any platform)
// FALSE POSITIVES: address reuse by unrelated tenants after the campaign ends
// LOOKBACK: 30d
#event_simpleName=/^(NetworkConnectIP4|DnsRequest)$/
| (RemoteAddressIP4=/^(45\.194\.92\.222|91\.92\.40\.202|176\.65\.139\.228|217\.60\.194\.116)$/ or DomainName=/(^|\.)proxystream\.st$/i)
| table([@timestamp, ComputerName, aid, ContextBaseFileName, RemoteAddressIP4, RemotePort, DomainName, ContextProcessId])
Q7 · Loopback-prefixed wildcard DNS lookup (token theft step)
CONF HIGHFP LOWCOST LOW

Looks for: A lookup of a hostname that begins with 127.0.0.1. and ends in a wildcard-DNS service such as sslip.io. The chain relies on the server attaching the owner token to any request whose URL starts with that prefix while DNS sends it to an attacker address. Legitimate software has no reason to resolve such a name. Observed in the honeypot's first attack. FP: Developers testing local wildcard-DNS setups.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A lookup the agent did not observe, such as one resolved by an in-process resolver without a system call, is not captured.
  • PowerQuery assumes: event.dns.request is the populated DNS name field; some schema versions populate dns.request instead.
  • PowerQuery assumes: DNS Resolved coverage on Linux agents varies by agent version.
  • KQL – Defender XDR: Linux DNS coverage by the sensor varies by version; a missing event type returns nothing.
  • KQL – Defender XDR assumes: AdditionalFields carries the queried name under DnsQueryString or QueryName; the readiness test prints AdditionalFields so you can see which key your tenant populates.
  • KQL – Defender XDR assumes: DnsQueryResponse is recorded for the Plex host's platform.
// HUNT: Loopback-prefixed wildcard DNS lookup (admin token theft step)
// MITRE: T1528, T1190
// CONF: high  FP: low  COST: low
// REQUIRES: DNS lookup telemetry on the Plex host (Linux DnsRequest coverage varies by sensor version)
// FALSE POSITIVES: developers testing local wildcard-DNS setups
// LOOKBACK: 14d
#event_simpleName=DnsRequest
| DomainName=/^127\.0\.0\.1\..+\.(sslip|nip)\.io$/i
| table([@timestamp, ComputerName, aid, DomainName, ContextProcessId])
Q8 · Executable written into a Plex Codecs directory by a non-server writer
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: A shared object (Linux) or DLL (Windows) written under a Codecs path by anything other than the Plex Media Server binary. CVE-2026-96656 makes the transcoder itself write the file, so the writer is Plex Transcoder. This is the write half of the write-then-load pattern; Q1 is the load half. FP: Plex's own codec download and updates, package-manager upgrades and container image refreshes. Baseline which process writes legitimate codecs in your fleet before alerting. ElfFileWritten is documented in the crowdstrike-logscale-v3 event reference; the syntax linter lists the same event as unknown, which is a linter defect, not a bad event name.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analyticProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The sensor-side executable classification is not reproduced, so non-executable writes under a Codecs path also match.
  • PowerQuery: A very small or malformed file may not be reported by the agent.
  • PowerQuery assumes: tgt.file.path holds the full path of the written file.
  • PowerQuery assumes: src.process.name is the writing process on file events.
  • KQL – Defender XDR: The sensor-side executable classification is not reproduced, so non-executable writes under a Codecs path also match.
  • KQL – Defender XDR assumes: FolderPath holds the directory of the written file.
  • KQL – Defender XDR assumes: FileCreated and FileModified are the recorded write action types.
// HUNT: Executable written into a Plex Codecs directory by a non-server writer
// MITRE: T1129, T1190
// CONF: medium  FP: medium  COST: low
// REQUIRES: Executable-file-written telemetry (Linux ElfFileWritten, Windows PeFileWritten). Sensor classifies by file content, so a very small or malformed file may not be reported.
// FALSE POSITIVES: Plex codec downloads and updates; package or image upgrades
// TUNING: baseline the legitimate writer for 30 days, then exclude it with | ContextBaseFileName!=/^approved_updater$/i. Keep Plex Transcoder as a writer you do NOT exclude.
// LOOKBACK: 14d
#event_simpleName=/^(ElfFileWritten|PeFileWritten)$/
| TargetFileName=/Codecs/i
| ContextBaseFileName!=/^(plex media serv|plexmediaserver)/i
| table([@timestamp, ComputerName, aid, ContextBaseFileName, TargetFileName, SHA256HashData])
Q9 · Plex process outbound to public addresses on unusual ports (hunt)
CONF LOWFP HIGHCOST MEDIUM

Looks for: Frequency of public destinations contacted by Plex server, script host or transcoder on ports other than 80 and 443. The chain makes the server call out to an attacker-chosen host and port, and the proxy payload adds new destinations. A hunt, not an alert: remote streaming and relay make this noisy. FP: Remote streaming, relay and DLNA traffic; metadata providers on non-standard ports.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analyticProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Remote streaming, relay and DLNA traffic on unusual ports is a known false positive; this is a hunt, not a detection.
  • PowerQuery assumes: src.process.name is the connecting process on IP Connect events.
  • PowerQuery assumes: dst.port.number compares as a number.
  • KQL – Defender XDR: Remote streaming, relay and DLNA traffic on unusual ports is a known false positive; this is a hunt, not a detection.
  • KQL – Defender XDR assumes: RemoteIPType is populated for outbound connections.
  • KQL – Defender XDR assumes: ConnectionSuccess is the recorded action type for a completed outbound connection.
// HUNT: Plex process outbound to public addresses on unusual ports
// MITRE: T1090, T1105
// CONF: low  FP: high  COST: medium
// REQUIRES: Outbound network connection with owning process name (validate ContextBaseFileName on your sensor)
// FALSE POSITIVES: remote streaming, relay and DLNA; metadata providers on non-standard ports
// TUNING: rank by rarity and review destinations seen on one host only; exclude your own approved relay ranges with | !cidr(RemoteAddressIP4, subnet="203.0.113.0/24")
// LOOKBACK: 14d
#event_simpleName=NetworkConnectIP4
| ContextBaseFileName=/^plex (media serv|script hos|transcoder)/i
| !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8","169.254.0.0/16"])
| RemotePort!=443 RemotePort!=80
| groupBy([aid, ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort], function=count(as=conns))
| sort(conns, order=desc, limit=200)
| table([ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort, conns])
09

Detection Rules by Platform

CrowdStrike: Custom IOA

IOA namePromotesPatternExclusionsDeployment path
Plex service spawns shell or downloaderPromote Q1Parent image matches Plex Media Server, Plex Transcoder or Plex Script Host; child is a shell, interpreter or downloaderApproved DVR post-processing script names after a 30 day baseline. Never exclude Plex Transcoder as parent.Custom IOA, process creation rule on Linux and Windows host groups containing Plex. Detect first. Do not set to prevent until baselined. Confirm rule-type availability for Linux in your tenant.
Executable written into Plex Codecs by non-server writerPromote Q8Executable file written, path contains Codecs, writer is not the Plex Media Server binaryThe one legitimate updater found in your baselineCustom IOA on file-write events where the sensor supports it. Detect only.
Named Plex2Shell payload or miner argumentsPromote Q4Process name myawesomeproxy, mylovelyminer or x12345; command line with proxystream.st, stratum URL, donate-level or rig-idNone expectedCustom IOA, detect. Names are attacker-chosen, so do not rely on this alone.
Domain proxystream.st is the only indicator proposed at prevent (two sources). Every hash, address and file name is detect. No stock Plex binary hash is shipped at any posture.

SentinelOne: custom detection rules

RuleSource queryRun asNotes
Plex service spawns shell or downloaderQ1, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written
Executable written into Plex Codecs by non-server writerQ8, PowerQuery tabHunt only until the gap is closedQ8 is only an approximation here. The CQL reads an executable-file-written event that the sensor classifies by file content. SentinelOne has no content-classified executable write event, so this hunts any file creation or modification under a Codecs path by a writer other than the Plex server, which is broader and noisier.. This pack's own text mentions a baseline or pilot; follow it as written
Named Plex2Shell payload or miner argumentsQ4, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"

Create the rule with POST /web/api/v2.1/cloud-detection/rules, queryType: "scheduled" and queryLang: "2.0", with the query in data.scheduledParams.query. Use 2.0 operator spellings: a rule written with the 1.0 token ContainsCIS was accepted and never fired on a tenant. The API does not warn, so lint the body before deploying.

Microsoft Defender: custom detection rules

RuleSource queryRun asNotes
Plex service spawns shell or downloaderQ1, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written
Executable written into Plex Codecs by non-server writerQ8, KQL – Defender XDR tabHunt only until the gap is closedQ8 is only an approximation here. The CQL reads an executable-file-written event that the sensor classifies by file content. Defender has no content-classified executable write, so this hunts any file creation or modification under a Codecs path by a writer other than the Plex server, which is broader and noisier.. This pack's own text mentions a baseline or pilot; follow it as written
Named Plex2Shell payload or miner argumentsQ4, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself

Q2, Q3, Q5, Q6, Q7 and Q9 stay a hunt in this pack, not a rule. A rule can generate only 150 alerts per run. Continuous (NRT) frequency needs a single table and no comment lines, so delete the generated comment header from the query first.

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
ipv4,45.194.92.222,detect,high,2027-01-22,Plex2Shell exploiting source,campaign:Plex2Shell
ipv4,91.92.40.202,detect,high,2027-01-22,Plex2Shell exploiting source,campaign:Plex2Shell
ipv4,176.65.139.228,detect,high,2027-01-22,Plex2Shell exploiting source,campaign:Plex2Shell
ipv4,217.60.194.116,detect,high,2027-01-22,Plex2Shell payload host,campaign:Plex2Shell
domain,proxystream.st,prevent,high,2027-04-22,Plex2Shell miner and proxy infrastructure named by vendor and honeypot report,campaign:Plex2Shell
domain,ota.proxystream.st,detect,high,2027-04-22,Plex2Shell mining server named by press report,campaign:Plex2Shell
sha256,5712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441,detect,high,2027-04-22,Plex2Shell docker dropper file,campaign:Plex2Shell
sha256,68938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7,detect,high,2027-04-22,Plex2Shell x miner (UPX packed),campaign:Plex2Shell
sha256,b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49,detect,high,2027-04-22,Plex2Shell x miner (unpacked),campaign:Plex2Shell
sha256,e546b10aee5edc754da4f9b0baace00392257121911f630145078eafad1e6bd9,detect,high,2027-04-22,Plex2Shell myawesomeproxy binary,campaign:Plex2Shell
sha256,2d3c618b21a7e2f8bcd735da188e44a7609ff3f204832dd0781cd09e4388c9db,detect,high,2027-04-22,Plex2Shell XMRig sample,campaign:Plex2Shell
Microsoft Defender indicators CSVSettings, Endpoints, Indicators, Import: audit and block
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert
IpAddress,45.194.92.222,Audit,Plex2Shell exploiting source,Plex2Shell exploiting source,2027-01-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,91.92.40.202,Audit,Plex2Shell exploiting source,Plex2Shell exploiting source,2027-01-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,176.65.139.228,Audit,Plex2Shell exploiting source,Plex2Shell exploiting source,2027-01-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,217.60.194.116,Audit,Plex2Shell payload host,Plex2Shell payload host,2027-01-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,proxystream.st,Block,Plex2Shell miner and proxy infrastructure named by vendor an,Plex2Shell miner and proxy infrastructure named by vendor and honeypot report,2027-04-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
DomainName,ota.proxystream.st,Audit,Plex2Shell mining server named by press report,Plex2Shell mining server named by press report,2027-04-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,5712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441,Audit,Plex2Shell docker dropper file,Plex2Shell docker dropper file,2027-04-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,68938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7,Audit,Plex2Shell x miner,Plex2Shell x miner (UPX packed),2027-04-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49,Audit,Plex2Shell x miner,Plex2Shell x miner (unpacked),2027-04-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,e546b10aee5edc754da4f9b0baace00392257121911f630145078eafad1e6bd9,Audit,Plex2Shell myawesomeproxy binary,Plex2Shell myawesomeproxy binary,2027-04-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,2d3c618b21a7e2f8bcd735da188e44a7609ff3f204832dd0781cd09e4388c9db,Audit,Plex2Shell XMRig sample,Plex2Shell XMRig sample,2027-04-22T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True

Column names and allowed values are from Microsoft's documented parameter table. Prevent rows import as Block, without remediation; network indicators cannot use BlockAndRemediate. Check each expirationTime before importing: an indicator whose date has passed is already expired. Download the sample CSV from the Import page to confirm the column order before a bulk import. Each file takes at most 500 indicators. Learn describes the import per entity type, so if a mixed file is rejected, split it by indicatorType. An imported indicator does nothing unless the matching Advanced features are on (for example Custom network indicators and Allow or block file).

SentinelOne IOC importnot included

This pack does not ship a SentinelOne import block: the import format could not be verified against SentinelOne's own documentation. Export a sample from your console and the block can be added with confidence.

SHA-256 listsingle source, detect
5712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441
68938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7
b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49
e546b10aee5edc754da4f9b0baace00392257121911f630145078eafad1e6bd9
2d3c618b21a7e2f8bcd735da188e44a7609ff3f204832dd0781cd09e4388c9db
Network indicatorsfirewall, proxy, DNS
45.194.92.222
91.92.40.202
176.65.139.228
217.60.194.116
proxystream.st
ota.proxystream.st
Named tooling and behavioral signaturesnot importable
# Process names (attacker-chosen, perishable)
myawesomeproxy
mylovelyminer
x12345
# Command-line tokens
proxystream.st
stratum+tcp / stratum+ssl
--donate-level
--rig-id
# Lineage
Plex Transcoder -> sh / bash / curl / wget
Plex Media Server -> sh -> any child
# DNS pattern
127.0.0.1.<attacker-ip>.sslip.io
# Request shape (server or proxy log)
player timeline subscribe request with protocol=http://...
user agent Python-urllib/3.10 (weak)
# File shape
shared object or DLL under a Codecs directory written by Plex Transcoder
Patch and exposure auditversion, port, process
# Fixed build: 1.43.3.10861 or later
v=$(curl -s http://127.0.0.1:32400/identity | sed -n 's/.*version="\([^"]*\)".*/\1/p')
v=${v%%-*}
if [ "$(printf '%s\n1.43.3.10861\n' "$v" | sort -V | head -1)" = "1.43.3.10861" ]; then echo "OK: $v"; else echo "UPDATE NEEDED: $v"; fi
dpkg-query -W plexmediaserver
rpm -q plexmediaserver
docker ps --format '{{.Names}}\t{{.Image}}' | grep -i plex
# Exposure
ss -ltnp | grep 32400
11

Detection Validation Gates

GateCriteria
Telemetry readyConfirm the sensor reports process creation with parent and grandparent, DNS lookup, network connection and executable-file-written events from at least one Plex host (CrowdStrike: ProcessRollup2, DnsRequest, NetworkConnectIP4, ElfFileWritten. SentinelOne: Process Creation, DNS Resolved, IP Connect, File Creation. Defender: DeviceProcessEvents, DeviceEvents, DeviceNetworkEvents, DeviceFileEvents). Validate that parent names arrive untruncated or prefix-matched, and whether Linux DNS lookups are present.
Benign baselineRun Q1, Q2, Q8 and Q9 for 30 days on Plex hosts. List legitimate script launches and the legitimate codec writer. Add them as exclusions with owner and date.
Positive tests (lab host only)Q1: create a harmless executable named Plex Transcoder that starts a shell running id. Q4: copy a benign binary to a file named mylovelyminer and run it. Q7: resolve a lab name of the form 127.0.0.1.192-0-2-1.sslip.io. Q8: have a non-server process write a copy of a harmless library into a lab directory named Codecs. Do not use exploit code.
PromotionPromote Q1, Q8 and Q4 to Custom IOA at detect after a clean baseline. Prevent only for proxystream.st at the network layer. Re-review indicators at expiry.
12

Hardening: Tiered

Immediate (this week)
  • Update every Plex Media Server to 1.43.3.10861 or later. Authority: MITRE M1051 Update Software; vendor advisory (S01). Verify: query the local identity endpoint (see Patch and exposure audit in section 10). For containers, pull the new image and recreate the container; a restart keeps the old build.
  • Remove internet exposure of port 32400. Delete router port forwards, turn off UPnP if unused, switch off Remote Access if nobody streams from outside. Authority: M1030 Network Segmentation, M1035 Limit Access to Resource over Network; vendor advisory (S01). Verify: from an outside network, request the identity endpoint on the public address; it must time out.
  • Deny the Plex service identity outbound access to internal ranges and cloud metadata. Authority: M1037 Filter Network Traffic; S06 workaround; ⚠ best-practice, no formal benchmark for Plex. Verify: iptables -S OUTPUT lists the rules; Q9 shows no new internal destinations.
  • Treat unpatched servers that were reachable after 2026-10-17 as compromised. Check Codecs, Preferences.xml and the process tree (section 7), then follow section 14.
Near term (1 to 4 weeks, pilot first)
  • Put Plex behind a VPN or an access proxy. The researcher's repository recommends keeping the server off the open internet (S09). Authority: M1035. Where a reverse proxy is used, strip or validate the protocol parameter on the player timeline path (S06). Verify: external scan of port 32400 returns nothing.
  • Inventory Plex everywhere, including NAS and containers, and record sensor status. Authority: ⚠ best-practice, no formal benchmark. This closes the unsensored gap that no query can. Verify: the inventory lists owner, version, exposure and whether a sensor reports in.
  • Audit-watch the Codecs directory and Preferences.xml for writes. Authority: M1047 Audit; CIS Linux benchmark auditd guidance (general). Verify: auditctl -l shows the watch, and a test file creates an event.
  • Container hardening for the Plex container: unprivileged user, no new privileges, dropped capabilities where the image allows, default-deny egress network. Authority: M1048 Application Isolation and Sandboxing; CIS Docker Benchmark (container runtime section). Verify: docker inspect shows the settings.
Strategic (1 to 3 months)
  • Scheduled updates where auto-update does not exist (Linux packages, Docker, NAS). Windows and macOS default to prompting, not installing (S10). Authority: M1051. Verify: update job log and a weekly version report.
  • Isolate media servers on a dedicated segment with default-deny egress and an allow-list. The chain needs the server to call out; a media server rarely needs arbitrary destinations (S09). Authority: M1030, M1037. Verify: flow logs show only approved destinations.
  • Rule for chained CVEs: escalate when several CVEs fixed in one release include an unauthenticated entry and a code-execution end (S09). Authority: ⚠ best-practice, no formal benchmark.
  • Make Plex hosts rebuildable. The Codecs directory is regenerable (S01), so a clean rebuild is the eviction path. Keep configuration and libraries backed up separately.
13

Deployable Playbooks

Every step lists prerequisites, whether a reboot is needed and how to undo it. Pilot on one host first.

Playbook 1: Patch and verify a Docker or package install

# Prerequisites: backup of the Plex config volume or Library directory; maintenance window; shell on the host.
# Reboot required: no (service restart only).
# Rollback: re-pin the previous image tag or reinstall the previous package ONLY if the new build breaks playback. Rolling back reintroduces the vulnerability, so never roll back on an internet-exposed server.

# Docker: pull and recreate (do not just restart)
docker compose pull plex && docker compose up -d plex
docker exec plex curl -s http://127.0.0.1:32400/identity

# Debian or Ubuntu
sudo dpkg -i plexmediaserver_*.deb
# Fedora or CentOS
sudo dnf install ./plexmediaserver-*.rpm

# Verify (must print 1.43.3.10861 or later)
curl -s http://127.0.0.1:32400/identity

Playbook 2: Egress restriction for the Plex service account (Linux)

# Prerequisites: root; the service account is named plex (confirm with: id plex); iptables available. Pilot first: this blocks Plex from reaching internal media shares reached by user-space clients and some DLNA behavior.
# Reboot required: no. Rules are runtime only until saved with your distro's persistence tool.
# Rollback: delete each rule by repeating it with -D instead of -I, or reload the saved ruleset.

iptables -I OUTPUT 1 -m owner --uid-owner plex -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -I OUTPUT 2 -m owner --uid-owner plex -d 169.254.169.254 -j DROP
iptables -I OUTPUT 3 -m owner --uid-owner plex -d 10.0.0.0/8 -j DROP
iptables -I OUTPUT 4 -m owner --uid-owner plex -d 172.16.0.0/12 -j DROP
# Add an ACCEPT for your own LAN client range before the next rule if clients are on 192.168.0.0/16
iptables -I OUTPUT 5 -m owner --uid-owner plex -d 192.168.0.0/16 -j DROP

# Verify
iptables -S OUTPUT | grep plex

Playbook 3: Audit watch on Codecs and Preferences.xml (Linux, auditd)

# Prerequisites: auditd installed and running; default Linux data path. If auditctl rejects a path containing spaces on your build, watch through a path without spaces (a bind mount) instead.
# Reboot required: no for runtime rules; persisting in rules.d is loaded at the next audit service restart.
# Rollback: auditctl -W with the same path and keys, or remove the rule file and restart auditd.

PMS="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server"
auditctl -w "$PMS/Codecs" -p wa -k plex_codecs
auditctl -w "$PMS/Preferences.xml" -p wa -k plex_prefs

# Verify
auditctl -l | grep plex_
ausearch -k plex_codecs -ts recent

Playbook 4: Close the internet path

# Prerequisites: router or firewall admin access; a VPN or access proxy ready for remote viewers.
# Reboot required: no.
# Rollback: re-add the forward only after the server is on 1.43.3.10861 or later and behind access control.

# 1. Router: delete any manual port forward to TCP 32400; disable UPnP and NAT-PMP if not needed.
# 2. Plex Web: Settings, Remote Access, disable if unused.
# 3. Host firewall (ufw example): allow 32400 only from the VPN or LAN range
ufw status numbered
ufw delete <rule-number-for-32400-from-anywhere>
ufw allow from 192.0.2.0/24 to any port 32400 proto tcp

# Verify from a network outside yours (expect a timeout)
curl -m 5 -s http://<public-ip>:32400/identity
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateCut the host from the network at the switch or firewall, keeping power on. Remove any port forward to 32400. For a container, disconnect it from its network rather than deleting it.IR leadTime of isolation, firewall change record
Isolate: by platformCrowdStrike: network-contain the host. SentinelOne: network quarantine (Disconnect from Network; confirm the label in your console version). Microsoft Defender: Isolate device (full network isolation).IR leadTime of isolation, firewall change record
PreserveBefore cleanup, copy the Plex log files (few rotations are kept), Preferences.xml, a listing of Codecs with hashes, the process tree and open connections.IR analystCopies with hashes, command output
ScopeRun Q1 to Q9 across the fleet for 2026-10-17 onward. Check what the Plex service account could read: mounted shares, SSH keys, cloud sync tokens, stored credentials.IR leadQuery results, access inventory
EradicateRebuild from a clean install rather than deleting files one by one (vendor guidance, S01). If a rebuild is impossible, empty the Codecs directory, remove transcoder options you did not set, and remove the miner and proxy. Killing the miner alone is not enough because the planted file reruns on the next transcode.Service ownerRebuild record, new image digest
RecoverUpdate to 1.43.3.10861 or later before reconnecting. Sign out of all devices, change the Plex account password, rotate every credential the Plex account could read, and re-enable access only through a VPN.Service owner + identityCredential rotation record, version check
Re-huntRun section 7 checks and Q1 to Q9 again after 7 days and when indicators change.Detection engineeringQuery results, closure decision

Escalate immediately: a shell child of Plex Transcoder, any published hash on a host, or contact with the payload host from a host that holds other credentials.

15

Detection Coverage Map

TechniqueBehaviorCQLIOACrowdStrike coverageSentinelOneDefender XDR
T1190SSRF chain requests to port 32400nonenoneGAP outside endpoint telemetry; server, proxy and network logs only (section 7)GAPGAP
T1528Token theft through loopback-prefixed wildcard DNSQ7nonePartial depends on Linux DNS telemetryPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1129Shared object written into Codecs then loadedQ8Promote Q8Partial file classification and baseline neededPartial: Q8 approximatePartial: Q8 approximate
T1059.004Shell spawned under PlexQ1, Q2, Q3Promote Q1Good on sensored hosts onlyGood, with assumptionsGood, with assumptions
T1105Payload download by curl or wgetQ1, Q6nonePartial network indicators are perishablePartial: same limit as CrowdStrikePartial: Q6 approximate
T1496XMRig-based minerQ4, Q5Promote Q4Partial names and hashes are attacker-controlledPartial: same limit as CrowdStrikePartial: Q5 telemetry gap
T1090Residential proxy toolQ4, Q5, Q6, Q9nonePartial Q9 is a noisy huntPartial: Q9 approximatePartial: Q5 telemetry gap, Q6 approximate, Q9 approximate
Host assetPlex on a NAS, router or home host with no sensornonenoneGAP outside endpoint telemetry; network controls and asset inventory carry this partGAPGAP
Resource useSustained CPU with no playbacknonenoneGAP no endpoint event carries CPU; native check onlyGAPGAP
Persistence by positionPlanted file reruns on later transcodesQ1, Q8Promote Q1Partial file removal needs a rebuild, not a queryPartial: Q8 approximatePartial: Q8 approximate

Validation gates are in section 11. Plex servers on NAS appliances and unmanaged hosts that carry no sensor are outside endpoint visibility. Network egress and ingress controls and asset inventory are the controls for that part; this pack does not claim coverage there.

16

Hunt Summary Ticket

TITLE:        Plex2Shell: Plex Media Server unauthenticated RCE chain, miner and proxy payloads
SEVERITY:     HIGH (exploited in the wild, CISA KEV 2026-10-20). Endpoint coverage limited to sensored hosts.
SCOPE:        Plex Media Server before 1.43.3.10861 on Linux, Docker, Windows, macOS. Unsensored NAS and home hosts outside endpoint telemetry.
HYPOTHESIS:   Exploited Plex servers show a shell under a Plex process and an executable written into Codecs by a non-server writer, then miner or proxy execution.
QUERIES:      Q1 to Q9 (Q1, Q8 first; Q9 is a noisy hunt)
DO FIRST:     Inventory Plex installs and versions. Run Q1 and Q8 over 14 days. Patch and remove port 32400 exposure.
FINDINGS:     (fill in)
GAPS:         Request-stage exploitation (server, proxy and network logs, outside endpoint telemetry); unsensored NAS and home hosts; CPU use; miner wallet and pool port not published.
ACTIONS:      Patch to 1.43.3.10861 or later, rebuild any exposed old build, rotate credentials the Plex account could read, block proxystream.st at the network layer.
OWNER:        SOC / Detection Engineering / Infrastructure
VERSION:      v0.1 Draft · 2026-10-22
17

Changelog

v0.12026-10-22Initial Plex2Shell pack: nine CQL queries (Linux, Windows and macOS lineage), 14 atomic indicators traced to saved snapshots, native hunts, tiered hardening with four playbooks, and a six-phase containment runbook. SentinelOne PowerQuery and Microsoft Defender KQL tabs added to Q1-Q9: SIEM Query Rosetta release 0.1.0 (public-beta; static-reviewed translations).
18

References

IDTierSourceUsed forAccessed
S011Plex forum: Important Security Update for Plex Media Server v1.43.2 and earlier (updated 2026-10-19)Vendor advisory, indicators, remediation2026-10-22
S021zmain: Plex2Shell write-up (2026-10-16)Chain description for defensive hunt logic2026-10-22
S031BoredHackerBlog: Plex vulnerabilities being exploited in the wild (2026-10-17)Honeypot observations and hashes2026-10-22
S041CISA: Adds Six Known Exploited Vulnerabilities to Catalog (2026-10-20)KEV confirmation2026-10-22
S051/2code-zm/plex2shell detection: hunt script and YARA rulesDefender triage logic (data only, not executed)2026-10-22
S062SentinelOne Vulnerability Database: CVE-2026-96652SSRF detail and workarounds2026-10-22
S072The Hacker News: Hackers Exploit Plex2Shell Flaws (2026-10-19)CVE roles, payload names, pool host2026-10-22
S082SecurityOnline: Plex2Shell Plex Media Server exploitedChain summary, IP list2026-10-22
S092Safeguard: Plex2Shell explained (2026-10-22)Timeline, mitigations, egress guidance2026-10-22
S102Gizmodo (2026-10-19)Shadowserver exposure, auto-update defaults2026-10-22
S112Security Affairs (2026-10-19)Loader behavior, investigation guidance2026-10-22
S122Cyber Security NewsHoneypot details, request shape2026-10-22
S132Daily Security Review (2026-10-19)Corroboration, single-honeypot caveat2026-10-22
S142Cord Cutters NewsIndicator list restatement2026-10-22

Source snapshots are saved in Plex2Shell-CVE-2026-96652-Hunt-sources/. No fetched page contained instruction-like text aimed at the analyst.