▣ TollBooth / OpenDoor

Setup

Get your environment running

Two independent paths to the same two scenarios. Run whichever your facilitator points you at — both were run end-to-end against this exact data before this guide was written, not just described.

Section 1 — Exact Arsenal setup, in a Kali Docker container

Original instructions: build one Kali image, clone to all laptops. tollbooth/Dockerfile does exactly that literally — tools, Claude Code, and the 10 skills are baked in at build time, rebuilt nightly, and published to GHCR, so every laptop just pulls the same pre-built image instead of building it (or a fleet of them) locally.

Prerequisites

Strip the quotes — this will break auth otherwise

casky-box/.env writes values as ANTHROPIC_API_KEY="sk-ant-..." (quoted). docker run --env-file does not strip quotes — it passes the literal string, quote marks included. Result: a key that starts with " instead of sk-ant-, and Claude Code fails with Invalid API key — looks like a bad/revoked key, isn't. Fix once, right after copying:

sed -i.bak -E 's/^([A-Z_][A-Z0-9_]*)="(.*)"$/\1=\2/' tollbooth/.env && rm tollbooth/.env.bak

Confirm it worked before moving on — should print 1, not 0:

grep '^ANTHROPIC_API_KEY=' tollbooth/.env | cut -d= -f2- | grep -c '^sk-ant-'

Steps

# Run every command below from THIS folder (casky-workshops/tollbooth/) — $(pwd) becomes the
# container's /root/tollbooth, and .env/the scripts only exist here, not the repo root.

# 0. Pull the pre-built image from GHCR (rebuilt nightly with the latest patches) instead
#    of building locally — much faster at a live session.
docker pull ghcr.io/casky-ai/casky-tollbooth:latest
docker tag ghcr.io/casky-ai/casky-tollbooth:latest casky-tollbooth

# Fallback, kept for reference — tshark/tcpdump/scapy, Claude Code, and the 10 skills are
# still baked into the image at build time (see Dockerfile); rebuild locally if you're
# iterating on the Dockerfile/skill list itself, or if GHCR is unreachable:
#   docker build -t casky-tollbooth .

# 1. Start a persistent Kali container with this folder mounted + the shared key available
docker run -d --name casky-tollbooth \
  --env-file .env \
  -v "$(pwd)":/root/tollbooth \
  casky-tollbooth

# Sanity check — catches a wrong-directory mount immediately instead of 3 steps from now
docker exec casky-tollbooth test -f /root/tollbooth/verify.sh \
  && echo "[+] mount OK — verify.sh found at /root/tollbooth/verify.sh" \
  || echo "[!] wrong directory — re-run from casky-workshops/tollbooth/, then: docker rm -f casky-tollbooth"

# 2. This folder is already at /root/tollbooth (bind-mounted in step 1 — no copy needed).
#    Run ./verify.sh — expect 9/9 PASS.
docker exec -w /root/tollbooth casky-tollbooth ./verify.sh

# 3. Print Arsenal-CheatSheet-Book.pdf (color; answer pages are red) — one per attendee.
open Arsenal-CheatSheet-Book.pdf   # macOS; use your OS's print/open command

# 4. Bash into the container and start Claude Code interactively — this is what you
#    actually drive during the exercise (steps 0-2 above were one-off unattended setup).
docker exec -it -w /root/tollbooth casky-tollbooth bash

#   ...now inside the container's shell:
export PATH="$HOME/.local/bin:$PATH"
./start.sh    # welcome banner + sanity check
claude        # drops into an interactive Claude Code session — paste the scenario
              # prompt from the cheat sheet (or this wiki's scenario pages) and let it work
Verified end-to-end

Tools/Claude Code/skills baked into the image at docker build time, pulled from GHCR: Claude Code on PATH, 10/10 skills linked, and a real round-trip against the Anthropic API:

$ claude --print "Reply with exactly the single word: PONG"
PONG
== tooling ==
  ok      tshark
  ok      jq
  ok      tcpdump
== pcap ==
  PASS  IMDS HTTP traffic present (2)
  PASS  SSRF request references metadata service (2)
  PASS  leaked AccessKeyId recoverable from pcap (2)
== cloudtrail ==
  PASS  attacker actions on leaked key (12)
  PASS  S3 GetObject exfil events (9)
  PASS  IAM enumeration present (2)
== scenario 2 (opendoor) ==
  PASS  S3 bucket policy is public (Principal *) (1)
  PASS  IAM privilege-escalation path present (3)
  PASS  IMDSv1 allowed (HttpTokens=optional)
----
RESULT: 9 passed, 0 failed
This laptop is READY.

./start.sh (step 4) confirmed too:

[+] lab data present: pcap + cloudtrail/ (S1) + opendoor/ (S2)
[+] agent skills mounted at /root/.claude/skills (10 skills)
[+] Claude Code found.
    mode: online (shared key)

Work Scenario 1 or 2 inside that interactive Claude Code session (skills already mounted), or fall back to the raw tshark/jq commands on the cheat sheet if the agent path isn't available. exit the claude session and the container shell (two exits) between attendees, then run docker exec -w /root/tollbooth casky-tollbooth ./reset.sh from outside to restore the data.


