PortSwigger Walkthrough - Manipulating the WebSocket handshake to exploit vulnerabilities
Walkthrough of PortSwigger's 'Manipulating the WebSocket handshake to exploit vulnerabilities' lab.
To browse all labs in this series, visit the full PortSwigger series.
All testing shown in this series is performed against PortSwigger Academy’s intentionally vulnerable labs.
Do not apply these techniques to systems you do not own or have explicit written permission to test.
What’s this?
Some WebSockets vulnerabilities don’t live in the messages themselves, they live in the handshake that establishes the connection. Design flaws here tend to involve misplaced trust in HTTP headers (like X-Forwarded-For) to make security decisions, or session logic tied to the handshake. Burp Repeater lets you reconnect or clone a WebSocket connection and edit the handshake request before it fires, which is exactly what you need to poke at this kind of bug.
Objective
Same live chat, this time protected by an aggressive but flawed XSS filter. Trigger an alert() popup in the support agent’s browser.
Walkthrough
Send a chat message first to get a WebSocket handshake logged, then grab it from the WebSockets history and send it to Repeater. Try a basic XSS payload:
1
<img src=1 onerror='alert(1)'>
The connection dies immediately:
Try reconnecting and you get an error: This address is blacklisted. So the filter isn’t just inspecting messages, it flagged our IP and blocked reconnections from it. Click Reconnect anyway, but before sending, add an X-Forwarded-For header with an arbitrary IP that isn’t ours:
1
X-Forwarded-For: 1.1.1.1
The handshake succeeds, the app trusts that header over the actual source IP for its blacklist check, and we’re back in the chat. Now, since a plain onerror payload presumably got flagged by whatever’s inspecting message content, send an obfuscated version instead, mixed case, backtick-style event call:
1
<img src=1 oNeRrOr=alert`1`>
That one slides past the filter and pops.
Two separate flaws stacked: trusting a spoofable header for IP-based blocking, and a content filter that’s pattern-matching specific casing/syntax instead of actually understanding what it’s looking at.


