def pre_start(self): logger.log('INFO', f'Starting user application: {self.name}') nukes = self.extract_all_nodes() for vm in nukes: path = f'{self.startup_dir}/{vm.hostname}-protonuke' if vm.topology.hardware.os_type.upper() == 'WINDOWS': kwargs = { 'src': path, 'dst': '/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup/protonuke-scheduler.cmd', } templates = utils.abs_path(__file__, 'templates/') with open(path, 'w') as f: utils.mako_serve_template('protonuke_scheduler.mako', templates, f, protonuke_args=vm.metadata.args) else: kwargs = { 'src': path, 'dst': '/etc/default/protonuke', } with open(path, 'w') as f: f.write('PROTONUKE_ARGS = {}'.format(vm.metadata.args)) self.add_inject(hostname=vm.hostname, inject=kwargs) logger.log('INFO', f'Started user application: {self.name}')
def stop(self): logger.log('INFO', f'Stopping user component: {self.name}') hostname = self.metadata.get('hostname', 'detector') wait = self.metadata.get('waitDuration', 30) mm = self.mm_init() self.print( f'waiting {wait} seconds before stopping Snort in {hostname}') time.sleep(wait) self.print( f'stopping Snort in {hostname} (be patient... it may take a while)' ) mm.cc_filter(f'name={hostname}') mm.cc_exec(f'pkill -INT snort') logfiles = ['alert', 'snort.log', 'snort.stats'] for log in logfiles: self.print(f'copying /var/log/snort/{log} from {hostname}') utils.mm_recv(mm, hostname, f'/var/log/snort/{log}', f'{self.base_dir}/{log}') if log == 'snort.stats' and os.path.exists( f'{self.base_dir}/{log}'): self.print(f'converting snort.stats to JSON') lines = [] with open(f'{self.base_dir}/{log}') as f: lines = f.readlines() # get rid of comment in file lines.pop(0) # remove comment field from header line header = lines.pop(0) header = header.replace('#', '') lines.insert(0, header) data = csv.DictReader(lines) with open(f'{self.base_dir}/snort-stats.jsonl', 'w') as f: for row in data: out = { 'timestamp': int(row['time']), 'total_alerts_per_second': int(row['total_alerts_per_second']), } json.dump(row, f) f.write('\n') logger.log('INFO', f'Stopped user component: {self.name}')
def start(self): logger.log('INFO', f'Starting user component: {self.name}') freq = self.metadata.get('pollPeriod', 1) vms = self.__vm_list() mm = self.mm_init() for vm in vms: mm.cc_filter(f'name={vm}') mm.cc_background(f"bash -c 'vmstat -n -t {freq} >> /vmstat.out'") logger.log('INFO', f'Started user component: {self.name}')
def cleanup(self): logger.log('INFO', f'Cleaning up user component: {self.name}') scripts = self.metadata.get('scripts', {}) targets = self.metadata.get('targets', None) mm = self.mm_init() for target in targets: target_host = target.get('hostname', 'traffic-server') target_script = scripts['trafficServer'] background = target.get('backgroundClient', None) if background: hostname = background.get('hostname', 'background-gen') script = scripts['backgroundGen'] self.print( f'deleting /{os.path.basename(target_script)} on {target_host}' ) mm.cc_filter(f'name={target_host}') mm.cc_exec(f'rm /{os.path.basename(target_script)}') self.print( f'deleting /{os.path.basename(script)} on {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec(f'rm /{os.path.basename(script)}') else: self.print( 'no background client configured for target {target_host}') malware = target.get('malwareClient', None) if malware: hostname = malware.get('hostname', 'malware-gen') script = scripts['malwareGen'] self.print( f'deleting /{os.path.basename(script)} on {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec(f'rm /{os.path.basename(script)}') else: self.print( 'no malware client configured for target {target_host}') logger.log('INFO', f'Cleaned up user component: {self.name}')
def cleanup(self): logger.log('INFO', f'Cleaning up user component: {self.name}') hostname = self.metadata.get('hostname', 'detector') logfiles = ['snort.log', 'snort.err', 'snort.stats'] mm = self.mm_init() for log in logfiles: self.print(f'deleting /var/log/snort/{log} from {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec(f'rm /var/log/snort/{log}') logger.log('INFO', f'Cleaned up user component: {self.name}')
def configure(self): logger.log('INFO', f'Configuring user component: {self.name}') scripts = self.metadata.get('scripts', {}) targets = self.metadata.get('targets', None) mm = self.mm_init() for target in targets: hostname = target.get('hostname', 'traffic-server') script = scripts['trafficServer'] self.print(f'copying {os.path.basename(script)} to {hostname}') # Copies script to root directory of VM. For example, if script is # /phenix/topologies/trafficgen-test/scripts/traffic-server.py, then # it will be copied to /traffic-server.py in the VM. utils.mm_send(mm, hostname, script, os.path.basename(script)) background = target.get('backgroundClient', None) if background: hostname = background.get('hostname', 'background-gen') script = scripts['backgroundGen'] self.print(f'copying {os.path.basename(script)} to {hostname}') utils.mm_send(mm, hostname, script, os.path.basename(script)) else: self.print( 'no background client configured for target {hostname}') malware = target.get('malwareClient', None) if malware: hostname = malware.get('hostname', 'malware-gen') script = scripts['malwareGen'] self.print(f'copying {os.path.basename(script)} to {hostname}') utils.mm_send(mm, hostname, script, os.path.basename(script)) else: self.print( 'no malware client configured for target {hostname}') logger.log('INFO', f'Configured user component: {self.name}')
def stop(self): logger.log('INFO', f'Stopping user component: {self.name}') vms = self.__vm_list() mm = self.mm_init() for vm in vms: mm.cc_filter(f'name={vm}') mm.cc_exec('pkill vmstat') utils.mm_recv(mm, vm, '/vmstat.out', f'{self.base_dir}/{vm}.out') mm.cc_exec('rm /vmstat.out') stats = [] for vm in vms: with open(f'{self.base_dir}/{vm}.out', 'r') as f: lines = f.readlines() for i, line in enumerate(lines): if i in (0, 1): continue items = [] for item in line.split(): try: items.append(int(item)) except ValueError: items.append(item) items[-2] = f'{items[-2]} {items[-1]}' del items[-1] stat = dict(zip(lines[1].split(), items)) stat['vm_name'] = vm stats.append(stat) with open(f'{self.base_dir}/vm_stats.jsonl', 'a+') as f: for datum in stats: json_record = json.dumps(datum) f.write(json_record + '\n') logger.log('INFO', f'Stopped user component: {self.name}')
def stop(self): logger.log('INFO', f'Stopping user component: {self.name}') vms = self.metadata.get('vms', None) mm = self.mm_init() convert = self.metadata.get('convertToJSON', False) if convert: self.print(f'PCAP --> JSON conversion enabled... please be patient') else: self.print(f'PCAP --> JSON conversion disabled') for vm in vms: hostname = vm.get('hostname', None) if not hostname: self.eprint('no hostname provided for VM config') sys.exit(1) pcap_out = f'{self.base_dir}/{hostname}.pcap' json_out = f'{self.base_dir}/{hostname}.pcap.jsonl' utils.mm_exec_wait(mm, hostname, 'pkill tcpdump') self.print(f'copying PCAP file from node {hostname}...') utils.mm_recv(mm, hostname, '/dump.pcap', pcap_out) mm.cc_exec('rm /dump.pcap') self.print(f'done copying PCAP file from node {hostname}') if convert: self.print(f'starting PCAP --> JSON conversion for node {hostname}...') subprocess.run( f"bash -c 'tshark -r {pcap_out} -T ek > {json_out} 2>/dev/null'", shell=True, ) self.print(f'PCAP --> JSON conversion for node {hostname} complete') logger.log('INFO', f'Stopped user component: {self.name}')
def pre_start(self): logger.log('INFO', f'Starting user application: {self.name}') nukes = self.extract_all_nodes() for vm in nukes: path = f'{self.startup_dir}/{vm.hostname}-protonuke' kwargs = { 'src' : path, 'dst' : '/etc/default/protonuke', } self.add_inject(hostname=vm.hostname, inject=kwargs) with open(path, 'w') as f: f.write('PROTONUKE_ARGS = {}'.format(vm.metadata.args)) logger.log('INFO', f'Started user application: {self.name}')
def stop(self): logger.log('INFO', f'Stopping user component: {self.name}') vms = self.metadata.get('vms', None) mm = self.mm_init() for vm in vms: hostname = vm.get('hostname', None) if not hostname: continue self.print(f'stopping ettercap in VM {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec('pkill ettercap') logger.log('INFO', f'Stopped user component: {self.name}')
def configure(self): logger.log('INFO', f'Configuring user component: {self.name}') hostname = self.metadata.get('hostname', 'detector') scripts = self.metadata.get('scripts', {}) configs = self.metadata.get('configs', []) mm = self.mm_init() config_snort = scripts.get('configSnort', None) if config_snort: script = config_snort['script'] executor = config_snort['executor'] self.print(f'copying {os.path.basename(script)} to {hostname}') # Copies script to root directory of VM. For example, if script is # /phenix/topologies/snort-test/scripts/configure-snort.sh, then it # will be copied to /configure-snort.sh in the VM. utils.mm_send(mm, hostname, script, os.path.basename(script)) self.print(f'running {os.path.basename(script)} on {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec(f'{executor} /{os.path.basename(script)}') for config in configs: src = config['src'] dst = config['dst'] self.print(f'copying {os.path.basename(src)} to {hostname}') utils.mm_send(mm, hostname, src, dst) iface = self.metadata.get('sniffInterface', 'eth0') self.print(f'ensuring {iface} is up in {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec(f'ip link set {iface} up') logger.log('INFO', f'Configured user component: {self.name}')
def start(self): logger.log('INFO', f'Starting user component: {self.name}') hostname = self.metadata.get('hostname', 'detector') iface = self.metadata.get('sniffInterface', 'eth0') mm = self.mm_init() self.print(f'clearing existing Snort logs in {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec('rm -rf /var/log/snort') mm.cc_exec('mkdir -p /var/log/snort') self.print(f'starting Snort in {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_background(f'snort -i {iface} -c /etc/snort/snort.conf') logger.log('INFO', f'Started user component: {self.name}')
def start(self): logger.log('INFO', f'Starting user component: {self.name}') vms = self.metadata.get('vms', None) mm = self.mm_init() for vm in vms: hostname = vm.get('hostname', None) iface = vm.get('iface', None) method = vm.get('method', 'arp') targets = vm.get('targets', None) if not hostname: continue if not iface: self.printf(f'missing interface name for VM {hostname}') continue if not targets: self.printf(f'missing targets for VM {hostname}') continue self.print(f'ensuring interface {iface} is up in VM {hostname}') cmd = f'ip link set {iface} up' mm.cc_filter(f'name={hostname}') mm.cc_exec(cmd) self.print( f'starting ettercap using {method} against {targets} in VM {hostname}' ) cmd = f'ettercap -Tq -i {iface} -M {method} {targets}' mm.cc_background(cmd) logger.log('INFO', f'Started user component: {self.name}')
def pre_start(self): logger.log('INFO', f'Starting user application: {self.name}') templates = utils.abs_path(__file__, 'templates/') guards = self.extract_all_nodes() for vm in guards: path = f"{self.startup_dir}/{vm.hostname}-wireguard.conf" kwargs = { 'src': path, 'dst': '/etc/wireguard/wg0.conf', } self.add_inject(hostname=vm.hostname, inject=kwargs) with open(path, 'w') as f: utils.mako_serve_template('wireguard_config.mako', templates, f, wireguard=vm.metadata) if vm.metadata.get('boot', False): path = f"{self.startup_dir}/{vm.hostname}-wireguard-enable.sh" kwargs = { 'src': path, 'dst': '/etc/phenix/startup/wireguard-enable.sh', } self.add_inject(hostname=vm.hostname, inject=kwargs) with open(path, 'w') as f: utils.mako_serve_template('wireguard_enable.mako', templates, f, name='wg0') logger.log('INFO', f'Started user application: {self.name}')
def start(self): logger.log('INFO', f'Starting user component: {self.name}') vms = self.metadata.get('vms', None) mm = self.mm_init() for vm in vms: hostname = vm.get('hostname', None) iface = vm.get('iface', None) options = vm.get('options', '') filter = vm.get('filter', '') if not hostname: self.eprint('no hostname provided for VM config') sys.exit(1) if not iface: self.eprint('no interface name provided for VM config') sys.exit(1) res = utils.mm_exec_wait(mm, hostname, 'which tcpdump') if not res: self.eprint(f'tcpdump is not installed in VM {hostname}') sys.exit(1) self.print(f'starting tcpdump on interface {iface} in VM {hostname}') if filter: self.print(f'using filter {filter} for tcpdump in VM {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_exec(f'ip link set {iface} up') mm.cc_background(f'tcpdump {options} -i {iface} -U -w /dump.pcap {filter}') logger.log('INFO', f'Started user component: {self.name}')
def start(self): logger.log('INFO', f'Starting user component: {self.name}') self.monitor = True thread = threading.Thread(target=self.__run_monitor) thread.start() logger.log('INFO', f'Started user component: {self.name}') # wait for calling process to send SIGTERM signal.pause() self.monitor = False logger.log('INFO', f'Stopping user component: {self.name}') thread.join() self.__flush_buffer() logger.log('INFO', f'Stopped user component: {self.name}')
def start(self): logger.log('INFO', f'Starting user component: {self.name}') scripts = self.metadata.get('scripts', {}) targets = self.metadata.get('targets', None) mm = self.mm_init() for target in targets: target_host = target.get('hostname', 'traffic-server') target_iface = target.get('interface', 'IF0') target_script = scripts['trafficServer'] target_ip = self.extract_node_ip(target_host, target_iface) duration = target.get('duration', 5) background = target.get('backgroundClient', None) if background: hostname = background.get('hostname', 'background-gen') rate = background.get('rate', 10000) prob = background.get('probability', .01) script = scripts['backgroundGen'] self.print( f'running {os.path.basename(target_script)} on {target_host}' ) mm.cc_filter(f'name={target_host}') mm.cc_background(f'python3 /{os.path.basename(target_script)}') self.print(f'running {os.path.basename(script)} on {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_background( f'python3 /{os.path.basename(script)} --ip {target_ip} ' f'--rate {rate} --duration {duration} --probability {prob}' ) else: self.print( 'no background client configured for target {target_host}') malware = target.get('malwareClient', None) if malware: hostname = malware.get('hostname', 'malware-gen') rate = background.get('rate', 20) prob = background.get('probability', 1.25) script = scripts['malwareGen'] self.print(f'running {os.path.basename(script)} on {hostname}') mm.cc_filter(f'name={hostname}') mm.cc_background( f'python3 /{os.path.basename(script)} --ip {target_ip} ' f'--rate {rate} --duration {duration} --probability {prob}' ) else: self.print( 'no malware client configured for target {target_host}') self.print( f'pausing for {int(duration) + 5}s while traffic is generated for {target_host}' ) time.sleep(int(duration) + 5) logger.log('INFO', f'Started user component: {self.name}')
def start(self): logger.log('INFO', f'Starting user component: {self.name}') goart = self.metadata.get('framework', None) technique = self.metadata.get('technique', None) test_name = self.metadata.get('testName', None) test_index = self.metadata.get('testIndex', None) mm = self.mm_init() if not goart or not os.path.exists(goart): self.eprint('goart executable not found') sys.exit(1) if not technique: self.eprint('no technique configured') sys.exit(1) if test_name is None and test_index is None: self.eprint('no technique test configured') sys.exit(1) vms = self.metadata.get('vms', None) for vm in vms: self.print(f'copying {goart} to {vm.hostname}') utils.mm_send(mm, vm.hostname, goart, f'/tmp/{os.path.basename(goart)}') mm.cc_filter(f'name={vm.hostname}') mm.cc_exec(f'chmod +x /tmp/{os.path.basename(goart)}') args = ['-q', '-f', 'json', '-t', self.metadata.technique] out_file = f'/tmp/{str(uuid.uuid4())}.json' args.extend(['-o', out_file]) if test_index is not None: args.extend(['-i', str(test_index)]) elif test_name is not None: args.extend(['-n', test_name]) else: # should never get here... self.eprint('no technique test configured') sys.exit(1) inputs = vm.get('inputs', {}) for name, value in inputs.items(): args.extend(['--input', f'{name}={value}']) env = self.metadata.get('env', {}) for name, value in env.items(): args.extend(['--env', f'{name}={value}']) env = vm.get('env', {}) for name, value in env.items(): args.extend(['--env', f'{name}={value}']) cmd = f"/tmp/{os.path.basename(goart)} {' '.join(args)}" self.print(f'executing {cmd} in {vm.hostname}') utils.mm_exec_wait(mm, vm.hostname, cmd) self.print(f'getting results file {out_file} from {vm.hostname}') results_file = os.path.join(self.base_dir, f'{vm.hostname}.json') try: utils.mm_recv(mm, vm.hostname, out_file, results_file) except Exception as ex: self.eprint(f'failed to get results file: {ex}') sys.exit(1) validator = self.metadata.get('validator', None) if validator: self.print(f'validating results from {vm.hostname}') tempfile = f'/tmp/{str(uuid.uuid4())}.sh' with open(tempfile, 'w') as tf: tf.write(validator) results = Box.from_json(filename=results_file) proc = subprocess.run( ['sh', tempfile, vm.hostname], input=results.Executor.ExecutedCommand.results.encode(), capture_output=True, ) if proc.returncode != 0: stderr = proc.stderr.decode() if stderr: self.eprint(f'results validation failed: {stderr}') else: self.eprint('results validation failed') if self.metadata.get('abortOnError', False): os.exit(1) else: self.print('results are valid') os.remove(tempfile) logger.log('INFO', f'Started user component: {self.name}')