Lion Cipher Lab
Close the rendered-output exfiltration channel in an LLM app (the zero-click markdown-image leak)
- AI Security
- Advanced
- ~110 min
- Free · no account required
Why it matters
Nearly everything written about attacks on LLM applications is about the INPUT side: how the malicious instruction gets into the model's context, and how you might filter it out. That framing has a comfortable implication — that if you got good enough at spotting bad instructions, you would be safe. This lab is about the other side of the pipeline, where that comfort disappears. Your application asks a model a question and then RENDERS the answer as rich text, because a chat UI that cannot show a screenshot is not a product. Rendering means your code takes a string the model produced and…
What you'll learn
- the zero-click rendered-output exfiltration channel: how RENDERING model output makes the user's own browser send your secret out, with no click
- the vocabulary, defined from zero: markdown, renderer, origin, indirect prompt injection, CSP, canary, zero-click vs one-click
- building an honest vulnerable app end to end — retrieval, a labelled stub model, a markdown+HTML renderer, and a fetcher — then breaking it with one poisoned document
- enumerating the carriers and measuring which auto-fetch: 7 of 10 leak with zero user interaction, 3 need exactly one click
- the PATH channel vs the QUERY channel, and why stripping query strings leaves the path channel wide open
- treating exfiltration as a BANDWIDTH problem: 1,477 raw bytes per 2,000-character image URL after base64url, so a 4 KB document leaves in one reply and 1 MB takes 36
- why output DLP is the wrong layer: a regex for the exact secret caught 3 of 11 encodings and 9 requests still reached the collector
- pricing the blunt fix: stripping every image stops all 7 zero-click carriers and breaks 5 of 5 image-bearing legitimate replies
- the three ways a trusted-domain allowlist still leaks — attacker-controlled path, open redirect, and userinfo-plus-late-decoding — each demonstrated with a real collector log line
- writing an allowlist that actually holds: full origin plus path prefix, no attacker-controlled query, decode BEFORE you validate, resolve redirects server-side
Steps in this lab
- What this lab is — and how it is different from the five you may have already done
- The vocabulary, from absolutely zero
- Why the model is a STUB, and exactly what that does and does not prove
- Open two terminals and check Python (zero-assumption setup)
- Terminal 1: build the local collector and the trusted-CDN stub
- Terminal 2: build the app, the renderer, the carriers, the defenses and the detection
- Run it — and here is the entire real output
- Read the break: what actually happened in sections 1, 2, 3 and 3b
- Section 4 — exfiltration is a bandwidth problem, so measure the bandwidth
- Defense 1 — output DLP, and why the obvious fix fails
- Defenses 2 and 3 — strip every image (priced), then the allowlist and its three bypasses
- Defense 4 — Content-Security-Policy, the layer that survives your renderer having a bug
- Defenses 5 and 6 — proxy everything server-side, and never auto-fetch
- Section 11 — the matrix: which layer stops which carrier
- Section 12 — four correct-looking fixes that still leak
- Section 13 — detection, because prevention is never the whole answer
- Judgment: when this applies, when it does not, why the popular fix is wrong, and how a pro does it
- Clean up, and the checklist to take to work