
☣️ 이 저장소는 CVE-2024-34312에 대한 설명과 개념 증명을 포함합니다.
Moodle용 Virtual Programming Lab v4.2.3까지는 IDE 구성 요소를 통해 XSS(Cross-Site Scripting) 취약점을 포함하는 것으로 발견되었습니다.
브라우저는 jail 서버에서 실행 중인 websocket에 직접 연결합니다. websocket을 통해 jail 서버는 브라우저에 직접 메시지를 보낼 수 있습니다. 이러한 메시지는 브라우저에서 구문 분석되어 "executionActions"에 의해 처리됩니다. "run:browser" 액션은 신뢰할 수 없는 입력과 HTML을 연결하여 페이지 본문에 주입하므로 XSS에 취약합니다. 이는 손상된 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);
}
};