During Cyber Security Awareness Month (2023), I took part in a month-long CTF ran by Huntress Labs, a US-based cybersecurity company. This CTF provided a host of challenges from OSINT to malware. Two of my favourite challenges from the competition were Black Cat and its sequel Black Cat II.
Black Cat
We’ve been hit by the infamous BlackCat Ransomware Group! We need you to help restore the encrypted files. Please help! My favorite rock got encrypted and I’m a wreck right now!
We are provided a blackcat.7z file comprising three files:
- victim-files/: directory containing the encrypted files (.encry)
- NOTE.png: the ransom note
- DecryptMyFiles.exe: a command-line utility which allows us to decrypt
From viewing the ransom note file, we know that the files are encrypted with “MILITARY GRADE ENCRYPTION”:

As the goal here is to decrypt the files, we are going to be concerned with the DecryptMyFiles.exe binary file.
Performing initial static analysis on the binary with PEStudio, we confirm this is a 64-bit Windows PE (Portable Executable) which has been written using GoLang:


Analysing the file with Ghidra, we see the binary is leveraging an XOR operation within its main function — a logical operation which compares two binary values, returning true if the values differ and false if they are the same.

In this instance, the files within victim-files/ have been XOR’d with the key to produce the .encry result.
.decry_file⊕ key = .encry_file
Running DecryptMyFiles.exe, we are given a prompt to enter the decryption key:

From some fuzzing, we see that the binary accepts a minimum key length of 8 bytes:

Now that we know the key is 8 bytes in length and that XOR is being leveraged for encryption, let’s look at the .encry files to see if we can find any commonality.
Looking specifically at the two encrypted PNG files, we see file signatures match:

The decrypted file signature for PNG files is:

As we now have the decrypted file signature, derive the key by carrying out an XOR operation between the encrypted signature and the decrypted signature:
#!/user/bin/env python3
ciphertext_file_sig = b'\xEA\x3F\x3D\x2A\x62\x68\x75\x63'
plaintext_file_sig = b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A'
key = bytearray()
for i in range(len(ciphertext_file_sig)):
k = plaintext_file_sig[i] ^ ciphertext_file_sig[i % len(plaintext_file_sig)]
key.append(k)
print(f'Key is: {key.decode()}')

Using this key, we can retrieve the decrypted files and the flag:


flag{092744b55420033c5eb9d609eac5e823}
Black Cat II
Be advised analyst: BlackCat is back! And they’re mad. Very mad. Help our poor user recover the images that they downloaded while browsing their favorite art site. Quickly!
Similar to the first challenge, we are provided a blackcatII.7z file
- victim-files/: directory containing the encrypted files (.encry)
- Decryptor.exe: GUI application which enables decryption
Running static analysis with PEStudio, we see this too is a Windows PE, but this time it is written with C# (.NET).

Opening the binary, we are given two input boxes to supply the path to the victim-files/ directory and the decryption key with ransom note stating that the new encryption algorithm is “UNBREAKABLE”.

Performing the same fuzzing we did with Black Cat I, we see that the key must be 64 characters:

As this is a .NET-compiled executable, we can get a near-sourcecode representation of the file by using dnSpy (or iLSpy):

As shown above, we have three main utilities within Decryptor:
- DecryptorUtil: Takes the form input and carries out decryption
- Form1: Contains the layout of the GUI and form actions
- Program: Handles the execution of the form
Most notably, within Form1, when the “Decrypt” button is clicked two input checks are made before making a call to DecryptorUtil.DecryptFiles, supplying the victim-files/ path and decryptor key as input.

Pivoting to DecryptFiles, the function takes the path to victim-files/, iterates over each .encry file (in alphabetical order), decrypting with AES and the provided key. The files are saved with the .decry extension as we saw in the last challenge.

However, looking at the for-loop, we see that every time a decrypted file is given, the decryption key is the SHA-256 hash of the decrypted file. As the files are read in alphabetical order, the hash of the first file is the decryption key for all files in the victim-files/ directory.
However, we must now find the decrypted version of this file in order to compute the SHA-256 hash. Looking at the victim-files/ directory, five of the files are encrypted JPG’s of famous paintings.
Using OSINT, we can search for websites with these paintings. Eventually, we stumble across an ATX Fine Arts article, titled 100 Most Famous Paintings in the World. From here, we can cross-reference each file with the paintings and confirm that all the files are present.

As “A Sunday Afternoon” is the first in alphabetical order, we can download the image to our host and compute the SHA-256 hash for the decryption key:
80d60bddb3b57a28d7c7259103a514cc05507c7b9cf0c42d709bdc93ffc69191
Using this key within Decryptor.exe, we can now decrypt the files and obtain the flag.


flag{03365961aa6aca589b59c683eecc9659}
Final Thoughts
As a final disclaimer, during the competition I managed to solve Black Cat I but failed to complete Black Cat II within the allotted time. Despite this, I had a lot of fun reading the different ways in which these challenges were solved and I would encourage you to read these other writeups to further your knowledge [2] [3].
I hope you enjoyed my first ever malware analysis post! More to come!
References
[1] Wikipedia: List of file signatures