feat: add HTTP request smuggling skill#405
feat: add HTTP request smuggling skill#405sandiyochristan wants to merge 2 commits intousestrix:mainfrom
Conversation
Add a new vulnerability skill covering HTTP request smuggling (HRS) across CL.TE, TE.CL, H2.CL, and H2.TE desync variants. HRS is absent from the existing skill set despite being a distinct, high-impact vulnerability class frequently present in any architecture using a reverse proxy or CDN in front of an application server. Coverage: - CL.TE: front-end uses Content-Length, back-end uses Transfer-Encoding - TE.CL: front-end uses Transfer-Encoding, back-end uses Content-Length - H2.CL: HTTP/2 front-end downgrades to HTTP/1.1 with injected Content-Length - H2.TE: Transfer-Encoding header injection through HTTP/2 desync - Transfer-Encoding obfuscation techniques (tab, space, duplicate, xchunked) - Front-end security control bypass via smuggled prefix - Cross-user request capture for session token theft - Response queue poisoning and WebSocket handshake hijacking - Timing-based and differential response detection methodology - HTTP/2 specific probing techniques Includes raw HTTP examples for each variant, step-by-step testing methodology, exploitation PoCs, false-positive conditions, and infrastructure topology guidance.
Greptile SummaryThis PR adds However, there are a few technical inaccuracies that should be corrected before merging, since agents load these skills verbatim and would act on incorrect information:
Confidence Score: 4/5Safe to merge after fixing the inverted TE.CL timing-probe description — the remaining issues are accuracy improvements that reduce agent confusion but are not blocking. One P1 correctness issue: the TE.CL timing-probe description is mechanically inverted and would cause an agent to construct the wrong type of probe (socket-poisoning instead of timeout). The remaining P2 items (pseudo-header terminology, PoC Content-Length values, \x20 representation) are meaningful accuracy concerns but do not render the skill non-functional. Score is 4 rather than 5 because the P1 item is a factual error in the core detection methodology section. strix/skills/vulnerabilities/http_request_smuggling.md — specifically the TE.CL timing-probe description, H2.CL pseudo-header references, and PoC Content-Length values. Important Files Changed
Prompt To Fix All With AIThis is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 176
Comment:
**TE.CL timing-probe description is inverted**
The description says "Content-Length set to *fewer* bytes than the chunk content. TE.CL back-end waits for more bytes per Content-Length." These two sentences contradict each other and the mechanics are wrong.
In TE.CL, the **front-end uses Transfer-Encoding** (so it reads until the `0\r\n\r\n` terminator and happily forwards a complete request), while the **back-end uses Content-Length**. For a *timing* probe you want the back-end to hang — that means `Content-Length` must declare **more bytes than the body actually provides**, so the back-end waits for additional data that never arrives.
If Content-Length is *fewer* bytes than the chunked body, the back-end simply reads its allotted bytes and returns a response immediately — this causes **socket poisoning / differential-response detection**, not a timeout.
Suggested fix:
```suggestion
**TE.CL:** Send a request with a complete chunked body (including the `0\r\n\r\n` terminator so the front-end is satisfied) but with `Content-Length` set to **more** bytes than the body actually provides. The back-end, using Content-Length, waits for the remaining bytes that never arrive — producing a 10–30 second timeout.
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 75
Comment:
**`content-length` is not an HTTP/2 pseudo-header**
The sentence describes the injected header as being placed in "HTTP/2 request pseudo-headers." HTTP/2 pseudo-headers are the four fields that begin with `:` — `:method`, `:path`, `:authority`, and `:scheme`. `content-length` is a *regular* header in HTTP/2, not a pseudo-header. The code example correctly shows it without the `:` prefix, but the prose is misleading.
The same incorrect term appears again in **Pro Tip #4 (line 248)**, which refers to `":content-length" pseudo-header` — there is no such HTTP/2 pseudo-header; the correct name is just `content-length`.
```suggestion
But when the front-end downgrades to HTTP/1.1 for the back-end, an attacker can inject a `content-length` header in the HTTP/2 request that conflicts with the actual body length:
```
And at line 248:
```
H2.CL is the most impactful modern variant — many CDNs translate HTTP/2 to HTTP/1.1 and inject `Content-Length` from the `content-length` regular header sent in the HTTP/2 request.
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 110
Comment:
**Content-Length values in PoC examples appear miscalculated**
The `Content-Length: 116` on this line (the Front-End Bypass PoC) and `Content-Length: 129` on line 132 (the Cross-User Capture PoC) are the outer request's Content-Length — they must match the exact byte count of the entire smuggled body (including CRLF line endings).
Quick manual count for the Bypass PoC body (assuming `\r\n` line endings):
- `0\r\n` = 3
- `\r\n` = 2
- `GET /admin HTTP/1.1\r\n` = 21
- `Host: target.com\r\n` = 18
- `X-Forwarded-Host: target.com\r\n` = 30
- `Content-Length: 10\r\n` = 20
- `\r\n` = 2
- `x=1` = 3
**Total ≈ 99**, not 116. If an agent uses the skill verbatim it will send a malformed outer request that most servers will reject or mis-parse.
Because the Validation section explicitly asks for "exact raw bytes," these values should be corrected, or the examples should include a clear callout that the Content-Length must be recomputed to match actual byte counts before use.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 186
Comment:
**`\x20` is a literal four-character string in a Markdown code block**
Inside a fenced code block, `\x20` is rendered as the four characters `\`, `x`, `2`, `0` — not as a space byte (0x20). An agent reading this skill may literally include `\x20` in a header value, producing an invalid header rather than the intended leading-space obfuscation.
Similarly, line 188 has the comment `# tab before value` but no tab character is visible — the obfuscated tab should be represented as an actual tab or documented with a clear note (e.g. `<TAB>`).
```suggestion
Transfer-Encoding: xchunked # non-standard value, some FE ignore, BE accept
Transfer-Encoding: chunked # leading space before value (send literal 0x20 before "chunked")
Transfer-Encoding: chunked # tab character before value (send literal 0x09 before "chunked")
Transfer-Encoding: x
Transfer-Encoding: chunked # duplicate TE headers, BE uses last
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat: add HTTP request smuggling skill" | Re-trigger Greptile |
| ``` | ||
| If response is delayed 10–30 seconds, CL.TE desync likely. | ||
|
|
||
| **TE.CL:** Send a request with `Transfer-Encoding` present but `Content-Length` set to fewer bytes than the chunk content. TE.CL back-end waits for more bytes per Content-Length. |
There was a problem hiding this comment.
TE.CL timing-probe description is inverted
The description says "Content-Length set to fewer bytes than the chunk content. TE.CL back-end waits for more bytes per Content-Length." These two sentences contradict each other and the mechanics are wrong.
In TE.CL, the front-end uses Transfer-Encoding (so it reads until the 0\r\n\r\n terminator and happily forwards a complete request), while the back-end uses Content-Length. For a timing probe you want the back-end to hang — that means Content-Length must declare more bytes than the body actually provides, so the back-end waits for additional data that never arrives.
If Content-Length is fewer bytes than the chunked body, the back-end simply reads its allotted bytes and returns a response immediately — this causes socket poisoning / differential-response detection, not a timeout.
Suggested fix:
| **TE.CL:** Send a request with `Transfer-Encoding` present but `Content-Length` set to fewer bytes than the chunk content. TE.CL back-end waits for more bytes per Content-Length. | |
| **TE.CL:** Send a request with a complete chunked body (including the `0\r\n\r\n` terminator so the front-end is satisfied) but with `Content-Length` set to **more** bytes than the body actually provides. The back-end, using Content-Length, waits for the remaining bytes that never arrive — producing a 10–30 second timeout. |
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 176
Comment:
**TE.CL timing-probe description is inverted**
The description says "Content-Length set to *fewer* bytes than the chunk content. TE.CL back-end waits for more bytes per Content-Length." These two sentences contradict each other and the mechanics are wrong.
In TE.CL, the **front-end uses Transfer-Encoding** (so it reads until the `0\r\n\r\n` terminator and happily forwards a complete request), while the **back-end uses Content-Length**. For a *timing* probe you want the back-end to hang — that means `Content-Length` must declare **more bytes than the body actually provides**, so the back-end waits for additional data that never arrives.
If Content-Length is *fewer* bytes than the chunked body, the back-end simply reads its allotted bytes and returns a response immediately — this causes **socket poisoning / differential-response detection**, not a timeout.
Suggested fix:
```suggestion
**TE.CL:** Send a request with a complete chunked body (including the `0\r\n\r\n` terminator so the front-end is satisfied) but with `Content-Length` set to **more** bytes than the body actually provides. The back-end, using Content-Length, waits for the remaining bytes that never arrive — producing a 10–30 second timeout.
```
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| ### H2.CL — HTTP/2 Front-end Downgrades to HTTP/1.1, Injects Content-Length | ||
|
|
||
| HTTP/2 has no `Content-Length` vs `TE` ambiguity in its own framing. But when the front-end downgrades to HTTP/1.1 for the back-end, an attacker can inject a `Content-Length` header in the HTTP/2 request pseudo-headers that conflicts with the actual body length: |
There was a problem hiding this comment.
content-length is not an HTTP/2 pseudo-header
The sentence describes the injected header as being placed in "HTTP/2 request pseudo-headers." HTTP/2 pseudo-headers are the four fields that begin with : — :method, :path, :authority, and :scheme. content-length is a regular header in HTTP/2, not a pseudo-header. The code example correctly shows it without the : prefix, but the prose is misleading.
The same incorrect term appears again in Pro Tip #4 (line 248), which refers to ":content-length" pseudo-header — there is no such HTTP/2 pseudo-header; the correct name is just content-length.
| HTTP/2 has no `Content-Length` vs `TE` ambiguity in its own framing. But when the front-end downgrades to HTTP/1.1 for the back-end, an attacker can inject a `Content-Length` header in the HTTP/2 request pseudo-headers that conflicts with the actual body length: | |
| But when the front-end downgrades to HTTP/1.1 for the back-end, an attacker can inject a `content-length` header in the HTTP/2 request that conflicts with the actual body length: |
And at line 248:
H2.CL is the most impactful modern variant — many CDNs translate HTTP/2 to HTTP/1.1 and inject `Content-Length` from the `content-length` regular header sent in the HTTP/2 request.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 75
Comment:
**`content-length` is not an HTTP/2 pseudo-header**
The sentence describes the injected header as being placed in "HTTP/2 request pseudo-headers." HTTP/2 pseudo-headers are the four fields that begin with `:` — `:method`, `:path`, `:authority`, and `:scheme`. `content-length` is a *regular* header in HTTP/2, not a pseudo-header. The code example correctly shows it without the `:` prefix, but the prose is misleading.
The same incorrect term appears again in **Pro Tip #4 (line 248)**, which refers to `":content-length" pseudo-header` — there is no such HTTP/2 pseudo-header; the correct name is just `content-length`.
```suggestion
But when the front-end downgrades to HTTP/1.1 for the back-end, an attacker can inject a `content-length` header in the HTTP/2 request that conflicts with the actual body length:
```
And at line 248:
```
H2.CL is the most impactful modern variant — many CDNs translate HTTP/2 to HTTP/1.1 and inject `Content-Length` from the `content-length` regular header sent in the HTTP/2 request.
```
How can I resolve this? If you propose a fix, please make it concise.| ```http | ||
| POST /not-restricted HTTP/1.1 | ||
| Host: target.com | ||
| Content-Length: 116 |
There was a problem hiding this comment.
Content-Length values in PoC examples appear miscalculated
The Content-Length: 116 on this line (the Front-End Bypass PoC) and Content-Length: 129 on line 132 (the Cross-User Capture PoC) are the outer request's Content-Length — they must match the exact byte count of the entire smuggled body (including CRLF line endings).
Quick manual count for the Bypass PoC body (assuming \r\n line endings):
0\r\n= 3\r\n= 2GET /admin HTTP/1.1\r\n= 21Host: target.com\r\n= 18X-Forwarded-Host: target.com\r\n= 30Content-Length: 10\r\n= 20\r\n= 2x=1= 3
Total ≈ 99, not 116. If an agent uses the skill verbatim it will send a malformed outer request that most servers will reject or mis-parse.
Because the Validation section explicitly asks for "exact raw bytes," these values should be corrected, or the examples should include a clear callout that the Content-Length must be recomputed to match actual byte counts before use.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 110
Comment:
**Content-Length values in PoC examples appear miscalculated**
The `Content-Length: 116` on this line (the Front-End Bypass PoC) and `Content-Length: 129` on line 132 (the Cross-User Capture PoC) are the outer request's Content-Length — they must match the exact byte count of the entire smuggled body (including CRLF line endings).
Quick manual count for the Bypass PoC body (assuming `\r\n` line endings):
- `0\r\n` = 3
- `\r\n` = 2
- `GET /admin HTTP/1.1\r\n` = 21
- `Host: target.com\r\n` = 18
- `X-Forwarded-Host: target.com\r\n` = 30
- `Content-Length: 10\r\n` = 20
- `\r\n` = 2
- `x=1` = 3
**Total ≈ 99**, not 116. If an agent uses the skill verbatim it will send a malformed outer request that most servers will reject or mis-parse.
Because the Validation section explicitly asks for "exact raw bytes," these values should be corrected, or the examples should include a clear callout that the Content-Length must be recomputed to match actual byte counts before use.
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| ```http | ||
| Transfer-Encoding: xchunked # non-standard value, some FE ignore, BE accept | ||
| Transfer-Encoding:\x20chunked # leading space |
There was a problem hiding this comment.
\x20 is a literal four-character string in a Markdown code block
Inside a fenced code block, \x20 is rendered as the four characters \, x, 2, 0 — not as a space byte (0x20). An agent reading this skill may literally include \x20 in a header value, producing an invalid header rather than the intended leading-space obfuscation.
Similarly, line 188 has the comment # tab before value but no tab character is visible — the obfuscated tab should be represented as an actual tab or documented with a clear note (e.g. <TAB>).
| Transfer-Encoding:\x20chunked # leading space | |
| Transfer-Encoding: xchunked # non-standard value, some FE ignore, BE accept | |
| Transfer-Encoding: chunked # leading space before value (send literal 0x20 before "chunked") | |
| Transfer-Encoding: chunked # tab character before value (send literal 0x09 before "chunked") | |
| Transfer-Encoding: x | |
| Transfer-Encoding: chunked # duplicate TE headers, BE uses last |
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/skills/vulnerabilities/http_request_smuggling.md
Line: 186
Comment:
**`\x20` is a literal four-character string in a Markdown code block**
Inside a fenced code block, `\x20` is rendered as the four characters `\`, `x`, `2`, `0` — not as a space byte (0x20). An agent reading this skill may literally include `\x20` in a header value, producing an invalid header rather than the intended leading-space obfuscation.
Similarly, line 188 has the comment `# tab before value` but no tab character is visible — the obfuscated tab should be represented as an actual tab or documented with a clear note (e.g. `<TAB>`).
```suggestion
Transfer-Encoding: xchunked # non-standard value, some FE ignore, BE accept
Transfer-Encoding: chunked # leading space before value (send literal 0x20 before "chunked")
Transfer-Encoding: chunked # tab character before value (send literal 0x09 before "chunked")
Transfer-Encoding: x
Transfer-Encoding: chunked # duplicate TE headers, BE uses last
```
How can I resolve this? If you propose a fix, please make it concise.Add strix/skills/vulnerabilities/http_request_smuggling.md covering CL.TE, TE.CL, H2.CL, H2.TE desync techniques, detection methodology, exploitation scenarios, and validation steps. Cherry-picked from usestrix#405.
…th values, \x20 representation Four reviewer findings addressed: P1 — TE.CL timing-probe description inverted: previous text said 'Content-Length set to fewer bytes than the chunk content' which describes socket-poisoning behavior (differential response), not a timeout. Corrected to: send a complete chunked body with CL set to MORE bytes than provided so the back-end waits for data that never arrives. Also corrected Testing Methodology step 3 to match. P2 — pseudo-header terminology: 'content-length' is a regular HTTP/2 header, not a pseudo-header (pseudo-headers are exclusively :method, :path, :authority, :scheme). Fixed the H2.CL explanation (line 75), HTTP/2-specific detection bullet, and Pro Tip usestrix#4 which referred to ':content-length pseudo-header'. P2 — PoC Content-Length values: outer Content-Length in the bypass PoC corrected from 116 to 100 (actual byte count of the body shown); capture PoC corrected from 129 to 120. P2 — \x20 representation: replaced the \x20 escape sequence in the code block (which renders as a literal four-character string, not a space byte) with an explanatory comment and actual whitespace characters so the intent is unambiguous.
Summary
HTTP request smuggling (HRS) is completely absent from the current skill set. It is a high-severity, infrastructure-level vulnerability present in virtually any application architecture using a CDN, load balancer, or reverse proxy in front of an origin server. The impact ranges from bypassing front-end authentication controls to cross-user session token capture. The SSRF, IDOR, and authentication skills do not cover this class at all.
This PR adds
strix/skills/vulnerabilities/http_request_smuggling.md.What's Added
New file:
strix/skills/vulnerabilities/http_request_smuggling.mdDesync variant coverage:
content-lengthpseudo-header conflicttransfer-encoding: chunkedinjected via HTTP/2 headers (spec-forbidden, often passed through)Transfer-Encoding obfuscation techniques:
Transfer-Encoding: xchunked, leading tab/space, duplicate TE headers, invalid chunk extensionsExploitation scenarios:
Detection methodology:
Raw HTTP examples provided for each variant.
Test Plan
Transfer-Encoding: xchunked— confirm desync via differential responseGET /adminprefix and confirm back-end serves 200 where direct request returns 403content-lengthdiffering from actual body lengthmake check-all