Flare-On 8 2021 Challenge #2: known
After unzip the files we received a folder with the encrypted files and the “ransomware”.
When I run the UnlockYourFiles.exe
I received the following message:
When I decrypt the long base64 string I received:
Encoded: KD4wXzApPiBJdCdzIGRhbmdlcm91cyB0byBhZGQrcm9yIGFsb25lISBUYWtlIHRoaXMgPCgwXzA8KQo=Decoded:(>0_0)> It's dangerous to add+ror alone! Take this <(0_0<)
Maybe it is a hint that we will need to use ROR (right rotate instruction).
When I tried to put the decryption key as abcd
I received:
This is because I wasn’t in the directory of the Files
folder, after changing it I run it again and add the abcd
key which generated the decoded algorithm but because it is the wrong key, it had garbage data inside the files:
I thought to reverse the binary, find the decryption algorithm, and tried to find the decryption key.
The decode function starts at 0x401370
:
The interesting algorithm starts at 0x4013BE
:
There are two calls which I want to understand: sub_401030
and sub_401220
.
Understanding 0x401030
It receives two parameters: sub_401030(var_50, FindFileData.cFileName)
In the first file it is: sub_401030(var_50, "capa.png.encrypted")
When I debugged it with x64dbg it seems that it copies the name to var_50
address.
It returns the length of the string to EAX
.
Understanding 0x401220
The function is being called with the following arguments:
sub_401220(lpFileName, var_50, decryption_key)
The var_50
is the name of the file without the “.encrypted” extension.
The function starts by opening the encrypted file lpFileName
, then creating a new file with the name without the “.encrypted” extension (using the CREATE_ALWAYS
disposition).
It then reads the encrypted file content and once it done, it will load push the decryption key and the content to sub_4011F0
. This function looks like doing the decoding:
From this function it seems that the decryption key should be in 8 length.
Using an example to understand the algorithm
This is a simple algorithm, let’s see an example how it works on the PNG file capa.png.encrypted
which the first char data is 0xC7
and the decryption key starts with a
(random char I selected).
I also used the debugger to understand it:
This is what happens:
// al=0xC7, bl=0x61
// xor al, bl -> encrypted ^ decryption_key
0xC7 ^ 0x61 = 166 = 0xA6// al=0xA6, cl=0
// rol al,cl -> rol(encrypted, index=0) rotate left
rol(0xA6,0) = 0xA6// sub al, cl
0xA6 - 0 = 0xA6
The result from 0xC7
is 0xA6
. Now let’s see an example for reversing this decoded algorithm.
// al=0xA6, cl=0
// Opposite for sub is add
0xA6 + 0 = 0xA6// opposite for rol is ror
ror(0xA6, 0) = 0xA6// We know the decryption key 0x61
0xA6 ^ 0x61 = 0xC7
Reversing the decryption key
We understood how it works. The missing piece is the decryption key, but we know what should be the decoded data in a specific formats like PNG because of the header and we also know its length, together we can guess that this is the data that should be figure out after decoding:
// PNG header 8 length
89 50 4E 47 0D 0A 1A 0A 00
The first 8 bytes of the encrypted data of capa.png.encrypted
are:
C7 C7 25 1D 63 0D F3 56 4E
I created this script to figure out the decryption key:
https://stackoverflow.com/a/63174981/2153777
def rotate_left(x, n):
return int(f"{x:08b}"[n:] + f"{x:08b}"[:n], 2)def rotate_right(x, n):
return int(f"{x:08b}"[-n:] + f"{x:08b}"[:-n], 2)decoded_data = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00]
encrypted_data = [0xC7, 0xC7, 0x25, 0x1D, 0x63, 0x0D, 0xF3, 0x56, 0x4E]decryption_key_arr = [-1]*8
for i in range(0,8):
result = decoded_data[i] + i
result = rotate_right(result, i)
decryption_key = result ^ encrypted_data[i]
decryption_key_arr[i] = decryption_key
>>> decryption_key_arr
[78, 111, 49, 84, 114, 117, 115, 116]
>>> ''.join([chr(i) for i in decryption_key_arr])
'No1Trust'
We have the decryption key: “No1Trust”.
I entered this key and it successfully decrypted the files, and the flag was inside the critical_data.txt
:
Flag: You_Have_Awakened_Me_Too_Soon_EXE@flare-on.com