Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 10 additions & 0 deletions src/main/resources/static/js/rms/account.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
function escapeHTML(str) {
if (str === null || str === undefined) return "";
return str.toString()
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

$(document).ready(function () {

$("#logoutNavBar").hide();
Expand Down
118 changes: 70 additions & 48 deletions src/main/resources/static/js/rms/devices.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
"<td>" +
"<a tabindex='0' type=\"button\" data-html=\"true\" class=\"btn btn-outline-info\" data-toggle=\"popover\" title='" + name + " Info' " +
" data-content=\"<div>" +
" <p>IP: " + IP + "</p>" +
" <p>Port: " + serverPort + "</p>" +
" <p>Encryption Key: " + encryptionKey + "</p>" +
"</div>\"> " + name + "</a>" +
"</td>" +
"<td>" + timeSinceNow + "</td>" +
"<td>" + associatedUser + "</td>" +
"<td>" + commands + "</td>" +
"<td>" + commandsOutput + "</td>" +
"<td>" +
"<button title='Insert Commands' aria-label='Insert Commands' type='button' class='btn btn-secondary' onclick=commandsM('" + name + "')><span class='fa fa-terminal'></span></button> " +
"<button title='View Output' aria-label='View Output' type='button' class='btn btn-primary' onclick=outputM('" + name + "')><span class='fa fa-eye'></span></button> " +
"<button title='Remove Device' aria-label='Remove Device' type=\"button\" class=\"btn btn-danger\" onclick=\"deleteDevice('" + name + "', '" + i + "')\"><span class=\"fa fa-trash\"></span></button>" +
"</td>");

tbody.appendChild(row);
let devicesList = JSON.parse(devicesListJSON);
let $tbody = $("<tbody>");

for (let i = 0; i < devicesList.length; ++i) {
let device = devicesList[i];
let name = device.name;
let timeSinceNow = timeSince(new Date(Number(device.lastConnection))) + " ago";

let $row = $("<tr>").attr("id", "tableRow" + i);

// Name cell with popover
let $popoverDiv = $("<div>");
$popoverDiv.append($("<p>").text("IP: " + device.ip));
$popoverDiv.append($("<p>").text("Port: " + device.serverPort));
$popoverDiv.append($("<p>").text("Encryption Key: " + device.encryptionKey));

let $nameA = $("<a>", {
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($("<td>").append($nameA));

// Other cells
$row.append($("<td>").text(timeSinceNow));
$row.append($("<td>").text(device.associatedUserEmail));
$row.append($("<td>").text(device.commands));
$row.append($("<td>").text(device.commandsOutput));

// Actions
let $actionsCell = $("<td>");

$actionsCell.append($("<button>", {
title: "Insert Commands",
"aria-label": "Insert Commands",
type: "button",
"class": "btn btn-secondary",
click: function() { commandsM(name); }
}).append($("<span>", {"class": "fa fa-terminal"})));

$actionsCell.append(" ");

$actionsCell.append($("<button>", {
title: "View Output",
"aria-label": "View Output",
type: "button",
"class": "btn btn-primary",
click: function() { outputM(name); }
}).append($("<span>", {"class": "fa fa-eye"})));

$actionsCell.append(" ");

$actionsCell.append($("<button>", {
title: "Remove Device",
"aria-label": "Remove Device",
type: "button",
"class": "btn btn-danger",
click: function() { deleteDevice(name, i); }
}).append($("<span>", {"class": "fa fa-trash"})));

$row.append($actionsCell);
$tbody.append($row);
}
devicesTable.appendChild(tbody);
$("#devicesTable").append($tbody);
popInfo();
}

Expand Down Expand Up @@ -123,11 +145,11 @@ function infoDev(devL) {
return '<table class="table" style="padding-left:50px;">' +
'<tr>' +
'<td>IP:Port</td>' +
'<td>' + devL.ip + ':' + devL.serverPort + '</td>' +
'<td>' + escapeHTML(devL.ip) + ':' + escapeHTML(devL.serverPort) + '</td>' +
'</tr>' +
'<tr>' +
'<td>EncryptionKey:</td>' +
'<td>' + devL.encryptionKey + '</td>' +
'<td>' + escapeHTML(devL.encryptionKey) + '</td>' +
'</tr>' +
'</table>';
}
Expand Down
45 changes: 19 additions & 26 deletions src/main/resources/static/js/rms/rmcs.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,38 +39,31 @@ function loadRmcsJSONtoTable(rmcsListJSON) {
console.log("loadRmcsJSONtoTable")
createRmcsTableHeader();

var rmcsList = JSON.parse(rmcsListJSON)//jQuery.parseJSON(devicesListJSON);
let rmcsList = JSON.parse(rmcsListJSON);
let $tbody = $("<tbody>");

var tbody = document.createElement("tbody");
for (let i = 0; i < rmcsList.length; ++i) {
let rmc = rmcsList[i];
let associatedUser = rmc.associatedUser;
let rmcId = rmc.rmcId;

console.log("rmcsList: " + rmcsList + " type: " + typeof (rmcsList) + " length: " + rmcsList.length)
//var index = 0;
//while (devicesList[index] != null) {
//for(var device in devicesList){
for (var i = 0; i < rmcsList.length; ++i) {
// $.each(devicesList, function (index, value) {
//var device = devicesList[index];
// var device = value;
console.log(rmcsList[i])
let $row = $("<tr>").attr("id", "tableRow" + i);

var associatedUser = rmcsList[i].associatedUser;
var rmcId = rmcsList[i].rmcId;
$row.append($("<td>").text(associatedUser));
$row.append($("<td>").text(rmcId));

var row = document.createElement("tr");
row.id = "tableRow" + i;
let $actionsCell = $("<td>");
$actionsCell.append($("<button>", {
title: "Remove",
type: "button",
"class": "btn btn-danger",
click: function() { deleteRmc(associatedUser, i, rmcId); }
}).append($("<span>", {"class": "fa fa-trash"})));

row.innerHTML = (
//"<td>" + (index + 1) + "</td>" +
"<td>" + associatedUser + "</td>" +
"<td>" + rmcId + "</td>" +
"<td><button title='Remove' type=\"button\" class=\"btn btn-danger\" onclick=\"deleteRmc('" + associatedUser + "', '" + i + "', '" + rmcId + "')\"><span class=\"fa fa-trash\"></button></td>");

// "<td><button type=\"button\" class=\"btn btn-warning\" onclick=\"location.href=\'/device?name=" + name + "\';\">Remove</button></td>");

tbody.appendChild(row);
// index++
$row.append($actionsCell);
$tbody.append($row);
}
rmcsTable.appendChild(tbody);
$("#rmcsTable").append($tbody);
}

function deleteRmc(associatedUser, indexTableRow, rmcId) {
Expand Down
72 changes: 47 additions & 25 deletions src/main/resources/static/js/rms/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,32 +93,54 @@ function createUsersTableHeader() {
function loadUsersJSONtoTable(usersListJSON) {
createUsersTableHeader();

var usersList = JSON.parse(usersListJSON)//jQuery.parseJSON(usersListJSON);

var tbody = document.createElement("tbody");
//var index = 0;
//while (usersList[index] != null) {
//for(var device in usersList){
for (var i = 0; i < usersList.length; ++i) {
var email = usersList[i].email
var password = usersList[i].password
var admin = usersList[i].admin

var row = document.createElement("tr");
row.id = "tableRow" + i;
row.innerHTML = (
//"<td>" + (index + 1) + "</td>" +
"<td>" + email + "</td>" +
"<td><a type=\"button\" class=\"btn btn-outline-secondary\" data-toggle=\"popover\" tabindex='0' title=\"Password\" data-content=\"" + password + "\">Show Password</a></td>" +
//"<td><button title='Show Password' type=\"button\" class=\"btn btn-secondary\" onclick=\"editDevicesTableField('" + email + "','" + password + "','" + admin + "')\"><span class=\"fa fa-pencil\">Show Password</button></td>" +
//"<td>" + password + "</td>" +
"<td>" + admin + "</td>" +
"<td><button title='Edit' type=\"button\" class=\"btn btn-primary\" onclick=\"editDevicesTableField('" + email + "','" + password + "','" + admin + "')\"><span class=\"fa fa-pencil\"></button></td>" +
"<td><button title='Remove' type=\"button\" class=\"btn btn-danger\" onclick=\"deleteUser('" + email + "','" + i + "')\"><span class=\"fa fa-trash\"></button></td>");

tbody.appendChild(row);
let usersList = JSON.parse(usersListJSON);
let $tbody = $("<tbody>");

for (let i = 0; i < usersList.length; ++i) {
let user = usersList[i];
let email = user.email;
let password = user.password;
let admin = user.admin;

let $row = $("<tr>").attr("id", "tableRow" + i);

$row.append($("<td>").text(email));

$row.append($("<td>").append($("<a>", {
type: "button",
"class": "btn btn-outline-secondary",
"data-toggle": "popover",
tabindex: "0",
title: "Password",
"data-content": password,
text: "Show Password"
})));

$row.append($("<td>").text(admin));

let $actionsCell = $("<td>");
$actionsCell.append($("<button>", {
title: "Edit",
type: "button",
"class": "btn btn-primary",
click: function() { editDevicesTableField(email, password, admin); }
}).append($("<span>", {"class": "fa fa-pencil"})));

$row.append($actionsCell);

let $removeCell = $("<td>");
$removeCell.append($("<button>", {
title: "Remove",
type: "button",
"class": "btn btn-danger",
click: function() { deleteUser(email, i); }
}).append($("<span>", {"class": "fa fa-trash"})));

$row.append($removeCell);

$tbody.append($row);
}
usersTable.appendChild(tbody);
$("#usersTable").append($tbody);
popInfo();
}

Expand Down