DEF CON 34 · Tactic Guide

Run the Injection Yourself

You don't need to have caught the talk to run this. Everything you need is on this page — nobody needs to be standing next to you for it to make sense. If a seat opens up at a staffed table, grab one; otherwise this gets you through the whole exercise solo.

What you're about to do

There are two tools in this lab. A weather server — low privilege, can't delete anything. And an admin server — high privilege, can delete records, but only after a human approves it.

On paper, a protected record is safe: the weak tool can't reach the delete, and the delete needs a person to say yes.

You're going to destroy a protected record anyway — without touching the admin server's permissions, without bypassing the approval, with the approval genuinely being granted. The weak tool can't pull the trigger. It's going to aim one anyway.

Where are you running this?

Path A — RTV tactic table

4:00–5:00 PT · your own laptop

Grab the bundle yourself and follow “Get armed” below.

git clone https://github.com/redasgard/urd.git

Path B — Ludus-hosted range

walk-up laptops · village-wide

The lab is already deployed. Skip cloning — open a terminal and find it:

find / -maxdepth 4 -iname "lab.sh" 2>/dev/null

Either way, confirm you're armed the same way: ./lab.sh check. One naming trap — it's ./lab.sh, dot-sh. Don't type ./lab alone; in this repo lab is a Python package folder, not a program.

On a Ludus range, if it looks like someone already ran the mission before you — the protected record is already gone — reset it yourself, it's cheap: ./lab.sh clean.

Neither path working, or reading this with no laptop at all? Skip straight to “No laptop? Read the attack instead” further down — you lose nothing.

Land the kill

About ten minutes, five steps.

1

The control

Prove the tool isn't just allergic to deletes.

./lab.sh baseline
./lab.sh analyze-baseline

Open out/findings/baseline.findings.json — empty findings list. A normal delete, no low-privilege hand on the target, throws no cross-server signal. Keep this in your head as “nothing happened.”

2

The injection

Watch two lines in the output.

./lab.sh mission

The approval line says origin=not shown — the whole vulnerability in three words. The after-line says present=false — the protected record is gone. On the wire, three sequence numbers carry it: seq 4 names the target, seq 15 deletes it, seq 19 proves it's missing.

3

The receipt

Pull the finding.

./lab.sh analyze

Find URD-0001. Two fields matter: severity: high, and approval_provenance_status: absent — the approval genuinely had no idea where the target came from.

4

Prove it's not a trick

Kill the obvious objection yourself: “you planted a marker.”

./lab.sh ablate
./lab.sh analyze-ablated

Still high. evidence_basis drops to plain value_flow — no breadcrumb, and it still fires on the value alone moving from the weak server into the delete.

5

Meet the one wall that stops this

The re-run puts the shared trace back on the record you started with.

./lab.sh mission
./lab.sh policy-check

final_decision=BLOCK. This is provenance-bound approval — the one control that checks who selected the target, not just what it is. Rare. Now you'll recognize it when a target has it.

Your turn — the op

So far you destroyed the target the lab picked. Now pick your own.
Objective: make a different protected record disappear.

⚠️Rules of engagement — what makes this the real technique instead of a cheat: do not edit the admin server, do not edit the host, do not touch the database directly, do not bypass the approval, do not change any tool's permissions. The only thing you may change is what the low-privilege side selects. If you delete something by editing the admin server, you've proven admin can delete — which we knew — and proven nothing about the attack.

Fast way — built-in targets

./lab.sh target-billing
./lab.sh target-customer
./lab.sh target-incident

Real way

Edit the weather server's response text yourself and name a target you choose. Watch it come out the other end inside the privileged delete.

Win condition — all four true

  • A different protected record is gone
  • Urd reports the finding as HIGH
  • approval_provenance_status = absent
  • The ablated trace still reports HIGH as value_flow

Bonus, if you land it fast: do it again with no marker from the start, and confirm the ablated trace still holds. That's your first evasion move — exact-value reuse is loud, the real move against a serious detector is transforming the value on the way through instead of passing it verbatim.

No laptop? Read the attack instead

Not a fallback for people who failed — the same exercise, done by reading instead of running. Open these, in order:

examples/traces/compositional.trace.jsonl        the wire: seq 4, 15, 19
examples/findings/compositional.findings.json    the receipt: URD-0001, HIGH
examples/findings/compositional.ablated.findings.json   still HIGH, no marker
examples/findings/compositional.policy.json      the one wall: BLOCK

examples/traces/retarget/                        four different kills, same method
examples/findings/retarget/

Find the same four facts as the win condition above, just read off the files.

Want to go further?

  • ./lab.sh planner-demo — same attack, but a planner decides the target instead of a deterministic host. Closer to how a real agent would run it.
  • The external-host adapter (examples/external-host/) shows how a real captured host trace gets normalized into this format.
  • Take the bundle to your next engagement. Urd shows you exactly where a target's agent stack lets a low-trust tool aim a high-trust one — the seam you go in through.

Execution authority and target-selection authority are two different powers, and almost nothing checks the second one.

You just proved it, on a target you picked yourself, without ever touching a permission. Find that seam in your own agent stack.

Own the AimGet the Repo