//nefariousplan

CVE-2026-35273: PeopleSoft's Environment Hub Deserializes Anyone Because It Was Built to Deserialize Its Agents

patterns

cve

proof of concept

On June 10, 2026, Oracle shipped an out-of-band Security Alert for CVE-2026-35273, an unauthenticated 9.8 in PeopleSoft PeopleTools that UNC6240 (ShinyHunters) had already been running against universities since May 27. A PeopleSoft administrator at one of those universities does the responsible thing: reads the advisory, searches the CVE, opens the top GitHub results, and starts hardening the file-upload path the proof-of-concepts point at. There is no file-upload bug. The three most-visible PoCs for this CVE describe a path-traversal-to-webshell chain that does not exist, the NVD record calls the flaw missing authentication, and the one artifact that actually fires sends a single POST to /PSEMHUB/hub with a Java object in a form field. Three sources, three different vulnerabilities, one CVE.

Three PoCs, three vulnerabilities, one CVE

Search GitHub for CVE-2026-35273 and the top results are detection scanners. Read them and they agree on a mechanism. They are all wrong about it in the same way.

The most complete of them, a 38KB Python scanner, ships a function called simulate_exploit_chain. It prints, in order, the attack it believes this CVE is:

exploit_flow = [
    ("GET",  "/PSEMHUB/hub", "Extract version and CSRF tokens"),
    ("POST", "/PSEMHUB/hub/upload", "Upload JSP webshell with path traversal"),
    ("GET",  "/PSEMHUB/../../webapps/ps/shell.jsp", "Access uploaded webshell"),
    ("GET",  "/PSEMHUB/../../webapps/ps/shell.jsp?c=whoami", "Command execution")
]

The same script probes for path traversal against the hub as if that were the primitive:

traversal_tests = [
    ("/PSEMHUB/../../../../etc/passwd", "Unix passwd test"),
    ("/PSEMHUB/hub/../../WEB-INF/web.xml", "Web.xml access test"),
    ("/PSEMHUB/%2e%2e/%2e%2e/%2e%2e/etc/passwd", "URL encoded traversal"),
]

A second repository is a byte-for-byte copy of a third, with the author name in the banner swapped and the original author's 0xBlackash-Scanner User-Agent string left in the request headers. All three look for the same thing: a /PSEMHUB/hub/upload endpoint, a JSP webshell, a directory traversal to /etc/passwd. None of those exist in this vulnerability. There is no upload endpoint in the exploit. There is no webshell written to the webroot. There is no .. in the payload.

The NVD record does not describe an upload either. It assigns CWE-306, Missing Authentication for Critical Function, and its prose says only that "an unauthenticated attacker with network access via HTTP" can compromise the system. The ProjectDiscovery nuclei template, marked verified: true, disagrees with both: it assigns CWE-502, Deserialization of Untrusted Data. Trend Micro's writeup calls it a Java deserialization SSRF chain that executes inside the WebLogic JVM.

Four sources. One says path traversal. One says missing authentication. One says deserialization. One says an SSRF-to-deserialization chain. They are looking at the same 9.8, the same endpoint, the same POST /PSEMHUB/hub. Only one of them ships a request that does anything.

The OPERATION parameter is a deserializer

Here is the request that fires, taken from the nuclei template:

POST /PSEMHUB/hub HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded

OPERATION={{generate_java_gadget("dns", "http://{{interactsh-url}}", "base64")}}

generate_java_gadget("dns", ...) produces a ysoserial-style URLDNS object: a native Java serialized object graph that, when passed through ObjectInputStream.readObject(), resolves a hostname as a side effect of being deserialized. The template confirms the hit with two matchers: the response body must contain rO0AB, and an out-of-band DNS interaction must fire.

rO0AB is not incidental. It is the base64 encoding of the bytes AC ED 00 05, the magic number that opens every Java serialization stream. When the response echoes rO0AB, the hub has serialized an object back to the caller. When the DNS callback lands, the hub has deserialized the object the caller sent. The OPERATION form field is fed to a Java object deserializer, and the deserializer runs before anything checks who sent it.

That is the whole primitive the scanners missed. Not a traversal, not an upload. A form parameter named OPERATION whose value is readObject-ed. The gadget in the nuclei template only resolves DNS, because a DNS callback is the safe way to prove deserialization without executing code. Swapping the URLDNS gadget for a CommonsCollections or CommonsBeanutils chain, the substitution ysoserial exists to make, turns the same request into code execution. The weaponized chain that ShinyHunters ran goes one step further: the published analysis has OPERATION invoking the hub's own message operations (FILECHUNKING, HANDLE_MESSAGE, REGISTER_WITHOUT_PEERNAME) to plant an XML file, which the hub's XMLDecoder deserializes into attacker-chosen objects on the next web-tier restart. Two deserialization sinks, one on the inbound request and one on the reboot, both reachable from a parameter the scanners treated as a place to look for ...

A defender who built detection from the top PoCs is watching for POSTs to /PSEMHUB/hub/upload, for %2e%2e in the path, for new .jsp files under the webroot. The actual exploit is a POST to /PSEMHUB/hub with a base64 blob in the body. Every indicator the PoCs published points away from it.

The hub was built to deserialize its peers

