Skip to content

Commit ec27c7a

Browse files
MDA2AVclaude
andcommitted
Add 30 new tests and glossary docs, remove redundant SMUG-HEADER-INJECTION
New tests across all three suites (116 → 146): - Compliance (13): connection semantics, HTTP/1.0, version edge cases, CONNECT origin-form, chunked trailers/hex - Smuggling (11): CL underscore/negative-zero/octal, TE obs-fold/trailing-comma, URI-Host mismatch, bare CR chunk term - Malformed Input (7): URL backslash/overlong-UTF8/percent-null/percent-CRLF, 64K chunk ext, overlapping ranges, huge CL no body Removed SMUG-HEADER-INJECTION (sent valid headers, tested nothing beyond baseline). Updated MAL-CHUNK-EXTENSION-LONG → MAL-CHUNK-EXT-64K (64KB, CVE-2023-39326 ref). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 865fec8 commit ec27c7a

36 files changed

Lines changed: 1873 additions & 71 deletions

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ HTTP/1.1 server compliance and security tester. Sends malformed, ambiguous, and
44

55
**Website:** [mda2av.github.io/Http11Probe](https://MDA2AV.github.io/Http11Probe/) — full documentation, test glossary with RFC citations, and live probe results across all tested servers.
66

7-
## 116 Tests across 3 Categories
7+
## 146 Tests across 3 Categories
88

99
| Category | Tests | What it covers |
1010
|----------|------:|----------------|
11-
| **Compliance** | 47 | RFC 9110/9112 protocol requirements — bare LF, obs-fold, missing Host, invalid versions, chunked encoding, upgrade handling, etc. |
12-
| **Smuggling** | 50 | CL/TE ambiguity, duplicate Content-Length, pipeline desync, TE obfuscation, chunk extension abuse, bare LF in chunked framing |
13-
| **Malformed Input** | 19 | Binary garbage, oversized URLs/headers/methods, NUL bytes, control characters, integer overflow, HTTP/2 preface |
11+
| **Compliance** | 60 | RFC 9110/9112 protocol requirements — bare LF, obs-fold, missing Host, invalid versions, chunked encoding, connection semantics, upgrade handling, etc. |
12+
| **Smuggling** | 60 | CL/TE ambiguity, duplicate Content-Length, pipeline desync, TE obfuscation, chunk extension abuse, bare LF in chunked framing, URI/Host mismatch |
13+
| **Malformed Input** | 26 | Binary garbage, oversized URLs/headers/methods, NUL bytes, control characters, integer overflow, overlong UTF-8, encoded CRLF injection |
1414

1515
Each test is scored against RFC normative language (MUST/SHOULD/MAY) and classified as **Pass**, **Fail**, or **Warn** (when the RFC permits both strict and lenient behavior).
1616

@@ -57,7 +57,7 @@ dotnet run --project src/Http11Probe.Cli -- --host localhost --port 8080 --outpu
5757
Results stream to the console as each test completes, with a summary at the end:
5858

5959
```
60-
Score: 97/97 19 warnings (116 tests, 35.5s)
60+
Score: 97/97 19 warnings (146 tests, 35.5s)
6161
```
6262

6363
## Building
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
title: "CHUNKED-HEX-UPPERCASE"
3+
description: "CHUNKED-HEX-UPPERCASE test documentation"
4+
weight: 12
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `COMP-CHUNKED-HEX-UPPERCASE` |
10+
| **Category** | Compliance |
11+
| **RFC** | [RFC 9112 §7.1](https://www.rfc-editor.org/rfc/rfc9112#section-7.1) |
12+
| **Requirement** | MUST accept |
13+
| **Expected** | `2xx` |
14+
15+
## What it sends
16+
17+
A valid chunked POST where the chunk size is expressed using an uppercase hexadecimal digit: `A` (which equals 10 in decimal), followed by exactly 10 bytes of data.
18+
19+
```http
20+
POST / HTTP/1.1\r\n
21+
Host: localhost:8080\r\n
22+
Transfer-Encoding: chunked\r\n
23+
\r\n
24+
A\r\n
25+
helloworld\r\n
26+
0\r\n
27+
\r\n
28+
```
29+
30+
The chunk size `A` is uppercase hex for 10. The chunk data `helloworld` is exactly 10 bytes.
31+
32+
## What the RFC says
33+
34+
> "chunk-size = 1*HEXDIG" -- RFC 9112 Section 7.1
35+
36+
`HEXDIG` is defined as `DIGIT / "A" / "B" / "C" / "D" / "E" / "F"` (case-insensitive per RFC 5234 ABNF conventions). Both `a` and `A` represent the decimal value 10. A compliant chunked parser must accept hex digits in any case.
37+
38+
## Why it matters
39+
40+
While most chunk sizes in practice are small decimal numbers (like `5` or `1a`), the grammar allows any combination of uppercase and lowercase hex digits. A parser that only handles lowercase hex, or only decimal digits, will fail on legitimate chunked bodies. This is a basic interoperability requirement for any HTTP/1.1 implementation.
41+
42+
## Sources
43+
44+
- [RFC 9112 §7.1 -- Chunked Transfer Coding](https://www.rfc-editor.org/rfc/rfc9112#section-7.1)
45+
- [RFC 5234 -- ABNF (HEXDIG definition)](https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
title: "CHUNKED-TRAILER-VALID"
3+
description: "CHUNKED-TRAILER-VALID test documentation"
4+
weight: 11
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `COMP-CHUNKED-TRAILER-VALID` |
10+
| **Category** | Compliance |
11+
| **RFC** | [RFC 9112 §7.1.2](https://www.rfc-editor.org/rfc/rfc9112#section-7.1.2) |
12+
| **Requirement** | MUST accept |
13+
| **Expected** | `2xx` |
14+
15+
## What it sends
16+
17+
A valid chunked POST with a single 5-byte chunk, a zero terminator, and a trailer field (`X-Checksum: abc`) after the final chunk.
18+
19+
```http
20+
POST / HTTP/1.1\r\n
21+
Host: localhost:8080\r\n
22+
Transfer-Encoding: chunked\r\n
23+
\r\n
24+
5\r\n
25+
hello\r\n
26+
0\r\n
27+
X-Checksum: abc\r\n
28+
\r\n
29+
```
30+
31+
The trailer section appears between the zero-length terminating chunk and the final empty line.
32+
33+
## What the RFC says
34+
35+
> "A trailer section allows the sender to include additional fields at the end of a chunked message in order to supply metadata that might be dynamically generated while the content is sent." -- RFC 9112 Section 7.1.2
36+
37+
The chunked encoding grammar explicitly includes an optional trailer section:
38+
39+
```
40+
chunked-body = *chunk last-chunk trailer-section CRLF
41+
trailer-section = *( field-line CRLF )
42+
```
43+
44+
Trailer fields are valid metadata that follow the zero-length terminating chunk. A compliant HTTP/1.1 server must be able to parse them, even if it chooses to discard them.
45+
46+
## Why it matters
47+
48+
Trailer fields are used in practice for checksums, signatures, and streaming metadata that cannot be known until the body has been fully generated. A server that rejects a valid chunked body just because it contains a trailer section has an incomplete chunked encoding parser. This can break interoperability with legitimate clients and proxies that use trailers.
49+
50+
## Sources
51+
52+
- [RFC 9112 §7.1.2 -- Chunked Trailer Section](https://www.rfc-editor.org/rfc/rfc9112#section-7.1.2)
53+
- [RFC 9110 Section 6.5 -- Trailer Fields](https://www.rfc-editor.org/rfc/rfc9110#section-6.5)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
title: "CONNECTION-CLOSE"
3+
description: "CONNECTION-CLOSE test documentation"
4+
weight: 6
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `COMP-CONNECTION-CLOSE` |
10+
| **Category** | Compliance |
11+
| **RFC** | [RFC 9112 §9.3](https://www.rfc-editor.org/rfc/rfc9112#section-9.3) |
12+
| **Requirement** | MUST |
13+
| **Expected** | `2xx` + connection closed |
14+
15+
## What it sends
16+
17+
A standard GET request with `Connection: close` indicating the client wants the server to close the connection after sending the response.
18+
19+
```http
20+
GET / HTTP/1.1\r\n
21+
Host: localhost:8080\r\n
22+
Connection: close\r\n
23+
\r\n
24+
```
25+
26+
## What the RFC says
27+
28+
> "A server that receives a 'close' connection option MUST initiate a close of the connection after it sends the final response to the request in which the 'close' was received." -- RFC 9112 Section 9.3
29+
30+
The server must both respond successfully and close the TCP connection afterward. Responding with `2xx` but leaving the connection open violates this requirement.
31+
32+
## Why it matters
33+
34+
If a server ignores `Connection: close` and keeps the connection alive, a client may send a second request on what it believes is a new connection. In proxy environments, this can lead to response mismatch: the proxy believes the connection is closed and assigns it to a different client, who then receives the first client's response. Honoring `Connection: close` is essential for correct connection lifecycle management.
35+
36+
## Sources
37+
38+
- [RFC 9112 §9.3 -- Persistence](https://www.rfc-editor.org/rfc/rfc9112#section-9.3)
39+
- [RFC 9110 Section 7.6.1 -- Connection](https://www.rfc-editor.org/rfc/rfc9110#section-7.6.1)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: "HTTP10-DEFAULT-CLOSE"
3+
description: "HTTP10-DEFAULT-CLOSE test documentation"
4+
weight: 7
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `COMP-HTTP10-DEFAULT-CLOSE` |
10+
| **Category** | Compliance |
11+
| **RFC** | [RFC 9112 §9.3](https://www.rfc-editor.org/rfc/rfc9112#section-9.3) |
12+
| **Requirement** | SHOULD |
13+
| **Expected** | `2xx` + connection closed |
14+
15+
## What it sends
16+
17+
An HTTP/1.0 GET request without a `Connection: keep-alive` header.
18+
19+
```http
20+
GET / HTTP/1.0\r\n
21+
Host: localhost:8080\r\n
22+
\r\n
23+
```
24+
25+
## What the RFC says
26+
27+
HTTP/1.0 connections are not persistent by default. Unlike HTTP/1.1, where persistent connections are the default, an HTTP/1.0 client must explicitly request persistence via `Connection: keep-alive`.
28+
29+
> "If the received protocol is HTTP/1.0, the 'close' connection option is to be assumed." -- RFC 9112 Section 9.3
30+
31+
Without `Connection: keep-alive`, the server should treat the connection as non-persistent and close it after delivering the response.
32+
33+
**Pass:** Server responds `2xx` and closes the connection.
34+
**Warn:** Server responds `2xx` but keeps the connection open (minor violation of SHOULD).
35+
36+
## Why it matters
37+
38+
If a server treats HTTP/1.0 connections as persistent by default, it may hold the connection open indefinitely waiting for another request that will never come, wasting resources. More critically, in proxy chains, a downstream server keeping an HTTP/1.0 connection alive when the proxy expects it to close can cause response desynchronization.
39+
40+
## Sources
41+
42+
- [RFC 9112 §9.3 -- Persistence](https://www.rfc-editor.org/rfc/rfc9112#section-9.3)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: "HOST-EMPTY-VALUE"
3+
description: "HOST-EMPTY-VALUE test documentation"
4+
weight: 6
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `COMP-HOST-EMPTY-VALUE` |
10+
| **Category** | Compliance |
11+
| **RFC** | [RFC 9112 §3.2](https://www.rfc-editor.org/rfc/rfc9112#section-3.2) |
12+
| **Requirement** | MUST |
13+
| **Expected** | `400` or close |
14+
15+
## What it sends
16+
17+
A request with a `Host` header present but with an empty value.
18+
19+
```http
20+
GET / HTTP/1.1\r\n
21+
Host: \r\n
22+
\r\n
23+
```
24+
25+
The `Host` header line exists, but its value is empty (nothing between the colon and CRLF).
26+
27+
## What the RFC says
28+
29+
> "A server MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message that lacks a Host header field and to any request message that contains more than one Host header field line or a Host header field with an invalid field value." -- RFC 9112 Section 3.2
30+
31+
An empty Host value is invalid when the request-target is in origin-form. The host identification is effectively absent, making the server unable to determine which virtual host is being addressed.
32+
33+
## Why it matters
34+
35+
A Host header with an empty value is functionally equivalent to having no Host header at all. If a server accepts this, it may fall back to a default virtual host, potentially serving content from an unintended application. In multi-tenant environments, this can lead to information disclosure or incorrect routing.
36+
37+
## Sources
38+
39+
- [RFC 9112 Section 3.2 -- Request Target](https://www.rfc-editor.org/rfc/rfc9112#section-3.2)
40+
- [RFC 9110 Section 7.2 -- Host and :authority](https://www.rfc-editor.org/rfc/rfc9110#section-7.2)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
title: "HTTP10-NO-HOST"
3+
description: "HTTP10-NO-HOST test documentation"
4+
weight: 7
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `COMP-HTTP10-NO-HOST` |
10+
| **Category** | Compliance |
11+
| **RFC** | [RFC 9112 §3.2](https://www.rfc-editor.org/rfc/rfc9112#section-3.2) |
12+
| **Requirement** | MAY (unscored) |
13+
| **Expected** | `200` = Warn, `400` = Pass |
14+
15+
## What it sends
16+
17+
An HTTP/1.0 request with no `Host` header at all.
18+
19+
```http
20+
GET / HTTP/1.0\r\n
21+
\r\n
22+
```
23+
24+
No `Host` header is present, and the HTTP version is 1.0.
25+
26+
## What the RFC says
27+
28+
The `Host` header requirement was introduced in HTTP/1.1. HTTP/1.0 predates this requirement, so an HTTP/1.0 request without a `Host` header is not technically a protocol violation:
29+
30+
> "A server MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message that lacks a Host header field..." -- RFC 9112 Section 3.2
31+
32+
Note the requirement explicitly says "HTTP/1.1 request message." For HTTP/1.0, the server may choose to accept the request (routing to a default virtual host) or reject it.
33+
34+
**Pass:** Server rejects with `400` (strict -- good security practice).
35+
**Warn:** Server accepts with `200` (valid -- HTTP/1.0 did not require Host).
36+
37+
## Why it matters
38+
39+
In a virtual hosting environment, a request without a `Host` header gives the server no indication of which site is being targeted. Accepting such requests means the server must fall back to a default host, which could serve unintended content. Rejecting HTTP/1.0 requests without `Host` is the safer approach, especially since legitimate modern clients always send a `Host` header regardless of HTTP version.
40+
41+
## Sources
42+
43+
- [RFC 9112 §3.2 -- Request Target](https://www.rfc-editor.org/rfc/rfc9112#section-3.2)
44+
- [RFC 9110 Section 7.2 -- Host and :authority](https://www.rfc-editor.org/rfc/rfc9110#section-7.2)
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,38 @@
11
---
2-
title: "CHUNK-EXTENSION-LONG"
3-
description: "CHUNK-EXTENSION-LONG test documentation"
2+
title: "CHUNK-EXT-64K"
3+
description: "CHUNK-EXT-64K test documentation"
44
weight: 18
55
---
66

77
| | |
88
|---|---|
9-
| **Test ID** | `MAL-CHUNK-EXTENSION-LONG` |
9+
| **Test ID** | `MAL-CHUNK-EXT-64K` |
1010
| **Category** | Malformed Input |
1111
| **Expected** | `400`/`431` or close |
1212

1313
## What it sends
1414

15-
A chunked request with a chunk extension containing 100KB of data.
15+
A chunked request with a chunk extension containing 64KB (65,536 bytes) of data.
1616

1717
```http
1818
POST / HTTP/1.1\r\n
1919
Host: localhost:8080\r\n
2020
Transfer-Encoding: chunked\r\n
2121
\r\n
22-
5;ext=AAAA...{100,000 × 'A'}...\r\n
22+
5;ext=aaaa...{65,536 x 'a'}...\r\n
2323
hello\r\n
2424
0\r\n
2525
\r\n
2626
```
2727

28-
The chunk extension value is 100,000 bytes of `A` characters.
28+
The chunk extension value is 65,536 bytes of `a` characters.
2929

3030

3131
## Why it matters
3232

33-
While chunk extensions are syntactically valid per RFC 9112 Section 7.1.1, a 100KB extension is pathological. A robust server should reject unreasonably large chunk extensions to prevent resource exhaustion and denial of service.
33+
While chunk extensions are syntactically valid per RFC 9112 Section 7.1.1, a 64KB extension is pathological. A robust server should reject unreasonably large chunk extensions to prevent resource exhaustion and denial of service. CVE-2023-39326 demonstrated that Go's `net/http` library could be exploited via large chunk extensions to cause excessive memory consumption and DoS.
3434

3535
## Sources
3636

37-
- RFC 9112 Section 7.1.1 — chunk extensions
37+
- [RFC 9112 Section 7.1.1 — chunk extensions](https://www.rfc-editor.org/rfc/rfc9112#section-7.1.1)
38+
- [CVE-2023-39326 — Go net/http chunk extension DoS](https://nvd.nist.gov/vuln/detail/CVE-2023-39326)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
title: "POST-CL-HUGE-NO-BODY"
3+
description: "POST-CL-HUGE-NO-BODY test documentation"
4+
weight: 26
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `MAL-POST-CL-HUGE-NO-BODY` |
10+
| **Category** | Malformed Input |
11+
| **RFC** | [RFC 9112 Section 6.2](https://www.rfc-editor.org/rfc/rfc9112#section-6.2) |
12+
| **Expected** | `400`/close/timeout |
13+
14+
## What it sends
15+
16+
A POST request declaring a ~1GB body via Content-Length but sending no body data at all.
17+
18+
```http
19+
POST / HTTP/1.1\r\n
20+
Host: localhost:8080\r\n
21+
Content-Length: 999999999\r\n
22+
\r\n
23+
```
24+
25+
No body follows the empty line. The connection remains open.
26+
27+
## Why it matters
28+
29+
Tests whether the server pre-allocates memory for the declared body size or waits for data to arrive. A server that allocates 1GB upfront from a Content-Length header is vulnerable to memory exhaustion DoS -- an attacker can send many such requests cheaply to exhaust server memory. The correct behavior is to either stream the body incrementally, reject absurdly large Content-Length values, or timeout waiting for the body data that never arrives.
30+
31+
## Sources
32+
33+
- [RFC 9112 Section 6.2 -- Content-Length](https://www.rfc-editor.org/rfc/rfc9112#section-6.2)
34+
- [CWE-770 -- Allocation of Resources Without Limits or Throttling](https://cwe.mitre.org/data/definitions/770.html)

0 commit comments

Comments
 (0)