Blog · Run notes

Encrypt Them All: the keys never touched a command line

Ali Hadi's Encrypt Them All, worked by the swarm: a rush for the same seat, a BitLocker volume opened by learning from each failed attempt, secrets passed by reference and never printed, and a critic that signed exact hashes.

By Halil Öztürkci ·

The run in figuresEncrypt Them All, on the basic flow
Case
Encrypt Them All, on the basic flow
Run
s2a59b2, 27 September 2026
Evidence
7.9 GB: one E01 image of a Windows machine, shared read-only into every agent's microVM and hashed at kickoff.
Team
3 on gpt-daybreak-blue, 3 on gpt-6-sol and 2 on gpt-6-luna, all through an openai-codex subscription; every agent in a microVM of the base image, every heavy step a job in the disk or full image
Agents
8
On the clock
15m 28s
Spend
No metered cost: a subscription (50M tokens)
Caps
400M tokens, $250 and 180 minutes for the swarm; 60M tokens per agent
Finish-line checks
8/8
Answers
Scored against the best-known answers: 4 correct, 1 partial, 0 wrong, 0 unanswered. The Part 3 message was not decrypted.
In the case ledger →

Challenge #9 of Ali Hadi's Digital Forensic Challenge Images is a single Windows machine that belongs to someone called Jane, and each of its parts is an encryption puzzle. Part 1, "Lost in Space", is an AES-encrypted README in her Documents with no known password; the brief suggests that the caches hold the conversation around it. Part 2, "Do Not Be Deceived!", is a volume named R2D2 under BitLocker full-disk encryption, with something hidden inside. Part 3, "Your Focus Determines Your Reality.", is a message encrypted to a public/private key pair whose keys file sits in her Downloads.

We had run this case before, on an older version of the harness, and that run had gone badly: it ran long, needed a library installed by hand halfway through, could not open the BitLocker volume at all, and was stopped by the examiner without ever reaching its sentinel. This post follows the rerun on the current platform. It describes every method the agents used and none of the answers, and it leaves out the names of the tools that would give an answer away, such as the exact encryption format of Part 1.

The setup

This run used the basic flow, the same as the Meeting Location run: each agent reasons in a microVM of its own with no forensic programs installed, and every extraction and parse runs as a job in a worker VM of the image that holds the program, its output sealed into the store and cited by job id. The team mixed gpt-daybreak-blue, gpt-6-sol and gpt-6-luna, all through a subscription, with a token cap, a spend cap and a wall clock, and no assignments.

The first minute: everyone wants the caches

The brief says the answer to Part 1 is probably in the caches, and the board shows that most of the team read it the same way. Within seconds of starting, one agent after another named itself after the caches: Cache Communicator, Cache communications examiner, Cache and communication examiner, Cache Comms Analyst. The hub refused one name outright as a duplicate. Then, just as fast, the others let go.

s2a59b201, #17

Overlap detected: s2a59b201 relinquishes caches.

That agent renamed again, and again, before it settled as Timeline Integrator. Another became the Registry and execution examiner because the caches already had enough volunteers. One took the file system journals, one the disk, one the BitLocker volume, one user activity, and one became the Key decryption examiner. Before the first minute was out, every agent held a distinct seat, and the key examiner pointed out the one that was still empty:

Key decryption examiner (s2a59b204), #24

One editor/critic gap remains … we should agree a non-author reviewer for report.

The console's story tab: the agents' first claims and intros, several on the caches, and the renames that sorted them into distinct seats
The negotiation read back from the board: several agents on the caches at once, then the renames that sorted them into distinct seats.

That gap was filled later by the journal examiner, who volunteered as critic and said it would not certify a report it had written.

Before the catalogue

For the first few minutes the agents had very little to work with. Their own VMs have no EWF or Sleuth Kit programs, so the image_layout tool saw an EWF container and could not look inside it. The BitLocker examiner recorded that as a limitation and was careful about what it meant:

Crypto/impact examiner (s2a59b206), #22

this limitation is explicit, not evidence of absent partitions.

The disk examiner's first job read the image properly from a worker: a logical NTFS volume with no partition table. Several agents then launched the same full file system listing at once, and with few workers the jobs queued behind each other. They sorted that out on the board too:

Registry and execution examiner (s2a59b203), #33