The reason OPERATION deserializes untrusted input is not that someone forgot a check. It is what the Environment Management Hub is.

PeopleSoft's Environment Management Framework is a fleet-management system. Agents (PSEMAgent) run on every host in a PeopleSoft environment and report their configuration to a central servlet, the hub, which resides in the PSEMHUB directory on the web server. Oracle's own documentation describes the hub as the component that "maintains an XML repository for all environment information collected by the EM Agents and routes all messages to and from the peers." The agents and the hub are peers on a message bus. OPERATION is the message. The hub deserializes it because a message bus that does not deserialize its messages is not a message bus.

There is no authentication on /PSEMHUB/hub because in the framework's design there is no untrusted sender. The peers are the environment. The agents are trusted the way one process on a host trusts another. The contract "only agents talk to the hub" is real, and it is enforced by nothing except the expectation that the hub sits on an internal deployment network where only agents can reach it. That expectation is documentation. The servlet reads OPERATION with full deserialization authority on every inbound HTTP request, from any source that can open a socket to it.

This is internal-only-by-convention: an input the framework treats as internal-IPC-only, read with security-relevant authority on every network request, where the internal-only status is a deployment assumption and not a runtime check. The Site header in FortiClient EMS was a tenant-router artifact that became a SQL statement the moment the internet could set it. OPERATION is an agent message that becomes a deserialization gadget the moment the internet can send it. Same shape. The framework wrote a trusted channel and shipped it on a public port.

Missing authentication and deserialize-anything are the same sentence

Now the four disagreeing sources reconcile.

NVD assigned CWE-306, Missing Authentication for Critical Function. The nuclei template assigned CWE-502, Deserialization of Untrusted Data. These read like two different bugs. They are one design decision, described from the two ends where it fails.

The hub deserializes the caller's object because it believes the caller is a peer. It does not authenticate the caller because, if the caller is a peer, there is nothing to authenticate. Missing authentication is not a second flaw sitting next to the deserialization. Missing authentication is why the deserialization is reachable, and the deserialization is what the missing authentication exposes. Remove either half and the bug is gone. Oracle shipped both, because both fall out of the same sentence: the hub trusts its peers, and treats every socket as a peer.

That is the content-is-command pattern in its oldest form. External content, the serialized OPERATION blob, feeds an interpreter, ObjectInputStream, that treats the bytes as instructions for which objects to construct and which methods to run during construction. Java native deserialization is content-is-command with fifteen years of published gadget chains behind it. We saw the same class in GlassFish evaluating Expression Language on the title of an XML it fetched: another Oracle-lineage Java server, another endpoint whose entire job is to fetch a document and hand it to an interpreter, another patch that retired one consumer and left the pipeline wired. The interpreter is always doing exactly what its spec says. The spec says OPERATION is a serialized message. The message was always going to be whatever the sender wanted to instantiate.

It is worth saying plainly what Oracle is accountable for here, because the CVE record softens it into "missing authentication." Oracle shipped a network-facing servlet whose documented purpose is to deserialize Java objects sent by remote peers, with no authentication, in a product deployed by universities and payroll departments to hold the most sensitive records an institution keeps. The unauthenticated deserialization endpoint is not a regression. It is the feature.

It fires on the restart, which is why nobody saw it

The reason this was a zero-day for two weeks and not a noisy one is in the second stage.

watchTowr's Jake Knott noted that CVE-2026-35273 "isn't just another trivial, easy-to-exploit single-request vulnerability." The XMLDecoder half of the chain deserializes the planted XML on the next web-tier restart, not on the inbound request. The code that runs the attacker's objects executes inside the application server's own JVM. There is no spawned child process to catch, no cmd.exe or /bin/sh under the Java process, and no outbound beacon required for the code itself to run. The exploit's trigger is a reboot the administrator performs, on a schedule the administrator sets, against a payload that arrived days earlier.

Google's Threat Intelligence Group, tracking the operators as UNC6240, confirmed exploitation between May 27 and June 9, 2026, and notified more than 100 organizations. Sixty-eight percent of them were higher-education institutions. The operators deployed MeshCentral remote-management agents renamed to look like Microsoft Azure binaries, moved laterally with per-victim fanout scripts, exfiltrated with zstd, and left ransom notes in PeopleSoft directories. Oracle's out-of-band alert landed on June 10. CISA set a June 15 KEV remediation deadline. The gap between first exploitation and vendor disclosure is two weeks, and this is disclosure-after-exploitation: the CVE exists because the intrusions did, not the other way around.

That timing is the last thing the PoCs got wrong. They are dated after June 10, written from the advisory, describing a mechanism the advisory does not contain and the exploit does not use. The administrator who patched to PeopleTools 8.62's fixed level closed the endpoint. The administrator who instead hardened the upload path, throttled directory traversal, and watched for new JSP files did the work the top search results asked for, against a bug that was never there, while a serialized object sat in an XML file waiting for the next maintenance window. The one thing every source agreed on was the endpoint. Only one of them read what it does with what you send it.

PoC: HORKimhab/CVE-2026-35273 and the ProjectDiscovery nuclei template

The patch adds a filter to the hub. The hub still reads what the network serializes to it, because reading what its peers serialize is the only thing the hub was ever for.