
☣️ Dieses Repository enthält die Beschreibung und einen Proof of Concept für CVE-2024-34312
Im Virtual Programming Lab für Moodle bis Version 4.2.3 wurde eine Cross-Site Scripting (XSS)-Sicherheitslücke in der IDE-Komponente entdeckt.
Der Browser verbindet sich direkt mit einem Websocket, der auf dem Jail-Server läuft. Durch den Websocket kann der Jail-Server Nachrichten direkt an den Browser senden. Diese Nachrichten werden vom Browser geparst und von „executionActions“ verarbeitet. Die Aktion „run:browser“ ist anfällig für XSS, da HTML mit nicht vertrauenswürdigen Eingaben verkettet und in den Seitenkörper eingefügt wird. Dies kann von einem kompromittierten Jail-Server ausgenutzt werden, um durch XSS administrativen Zugriff auf die Moodle-Instanz zu erlangen.
Ein Angreifer, der den nicht vertrauenswürdigen Jail-Server kontrolliert, kann einen bösartigen Jail-Server installieren, der eine schädliche Nutzlast an jeden Benutzer sendet, der das Virtual Programming Lab verwendet, und so XSS auslöst.
Ein beispielhafter Payload würde so aussehen: run:browser:test\">test</a><script>alert(1)</script><a href=\"test\". Dies würde dazu führen, dass <script>alert(1)</script> in das DOM eingefügt wird und alert(1) ausgeführt wird.

Der anfällige Code in vplide.js interpretiert dies als Befehl und hängt sein zweites Argument (Argumente werden durch : getrennt) direkt an das DOM an:
executionActions = {
// ...
'run': function(content, coninfo, ws) {
var parsed = /^([^:]*):?(.*)/i.exec(content);
var type = parsed[1];
if (type == 'terminal' || type == 'webterminal') {
// ...
} else if (type == 'vnc') {
// ...
} else if (type == "browser") {
var URL = (coninfo.secure ? "https" : "http") + "://" + coninfo.server + ":" + coninfo.portToUse + "/";
URL += parsed[2] + "/httpPassthrough";
if (isTeacher) {
URL += "?private";
}
var message = '<a href="' + URL + '" target="_blank">';
message += VPLUtil.str('open') + '</a>';
var options = {
width: 200,
icon: 'run',
title: VPLUtil.str('run'),
};
showMessage(message, options);
} else {
// ...
}
},
// ...
}
diff --git a/amd/src/vplide.js b/amd/src/vplide.js
index 586b5ff5..d1f88f47 100644
--- a/amd/src/vplide.js
+++ b/amd/src/vplide.js
@@ -2024,8 +2024,8 @@ define(
'setResult': self.setResult,
'ajaxurl': options.ajaxurl,
'run': function(content, coninfo, ws) {
- var parsed = /^([^:]*):?(.*)/i.exec(content);
- var type = parsed[1];
+ var parsed = /^([^:]*):?(.*)/.exec(content);
+ var type = VPLUtil.sanitizeText(parsed[1]);
if (type == 'terminal' || type == 'webterminal') {
if (lastConsole && lastConsole.isOpen()) {
lastConsole.close();
@@ -2055,7 +2055,7 @@ define(
});
} else if (type == "browser") {
var URL = (coninfo.secure ? "https" : "http") + "://" + coninfo.server + ":" + coninfo.portToUse + "/";
- URL += parsed[2] + "/httpPassthrough";
+ URL += VPLUtil.sanitizeText(parsed[2]) + "/httpPassthrough";
if (isTeacher) {
URL += "?private";
}
diff --git a/amd/src/vplui.js b/amd/src/vplui.js
index 36504a33..648472d6 100644
--- a/amd/src/vplui.js
+++ b/amd/src/vplui.js
@@ -582,8 +582,8 @@ define(
var messageActions = {
'message': function(content) {
var parsed = /^([^:]*):?([^]*)/.exec(content);
- var state = parsed[1];
- var detail = parsed[2];
+ var state = VPLUtil.sanitizeText(parsed[1]);
+ var detail = VPLUtil.sanitizeText(parsed[2]);
if (state == 'running') {
state = running;
}
@@ -607,7 +607,7 @@ define(
}
},
'retrieve': function() {
- var data = {"processid": VPLUtil.getProcessId()};
+ var data = {"processid": coninfo.processid};
pb.close();
delegated = true;
VPLUI.requestAction('retrieve', '', data, externalActions.ajaxurl)
@@ -627,7 +627,7 @@ define(
'close': function() {
VPLUtil.log('ws close message from jail');
ws.close();
- var data = {"processid": VPLUtil.getProcessId()};
+ var data = {"processid": coninfo.processid};
VPLUI.requestAction('cancel', '', data, externalActions.ajaxurl, true);
}
};