Skip to content

Web Challenge

In this challenge, the defender has a secret Macguffin, in this case a random code which the attacker (below) is trying to guess. Everything is running inside a single, locked-down Hardened JavaScript realm. The attacker’s code (pasted into the text box) is evaluated by the defender in a separate Compartment when the Execute button is pressed.

The secret consists of a ten-character alphanumeric code (about 52 bits of entropy). The attacker’s program gets a “check my guess” function, which returns a Promise that fires with true for a correct guess and false for a wrong one. If the program guesses correctly, red lights flash and the attacker wins.

To make things easier for our attacker, we’ve added a classic timing side-channel. Our check function tests one character at a time, and takes 10 milliseconds for each comparison. An attacker with full access to a clock would try all possible values for the first character and see which one takes the least time, concluding that the full password must start with that character. Then they iterate on the second character, and so on until they’ve worked out the full password, roughly 18 seconds later.

However, Hardened JavaScript is deterministic and has no timers by default. Shared intrinsics like new Date(), Date.now(), and Math.random() are disabled for programs confined to a Compartment after lockdown() in Hardened JavaScript.

So this attacker doesn’t get a clock, and cannot read from the covert channel. We provide an option to leak the real Date.now() to demonstrate that the attack works if attackers receive a clock.

Meanwhile, the defender running in the start Compartment gets access to powerful JS globals. This includes sources of non-determinism like window.setTimeout and window.crypto.getRandomValues as well as the DOM.

Secret

uninitialized

Guess

uninitialized

Attacker Code:

Sample Attacks

Try a sample attack! Each of these will configure and execute a sample attack.

View Source

Find this demo on Github.