This is the fourth challenge in the AIS CTF Challenge Series, and is the first within the ‘programming’ section.
Brutal Force
This challenge requires us to provide a pin that is 3-4 digits long, it’s as simple as that, guess a 3-4 digit number.
As fun as manually iterating through all of the different combinations would be, we weren’t too keen to take that route. So the first thing we did was open the console with CTRL+SHIFT+C and we were instantly greeted with the following:
As curiosity took hold and we began typing “BrutalForce_” autocomplete turned over a little secret:
As the name suggests, it looks like this string of hex is a hash of some form, and pasting the hash into CyberChef with the “Analyze Hash” module added seems to confirm that this is indeed the case with the following output indicating that sha256 is the most likely candidate:
Hash length: 64
Byte length: 32
Bit length: 256
Based on the length, this hash could have been generated by one of the following hashing functions:
SHA-256
SHA3-256
BLAKE-256
ECOH-256
FSB-256
GOST
Grøstl-256
HAVAL-256
PANAMA
RIPEMD-256
Snefru
Now, this is where it’s time for a little honesty – it was at this point that we navigated to dcode.fr, to use their sha256 decode page, now obviously this wouldn’t work for anything much more complex or salted, as this relies on a database of some kind hosted by dcode.fr, either in the form of a lookup table, or rainbow table, used to quickly resolve precomputed hash->value pairs.
But, as shown above, it did indeed resolve the hash to be 2947. However, this isn’t in the spirit of the challenge, given this is the “Programming” section, so, despite having found the flag, we continued on to the next step.
We need to create some code that will keep generating sequences of digits, hashing them with sha256 and then checking the resulting hash against our BrutalForce_pin_hash variable.
Pseudocode for this would look like this:
FOR I:=0 TO 9999 DO
VALUE:= SHA256(I)
IF VALUE == BrutalForce_pin_hash THEN
SUBMIT_FLAG(i)
BREAK
Now, we could do this in any language really, but for the sake of elegance, we’ll stick to Javascript. Unfortunately, unlike R4Wizard, I’m no Javascript pro, so my first trip was to Stackoverflow to find a sha256 function that doesn’t require a library. This lead me to the following:
async function sha256(message) {
// encode as UTF-8
const msgBuffer = new TextEncoder().encode(message);
// hash the message
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
// convert ArrayBuffer to Array
const hashArray = Array.from(new Uint8Array(hashBuffer));
// convert bytes to hex string
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex;
}
That function gets us 90% of the way there, as it provides the most complex part – now we just need to implement the iteration logic. To keep it as hands-off as possible; it’d be nice to be able to submit the result automatically, as we described in the pseudocode. Unfortunately, I didn’t find the BrutalForce_submit function particularly helpful, owing to the fact that it doesn’t seem to return anything to indicate success from the serverside, and after significant testing, it would always print “Invalid Pin!”, even when the pin was definitely correct! However, I remembered HackerChallenge.submitAnswer from a previous challenge, so I figured that could be useful in this case. After submitting invalid values a number of times manually and through HackerChallenge.submitAnswer we found the following object returned:
Object
hc_challenge: Object
js_file: "challenges/programming/brutal_force.js"
js_function: "BrutalForce"
meta: Object
bin_hashes: null
category: Object
value: "programming"
name: "Programming"
challenge_id: "brutal_force"
console_message: "To submit a pin here, use...
description: "Brute force programming challenge...
encoded: false
error_msg: "Invalid answer."
inputs: Array
mobile_friendly: false
name: "Brutal Force"
points: 15
prompt: "Submit the correct PIN to proceed (3 - 4 digits long)."
retired: false
version: "1.0"
oid: "899e4037b8d84cb99eae970cf796fb93"
pin_hash: "65cb2d994114c6d2c4a627f161cf6f954829474e45f680a141e5218187a9e967"
solved: false
hc_points: 0
So, it looks like we can just return the response from HackerChallenge.submitAnswer into a variable, and then use variable.hc_challenge.solved to check if our submitted solution was accepted. (There’s no real reason it shouldn’t be, given the code will only submit if the hashes match).
The resulting code can be seen below:
let guessAll = async () => {
var hash = BrutalForce_pin_hash
for(let i = 0; i < 9999; i++) {
var test = await sha256(i)
if (test == hash) {
console.log("Found Pin: "+i)
var submission = await HackerChallenge.submitAnswer("brutal_force",i)
if (submission.hc_challenge.solved) {
console.log("Success!")
} else {
console.log("We shouldn't be here...")
}
break
} else {
console.log("Tested: "+i);
}
}
}
Pasting the two above snippets into our browser after reloading the page, opening the challenge modal and then calling the guessAll function with guessAll() gives us the following:
So it looks like it worked! Refreshing the page and checking the modal again…
15 more points banked, and our first programming challenge solved!
I hope you enjoyed this section of the AIS – ‘c4n y0u h4ck 1t’ Series, do feel free to share critique/thoughts/alternative solutions!