functionstartGrade(){vartext=document.getElementById("assignmentText").value;checkLength(text);result=window.result||{message:"Your submission is too short.",error:1,};//If the result object hasn't been defined yet, the submission must be too short
if(result.error){endGrade();}else{getQAnswer();if(!passQuiz()){result.message="We don't allow robots at the Unicodeversity (yet)!";result.error=1;}else{result.grade="ABCDEF"[Math.floor(Math.random()*6)];//Don't tell the students we don't actually read their submissions
}endGrade();}}functionendGrade(){document.getElementById("message").innerText=result.message;if(result.grade){document.getElementById("grade").innerText=`You got a(n) ${result.grade}!`;}document.getElementById("share").style.visibility="initial";document.getElementById("share-link").href=`https://challenge-0221.intigriti.io/?assignmentTitle=${document.getElementById("assignmentTitle").value}&assignmentText=${document.getElementById("assignmentText").value}`;deleteresult;}functioncheckLength(text){if(text.length>50){result={message:"Thanks for your submission!"};}}functiongetQAnswer(){varanswer=document.getElementById("answer").value;if(/^[0-9]+$/.test(answer)){if(typeofresult!=="undefined"){result.questionAnswer={value:answer};}else{result={questionAnswer:{value:answer}};}}}functionpassQuiz(){if(typeofresult.questionAnswer!=="undefined"){returneval(result.questionAnswer.value+" == "+question);}returnfalse;}varquestion=`${Math.floor(Math.random()*10)+1} + ${Math.floor(Math.random()*10)+1}`;document.getElementById("question").innerText=`${question} = ?`;document.getElementById("submit").addEventListener("click",startGrade);consturlParams=newURLSearchParams(location.search);if(urlParams.has("autosubmit")){startGrade();}
It is a common behavior for web apps to reflect an input’s name or id if provided via request body or query string, so does this app (escaping, unfortunately).
But if we try a Unicode sequence (բարև), we can see it doesn’t get processed normally.
The first part of a Unicode character byte pair gets rendered and the second pair is getting reflected as is,
for example, in the case of letter բ (U+0562), 05 gets rendered and 62 is being returned.
Issue 2 - DOM clobbering as a source to the dangerous sink
Now we can escape the reflection point with a doublequote using some character like ∀ (U+2200), but we cannot use event handlers because of CSP nor can construct an inline script element as we cannot guess a server-generated nonce value.
And to reach it we should be able to control either result.questionAnswer.value or question variable.
No source reaches question, but in line 4 we can see that result can be taken from the window object.
1
2
3
4
5
6
functionstartGrade(){vartext=document.getElementById("assignmentText").value;checkLength(text);result=window.result||{message:"Your submission is too short.",error:1,
So to set its value we should abuse DOM clobbering. And the rendered payload should look something like this
Looking through MDN documentation, the only usable element that has value attribute is <data> as only its first two letters (da) can be embedded as a hexadecimal value to start a tag normally instead of a gibberish.
Finally we can construct our payload ?assignmentTitle=∀㸀㳚ta%20id=result㸀㳚ta%20id=result%20name=questionAnswer%20value=alert(origin)//&autosubmit
∀ (U+2200) to render a doublequote ("),
㸀 (U+3E00) to render a greater than sign (>),
㳚 (U+3CDA) to render a less than sign (<) and reflect DA characters to start the <data> tag,
and autosubmit query parameter to execute startGrade function without user interaction (line 71).
Which should result in something like this being injected into the page