portswigger/http-terminator/desync-validate-0cl

Claude Code and agent skill by portswigger/http-terminator

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

build a validated proof of concept for a 0.CL desync/response-poisoning vulnerability

Install

npx vskill@latest install portswigger/http-terminator/desync-validate-0cl

SKILL.md

0.CL Desync response poisoning

Response poisoning occurs when you smuggle a request and the VICTIM receives a poisoned response containing attacker-controlled content.

0.CL Mechanism

In a 0.CL desync, the front-end ignores the Content-Length header but the back-end honors it:

  1. Attacker sends: the desync trigger request (no body)
  2. Front-end ignores CL -> treats POST as complete (no body)
  3. Back-end sees CL -> waits for body data
  4. Front-end gets early response, sends victim's request on same connection
  5. Back-end reads first N bytes of victim's request as POST body (N = CL value)
  6. Remaining bytes of victim are treated as a NEW request
  7. Victim receives the response to this new request - THIS IS RESPONSE POISONING

The smuggled request line is embedded in the victim's headers at a calculated offset. The CL value skips past the victim's initial bytes so the back-end starts parsing from the smuggled line.

Key differences from CL.0

CL.00.CL
Trigger + prefix sent togetherTrigger sent alone (no body)
Separate smuggled prefixSmuggled line embedded in victim
CL = length of prefixCL = offset of smuggled line in victim
Back-end reads prefix as trigger bodyBack-end reads victim bytes as trigger body
Needs path that ignores CLNeeds early-response gadget

Critical Direction

Response poisoning = Victim receives attacker's smuggled response

NOT: Attacker receives victim's response (that's victim response theft / VRT)

Impact

Poisoned responses can deliver XSS payloads, redirect victims to attacker-controlled domains, or serve arbitrary attacker-controlled content to arbitrary users.

Triggering response poisoning

  1. Retrieve the desync trigger request from the organizer. Paste it VERBATIM into trigger_request — do NOT rewrite, simplify, or omit any headers or the path. The trigger already contains everything needed (path, headers, early-response gadget). The ONLY change is replacing the Content-Length value with %s.
  2. Find an endpoint where the smuggled request's response will contain attacker-controlled content (a canary).

Use the turbo simulator MCP to run the micro-crawl.py example script (turbo://examples/micro-crawl) to find endpoints that reflect input.

  1. Using the desync-validated-0cl.py template as a starting point, create a script which provides:
  • The trigger request: the EXACT desync trigger from the organizer, pasted verbatim, with only the Content-Length value replaced by %s, ending with \r\n\r\n (NO body). Do NOT change the path, remove headers, or rewrite the request.
  • The smuggled line: a single request line (e.g. GET /reflect?q=$CANARY$ HTTP/1.1). This gets automatically embedded in the victim's headers by the template.
  • The victim request: a simple GET request with $randomplz in the path to bust the cache (ensures the victim reaches the backend rather than getting a cached response). The template automatically inserts the smuggled line.
  • attacker_fingerprint: use $CANARY$ if you have input reflection.
  1. Pass the script directly to mcp__turbo-simulator__start_run as the script parameter. Do NOT write the script to a file — just pass it inline. Wait patiently for completion.
  2. Check the run status with turbo://runs/{run_id}?wait=true. If the status message contains 'poisoned!', find the response with that label using search_responses(run_id, query="poisoned!", search_in="labels") and review it.

a) If response poisoning succeeded, submit for validation (see below) b) If not, consider why it failed and retry. Analyze the script and refer to "Reporting progress" and "If there's no evidence of poisoning"

MANDATORY: You MUST use desync-validated-0cl.py as the base for your script

Reporting progress

If the script ran fine but there's no evidence of poisoning, output this:

DESYNC-SAVE-ID: <run-id>

If you see a highly anomalous response that may have been intended for a third party (not a simulated victim) at any point, save it to the organizer with the note ANOMALY. This ANOMALY case is the ONLY time you may call save_to_organizer yourself.

Checking your results

Before claiming success, you MUST spawn a validation agent to verify:

Task(
subagent_type="general-purpose",
prompt="Use the desync-validate skill to validate run_id: {run_id}",
description="Validate desync run"
)

The validator starts fresh with no knowledge of your reasoning and independently evaluates the evidence.

If validation fails, review the reason and adjust your approach. You can retry and re-validate. Only claim success after the validator confirms it.

Reporting final results

After your attempts are complete, you MUST end your response with exactly one of:

If poisoning succeeded (you have a run with "poisoned!" results):

DESYNC-RUN-ID: <run_id>

If poisoning failed despite multiple attempts:

DESYNC-OUTCOME: desync-fail

Do NOT save to the Organizer yourself

The pipeline owns ALL evidence saves and the authoritative classification. You MUST NOT call save_to_organizer for any normal result — success OR failure. On a desync-fail outcome, save NOTHING: a desync that only makes the victim error (e.g. a 400/connection-level effect) with no attacker-controlled content is NOT response poisoning, and must not be written to the Organizer as "evidence". The pipeline saves representative requests automatically when — and only when — poisoning is confirmed.

The single exception is a genuine third-party ANOMALY response (see "Reporting progress"). Just emit DESYNC-RUN-ID: or DESYNC-OUTCOME: desync-fail and stop.

If there's no evidence of poisoning

On some servers, response poisoning is very easy to trigger using tens of requests per second. On others, it's hard.

To get a list of techniques/ideas that may help, run hypothesis_report.py

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
125
Repository forks
12
7-day trend score
0

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/portswigger/http-terminator/desync-validate-0cl/badge)](https://verified-skill.com/skills/portswigger/http-terminator/desync-validate-0cl)

Something wrong with this skill or its page? Tell us.