BraekerCTF 2024 - Block construction [crypto]
Difficulty : Easy to medium
Team : Crampting
“Sir, sir! This is a construction site.” You look up at what you thought was a building being constructed, but you realize it is a construction bot. “Sir please move aside. I had to have these blocks in order since last week, but some newbie construction bot shuffled them.” “I can move aside, " you tell the bot, “but I might be able to help you out.”
Source code
|
|
Code explanation
- A seed based on the time is defined with
random.seed(x)
. - The script shuffle
string.printable
from the string library and stock it inrand_printable
. - Concatenate a character of the
flag
with 31 times a caracter ofrand_printable
so the concatenation has a size of 32. - Then encrypt the concatenation with AES ECB and a randomly generated key of size 32, and repeat the concatenation with every character of
rand_printable
. This process is repeated for every character offlag
. - Each encryption is encoded in hex and appended to the file
ciphertext
Solving
Understanding the encryption
First of all, we need to know that AES encrypt block of 16 characters. However, in this challenge, strings of 32 characters are encrypted. Meaning thatm the first 16 characters are encrypted, then the 16 other and finally the two cipher are concatenated to make the final ciphertext.
In this image, we can understand that if the two 16 chars halves of a 32 chars bloc are the same, thus the encryption of the two halves will be the same.
For the challenge we have each character of the flag concatenated with every chars of rand_printable
.
Exemple for first knwon char ‘b’: Encrypt(‘b’ + 31 * ‘M’) , then encrypt(‘b’ + 31 * ‘8’) and so on for all 100 characters of rand_printable
.
I chose ‘8’ and ‘M’ randomly because
rand_printable
also is.
So there will be a moment where our first character ‘b’ will be concatened with 31 * ‘b’ from rand_printable and as we said earlier, then the two halves of the 32 chars (64 chars in hex) of th 32 chars encrypted will be the same. It is how we know which character of the flag is actually encrypted.
To get the flag, knowing all flag characters are encrypted 100 times into 64 hex blocs:
- loop every 6400 chars in
ciphertext
- inside this 6400 chars, loop 100 times until we find a 64 hex chars encrypted blocks where its first half is equal to its second half.
- Note the position of this block in his group of 100 encrypted blocks.
This position will be the position of the char in rand_printable
.
For instance, if the good bloc is at the position 24
, then the corresponding character will be rand_printable[24]
, supposing we know rand_printable.
Now after getting every position, we need to find the right rand_printable
shuffle to get the flag.
Retrieving the correct rand_printable
The list of the correct indices is the following:
blocPositions = [85, 89, 11, 63, 32, 51, 84, 74, 40, 36, 42, 14, 23, 36, 84, 75]
So flag[0] = rand_printable[blocPositions[0]]
with of course the correct rand_printable.
As said in the code explanation section, rand_printable
is a random.shuffle()
of string.printable
with the random seed defined by the time.
We can get the file creation date which is 2024:02:21 14:37:16+01:00
get the unix time of it : 1708526236
.
In the description of the challenge, a bot said the blocks were in place a “week” ago, so i also got the unix time une week before and bruteforced the timestamp from here: 1707865200
.
In a for loop, define the seed, generate a new rand_printable
, reconstructing a flag with the indice list, and look for ‘brck’ in the reconstructed flag.
Script
|
|
TL;DR
-
Each character are concatenated with 31 times a random character of a charset and then encrypted.
-
Some times the random character will be the flag character
-
In this case, the two halves of the encrypted block will be the same and we can determine the position of this block in his group of 100 other encrypted blocks.
-
Get the flag like this:
- Set the seed of random with the timestamp of the file
- shuffle hte charset string.printable
- Map the blocks position with the chars position of the right charset like that:
flag[i] = rand_printable[blocPositions[i]]
- Try to find ‘brck’ in the generated flag
- Iterate again till it works
Flag:
brck{EZP3n9u1nZ}