The idea of this blog post was born after reading an italian article about CTB-Locker. I don’t remember where I read it, but a single phrase caught my attention. It sounds like: “the malware proves that it’s able to decrypt some files, so the keys are saved somewhere”. I received a CTB-Locker sample on my personal mail box, so I decided to give it a try, just to satisfy my curiosity.
The post is based on a reversing session over a single file (810d51f6a5b4f8396ecf9407e427b999b316ecc28d53a759401143442b1a5cf8), but I think you can apply the general scheme to another sample of the same family.
Basically, all the information required by the malware are saved inside two distinct files: HelpFile and HiddenInfo. You won’t find these two files on an infected machine because these are confidential names I used while I was reversing the malware, the real names are generated following a precise scheme. To understand how the real names are generated I have to introduce the very first crypto scheme used by the malware, this is very important for the entire encryption/decryption process.
Everything starts from the value of the key:
The malware removes ‘–‘, converting everything into a sequence of 32 bytes (i.e. “463109ab-151a-463a-348e-79bbc9369cbd” becomes b’\x46\x31\x09\xab\x15\x1a\x46\x3a\x34\x8e\x79\xbb\xc9\x36\x9c\xbd’). Then SHA256 is applied:
CoreHash = SHA256(converted_MachineGUID)
CoreHash is composed by 8 dwords and each one of them is used for a specific job, for this blog post I’ll focus on the first and fourth dwords.
The HelpFile real name is constructed calling the next function with the CoreHash’s fourth dword as input:
The real name is 7 bytes long and every single char is built after two simple math operations. HelpFile has “.html” extension and it’s used to show ransom info to the infected user, it’s just like a *FAQ* page on how to restore all the compromised files. The message is available in four languages: de, it, nl and en.
This is the it version, but you can easily identify the server addresses, the tor access and the public keys. The last part of the file contains the list of the compromised files. Well… interesting but the keys I was searching for are not here!
The real name of HiddenInfo is obtained in the same way, but this time the parameter passed to the routine is the first dword of CoreHash. The file is without extension, it’s an hidden file and there are 0x28E meaningless bytes inside.
HiddenInfo is very important and it has a specific format, but everything is visible after a quite simple decryption process.
The malware uses AES:
DD1163 mov ecx, 0EF93F8h ; Decryption key: CoreHash DD1168 call AESExpandKey ... DD1181 lea edi, [esi+27Eh] ; Decryption starts from block at offset 0x27E DD1187 Decrypt_32_bytes_block: DD1187 lea eax, [ebp+expandedKey] DD118D push eax DD118E push edi ; Block to decrypt DD118F push edi ; Output buffer (bytes are replaced) DD1190 call AESDecrypt DD1195 add esp, 0Ch DD1198 dec [ebp+arg_0] ; initial value is 0x27E DD119B dec edi ; Update the pointer to the block to decrypt DD119C cmp [ebp+arg_0], 0 DD11A0 jge Decrypt_32_bytes_block
I told you it’s simple.
This file is important for the malware, and to be sure of its integrity the malware uses a checksum algo over the first 0x28C decrypted bytes:
DD11A2 push 28Ch ; Number of bytes used for the checksum DD11A7 mov ecx, esi ; Sequence of 0x28E decrypted bytes DD11A9 call Checksum ; ax gets the final value DD11AE pop ecx DD11AF pop edi DD11B0 cmp [esi+28Ch], ax ; Is checksum ok? DD11B7 jnz short corrupted_file
The value from the last two bytes of HiddenInfo is the valid checksum value, it was calculated during the encryption process. If the checksum is ok, at the end of the procedure you have the clean and readable HiddenInfo file. It’s full of hidden info, but in this case I’m interested in a minor part of the file only. Let’s start with this interesting block of 160 bytes:
What’s behind the selected bytes starting from 0xEC offset? It’s the sequence of 5 keys used by the malware in the demonstration process to correctly decrypt 5 files. I have 5 keys and nothing else, the malware doesn’t specify the name of the files associated to the defined keys. To get the list of the 5 files I had to reverse the malware a little bit more, look here:
The idea is simple: it tries to apply the keys to every single compromised file. It stops the process when all the right candidate files are found. The malware has a complete list of the compromised files. How does he know what’s the right key?
As you can see, in order to pass the *key test*, the malware decrypts a little part of the candidate file only (bytes from 0x20 to 0x2F offset). The resulting 16 bytes of a valid candidate are:
4 bytes: unique string “CTB1”, it’s the same for every compromised file
4 bytes: number of bytes of the original file
4 bytes: number of bytes of the compromised file
4 bytes: fixed value 1
The first and last dword are used in the key check. In the end of all, there are two simple checks to get the connection between a candidate file and his key.
Now that the key has been found the malware is ready to restore the original file. This is done in two distinct steps, and it involves the entire file except the first initial 0x30 bytes.
DCE780 mov [ebp+var_8], eax DCE783 decrypt_next_block: DCE783 lea eax, [ebp+expandedKey] DCE789 push eax DCE78A push [ebp+var_4] ; Buffer of 16 bytes to decrypt DCE78D push [ebp+var_4] ; Save the result in the same buffer DCE790 call AESDecrypt ; Decrypt the current block DCE795 add [ebp+var_4], 10h ; Switch to the next block DCE799 add esp, 0Ch DCE79C dec [ebp+var_8] ; Update the number of block to decrypt DCE79F jnz short decrypt_next_block
Decryption is done in blocks of 32 bytes (16 from the file and 16 equals to 0x00). The last step consists in a Zlib decompression (the compression is done with level 3).
Now you have your original file back.
Conclusion: the CTB-Locker demonstration process works with some files only and the keys are all hardcoded inside HiddenInfo file, it’s impossible to restore other files following this method.
Curiosity satisfied, see you soon!