Build1 publisher2 min readPublished Updated
HFS leaks the Math.random() state that signs its admin cookies
Rejetto HFS 3.0.0 through 3.2.0 signs session cookies with a Math.random() key, and five leaked PRNG outputs let an attacker forge an admin session. Exploitation began on October 1, and version 3.2.1 restores secure key generation.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- V8's Math.random() is xorshift128+, a deterministic generator whose 128-bit internal state fixes every value it has produced or will produce once recovered.
- A forged cookie naming the user admin lets set_config install a server_code snippet, and that snippet ran id as uid=0(root) in Horizon3.ai's test.
- The flaw is rated CVSS 9.8 under version 3.1 and classified CWE-338, use of a cryptographically weak PRNG.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure The chain reconstructs the server's secret from HTTP responses alone, so any reachable HFS admin API in this range is forgeable without credentials or local access.
- decision Operators who cannot upgrade immediately can set COOKIE_SIGN_KEYS, which replaces the Math.random() default with their own key and breaks the forgery.
- precedent Any code that seeds a secret from Math.random() and also returns some of its output shares this failure mode; here the handshake id exposed the generator state used for the key.
loginSrp1 is the first step of HFS's login, and anyone can call it with no credentials [7]. Each call runs Math.random() once and returns the result to the client as the session id, sitting inside the session cookie [7].
The cookie-signing key comes from the same generator. At startup HFS calls randomId(30), which runs Math.random() three times and concatenates the base-36 output into the key that signs every session cookie afterward [6]. HFS runs on Koa, and both the key and the handshake id are produced by Math.random() [5].
That generator is not secure. V8's Math.random() is xorshift128+, a deterministic algorithm with 128 bits of internal state, and once you recover the state you can compute every value it has produced or will produce [8]. Each output exposes only the 53-bit mantissa of a double, with the other 11 bits dropped in rounding [9].
Recovering the generator state is cheap. The attacker makes six unauthenticated loginSrp1 calls, then takes five consecutive session ids. For each one they recover the 53 visible bits and brute-force the 11 missing bits, 2,048 combinations in all [10]. Reversing the xorshift128+ recurrence then yields the one internal state consistent with every observation [10]. Rewind that state to startup and it reproduces the randomId(30) call that made the signing key. Because V8 rounds strings to their shortest form, a handful of candidate keys come out, and the attacker keeps the one whose HMAC matches a real cookie the server already sent [11].
The rest is Koa's own machinery. The attacker builds a session object naming the user admin and signs it with the recovered key the way Koa does. They send it to an admin-only endpoint such as get_config. It answers 200 with no login [12]. HFS lets admins register a server_code snippet that the server runs. The forged session calls set_config to install a payload that executes with the server process's privileges. In Horizon3.ai's test, running id returned uid=0(root) [13].
The attacker never touches the filesystem, process memory, or environment variables [14]. Zach Hanley at Horizon3.ai found the bug in September 2026 using Claude for the analysis, and real-world exploitation started on October 1 [3][4]. It affects HFS 3.0.0 through 3.2.0 [2].
HFS 3.2.1 swaps both weak spots for Node's secure primitives. The signing key becomes randomBytes(32).toString('base64url'), 256 bits from the OS CSPRNG. The handshake id becomes randomUUID(), so it no longer leaks Math.random() [15]. If you cannot move to 3.2.1 yet, set COOKIE_SIGN_KEYS and the server signs with your key instead of the Math.random() default [16].
What to watch
- Whether the October 1 exploitation Horizon3.ai reported spreads, and to which internet-facing HFS deployments.
- Whether the same technique gets turned on other Koa or Node apps that seed keys from Math.random() now the method is public.
- Whether CISA or Linux distributions flag CVE-2026-61500 for mandated patching.