Ready Gladiator 2 picoCTF 2023 Solution

Published: April 26, 2023

Description

For the final duel you must win every single round against the Imp. A tuned bomber warrior is the classic 100% solution: it drops DAT instructions across memory on a stride that covers every cell the Imp will ever visit, killing the Imp before it completes a full sweep.

Craft or copy a warrior with a 100 percent win rate versus the Imp (Herem/Scimitar is a reliable bomber).

Test locally: pmars -r 100 herem.red imp.red and confirm 100/0 before connecting.

Pipe the warrior into nc saturn.picoctf.net 54217 and ensure it records 100 wins.

bash
cat <<'RED' > herem.red
;redcode-94
;name Herem/Scimitar
;author aCa
;strategy bomber tuned versus the Imp
bomb       dat #4, #4
start      add.ab #4, bomb
           mov.i   bomb, @bomb
           jmp     start
end start
RED
bash
pmars -r 100 herem.red imp.red
bash
nc saturn.picoctf.net 54217 < herem.red

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Use a bomber strategy
    Observation
    I noticed the challenge requires 100 percent wins against the Imp (not just a majority), which suggested a deterministic bomber strategy whose stride covers every cell the Imp can ever occupy, rather than a probabilistic fighter.
    Herem/Scimitar walks memory dropping DAT bombs every 4 cells. The Imp marches at 1 cell per turn, so a 4-stride bomb pattern covers every cell the Imp will ever visit.
    Learn more

    Herem/Scimitar is a tuned bomber written in ICWS-94 Redcode. Two modifier choices matter. add.ab adds only into the B-field of the bomb pointer, leaving the A-field alone; that is faster and more compact than add.f (which writes both fields), and matters when every cycle counts in a sweep race against the Imp. mov.i copies the entire instruction (both fields plus opcode) so the destination receives a real dat bomb, not just one field.

    Concrete trace. Picture an 8000-cell circular memory. Bomb stride is 4, so on successive cycles the bomber drops dat instructions into cells 0, 4, 8, 12, .... The Imp starts somewhere random (call it cell 1) and steps cell-by-cell: 1, 2, 3, 4, .... When the Imp reaches cell 4 (or any multiple of 4 that the bomber has already covered), it executes dat and dies. Even if the Imp's starting offset is unfavourable, the bomber laps it long before the Imp completes a full sweep of the core, because writing one bomb takes 1 cycle and the Imp only advances 1 cell per cycle while the bomber covers 4.

    The 100/100 rule is what raises the bar from Gladiator 1 to Gladiator 2. The server samples random starting offsets across rounds; a 100 percent win rate means the warrior beats the Imp regardless of where they start relative to each other in memory. A warrior that wins 99 of 100 had one offset configuration where the strategy fails, and the server will not award the flag. If you only see 99/1 locally with pMARS, change the bomb stride or pick a stronger warrior; do not just retry hoping for variance.

  2. Step 2
    Verify 100 wins
    Observation
    I noticed the server evaluates exactly 100 rounds with randomised starting offsets, which suggested running pmars with -r 100 locally to reproduce that exact condition and confirm zero losses before piping the warrior to the remote.
    Run pmars -r 100 herem.red imp.red locally to confirm 100/0 before submitting.
    bash
    pmars -r 100 herem.red imp.red
    bash
    nc saturn.picoctf.net 54217 < herem.red

    Expected output

    picoCTF{d3m0n_3xpu...24e}
    What didn't work first

    Tried: Skipping the local pmars test and piping a warrior that won 99/100 rounds directly to the server, hoping the unlucky offset does not appear.

    The server samples starting offsets across exactly 100 rounds and requires 100/0; a warrior that loses even one round will fail reliably because the server is not random in the sense of sometimes skipping the bad offset. If pmars -r 100 shows a 99/1 result locally, the warrior's stride must be fixed - tune it so it no longer misses any cell before connecting.

    Tried: Running pmars without the -r 100 flag (just pmars herem.red imp.red) and concluding the warrior is good enough from a single round result.

    A single-round pmars run uses only one starting offset and will almost certainly report a win, masking the one or two offsets where the strategy fails. The -r 100 flag is what randomises offsets across 100 rounds to match server behaviour; omitting it gives a false sense of certainty and leaves the losing offset undiscovered until the server rejects the submission.

    Learn more

    -r 100 runs 100 rounds with randomised starting offsets, matching the server setup. The standard cycle limit is 80000 per round; omitting -c uses the pMARS default, which is a faithful match for typical CTF server configurations. A warrior that wins 100-0 locally will succeed on the server.

    If you only manage 99/1 locally: tune the stride (Imp moves 1 per turn, so strides of 3, 4, or 5 are the candidate set; the key is that the bomber must seed enough cells to intercept the Imp before the Imp completes a full sweep, not that the stride must be coprime to the core size), or upgrade to a stronger warrior (a quick scan of the King of the Hill archives turns up dozens of 100/0 anti-Imp bombers).

    Beyond pure bomber tactics, this exact analysis (proving a strategy wins regardless of offset) is the spirit of formal verification: do not just observe a few good runs, prove the win condition holds across the entire input space.

Flag

Reveal flag

picoCTF{d3m0n_3xpu...24e}

Consistency is key: choose a warrior with deterministic wins rather than probabilistic ones.

Key takeaway

Winning every round under randomised starting conditions requires a strategy that is provably correct across all inputs, not just observed to work on a sample. A bomb stride that is fast enough to seed every region the Imp will walk through before the Imp completes a full sweep is what guarantees a win, regardless of whether the stride is coprime to the core size. Stride 4 covers every fourth cell (2000 cells in an 8000-cell core) and does so in 2000 cycles, while the Imp needs 8000 cycles for a full lap, so the Imp must hit a bombed cell before it can escape. Validating locally with the same round count and randomisation parameters as the server is the practical analog of formal verification: observe zero failures before committing.

Related reading

Want more picoCTF 2023 writeups?

Useful tools for Reverse Engineering

What to try next