AIS CTF Series – 2.1. – Brutal Force

Spexeah Community

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:

I told you console output would be important!

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:

Now, this is where it’s time for a little honesty – it was at this point that we navigated to, 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, 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

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 = => 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:

	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) {
			} else {
				console.log("We shouldn't be here...")
		} 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!

Leave a Reply