
Python PoC and Docker lab for CVE-2026-61500: recovers Rejetto HFS V8 PRNG state to forge an admin session cookie and achieve RCE via server_code.
CVE-2026-61500 is an unauthenticated session-forgery vulnerability in Rejetto
HFS 3.0.0 through 3.2.0. HFS generated its Koa session-cookie signing key with
JavaScript Math.random() and exposed outputs from the same V8 PRNG in the
unauthenticated SRP login handshake. An attacker can reconstruct the PRNG state,
recover the signing key, forge an administrator session, and use the documented
server_code configuration feature to execute server-side JavaScript.
This repository contains a Python proof of concept and a disposable Docker comparison using the official HFS 3.2.0 and 3.2.1 images. The publication build is intentionally restricted to HTTP targets on the local loopback interface.
| Statement | Status |
|---|---|
| Recover V8 xorshift128+ state from unauthenticated login responses | Confirmed |
| Recover the active HFS cookie-signing key | Confirmed |
| Forge a session accepted as HFS administrator | Confirmed |
Execute a benign server_code marker in official HFS 3.2.0 | Confirmed |
| Receive a root reverse shell inside the isolated Compose network | Confirmed |
| Stop before session forgery on official HFS 3.2.1 | Confirmed |
| Internet-wide targeting or persistence | Not provided or claimed |
loginSrp1 API requests for the known admin
account. Each vulnerable response places a numeric loggingIn.sid and its
signed session cookie in Set-Cookie headers.randomId(30). The PoC
accounts for V8 shortest-string rounding and checks candidates against an
observed hfs_http.sig HMAC, which also identifies the startup offset.username: admin, then call
get_config to prove that the forged cookie has administrator access.set_config with a small server_code module. The default payload
writes a benign marker in /data; --command is available only for the
loopback Docker lab.This is a black-box HTTP chain: the PoC does not read files, memory, environment variables, or process state from the target. Source knowledge is used to model the vulnerable algorithm.
59472e534bf7e056d708382d02935c2eaf956927.The fix replaces the signing key with 32 bytes from Node.js randomBytes() and
replaces the exposed numeric login identifier with randomUUID(). Supplying an
explicit strong COOKIE_SIGN_KEYS value mitigates signing-key prediction, but
upgrading remains the recommended remediation.
rejetto/hfs:v3.2.0, digest
sha256:d6765e93b68de222583be7788afad699695fd08aa2f56377337f5139779e0746.rejetto/hfs:v3.2.1, digest
sha256:61db4da1f494df254aa7f48889c676b424b413e45b7b92cf4276f4b8e632aaec.python:3.13-alpine, digest
sha256:1a63a53928ce53d2b0baf08092a703f4840ac5dfbd61fd48802dbf48e08c801e.Both HFS services bind only to host loopback; the callback publishes no port.
The lab creates a synthetic
administrator because loginSrp1 must be invoked for an existing username; the
password is neither known nor used by the exploit.
Requirements are Docker with Compose, Python 3.10 or newer, and curl.
./verify.sh
The verifier removes only lab/runtime/vulnerable and lab/runtime/fixed,
starts both digest-pinned images, runs the positive and negative controls, and
stops the containers by default. Use KEEP_LAB=1 ./verify.sh to leave the lab
running for inspection.
With the lab retained, the direct benign-marker invocation is:
python3 cve-2026-61500-poc.py \
--target http://127.0.0.1:28182 \
--marker cve-2026-61500-rce-marker.txt
To demonstrate command execution inside the owned lab container:
python3 cve-2026-61500-poc.py \
--target http://127.0.0.1:28182 \
--command 'id > /data/cve-command-output.txt'
Any non-loopback hostname, HTTPS target, or remote IP is rejected by argument
validation. The exploit modifies HFS server_code; use only the disposable lab
or a system for which you have explicit authorization.
The recorded demo goes one step further: demo.sh starts the unexposed
callback service on the Compose network and uses --command to connect a Bash
reverse shell to it. The callback sends only id, uname -a, pwd, and
exit, records the transcript under ignored lab/runtime/, and closes. No
callback port is bound to the host.
The real 2026-09-26 run recovered one PRNG state and its signing key, received
HTTP 200 for a forged administrative get_config, installed the marker payload,
and observed CVE_2026_61500_RCE_CONFIRMED in the vulnerable container. A
separate --command 'id > /data/cve-command-output.txt' control produced
uid=0(root) gid=0(root) groups=0(root) inside that official container. The
recorded Docker-only reverse shell independently returned the same root identity
and /data working directory. Against
3.2.1, the first login response contained an opaque UUID and the PoC exited with
status 3 before attempting session forgery. See
docs/example-output.txt and
docs/e2e-results.json.
On 2026-09-26, exact CVE and exploit/PoC searches were run against SearchSploit (local Exploit-DB index), GitHub-indexed web results, Packet Storm, Exploit-DB, and the general web. No working public exploit was identified at that point; results found CVE/advisory metadata and exploit-tracking pages only. This is a dated, best-effort result, not a claim that no exploit can exist elsewhere or appear later.
<[email protected]>
(Twitter: @aramosf).For authorized security research, defensive validation, and education only. You are responsible for obtaining permission and complying with applicable law.