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. One container instead of a fleet — same steps, no physical laptops to provision.

Prerequisites

Strip the quotes — this will break auth otherwise

casky-runner-phase1/.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. Start a persistent Kali container with this folder mounted + the shared key available
docker run -d --name kali-tollbooth \
  --env-file .env \
  -v "$(pwd)":/root/tollbooth \
  kalilinux/kali-rolling sleep infinity

# Sanity check — catches a wrong-directory mount immediately instead of 3 steps from now
docker exec kali-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 kali-tollbooth"

# 1. tshark, jq (tcpdump, python3-scapy usually present on Kali)
docker exec kali-tollbooth bash -c \
  "apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y tshark jq tcpdump python3-scapy git curl ca-certificates"

# 2. Install Claude Code; pre-authenticate with the shared key
#    (ANTHROPIC_API_KEY is already in the container's env via --env-file above — Claude Code
#    picks it up automatically, no login flow needed)
docker exec kali-tollbooth bash -c "curl -fsSL https://claude.ai/install.sh -o /tmp/install.sh && bash /tmp/install.sh"
docker exec kali-tollbooth bash -c 'export PATH="$HOME/.local/bin:$PATH"; claude --version'

# 3. Mount skills into ~/.claude/skills
docker exec -w /root/tollbooth kali-tollbooth bash -c "chmod +x setup-skills.sh verify.sh reset.sh start.sh && ./setup-skills.sh"

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

# 5. 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

# 6. Bash into the container and start Claude Code interactively — this is what you
#    actually drive during the exercise (steps 0-4 above were one-off unattended setup).
docker exec -it -w /root/tollbooth kali-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

All packages installed clean, Claude Code 2.1.241 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 6) 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 kali-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-runner-phase1 running, using its own .env — nothing to copy in here, unlike Section 1. 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 /path/to/casky-runner-phase1
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.

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-runner-phase1/ instead silently writes into the wrong repo 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"

# 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 kali-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 \
  > 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 tollbooth-pcap.txt
  echo; echo "=== CloudTrail events (same account, following hours) ==="
  jq -c '.Records[]' cloudtrail/*.json
} > 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 > opendoor-full.txt

# 4. Copy both into casky-runner-phase1's evidence bind mount — a LIVE bind mount, not a
#    copy-once. Anything dropped here shows up inside the running container immediately.
cp tollbooth-full.txt opendoor-full.txt /path/to/casky-runner-phase1/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.