Quick Reference Detection Indicators
Primary Indicators
- Simultaneous presence of
Content-LengthandTransfer-Encodingheaders Transfer-Encodingvalues containing obfuscation (tabs, vertical tabs, excessive spaces)- Request bodies containing embedded HTTP request syntax (e.g., nested
GET,POST,HEAD) - Responses returning data from endpoints different than the original request
Secondary Indicators
- Unusual delays during request processing
- Multiple requests sent over a single TCP connection by the same client
- Low-privilege users accessing restricted or administrative endpoints
4xxerrors immediately followed by2xxresponses on the same connection
WAF Detection Rules
ModSecurity Rules (OWASP CRS Compatible)
# HTTP Request Smuggling Detection - Content-Length and Transfer-Encoding Conflict
SecRule REQUEST_HEADERS:Content-Length "^[0-9]+$" \
"id:1001,phase:2,chain,deny,status:400,log,msg:'HTTP Request Smuggling: Conflicting Headers Detected'"
SecRule REQUEST_HEADERS:Transfer-Encoding "!@streq identity"
# HTTP Request Smuggling Detection - Transfer-Encoding with Obfuscation
SecRule REQUEST_HEADERS:Transfer-Encoding "(?:\x09|\x0b|\x20{2,}|[\x00-\x08\x0a-\x1f])" \
"id:1002,phase:2,deny,status:400,log,msg:'HTTP Request Smuggling: Obfuscated Transfer-Encoding'"
# HTTP Request Smuggling Detection - Multiple Transfer-Encoding Values
SecRule REQUEST_HEADERS:Transfer-Encoding ".*,.*" \
"id:1003,phase:2,deny,status:400,log,msg:'HTTP Request Smuggling: Multiple Transfer-Encoding Values'"
# HTTP Request Smuggling Detection - Chunked with Invalid Format
SecRule REQUEST_HEADERS:Transfer-Encoding "chunked" \
"id:1004,phase:2,chain,log,msg:'HTTP Request Smuggling: Potential Chunked Encoding Attack Detected'"
SecRule ARGS "@rx ^[0-9a-fA-F]+.*" "chain"
SecRule REQUEST_BODY "(?:GET|POST|HEAD|PUT|DELETE|PATCH|OPTIONS|CONNECT|TRACE)\s+\/" \
"deny,status:400,log"
# HTTP Request Smuggling Detection - Content-Length Zero with Body
SecRule REQUEST_HEADERS:Content-Length "^0$" \
"id:1005,phase:2,chain,log,msg:'HTTP Request Smuggling: Content-Length Zero with Request Body'"
SecRule CONTENT_LENGTH "!@eq 0" "deny,status:400,log"
# HTTP Request Smuggling Detection - Embedded HTTP Request in Body
SecRule REQUEST_BODY "@rx (?:GET|POST|PUT|DELETE|HEAD|PATCH|OPTIONS|TRACE|CONNECT)\s+\/[\w\-\.\/]*\s+HTTP\/1" \
"id:1006,phase:2,deny,status:400,log,msg:'HTTP Request Smuggling: Embedded HTTP Request in Body'"
Nginx + ModSecurity Rules
location / {
if ($http_content_length ~* ^[0-9]+$ && $http_transfer_encoding !~* ^identity$) {
return 400;
}
if ($http_transfer_encoding ~* "[\t\v]| |^.*[^\w,-].*$") {
return 400;
}
if ($request_body ~* "(?:GET|POST|HEAD|PUT|DELETE|PATCH|OPTIONS|CONNECT|TRACE)\s+/") {
return 400;
}
proxy_pass http://backend;
}
Microsoft Sentinel (KQL) Detection Queries
Query 1: Conflicting Headers Detection
let TimeRange = ago(24h);
let CriticalPaths = dynamic([
"/admin", "/manage", "/api/admin", "/api/internal", "/api/settings",
"/console", "/dashboard", "/auth", "/login", "/api/auth"
]);
AppServiceHTTPLogs
| where TimeGenerated > TimeRange
| extend HttpHeaders = tolower(CsUserAgent) + " " + tolower(CsUriStem)
| extend
HasContentLength = (CsBytes > 0),
HasTransferEncoding = (HttpHeaders has "transfer-encoding" or HttpHeaders has "chunked"),
TargetPath = tolower(CsUriStem)
| where HasContentLength and HasTransferEncoding
| where TargetPath in (CriticalPaths) or TargetPath startswith "/api/"
| summarize
RequestCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
TargetPaths = make_set(CsUriStem, 10),
StatusCodes = make_set(HttpStatus)
by ClientIP, csUserName, bin(TimeGenerated, 5m)
| where RequestCount >= 2
Query 2: Suspicious Smuggling Patterns
let TimeRange = ago(24h);
CommonSecurityLog
| where TimeGenerated > TimeRange
| where Request contains "Transfer-Encoding" or Request contains "Content-Length"
| extend RequestLower = tolower(Request)
| extend HasEmbeddedRequest =
RequestLower has "http/1" and
(RequestLower has "get " or RequestLower has "post ")
| where HasEmbeddedRequest
| summarize
Occurrences = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
SampleRequests = make_set(Request, 5)
by SourceIP, SourceUserName
| where Occurrences >= 2
Query 3: Low-Privilege User Accessing Restricted Endpoints
let TimeRange = ago(24h);
let AdminEndpoints = dynamic([
"/admin", "/manage", "/api/admin", "/settings",
"/users", "/roles", "/permissions", "/config"
]);
AppServiceHTTPLogs
| where TimeGenerated > TimeRange
| where CsUriStem in (AdminEndpoints)
| where HttpStatus == 200
| summarize
AccessCount = count(),
TargetEndpoints = make_set(CsUriStem),
LastAccess = max(TimeGenerated)
by csUserName, ClientIP
| where AccessCount >= 3
Query 4: Timing Anomalies
let TimeRange = ago(24h);
let AvgResponseTimes =
AppServiceHTTPLogs
| where TimeGenerated > TimeRange
| summarize AvgTime = avg(TimeTaken) by CsUriStem;
AppServiceHTTPLogs
| where TimeGenerated > TimeRange
| join kind=inner AvgResponseTimes on CsUriStem
| extend Deviation = TimeTaken - AvgTime
| where Deviation > (AvgTime * 2)
| where CsUriStem contains "admin" or CsUriStem contains "api"
| summarize
Count = count(),
MaxDeviation = max(Deviation)
by ClientIP, csUserName, CsUriStem
Splunk Detection Rules (SPL)
Rule 1: Header Conflict Detection
index=web sourcetype=access_log
| rex field=http_headers "content-length:\s*(?<content_length>\d+)"
| rex field=http_headers "transfer-encoding:\s*(?<transfer_encoding>\S+)"
| where isnotnull(content_length) AND isnotnull(transfer_encoding)
| where transfer_encoding!="identity"
| stats count by src_ip, user, uri
| where count>=2
Rule 2: Embedded HTTP Request Detection
index=web sourcetype=access_log
| search http_body="*GET*HTTP*" OR http_body="*POST*HTTP*"
| stats count by src_ip, user, uri
Rule 3: Response Time Anomalies
index=web sourcetype=access_log uri=*admin* OR uri=*api*
| stats avg(response_time) as avg_time, stdev(response_time) as std_dev by uri
Rule 4: Low-Privilege Admin Access
index=web sourcetype=access_log uri IN (/admin, /manage, /api/admin)
| stats count by host, user
| where count>=3
ELK Stack (Elasticsearch) Detection Rules
Rule 1: Conflicting Headers
{
"name": "HTTP Request Smuggling - Conflicting Headers",
"query": "http.headers.content-length:* AND http.headers.transfer-encoding:* AND NOT http.headers.transfer-encoding:identity",
"severity": "high",
"tags": ["http-request-smuggling", "cve-2025-55315"]
}
Rule 2: Obfuscated Transfer-Encoding
{
"name": "HTTP Request Smuggling - Obfuscated Transfer-Encoding",
"query": "http.headers.transfer-encoding:(* AND (\"\\t\" OR \"\\x0b\" OR \" \" OR *,*))",
"severity": "critical"
}
Log Analysis Examples
Apache Access Log
192.168.1.100 admin "POST /api/users HTTP/1.1" 200 content-length=4 transfer-encoding=chunked
Nginx Error Log
upstream sent invalid chunked response
IIS Log
Content-Length: 0; Transfer-Encoding: chunked
Threat Hunting Queries
Hunting Query 1: Requests with Both Headers
AppServiceHTTPLogs
| where isnotempty(CsBytes)
| summarize count() by CsUriStem
Hunting Query 2: Timeline Analysis
AppServiceHTTPLogs
| where ClientIP=="SOURCE_IP_OF_INTEREST"
| order by TimeGenerated asc
Payload Examples for Testing
Proof-of-Concept Payload (Authorized Testing Only)
POST / HTTP/1.1
Content-Length: 4
Transfer-Encoding: chunked
1e
GET /admin HTTP/1.1
0
Detection Test Payload
POST / HTTP/1.1
Content-Length: 60
Transfer-Encoding: chunked
0
GET /test123 HTTP/1.1
0
Alert Response Playbook
Immediate Actions (0–5 Minutes)
- Validate alert
- Identify source IP and user
- Confirm patch status
- Isolate attacker if active
Investigation (5–30 Minutes)
- Review logs
- Check accessed resources
- Look for data exposure
Containment (30 Minutes – 2 Hours)
- Reset credentials
- Block source
- Revoke sessions
- Patch systems
Root Cause Analysis
- Confirm exploitation
- Identify exposed data
- Document timeline
- Strengthen controls
Note:
All detection rules must be tested and tuned before production use. Adjust thresholds, endpoints, and baselines to match your environment.
