Virtual Programming Lab for Moodle v4.2.3 及以下版本被发现通过其 IDE 组件存在**跨站脚本(XSS)**漏洞。
浏览器直接连接到运行在 jail 服务器上的 websocket。 通过 websocket,jail 服务器可以直接向浏览器发送消息。这些消息由浏览器解析,并由 "executionActions" 处理。 "run:browser" 操作容易受到 XSS 攻击,因为它将 HTML 与不受信任的输入拼接并注入到页面正文中。 这可能会被入侵的 jail 服务器滥用,通过 XSS 在 moodle 实例上获得管理员访问权限。
控制不受信任的 jail 服务器的攻击者,可以安装一个恶意的 jail 服务器,向任何使用 Virtual Programming Lab 的用户发送恶意负载,从而触发 XSS。
示例负载如下所示:run:browser:test\">test</a><script>alert(1)</script><a href=\"test\"。这将导致 <script>alert(1)</script> 被包含在 DOM 中,从而执行 alert(1)。

vplide.js 中的漏洞代码将此解释为一条命令,并将其第二个参数(参数以 : 分隔)直接附加到 DOM 中:
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);
}
};