From db6ff6d6e39bdc7aabf0505d2b688d45f515c6d6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:40:36 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Fi?= =?UTF-8?q?x=20Stored=20XSS=20in=20Devices,=20Users=20and=20RMCs=20dashboa?= =?UTF-8?q?rds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored the dynamic table construction in Devices, Users, and RMCs dashboards to use safe jQuery methods. - Replaced `.innerHTML` usage with jQuery element creation and `.text()` for all user-controllable data. - Replaced inline string-based `onclick` attributes with jQuery event handlers. - Added a global `escapeHTML` utility in `account.js` for manual sanitization where needed. - Safely constructed popover content using jQuery and `.text()`. This fix mitigates a critical Stored XSS vulnerability where malicious data from infected hosts or other users could execute scripts in the context of the RMS dashboard. Co-authored-by: richkmeli <7313162+richkmeli@users.noreply.github.com> --- .jules/sentinel.md | 4 + src/main/resources/static/js/rms/account.js | 10 ++ src/main/resources/static/js/rms/devices.js | 118 ++++++++++++-------- src/main/resources/static/js/rms/rmcs.js | 45 ++++---- src/main/resources/static/js/rms/users.js | 72 +++++++----- 5 files changed, 150 insertions(+), 99 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..f7628a9 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2026-02-01 - Recurring Stored XSS in Frontend +**Vulnerability:** Widespread Stored XSS across multiple dashboard pages (Devices, Users, RMCs) due to direct use of `.innerHTML` with unsanitized data from the server. +**Learning:** The application follows a pattern of building table rows as large HTML strings and injecting them into the DOM. This is particularly dangerous for a C2 server where data often originates from untrusted "infected" hosts. +**Prevention:** Always escape dynamic content before inserting it into HTML. The most robust approach in this codebase is to use jQuery's safe DOM construction methods (e.g., `.text()`) and event handlers instead of string-based `innerHTML` and `onclick` attributes. diff --git a/src/main/resources/static/js/rms/account.js b/src/main/resources/static/js/rms/account.js index 067ac48..29df492 100644 --- a/src/main/resources/static/js/rms/account.js +++ b/src/main/resources/static/js/rms/account.js @@ -1,3 +1,13 @@ +function escapeHTML(str) { + if (str === null || str === undefined) return ""; + return str.toString() + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + $(document).ready(function () { $("#logoutNavBar").hide(); diff --git a/src/main/resources/static/js/rms/devices.js b/src/main/resources/static/js/rms/devices.js index b07ca80..a04017c 100644 --- a/src/main/resources/static/js/rms/devices.js +++ b/src/main/resources/static/js/rms/devices.js @@ -43,53 +43,75 @@ function loadDevicesJSONtoTable(devicesListJSON) { console.log("loadDevicesJSONtoTable"); createDevicesTableHeader(); - let devicesList = JSON.parse(devicesListJSON);//jQuery.parseJSON(devicesListJSON); - let tbody = document.createElement("tbody"); - - console.log("devicesList: " + devicesList + " type: " + typeof (devicesList) + " length: " + devicesList.length); - - for (var i = 0; i < devicesList.length; ++i) { - - console.log(devicesList[i]) - - var name = devicesList[i].name; - var IP = devicesList[i].ip; - var serverPort = devicesList[i].serverPort; - var lastConnection = devicesList[i].lastConnection; - var encryptionKey = devicesList[i].encryptionKey; - var associatedUser = devicesList[i].associatedUserEmail; - var commands = devicesList[i].commands; - var commandsOutput = devicesList[i].commandsOutput; - var installationId = devicesList[i].installationId; - var location = devicesList[i].locationAsPosition; - var deviceInfo = devicesList[i].deviceInfoDevName; - - var timeSinceNow = timeSince(new Date(Number(lastConnection))) + " ago"; - - var row = document.createElement("tr"); - row.id = "tableRow" + i; - row.innerHTML = ( - "
IP: " + IP + "
" + - "Port: " + serverPort + "
" + - "Encryption Key: " + encryptionKey + "
" + - "\"> " + name + "" + - "").text("IP: " + device.ip)); + $popoverDiv.append($("
").text("Port: " + device.serverPort)); + $popoverDiv.append($("
").text("Encryption Key: " + device.encryptionKey)); + + let $nameA = $("", { + tabindex: "0", + type: "button", + "class": "btn btn-outline-info", + "data-html": "true", + "data-toggle": "popover", + title: name + " Info", + "data-content": $popoverDiv.html(), + text: name + }); + $row.append($("