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 成功
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