I cancelled my duplicate full-fls job j000012; s200 has j000006 queued, will search that output when ready.

When the kickoff's catalogue arrived a few minutes in, with the file list, the body file and the MAC timeline, the agents searched it almost immediately, and the Timeline Integrator asked the BitLocker examiner to retire its now-stale limitation. It did, with a narrower one in its place.

The agents tab: each agent's chosen name and what it said it was doing, its calls, tokens and failures, and its context with any hand-offs
Each agent with the name it chose and the work it described, with its calls and its context.

Part 1: the password, and why the other routes were dead

The cache seat found the conversation around the README early, in a job that extracted every cached object of the user's browser cache by inode, hashed them and searched them in one pass, so that every later claim cites one sealed manifest. From there the Key decryption examiner decrypted the file.

How it did that is the part I want every examiner to see. The job opened the sealed cache object, pulled the password out with a regular expression, asserted that there was a single match, and handed it to the decryption library in memory. The password never appears in the job's command, its arguments or the trace. On the board, the same agent asked its peers not to post it either:

Key decryption examiner (s2a59b204), #57

please send its location only, not value, to avoid exposure in board.

While that was happening, the journal examiner worked through the other routes the question lists and showed why none of them would have worked. There were no shadow copies. It extracted $J and $LogFile in a job and decoded the change journal with a hand-written parser that kept each record's byte offset; splitting the file references into their MFT number and sequence proved that the plaintext's old MFT entry had been reused by another file. $LogFile held no literal hits. And it said plainly that it had not carved unallocated space. The ledger records each of those dead ends as a search that found nothing, with its scope.

Part 2: opening the volume by learning from each failure

The BitLocker examiner's sequence is short and worth telling step by step, because it is what an examiner does at a bench and here it happened in sealed jobs in the full image.

It first asked which BitLocker programs the image held, and read the container's boot sector to find where the partition started. The first unlock attempt failed: the regular expression for the recovery password format found nothing, because the recovery material was stored as UTF-16. The next job printed the line structure to see why. The one after decoded UTF-16, matched the format, and called the unlock program, which answered that it could not parse the volume header, because no offset had been given. The next job passed the partition's byte offset and succeeded. The last one repeated the unlock read-only and sealed the decrypted view. At no point was the key printed.

Then the harness did something nobody asked for. When the encrypted container was sealed, the derived catalogue had already run the disk recipe over it and reported it partial: encryption detected. When the decrypted view was sealed, the catalogue took that too, completed it, and linked the new generation to the old one as a readable form of the same object. The BitLocker examiner found the hidden file's inode in the new generation's file list, another agent searched the decrypted volume for a passphrase clue, and when the BitLocker examiner asked for the image to be catalogued itself, the service answered that it was already catalogued, by content.

The Registry and execution examiner, meanwhile, was corroborating the volume's use from the other side, in separate artefact classes: Prefetch for the BitLocker programs, MountedDevices and MountPoints2 tying a drive letter to the volume's GUID, shortcut files in the Recent folder, and the BitLocker API event log. One event registered a recovery protector whose identifier matched the recovery material the BitLocker examiner had used, on the same volume GUID the registry named. When it found those events it announced them on the board with an exclamation mark, which is the most excited any agent got in this run.

The VM panel: one image digest for every agent's VM, the network each may reach, the placeholders where the keys would be, and each VM's life from creation to being put away
The agents' VMs: all from the same base image with no forensic programs, the network each may reach, and each VM's life from creation to being put away with its checks held.

Part 3: only what the evidence offered

The key pair and the keys file were found and the message's structure was listed without decrypting it. The private key is protected by a passphrase, and here the team drew a line. The Key decryption examiner tried exactly the candidates the evidence itself offered: the Part 1 password, and a string recovered during Part 2. Each was tried once, offline; the second was announced on the board before it ran:

Key decryption examiner (s2a59b204), #91

This is a one-candidate offline authentication attempt (not a wordlist/guessing loop); I will not reveal the candidate on command line or in trace, and will record failure if it fails.

Both failed, and the report says so. No brute force was attempted. None of our runs of this case has decrypted that message.

Keeping secrets out of the record

A ground rule in the goal said that no characters of a password should be shown unless a question asks for the value itself. The critic took it literally. Late in the run it noticed that some ledger entries and a timeline row still quoted the Part 1 password verbatim:

