Takeover Without a Password: Four Simple Oversights Turned Media Servers into Miners

Takeover Without a Password: Four Simple Oversights Turned Media Servers into Miners

CybersecurityPlexVulnerability AnalysisEdge Computing

Sources:HN + web research

A Screaming Living Room Fan and a Secret Background Cryptominer

In the corner of a quiet living room, a silent home theater PC suddenly began spinning its cooling fans at full throttle through the night. The box was normally reserved for streaming 4K movies and spent most of its day virtually idle. When the owner checked the system dashboard, CPU utilization had been pinned at 100% for days.

Two unexpected processes were churning through compute cycles day and night: one masquerading as a network proxy named myawesomeproxy, and another operating as a cryptocurrency miner under mylovelyminer. The owner had installed no untrusted packages, and the server administration interface was protected behind a strong, unique password.

The breach stemmed from Plex Media Server. Security researchers documented an unauthenticated remote code execution chain dubbed plex2shell (CVE-2026-96652, -96654, -96655, -96656), impacting version 1.43.2 and earlier. A remote attacker on the public internet needed no credentials whatsoever—just a sequence of specially crafted HTTP requests to take complete control of the host.

Honeypot capture log Figure: Automated exploit script pulling malicious payloads captured in honeypot logs. Source: BoredHackerBlog.

Tricking the Server into Attacking Itself via Unchecked Callbacks

Many local network services share a foundational assumption: requests originating from localhost are inherently trustworthy. To avoid internal verification overhead, Plex relaxed its filtering rules for loopback traffic. That trust model became the wedge that broke the entire security boundary. An attacker merely had to trick the server into attacking itself.

The first crack surfaced in the timeline endpoint /player/timeline, used by player clients to report playback progress. The endpoint was supposed to require an X-Plex-Token header. However, the server-side code only checked hasParam(), confirming header presence without inspecting its value or authenticity—any dummy string sufficed.

Once through that gate, the server registered event subscriptions by building a callback URL using boost::format("%1%://%2%:%3%"). Critically, the protocol parameter underwent zero format validation or character sanitization.

By supplying an entire URL beginning with the loopback address inside that parameter, an attacker tricked Plex into firing an HTTP POST directly at its own internal management port. At the transport layer, the request’s source IP was genuine 127.0.0.1, instantly granting the attacker trusted local status.

A Flawed Prefix Check Surrenders Administrative Tokens

Armed with trusted local status, the attacker pivoted to the internal plug-in management endpoint /system/agents/*. Because the request originated from loopback, the endpoint bypassed normal authentication checks. Within agentservice.py, developers had implemented an allowlist inside searchOne.

However, the function was decorated with a @lock mutex. The decorator failed to propagate the return value of the underlying inner() function. When the allowlist check triggered an early return to block execution, the return code was silently swallowed, leaving the restriction completely ineffective.

Simultaneously, the identifier parameter lacked proper sanitization. Supplying a question mark ? enabled query parameter injection, while a hash # truncated the default /messaging/... path. That cemented the second flaw.

The attacker then set sights on global administrative privileges, where Plex’s plug-in framework offered a convenient shortcut: outbound requests directed to addresses starting with 127.0.0.1 automatically had administrative local-* tokens appended. But the framework used unanchored string prefix matching rather than parsing hostname boundaries. An address like 127.0.0.1.evil.com satisfied the rule.

Using the wildcard DNS service sslip.io, the attacker forged the hostname 127.0.0.1.10-0-0-9.sslip.io. The string passed the prefix test, yet resolved over public DNS directly to the attacker’s server at 10.0.0.9. Plex gladly handed over its administrator token, completing step three.

Attack log capture Figure: Real-time attack logs captured by researchers as malicious external IPs probed and exploited the vulnerability. Source: BoredHackerBlog.

Hiding Binaries in Video Pixels: Abusing the Transcoder for Disk Writes

Holding an administrator token gave the attacker the right to alter server preferences. Plex relies on an internal transcoder for cross-device media delivery. The underlying x264 encoder exposed debugging flags, allowing an attacker to inject custom options: TranscoderH264Options=dump-yuv=<path>:qp=0.

The dump-yuv option invoked fopen(path, "w") under the hood, dumping raw decoded video frames directly to an arbitrary file path. Setting the quantization parameter qp=0 enforced lossless encoding, preventing pixel distortion. In advance, the attacker had compiled a minimal ELF shared library containing a single system constructor.

By writing the binary bytes directly into the luma plane of an MP4 video’s very first frame, the attacker created an ordinary-looking, valid H.264 video container.

Next, using the admin token, the attacker triggered a transcode request pointing to an attacker-controlled remote item.xml playlist. Plex pulled the carrier video and fired up the transcoder. The moment the first frame decoded, the debug routine triggered. The malicious shared library hidden inside raw pixel values was written straight into the server’s internal Codecs directory.

Unsigned Libraries and Automatic Execution

Landing a file on disk is only half the battle; getting the process to execute it is the endgame. Plex’s bundled libavutil.so.58 library called avpriv_load_new_libs whenever the transcoder spawned. The routine swept the Codecs directory, scanning every file ending in .so and immediately invoking dlopen(path, RTLD_NOW).

This loader routine skipped foundational defensive checks: it required no standard naming prefix, skipped file permission verification, and enforced zero digital signature checks. Even an arbitrary shared library with standard read-only permissions was loaded straight into memory.

Under ELF shared library conventions, dlopen automatically executes functions defined in DT_INIT_ARRAY before symbol lookup, requiring no exported functions. The embedded constructor called fork() and execve(), spawning a reverse shell under uid=1000(plex). The full chain was complete.

LLMs Lower the Barrier to Exploit Chaining

The discovery-to-patch timeline moved quickly. The vulnerability researcher reported the findings on July 27, 2026. The Plex team confirmed and paid a bug bounty on August 4. Four CVEs were assigned on September 23, and details and patches were published on October 16.

Immediate in-the-wild exploitation followed disclosure. Honeypots recorded automated probing from 45.194.92.222, 91.92.40.202, and 176.65.139.228. Attackers staged payloads from 217.60.194.116.

Captured samples included a container image with hash 5712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441, and a UPX-packed binary 68938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7. Unpacked, the core binary hashed to b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49. Threat actors took advantage of always-on home edge hardware, silently running proxies and mining crypto.

The methodology behind the discovery also stood out. The researcher disclosed using an LLM-assisted workflow structured across reconnaissance, triage, verification, and chaining, with dedicated sub-agents attempting to disprove each intermediate hypothesis.

Human analysts directed architectural priorities and exploitation logic, while automated tools lowered the friction of auditing large codebases. The cost of connecting discrete low-severity bugs dropped precipitously, leaving unmanaged home edge devices on the front lines.

There were no elaborate memory corruptions in this chain. An unchecked header, an ignored return value inside a decorator, an unanchored prefix match, and unvalidated library loading—each stood alone as everyday developer oversights.

Plex’s assumption that localhost traffic could always be trusted turned these isolated errors into an unauthenticated master key. In the face of widespread automated scanning, updating to version 1.43.3 or later is critical to keep home servers from being pressed into mining gangs.

References:

  • zmain.info: plex2shell vulnerability writeup
  • BoredHackerBlog: plex2shell in the wild
  • GitHub code-zm/plex2shell
  • GitHub BoredHackerBlog/plex2shell_pcap
  • Hacker News discussion (item?id=50152321)