DarkCON CTF 2021 - Take It Easy
The challenge
For this challenge we were given a file (namely TAKE_IT_EASY.zip) that contains two other files inside. One of them is a password-protected zip file and the other, getkey.txt, contains three numbers (p, ct and e) related to the RSA-encrypted password we need to decompress the first file.
Part I
To proceed into solving this challenge, we first need to break the encryption and recover the password. The three numbers were:
There numbers are the public exponent e, the ciphertext ct and p, one of the primes used to generate the modulus. The problem is that without n, recovering the plain text would be very hard as we would need q as well to calculate the private exponent d. But soon I noticed that the public exponent e was equal to 3, which is generally considered too small for encrypting anything because there is always the chance that \(m^e < n\) what means that the resulting ct would not be really encrypted as it did not wrap up the modulus and as such, m could be recovered by calculating \(ct^{1/e}\).
Seeing as p is a very large number (1024 bits), there is a big chance that this would happen, specially if no padding was used. To test this, I used the Perl module Math::BigInt to calculate the cube root of ct and convert it to bytes:
And I got the key: Ju5t_@_K3Y
Using it, I extracted the zip file for the second part of the challenge.
Part II
After decompressing the file, we would get two other files (namely chall.py and cipher.txt). The contents of cipher.txt were as follows:
Each line (from B0 to B4) seems to be a python3 bytes object that was encrypted somehow. Looking into chall.py, we can get a clue of how to decrypt them:
As we can see, the flag was divided into several chunks of 4 bytes each. These chunks are then converted to 32 bit integers using the unpack() function from python and combined using a bit-wise xor operation, where each element i is combined with the element i+2. Seeing as we have 5 lines on the cipher.txt file and there were two more elements on the original flag, the original size of it was of 28 bytes ((5 + 2) * 4).
To revert this operation, let’s start with how theses chunks were combined with others:
Now to undo this, we would need to know at least one of the chunks of the original flag in order to decrypt the others. Luckily, we already know that the flag must start with “darkCON{“, so the first 4 bytes (F0) would be “dark” and the next four bytes (F1) would be “CON{“.
Using this information, we can recover F2 and F3, which in turn allows us to recover F4 and F5, and then F6 can be recovered from F4. To do this, each 4-byte chunk must be converted to a 32-bit integer using the unpack() function and then xor’d against the respective element as follows:
Because xor has the interesting property of being the inverse of itself (i.e, if n=a^b, n^a=b and n^b=a), the operations are reversed and we got our flag: darkCON{n0T_Th@t_haRd_r1Ght}