HTB: HackTheBoo 2022 Crypto_whole_lotta_candy writeup

Khris Tolbert
Maveris Labs
5 min readOct 31, 2022

--

Had a chance to meddle with HTB:HackTheBoo while it was live from October 23rd through the 27th. Plenty of fun and unique challenges despite most of the puzzles being rated “easy”.

Yeah, maybe for More Smoked Leet Chicken, but throw us a frickin’ bone here

One of the challenges I found very interesting was the crypto_whole_lotta_candy challenge. The challenge revolved around decrypting a given AES encrypted flag by a cipher chosen at “random”. Provided to the CTF participant is the python code for the server (and its encryption code — more on that in a sec):

As seen in the main function of the gist above, the server selects an AES mode at random (line 32), instantiates the Encryptor class, then allows the client the option to do one of 4 things:

  • retrieve the encrypted flag (using the current AES mode)
  • encrypt a plaintext of their choosing (using the current AES mode)
  • switch the AES mode to a “random” one via user supplied list
  • punt and give up like the stinking Steelers did last weekend down 6 points…

Oh did I also mention that the server demands you provide it your choice using JSON?

Hey challenge devs, seriously.

Anyway, peeking at the Encryptor class (below), it is apparent that the key is only created once while the server is running, and the IVs of the respective AES modes are rolled every time they are called.

So of the five AES modes available (‘ECB’, ‘CBC’, ‘CFB’, ‘OFB’, ‘CTR’), which is the weakest? ECB? While yes, ECB is not without its flaws, it actually isn’t the quick win here. I made the same stupid assumption at first and almost walked away from this challenge after trying to derive the key using many different plaintexts. This approach, as you can guess, failed to provide any indication that this was the path to obtaining the flag. Much anger out of desperation may have followed…

I’m feeling ya, dude, stupid ECB not decrypting stupid challenge flag…[inaudible grumbling continues]

After a much needed break to clear my head, the thought of maybe, just maybe, one of the other AES modes might be the key to this challenge.

Revisiting the Encryptor class, I began crossing off modes due to my assumptions of what might be needed to crack them (outside of brute forcing the key):

  • ECB? Figured out I really needed an Oracle. I really don’t know enough of the flag to assume I could reliably get a match and brute 1 character at a time
  • CBC? IVs are regenerated every time it is called. Assumed too large for a single block bit flipping attack. Not sure of another avenue off the top of my head except for weak key
  • CFB?OFB? Same as above. Man, this challenge is more than I bargained for…
  • CTR? What is this counter thing? Wait... The key never gets rolled. Where is the IV initialization? Is there a chance?

My lazy google-fu brought me to an article which essentially states that the use of the same key, IV pair for multiple plaintexts can lead to recovery of the plaintexts via XOR. Again, glancing at the implementation of the CTR, the challenge author did not choose to include an IV (or nonce). The randomness of the Key and IV are not present, so two ciphertexts XOR’d against each other should equal the 2 plaintexts XOR’d against each one another. The server will show the flag ciphertext, and allows the user to supply plaintext to generate a ciphertext using the same AES mode. As the wheels creaked in my head, it dawned on me that if I could force CTR mode, I could then decrypt the flag just using two (2!!!) XOR calls. Sounds crazy, I know, but that is what the internet told me, so it must be true, right? (Ok in this case it was)

Armed with the insight that I may just have this one in the bag, I now had to put to use my “C-student level” python skills to automate the interaction with the server.

I first knew I needed to either A) rely on the server to randomly choose CTR for me, or B) force it to do so. As shown in lines 69–71 in the server code, the server randomly selects a list item from the user supplied JSON.

Below is the code I quickly (or maybe not so quickly) cobbled together (I’ll step through it following it):

If everything in the list is CTR then the server will happily choose CTR for us (#L29)!

Next, I needed to cycle through and dump (and store) the ciphertext of the flag (#L38–48).

Followed up by sending some known plaintext as a sacrifice to the AES gods for my (hopefully) clever XOR trick (#L60–69).

Lastly, I XOR’d the two ciphertexts together, and then the result of that against the known test string, I will have the flag(#L71–73).

Running the full script, results in the following output:

And the flag is recovered!

Woo-hoo, Flag secured!

Interesting challenge and again proves that correct implementation of a Crypto cipher is key to how secure it may be!

Maveris is an IT and cybersecurity company committed to helping organizations create secure digital solutions to accelerate their mission. We are Veteran-owned and proud to serve customers across the Federal Government and private sector. Maveris Labs is a space for employees and customers to ask and explore answers to their burning “what if…” questions and to expand the limits of what is possible in IT and cybersecurity. To learn more, go to maveris.com

--

--

Khris Tolbert
Maveris Labs

Sometimes things break and I happen to be behind the keyboard. I’m just as confused as you are.