Detect Starlette HTTP Request/Response Smuggling (CVE-2026-48710) Exploitation in Microsoft Sentinel
Detects exploitation attempts against CVE-2026-48710, an HTTP Request/Response Smuggling vulnerability (CWE-444) in Kludex Starlette, the ASGI framework underpinning FastAPI and many Python web services. Ambiguous or conflicting message-framing headers (both Content-Length and Transfer-Encoding, duplicated Content-Length, obsolete line folding, or malformed Transfer-Encoding values) allow an attacker to desynchronize the interpretation of request boundaries between an upstream proxy/load balancer and the Starlette backend. This enables request smuggling for cache poisoning, security-control bypass, credential/session hijacking, and request queue poisoning. The vulnerability is listed in CISA KEV (BOD 26-04) as known-exploited. This detection surfaces smuggling-indicative header anomalies and framing discrepancies in web/proxy telemetry reaching Starlette/ASGI endpoints.
MITRE ATT&CK
- Tactic
- Initial Access Lateral Movement
KQL Detection Query
let smugglingKeywords = dynamic(["transfer-encoding","content-length","chunked"]);
let suspiciousTE = dynamic(["chunked\r\nchunked","chunked, chunked","identity","chunked ,",", chunked","xchunked","chunked\t"]);
CommonSecurityLog
| where DeviceVendor has_any ("F5","Imperva","Cloudflare","NGINX","Envoy","HAProxy") or DeviceProduct has_any ("WAF","Proxy","LoadBalancer")
| where isnotempty(RequestURL) or isnotempty(AdditionalExtensions)
| extend Headers = tolower(coalesce(AdditionalExtensions, ""))
| where (Headers has "transfer-encoding" and Headers has "content-length")
or (Headers has_any (suspiciousTE))
or (countof(Headers, "content-length") > 1)
or (Headers matches regex @"transfer-encoding\s*:[^\r\n]*[^a-z\r\n]chunked")
| project TimeGenerated, SourceIP, DestinationIP, DestinationPort, RequestURL, RequestMethod, Headers, DeviceVendor, DeviceProduct, Activity
| extend Reason = case(Headers has "transfer-encoding" and Headers has "content-length", "TE+CL both present", countof(Headers, "content-length") > 1, "Duplicate Content-Length", "Malformed Transfer-Encoding")
| summarize Count = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), URLs = make_set(RequestURL, 20) by SourceIP, DestinationIP, DestinationPort, Reason Identifies HTTP requests to Starlette/ASGI backends carrying ambiguous message framing (simultaneous Transfer-Encoding and Content-Length, duplicate Content-Length, or malformed chunked encoding) indicative of CVE-2026-48710 request smuggling. Requires proxy/WAF logs that preserve request headers.
Data Sources
Required Tables
False Positives & Tuning
- Legacy HTTP client libraries that erroneously emit both Content-Length and Transfer-Encoding on the same request
- Load-testing or fuzzing tools (e.g. wrk, k6, ffuf) run by internal security teams that intentionally craft malformed framing
- Middleboxes that re-add Content-Length to already chunked responses, producing benign duplication in logs
Other platforms for CVE-2026-48710
Testing Methodology
Validate this detection against 4 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1 TE+CL conflicting framing request to Starlette
Expected signal: Proxy/WAF or packet-capture log entry showing a single request with both Content-Length and Transfer-Encoding headers.
- Test 2 Duplicate Content-Length header
Expected signal: Log event containing two Content-Length header occurrences for one request.
- Test 3 Malformed chunked Transfer-Encoding token
Expected signal: Log entry showing a Transfer-Encoding header with a doubled/obfuscated chunked token.
- Test 4 HTTP desync scanner probe (Windows)
Expected signal: Network/HTTP telemetry from the Windows host showing an outbound request with conflicting framing headers.
References (4)
- https://github.com/Kludex/starlette/security/advisories/GHSA-86qp-5c8j-p5mr
- https://www.cisa.gov/news-events/directives/bod-26-04-prioritizing-security-updates-based-risk
- https://www.cisa.gov/news-events/directives/bod-26-04-implementation-guidance-prioritizing-security-updates-based-risk
- https://nvd.nist.gov/vuln/detail/CVE-2026-48710
Response Playbook
Triage
- Confirm the destination host runs Starlette (or a FastAPI/ASGI app built on it) and identify the deployed version; compare against the fixed release referenced in GHSA-86qp-5c8j-p5mr.
- Extract the raw request headers from the alert and determine the specific framing anomaly: both Transfer-Encoding and Content-Length present, duplicated Content-Length, obsolete line folding, or malformed chunked token.
- Correlate the source IP across the fronting proxy/load balancer and the Starlette backend logs to determine whether the two tiers disagreed on request boundaries (a hallmark of successful desync).
- Check whether the smuggled request reached an authenticated or administrative endpoint, or whether cache entries were served to other clients after the event.
Containment
- Deploy or tighten a WAF/proxy rule that rejects any request carrying both Transfer-Encoding and Content-Length, duplicated Content-Length, or non-RFC-compliant chunked encoding, returning HTTP 400.
- Upgrade Starlette to the patched version across all affected services; where immediate patching is impossible, place a normalizing reverse proxy that enforces strict HTTP/1.1 framing in front of the backend.
- Invalidate and purge shared HTTP caches (CDN, Varnish, reverse-proxy cache) that may hold poisoned entries, and rotate session tokens for any users potentially served smuggled responses.
Evidence Collection
- Preserve full raw request/response captures (headers and bodies) from both the proxy and Starlette tiers for the affected time window.
- Export proxy, WAF, and application access logs for the source IP and destination service, plus Starlette/ASGI application logs showing request parsing.
- Snapshot the running Starlette service version, dependency lockfile (requirements.txt/poetry.lock), and container image digest to establish exposure.
Escalation Criteria
- !Escalate to incident response if evidence shows a smuggled request reached an authenticated, admin, or state-changing endpoint, or if cache poisoning served attacker content to other users.
- !Escalate if the same source or technique targets multiple internet-facing Starlette services, indicating active exploitation of the KEV-listed CVE at scale.
- !Escalate to legal/compliance if session hijacking or credential capture against real user sessions is confirmed.
Investigation Guide
Forensic Artifacts
- >
Proxy and WAF access logs showing conflicting framing headers for a source IP - >
Starlette/uvicorn/gunicorn application logs reflecting misparsed or split requests - >
Shared HTTP cache entries (CDN/Varnish) with anomalous keys or bodies - >
Packet captures demonstrating request/response boundary disagreement between tiers
Tuning Guidance
Baseline which internal clients and middleboxes legitimately emit dual framing headers and exclude them by source IP/user-agent to cut noise. Focus the highest-severity alerting on external source IPs hitting authenticated or cacheable endpoints. Where the proxy already strips or normalizes framing before the backend, move detection to the outermost tier (CDN/edge) so anomalies are observed before normalization. Suppress internal red-team and load-test ranges via allowlist rather than disabling the rule.
Hunting Queries
Retrospective hunt for co-occurring Transfer-Encoding and Content-Length headers toward Starlette services over the past 30 days, revealing prior desync probing.
CommonSecurityLog | where TimeGenerated > ago(30d) | extend H = tolower(AdditionalExtensions) | where H has "transfer-encoding" and H has "content-length" | summarize count() by SourceIP, DestinationIP, bin(TimeGenerated, 1h) index=proxy OR index=web earliest=-30d | eval h=lower(_raw) | where match(h,"transfer-encoding") AND match(h,"content-length") | stats count by src_ip dest_ip Atomic Red Team Tests
Sends a raw HTTP/1.1 request carrying both Transfer-Encoding: chunked and Content-Length headers to a lab Starlette instance to trigger framing-anomaly telemetry.
Command
printf 'POST / HTTP/1.1\r\nHost: 127.0.0.1:8000\r\nContent-Length: 6\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nG' | nc -w2 127.0.0.1 8000 Cleanup
echo 'No persistent artifacts; restart the lab Starlette service if request queue appears poisoned: pkill -f uvicorn' Expected Telemetry
Proxy/WAF or packet-capture log entry showing a single request with both Content-Length and Transfer-Encoding headers.
Expected Detection
The kql/spl framing-conflict rule fires with Reason 'TE+CL both present'.
Issues a request with two Content-Length headers to a lab endpoint fronting Starlette to simulate duplicate-length desync.
Command
printf 'GET / HTTP/1.1\r\nHost: 127.0.0.1:8000\r\nContent-Length: 0\r\nContent-Length: 44\r\n\r\n' | nc -w2 127.0.0.1 8000 Cleanup
echo 'No cleanup required; no files written.' Expected Telemetry
Log event containing two Content-Length header occurrences for one request.
Expected Detection
Detection fires with Reason 'Duplicate Content-Length'.
Sends an obfuscated Transfer-Encoding value ('chunked, chunked') designed to be parsed differently by proxy and Starlette backend.
Command
printf 'POST / HTTP/1.1\r\nHost: 127.0.0.1:8000\r\nTransfer-Encoding: chunked, chunked\r\n\r\n0\r\n\r\n' | nc -w2 127.0.0.1 8000 Cleanup
echo 'No persistent artifacts to remove.' Expected Telemetry
Log entry showing a Transfer-Encoding header with a doubled/obfuscated chunked token.
Expected Detection
Detection fires with Reason 'Malformed Transfer-Encoding'.
Runs an HTTP desync probe from a Windows host using a raw socket PowerShell client against a lab Starlette endpoint.
Command
$c=New-Object Net.Sockets.TcpClient('127.0.0.1',8000);$s=$c.GetStream();$b=[Text.Encoding]::ASCII.GetBytes("POST / HTTP/1.1`r`nHost: 127.0.0.1`r`nContent-Length: 6`r`nTransfer-Encoding: chunked`r`n`r`n0`r`n`r`nG");$s.Write($b,0,$b.Length);Start-Sleep 2;$c.Close() Cleanup
Remove-Variable c,s,b -ErrorAction SilentlyContinue Expected Telemetry
Network/HTTP telemetry from the Windows host showing an outbound request with conflicting framing headers.
Expected Detection
The crowdstrike_cql / chronicle_yaral rules flag the conflicting-framing request from the source host.