Journal recovery examiner (s2a59b205), #110

Ground rule says do not show any characters of a password unless a question asks for the value itself (goal only asks how recovered plaintext/password).

The cache seat superseded its entries with versions that name where the password is and do not print it, and the Timeline Integrator redacted the row and then relinked it. The ledger marks such entries as sensitive, the console keeps them blurred until they are clicked, and a package made for handing over can take out what a sensitive entry says while every chain in it still verifies.

Arguments and corrections

There were no vetoes in this run, but there was plenty of correction. An early inference that the decrypted README would carry the volume's unlock secret was withdrawn once the README was read and did not bear it out. An early reading of the browser history was contradicted by disk evidence and by the decryption itself; the entries were superseded, and the report says which reading stands.

The report had its own review. The disk examiner caught a wrong timeline row count. The key examiner and the critic caught that the report understated how many Part 3 candidates had been tried. After the secret-handling pass, the report still cited superseded ledger entries, and several agents grepped the file independently and posted the exact lines. The disk examiner put a hold on the report editor:

Disk Cartographer (s2a59b200), hold, #132

Your rev7 changed only line 5. I re-grepped hash 67a09454 and … stale refs remain.

The next revision cleared them, and peers re-verified it by hash. The critic then signed off on the report and the timeline by their hashes:

Journal recovery examiner (s2a59b205), #137

Sign-off is on these exact hashes; any subsequent report/timeline edits need re-review.

The every post tab: the primary thread with each agent's post count, and what the board adds up to: posts, who spoke, the busiest minute, the longest silence and the mix of tags
The board, counted from the files: who spoke, how often, the longest silence, and the mix of claims, asks, results, holds and stops.

What the harness did

At the finish, several agents called done in the same few seconds, and the harness refused each one: posts had landed after the report was last written. They read the late posts, said on the board that they were verification and sign-off only, and the disk examiner's next done wrote the sentinel.

The rest was quiet. The harness built the catalogue at the kickoff and the derived generations as the jobs sealed their outputs, reran in-VM tools that needed missing programs as jobs in the pack's image, nudged a few agents that went quiet, and ran their self-compactions. It blocked one direct write to a shared file and pointed the agent at the publish step. Nobody installed anything, nothing breached a lease, no cap fired, and every claim was released by the end.

The custody tab: the verdict and every check with its status
Custody at the stop: the evidence re-hashed in full, every chain sealed, every job output checked against its manifest, and the checks that did not apply named as such.

What it found and what it missed

Graded against the best-known answers (the challenge's author publishes no answer key):

Question What it asks Result Why, in method terms
Part 1 The README: where, how encrypted, how recovered, what it says Correct Located, the format identified, the recovery route found in the cache and proven by an authenticated decryption, the plaintext recorded. The password's value is deliberately withheld from the report.
Part 2 The R2D2 volume: the key, the decryption, what was hidden Correct Recovery material found and matched to the volume's protector, decrypted read-only in a job, the hidden content recovered and described. The older run could not decrypt it.
Part 3 The key pair, the message, what it was used for Partial The key material, the keys file and the message's structure are identified. The message itself was not decrypted: the private key's passphrase was not found in the evidence, and the evidence-derived candidates failed.
Timeline The user's encryption activity and communication Correct Built from the ledger's events, with the parts connected through the communication, the files and the event logs.
Approach Methods, tools, uncertainties Correct Stated in full, including the Part 3 limit. No tools were forged, and the report claims none.

The finish line certified the report's structure, that each part rests on a ledger finding with references, the timeline's length, the sign-off on the board and that the evidence was unchanged. Correctness is graded separately, above.

What I take from this run

Most of the difference from the older run is in the platform. The programs the older run lacked are in the job images now, so nobody had to install anything by hand, and the BitLocker volume opened in a handful of short jobs. The derived catalogue turned a sealed decryption into a searchable volume for the whole team without anyone asking. The claim violations that the older run's shared extraction directory produced are gone, because each job writes only its own output.

The part I did not design is the secret discipline. Nothing in the harness told the agents to pass a password by reference inside a job, to ask peers for a location instead of a value, or to supersede their own ledger entries to take a password out of them. The goal had one ground rule about showing passwords, and the team built a practice around it, then enforced it on each other.

The case ledger has this run beside the earlier run of the same case.