def update_ESCU_app(self): self.log.info("Update ESCU App. This can take some time") # upload package if self.config['cloud_provider'] == 'aws': splunk_ip = aws_service.get_single_instance_public_ip( 'ar-splunk-' + self.config['range_name'] + '-' + self.config['key_name'], self.config) elif self.config['cloud_provider'] == 'azure': splunk_ip = azure_service.get_instance( self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log)['public_ip'] # Upload the replay logs to the Splunk server ansible_vars = {} ansible_vars['ansible_user'] = '******' ansible_vars['ansible_ssh_private_key_file'] = self.config[ 'private_key_path'] ansible_vars['splunk_password'] = self.config['attack_range_password'] ansible_vars['security_content_path'] = self.config[ 'security_content_path'] cmdline = "-i %s, -u ubuntu" % (splunk_ip) runner = ansible_runner.run( private_data_dir=os.path.join(os.path.dirname(__file__), '../'), cmdline=cmdline, roles_path=os.path.join(os.path.dirname(__file__), '../ansible/roles'), playbook=os.path.join(os.path.dirname(__file__), '../ansible/playbooks/update_escu.yml'), extravars=ansible_vars)
def dump_attack_data(self, dump_name, last_sim): self.log.info("Dump log data") folder = "attack_data/" + dump_name os.mkdir(os.path.join(os.path.dirname(__file__), '../' + folder)) server_str = ("ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name']) if self.config['cloud_provider'] == 'aws': target_public_ip = aws_service.get_single_instance_public_ip( server_str, self.config) ansible_user = '******' ansible_port = 5986 elif self.config['cloud_provider'] == 'azure': target_public_ip = azure_service.get_instance( self.config, server_str, self.log)['public_ip'] ansible_user = '******' ansible_port = 5985 with open( os.path.join(os.path.dirname(__file__), '../attack_data/dumps.yml')) as dumps: for dump in yaml.full_load(dumps): if dump['enabled']: dump_out = dump['dump_parameters']['out'] if last_sim: # if last_sim is set, then it overrides time in dumps.yml # and starts dumping from last simulation with open( os.path.join( os.path.dirname(__file__), "../attack_data/.%s-last-sim.tmp" % self.config['range_name']), 'r') as ls: sim_ts = float(ls.readline()) dump['dump_parameters']['time'] = "-%ds" % int( time.time() - sim_ts) dump_search = "search %s earliest=%s | sort 0 _time" \ % (dump['dump_parameters']['search'], dump['dump_parameters']['time']) dump_info = "Dumping Splunk Search to %s " % dump_out self.log.info(dump_info) out = open( os.path.join( os.path.dirname(__file__), "../attack_data/" + dump_name + "/" + dump_out), 'wb') splunk_sdk.export_search( target_public_ip, s=dump_search, password=self.config['attack_range_password'], out=out) out.close() self.log.info("%s [Completed]" % dump_info)
def replay_attack_data(self, dump_name, dump, replay_parameters = None): if self.config['cloud_provider'] == 'aws': splunk_ip = aws_service.get_single_instance_public_ip("ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.config) elif self.config['cloud_provider'] == 'azure': splunk_ip = azure_service.get_instance(self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log)['public_ip'] if replay_parameters == None: with open(os.path.join(os.path.dirname(__file__), '../attack_data/dumps.yml')) as dump_fh: for d in yaml.full_load(dump_fh): if (d['name'] == dump or dump is None) and d['enabled']: self.replay_attack_dataset(splunk_ip, dump_name, d['replay_parameters']['index'], d['replay_parameters']['sourcetype'], d['replay_parameters']['source'], d['dump_parameters']['out']) else: self.replay_attack_dataset(splunk_ip, dump_name, 'test', replay_parameters['sourcetype'], replay_parameters['source'], replay_parameters['out'])
def execute_savedsearch(self, search_name, earliest, latest): self.log.info("Execute savedsearch " + search_name) if self.config['cloud_provider'] == 'aws': splunk_ip = aws_service.get_single_instance_public_ip( "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.config) elif self.config['cloud_provider'] == 'azure': splunk_ip = azure_service.get_instance( self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log)['public_ip'] splunk_sdk.execute_savedsearch(splunk_ip, self.config['attack_range_password'], search_name, earliest, latest)
def store_attack_data(self, results, test_file): target_public_ip = aws_service.get_single_instance_public_ip( test_file['target'], self.config) if test_file['target'] == 'attack-range-windows-client': runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/attack_data.yml', extravars={ 'ansible_user': '******', 'ansible_password': self.config['win_password'], 'ansible_port': 5985, 'ansible_winrm_scheme': 'http' }, verbosity=0) else: runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/attack_data.yml', extravars={ 'ansible_user': '******', 'ansible_password': self.config['win_password'] }, verbosity=0) aws_service.upload_file_s3_bucket('tmp/attack_data.txt', results, test_file, False) with tarfile.open('tmp/attack_data.tar.gz', "w:gz") as tar: tar.add('tmp/attack_data.txt', arcname="attack_data.txt") aws_service.upload_file_s3_bucket('tmp/attack_data.tar.gz', results, test_file, True) if os.path.exists('tmp/attack_data.tar.gz'): os.remove('tmp/attack_data.tar.gz') if os.path.exists('tmp/attack_data.txt'): os.remove('tmp/attack_data.txt') if runner.status == "successful": self.log.info("successfully stored attack data in S3 bucket") else: self.log.info("failed to store attack data in S3 bucket")
def simulate(self, target, simulation_techniques): target_public_ip = aws_service.get_single_instance_public_ip( target, self.config) if target == 'attack-range-windows-client': runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/atomic_red_team.yml', extravars={ 'art_run_techniques': simulation_techniques, 'ansible_user': '******', 'ansible_password': self.config['win_password'], 'ansible_port': 5985, 'ansible_winrm_scheme': 'http', 'art_repository': self.config['art_repository'], 'art_branch': self.config['art_branch'] }, verbosity=0) else: runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/atomic_red_team.yml', extravars={ 'art_run_techniques': simulation_techniques, 'ansible_user': '******', 'ansible_password': self.config['win_password'], 'art_repository': self.config['art_repository'], 'art_branch': self.config['art_branch'] }, verbosity=0) if runner.status == "successful": self.log.info( "successfully executed technique ID {0} against target: {1}". format(simulation_techniques, target)) else: self.log.error( "failed to executed technique ID {0} against target: {1}". format(simulation_techniques, target)) sys.exit(1)
def replay_attack_data(self, dump_name, attack_data): if self.config['cloud_provider'] == 'aws': splunk_ip = aws_service.get_single_instance_public_ip( "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.config) elif self.config['cloud_provider'] == 'azure': splunk_ip = azure_service.get_instance( self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log)['public_ip'] # preset our ansible vars ansible_vars = {} ansible_vars['dump_name'] = dump_name ansible_vars['ansible_user'] = '******' ansible_vars['ansible_ssh_private_key_file'] = self.config[ 'private_key_path'] ansible_vars['splunk_password'] = self.config['attack_range_password'] ansible_vars['ansible_port'] = 22 ansible_vars['out'] = attack_data['file_name'] ansible_vars['sourcetype'] = attack_data['sourcetype'] ansible_vars['source'] = attack_data['source'] ansible_vars['index'] = attack_data['index'] ansible_vars['update_timestamp'] = attack_data['update_timestamp'] if 'data' in attack_data: ansible_vars['data'] = attack_data['data'] ansible_vars['local_data'] = False else: ansible_vars['local_data'] = True # call ansible cmdline = "-i %s, -u ubuntu" % (splunk_ip) runner = ansible_runner.run( private_data_dir=os.path.join(os.path.dirname(__file__), '../'), cmdline=cmdline, roles_path=os.path.join(os.path.dirname(__file__), '../ansible/roles'), playbook=os.path.join(os.path.dirname(__file__), '../ansible/playbooks/attack_test.yml'), extravars=ansible_vars, )
def replay_attack_data(self, dump_name, dump): with open( os.path.join(os.path.dirname(__file__), '../attack_data/dumps.yml')) as dump_fh: for d in yaml.full_load(dump_fh): if (d['name'] == dump or dump is None) and d['enabled']: if self.config['cloud_provider'] == 'aws': splunk_ip = aws_service.get_single_instance_public_ip( "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.config) elif self.config['cloud_provider'] == 'azure': splunk_ip = azure_service.get_instance( self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log)['public_ip'] #splunk_ip = aws_service.get_single_instance_public_ip("aws-" + self.config['range_name'] + "-splunk", self.config) # Upload the replay logs to the Splunk server ansible_vars = {} ansible_vars['dump_name'] = dump_name ansible_vars['ansible_user'] = '******' ansible_vars['ansible_ssh_private_key_file'] = self.config[ 'private_key_path'] ansible_vars['splunk_password'] = self.config[ 'attack_range_password'] ansible_vars['out'] = d['dump_parameters']['out'] ansible_vars['sourcetype'] = d['replay_parameters'][ 'sourcetype'] ansible_vars['source'] = d['replay_parameters']['source'] ansible_vars['index'] = d['replay_parameters']['index'] cmdline = "-i %s, -u ubuntu" % (splunk_ip) runner = ansible_runner.run( private_data_dir=os.path.join( os.path.dirname(__file__), '../'), cmdline=cmdline, roles_path=os.path.join(os.path.dirname(__file__), '../ansible/roles'), playbook=os.path.join( os.path.dirname(__file__), '../ansible/playbooks/attack_replay.yml'), extravars=ansible_vars)
def dump_attack_data(self, dump_name, dump_data): self.log.info("Dump log data") folder = "attack_data/" + dump_name if os.path.isdir( os.path.join(os.path.dirname(__file__), '../' + folder)): self.log.error("folder already. please specify another directory") sys.exit(1) else: os.mkdir(os.path.join(os.path.dirname(__file__), '../' + folder)) server_str = ("ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name']) if self.config['cloud_provider'] == 'aws': target_public_ip = aws_service.get_single_instance_public_ip( server_str, self.config) ansible_user = '******' ansible_port = 5986 elif self.config['cloud_provider'] == 'azure': target_public_ip = azure_service.get_instance( self.config, server_str, self.log)['public_ip'] ansible_user = '******' ansible_port = 5985 dump_search = "search %s earliest=-%s latest=%s | sort 0 _time" \ % (dump_data['search'], dump_data['earliest'], dump_data['latest']) dump_info = "Dumping Splunk Search to %s " % dump_data['out'] self.log.info(dump_info) out = open( os.path.join( os.path.dirname(__file__), "../attack_data/" + dump_name + "/" + dump_data['out']), 'wb') splunk_sdk.export_search(target_public_ip, s=dump_search, password=self.config['attack_range_password'], out=out) out.close() self.log.info("%s [Completed]" % dump_info)
def dump_attack_data(self, dump_name): # copy json from nxlog # copy raw data using powershell # copy indexes # packet capture with netsh # see https://medium.com/threat-hunters-forge/mordor-pcaps-part-1-capturing-network-packets-from-windows-endpoints-with-network-shell-e117b84ec971 self.log.info("Dump log data") folder = "attack_data/" + dump_name os.mkdir(folder) servers = [] if self.config['windows_domain_controller'] == '1': servers.append('windows_domain_controller') if self.config['windows_server'] == '1': servers.append('windows_server') # dump json and windows event logs from Windows servers for server in servers: server_str = ("attack-range-" + server).replace("_", "-") target_public_ip = aws_service.get_single_instance_public_ip( server_str, self.config) if server_str == 'attack-range-windows-client': runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/attack_data.yml', extravars={ 'ansible_user': '******', 'ansible_password': self.config['win_password'], 'ansible_port': 5985, 'ansible_winrm_scheme': 'http', 'hostname': server_str, 'folder': dump_name }, verbosity=0) else: runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/attack_data.yml', extravars={ 'ansible_user': '******', 'ansible_password': self.config['win_password'], 'hostname': server_str, 'folder': dump_name }, verbosity=0) if self.config['sync_to_s3_bucket'] == '1': for file in glob.glob(folder + "/*"): self.log.info( "upload attack data to S3 bucket. This can take some time") aws_service.upload_file_s3_bucket( self.config['s3_bucket_attack_data'], file, str(dump_name + '/' + os.path.basename(file)))
def simulate(self, target, simulation_techniques, simulation_atomics, var_str='no'): target_public_ip = aws_service.get_single_instance_public_ip( target, self.config) # check if specific atomics are used then it's not allowed to multiple techniques techniques_arr = simulation_techniques.split(',') if (len(techniques_arr) > 1) and (simulation_atomics != 'no'): self.log.error( 'ERROR: if simulation_atomics are used, only a single simulation_technique is allowed.' ) sys.exit(1) run_specific_atomic_tests = 'True' if simulation_atomics == 'no': run_specific_atomic_tests = 'False' if target == 'attack-range-windows-client': runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/atomic_red_team.yml', extravars={ 'var_str': var_str, 'run_specific_atomic_tests': run_specific_atomic_tests, 'art_run_tests': simulation_atomics, 'art_run_techniques': simulation_techniques, 'ansible_user': '******', 'ansible_password': self.config['win_password'], 'ansible_port': 5985, 'ansible_winrm_scheme': 'http', 'art_repository': self.config['art_repository'], 'art_branch': self.config['art_branch'] }, verbosity=0) else: runner = ansible_runner.run( private_data_dir='.attack_range/', cmdline=str('-i ' + target_public_ip + ', '), roles_path="../ansible/roles", playbook='../ansible/playbooks/atomic_red_team.yml', extravars={ 'var_str': var_str, 'run_specific_atomic_tests': run_specific_atomic_tests, 'art_run_tests': simulation_atomics, 'art_run_techniques': simulation_techniques, 'ansible_user': '******', 'ansible_password': self.config['win_password'], 'art_repository': self.config['art_repository'], 'art_branch': self.config['art_branch'] }, verbosity=0) if runner.status == "successful": self.log.info( "successfully executed technique ID {0} against target: {1}". format(simulation_techniques, target)) else: self.log.error( "failed to executed technique ID {0} against target: {1}". format(simulation_techniques, target)) sys.exit(1)
def simulate(self, target, simulation_techniques, simulation_atomics, var_str='no'): if self.config['cloud_provider'] == 'aws': target_public_ip = aws_service.get_single_instance_public_ip( target, self.config) ansible_user = '******' ansible_port = 5986 elif self.config['cloud_provider'] == 'azure': target_public_ip = azure_service.get_instance( self.config, target, self.log)['public_ip'] ansible_user = '******' ansible_port = 5985 start_time = time.time() # check if specific atomics are used then it's not allowed to multiple techniques techniques_arr = simulation_techniques.split(',') if (len(techniques_arr) > 1) and (simulation_atomics != 'no'): self.log.error( 'ERROR: if simulation_atomics are used, only a single simulation_technique is allowed.' ) sys.exit(1) run_specific_atomic_tests = 'True' if simulation_atomics == 'no': run_specific_atomic_tests = 'False' if target == "ar-win-client-" + self.config[ 'range_name'] + "-" + self.config['key_name']: runner = ansible_runner.run( private_data_dir=os.path.join(os.path.dirname(__file__), '../'), cmdline=str('-i ' + target_public_ip + ', '), roles_path=os.path.join(os.path.dirname(__file__), '../ansible/roles'), playbook=os.path.join( os.path.dirname(__file__), '../ansible/playbooks/atomic_red_team.yml'), extravars={ 'ansible_port': 5985, 'var_str': var_str, 'run_specific_atomic_tests': run_specific_atomic_tests, 'art_run_tests': simulation_atomics, 'art_run_techniques': simulation_techniques, 'ansible_user': ansible_user, 'ansible_password': self.config['attack_range_password'], 'ansible_port': 5985, 'ansible_winrm_scheme': 'http', 'art_repository': self.config['art_repository'], 'art_branch': self.config['art_branch'] }, verbosity=0) else: runner = ansible_runner.run( private_data_dir=os.path.join(os.path.dirname(__file__), '../'), cmdline=str('-i ' + target_public_ip + ', '), roles_path=os.path.join(os.path.dirname(__file__), '../ansible/roles'), playbook=os.path.join( os.path.dirname(__file__), '../ansible/playbooks/atomic_red_team.yml'), extravars={ 'ansible_port': ansible_port, 'var_str': var_str, 'run_specific_atomic_tests': run_specific_atomic_tests, 'art_run_tests': simulation_atomics, 'art_run_techniques': simulation_techniques, 'ansible_user': ansible_user, 'ansible_password': self.config['attack_range_password'], 'art_repository': self.config['art_repository'], 'art_branch': self.config['art_branch'] }, verbosity=0) if runner.status == "successful": output = [] if 'output_art' in runner.get_fact_cache(target_public_ip): stdout_lines = runner.get_fact_cache( target_public_ip)['output_art']['stdout_lines'] else: stdout_lines = runner.get_fact_cache( target_public_ip)['output_art_var']['stdout_lines'] i = 0 for line in stdout_lines: match = re.search(r'Executing test: (.*)', line) if match is not None: #print(match.group(1)) if re.match(r'Done executing test', stdout_lines[i + 1]): msg = 'Return value unclear for test ' + match.group(1) self.log.info(msg) output.append(msg) else: msg = 'Successful Execution of test ' + match.group(1) self.log.info(msg) output.append(msg) i += 1 with open( os.path.join( os.path.dirname(__file__), "../attack_data/.%s-last-sim.tmp" % self.config['range_name']), 'w') as last_sim: last_sim.write("%s" % start_time) return output else: self.log.error( "failed to executed technique ID {0} against target: {1}". format(simulation_techniques, target)) sys.exit(1)
def test(self, test_file): # read test file test_file = self.load_file(test_file) # build attack range self.build() random_number = str(randrange(10000)) folder_name = "attack_data_" + random_number os.mkdir( os.path.join(os.path.dirname(__file__), '../attack_data/' + folder_name)) simulation = False output = 'loaded attack data' if 'attack_data' in test_file: for data in test_file['attack_data']: dumps_yml = self.load_file( os.path.join(os.path.dirname(__file__), '../attack_data/dumps.yml')) url = data['data'] r = requests.get(url, allow_redirects=True) open( os.path.join( os.path.dirname(__file__), '../attack_data/' + folder_name + '/' + data['file_name']), 'wb').write(r.content) if self.config['cloud_provider'] == 'aws': splunk_ip = aws_service.get_single_instance_public_ip( 'ar-splunk-' + self.config['range_name'] + '-' + self.config['key_name'], self.config) elif self.config['cloud_provider'] == 'azure': splunk_ip = azure_service.get_instance( self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log)['public_ip'] # Upload the replay logs to the Splunk server ansible_vars = {} ansible_vars['dump_name'] = folder_name ansible_vars['ansible_user'] = '******' ansible_vars['ansible_ssh_private_key_file'] = self.config[ 'private_key_path'] ansible_vars['splunk_password'] = self.config[ 'attack_range_password'] ansible_vars['out'] = data['file_name'] ansible_vars['sourcetype'] = data['sourcetype'] ansible_vars['source'] = data['source'] ansible_vars['index'] = 'test' cmdline = "-i %s, -u ubuntu" % (splunk_ip) runner = ansible_runner.run( private_data_dir=os.path.join(os.path.dirname(__file__), '../'), cmdline=cmdline, roles_path=os.path.join(os.path.dirname(__file__), '../ansible/roles'), playbook=os.path.join( os.path.dirname(__file__), '../ansible/playbooks/attack_replay.yml'), extravars=ansible_vars) else: simulation = True # update ESCU if self.config['update_escu_app'] == '1': # upload package if self.config['cloud_provider'] == 'aws': splunk_ip = aws_service.get_single_instance_public_ip( 'ar-splunk-' + self.config['range_name'] + '-' + self.config['key_name'], self.config) elif self.config['cloud_provider'] == 'azure': splunk_ip = azure_service.get_instance( self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log)['public_ip'] # Upload the replay logs to the Splunk server ansible_vars = {} ansible_vars['ansible_user'] = '******' ansible_vars['ansible_ssh_private_key_file'] = self.config[ 'private_key_path'] ansible_vars['splunk_password'] = self.config[ 'attack_range_password'] ansible_vars['security_content_path'] = self.config[ 'security_content_path'] cmdline = "-i %s, -u ubuntu" % (splunk_ip) runner = ansible_runner.run( private_data_dir=os.path.join(os.path.dirname(__file__), '../'), cmdline=cmdline, roles_path=os.path.join(os.path.dirname(__file__), '../ansible/roles'), playbook=os.path.join(os.path.dirname(__file__), '../ansible/playbooks/update_escu.yml'), extravars=ansible_vars) if simulation: # wait self.log.info('Wait for 200 seconds before running simulations.') time.sleep(200) # simulate attack # create vars string for custom vars: if 'vars' in test_file: var_str = '$myArgs = @{ ' i = 0 for key, value in test_file['vars'].items(): if i == 0: var_str += '"' + key + '" = "' + value + '"' i += 1 else: var_str += '; "' + key + '" = "' + value + '"' i += 1 var_str += ' }' print(var_str) output = self.simulate(test_file['target'], test_file['simulation_technique'], 'no', var_str) else: output = self.simulate(test_file['target'], test_file['simulation_technique'], 'no') # wait self.log.info('Wait for 200 seconds before running the detections.') time.sleep(200) # run detection result = [] for detection_obj in test_file['detections']: detection_file_name = detection_obj['file'] detection = self.load_file( os.path.join( os.path.dirname(__file__), '../../security-content/detections/' + detection_file_name)) result_obj = dict() result_obj['detection'] = detection_obj['name'] result_obj['detection_file'] = detection_obj['file'] if self.config['cloud_provider'] == 'aws': instance = aws_service.get_instance_by_name( 'ar-splunk-' + self.config['range_name'] + '-' + self.config['key_name'], self.config) if instance['State']['Name'] == 'running': result_obj['error'], result_obj[ 'results'] = splunk_sdk.test_search( instance['NetworkInterfaces'][0]['Association'] ['PublicIp'], str(self.config['attack_range_password']), detection['search'], detection_obj['pass_condition'], detection['name'], detection_obj['file'], self.log) else: self.log.error('ERROR: Splunk server is not running.') elif self.config['cloud_provider'] == 'azure': instance = azure_service.get_instance( self.config, "ar-splunk-" + self.config['range_name'] + "-" + self.config['key_name'], self.log) if instance['vm_obj'].instance_view.statuses[ 1].display_status == "VM running": result_obj['error'], result_obj[ 'results'] = splunk_sdk.test_search( instance['public_ip'], str(self.config['attack_range_password']), detection['search'], detection_obj['pass_condition'], detection['name'], detection_obj['file'], self.log) result.append(result_obj) # destroy attack range self.destroy() # return results return {'results': result, 'simulation_output': output}