Hardened JavaScript Escape Room

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 SES 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 a SES-confined program only gets to access non-determinism in the start Compartment (the one that ran lockdown()), so this attacker doesn’t get a clock, and cannot read from the covert channel. Load this page with ?dateNow=enabled to demonstrate the attack with Date.now enabled, or with ?dateNow=disabled to properly confine the attacker.

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:

Click the button to populate the test code, then use Execute to run it.

(challenge source code)