Section 2 — Same scenarios, run on Casky Box

Casky Box doesn't need per-laptop Claude Code installs or hand-picked skill symlinks — the casky-runner container already ships all 817 skills (including the 10 this kit hand-picks) mounted read-only, and its classifier picks the relevant ones from evidence automatically instead of you curating a list. The trade: no live "attack traffic" step here — TollBooth/OpenDoor's data is pre-built synthetic evidence, so this is a pure Path A (evidence-driven) investigation, not a lab-target exercise.

Prerequisites

Casky Box running — pulled from GHCR, no separate repo to clone. docker compose up -d automatically reads .env from the same directory as docker-compose.yml, no --env-file flag needed, and (unlike Section 1's docker run --env-file) Compose's own parser strips quotes correctly, so the file works as-is:

cd ../casky-box    # casky-workshops/casky-box, shared by every workshop
cp .env.example .env   # first time only — then set ANTHROPIC_API_KEY
docker compose pull
docker compose up -d

If casky-runner/casky-db are already up from earlier, this is a no-op — just confirm with docker compose ps. See casky-box/README.md for what's in it.

Steps

# Run every command below from THIS folder (casky-workshops/tollbooth/) — cloudtrail/*.json,
# opendoor/*.json below are relative paths. Live-caught twice: running from
# casky-box/ instead silently writes into the wrong folder and
# 'cloudtrail/*.json' matches nothing ("zsh: no matches found").
cd /path/to/casky-workshops/tollbooth
test -f lab-tollbooth.pcap && echo "[+] correct folder" || echo "[!] wrong folder — cd to casky-workshops/tollbooth first"
mkdir -p evidence   # this folder's own scratch output, gitignored — see evidence/README.md

# 1. Turn the pcap into readable evidence text (tshark isn't needed inside casky-runner —
#    reuse the Kali container from Section 1, or run tshark locally if you have it)
docker exec casky-tollbooth tshark -r /root/tollbooth/lab-tollbooth.pcap -Y http \
  -T fields -e frame.time -e ip.src -e ip.dst -e http.request.method -e http.request.full_uri -e http.response.code \
  > evidence/tollbooth-pcap.txt

# 2. Combine with the CloudTrail evidence (same incident, later stage of the same attack)
{
  echo "=== HTTP/pcap traffic (SSRF -> IMDS credential leak) ==="; cat evidence/tollbooth-pcap.txt
  echo; echo "=== CloudTrail events (same account, following hours) ==="
  jq -c '.Records[]' cloudtrail/*.json
} > evidence/tollbooth-full.txt

# 3. OpenDoor's evidence is already text — just concatenate the configs
for f in opendoor/*.json; do echo "--- $(basename "$f") ---"; cat "$f"; echo; done > evidence/opendoor-full.txt

# 4. Copy both into Casky Box's evidence bind mount — a LIVE bind mount, not a
#    copy-once. Anything dropped here shows up inside the running container immediately.
cp evidence/tollbooth-full.txt evidence/opendoor-full.txt ../casky-box/evidence/
docker exec casky-runner ls /var/casky/evidence/   # confirm they're already there

# 5. Investigate — TollBooth (reactive). --auto runs every step's agent for real;
#    drop --auto for the manual mode where you paste each step's tool output yourself.
docker exec -it casky-runner casky harness --auto -i /var/casky/evidence/tollbooth-full.txt

# 6. Investigate — OpenDoor (proactive)
docker exec -it casky-runner casky harness --auto -i /var/casky/evidence/opendoor-full.txt
Verified just now

Against the real TollBooth evidence, the classifier independently found and validated 7 MITRE techniques at 83.4% confidence from the combined pcap + CloudTrail text alone — no hints, no pre-picked skill list:

#TechniqueSkillCategory
1Exploit Public-Facing Application (T1190)exploiting-server-side-request-forgeryweb-app
2Unsecured Credentials: Credentials In Files (T1552.001)detecting-aws-credential-exposure-with-trufflehogcloud
3Gather Cloud Infrastructure Details (T1526)detecting-aws-cloudtrail-anomaliescloud
4Data from Cloud Storage (T1530)detecting-s3-data-exfiltration-attemptscloud
5Valid Accounts: Cloud Accounts (T1078.004)detecting-compromised-cloud-credentialscloud
6Unsecured Credentials: Cloud Instance Metadata API (T1552.007) — the core TollBooth storyanalyzing-kubernetes-audit-logscloud
7Remote Services: SSH (T1021.004)detecting-aws-cloudtrail-anomaliescloud

That's the same territory the original kit's 10 hand-picked skills cover, reached without anyone curating a list — including step 6 correctly landing on T1552.007, the exact technique this scenario is built around, purely from the evidence text.

--auto runs every step's agent for real and produces a real, inspectable tool-call transcript per step ([VERIFIED] Skill script executed: YES/NO) — a non-hallucination guarantee. Open casky-ui (http://localhost:8766) afterward for the Plan / Execution / Findings / Remediation tabs.

See the comparison table on the home page for what's different between Section 1 and Section 2.


Done for the day, or resetting for the next attendee? See Cleanup on the Troubleshooting page.