“Severity High Protector” was a challenge in Northsec’s 2020 online CTF. Teams were given a zip file which contained two files:
ExamSolution.txt.protected - an encrypted file
SeverityHighProtector.exe - a binary with options to “Protect” or “Unprotect” a given file with a password.
The binary was .NET-based and it was not obfuscated, so I loaded it into dnSpy to get the full source code.
After skimming the code, it was obvious the most important pieces were in the Protector class, and specifically three places:
First, CreateAes:
The interesting part of this function is it’s use of the Rfc2898DeriveBytes class. This class uses PBKDF2 to derive keys, using HMACSHA1.
Next, Protect. This function encrypted a file using the derived keys from CreateAes. Interestingly, it also prepended the plaintext SHA hash to the encrypted file.
Finally, Unprotect. This checked if the password passed via STDIN matched the hash of the password stored in the encrypted file. Then it performed a simple decryption.
The first, obvious approach was cracking the SHA hash. I started hashcat against it while I did more research. The fact that we had a SHA1 hash, and the key derivation formula was based on SHA seemed like a potential avenue, and I was quickly able to find this post describing the following:
PBKDF2_HMAC_SHA1(password) == PBKDF2_HMAC_SHA1(HEX_TO_STRING(SHA1(password)))if the chosen_password is larger than 64 bytes.
From there, I took the decompilation from dnSpy and created my own solution in Visual Studio which copied the Unprotect and CreateAes functions. Luckily, Rfc2898DeriveBytes had a defintion which accepted two byte arrays, plus an interation count, so I simply updated CreateAes to be:
I deleted the password hash check from Unprotect, compiled, ran, and got the flag!