def test_set_pending_executor_removal(self): agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows') executor_to_remove = 'test' want = dict(executor=executor_to_remove, action='remove') agent.set_pending_executor_removal(executor_to_remove) assert agent.executor_change_to_assign == want assert agent.executors == ['cmd']
async def handle_heartbeat(self, paw, platform, server, group, host, username, executors, architecture, location, pid, ppid, sleep, privilege, c2): """ Accept all components of an agent profile and save a new agent or register an updated heartbeat. :param paw: :param platform: :param server: :param group: :param host: :param username: :param executors: :param architecture: :param location: :param pid: :param ppid: :param sleep: :param privilege: :return: the agent object from explode """ self.log.debug('HEARTBEAT (%s) (%s)' % (c2, paw)) agent = Agent(paw=paw, host=host, username=username, platform=platform, server=server, location=location, executors=executors, architecture=architecture, pid=pid, ppid=ppid, privilege=privilege, c2=c2) if await self.data_svc.locate('agents', dict(paw=paw)): return await self.data_svc.store(agent) agent.sleep_min = agent.sleep_max = sleep agent.group = group agent.trusted = True return await self.data_svc.store(agent)
def test_builtin_fact_replacement_with_upstream_dest( self, loop, obfuscator, init_base_world): executor = Executor( name='psh', platform='windows', command= 'echo #{paw} #{server} #{group} #{location} #{exe_name} #{upstream_dest}' ) ability = Ability(ability_id='123', executors=[executor]) agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows', group='my_group', server='http://10.10.10.10:8888', location='testlocation', exe_name='testexe', upstream_dest='http://127.0.0.1:12345') loop.run_until_complete(agent.task([ability], 'plain-text', [])) assert 1 == len(agent.links) link = agent.links[0] decoded_command = b64decode(link.command).decode('utf-8') want = 'echo 123 http://10.10.10.10:8888 my_group testlocation testexe http://127.0.0.1:12345' assert want == decoded_command
async def handle_heartbeat(self, paw, platform, server, group, executors, architecture, location, pid, ppid, sleep, privilege): """ Accept all components of an agent profile and save a new agent or register an updated heartbeat. :param paw: :param platform: :param server: :param group: :param executors: :param architecture: :param location: :param pid: :param ppid: :param privilege: :return: the agent object from explode """ self.log.debug('HEARTBEAT (%s)' % paw) now = self.get_current_timestamp() agent = Agent(last_seen=now, paw=paw, platform=platform, server=server, location=location, executors=executors, architecture=architecture, pid=pid, ppid=ppid, trusted=True, last_trusted_seen=now, privilege=privilege) if await self.data_svc.locate('agents', dict(paw=paw)): return await self.data_svc.store(agent) agent.sleep_min = agent.sleep_max = sleep agent.group = group return await self.data_svc.store(agent)
async def handle_heartbeat(self, paw, platform, server, group, host, username, executors, architecture, location, pid, ppid, sleep, privilege, c2, exe_name): """ Accept all components of an agent profile and save a new agent or register an updated heartbeat. :param paw: :param platform: :param server: :param group: :param host: :param username: :param executors: :param architecture: :param location: :param pid: :param ppid: :param sleep: :param privilege: :return: the agent object from explode """ agent = Agent(paw=paw, host=host, username=username, platform=platform, server=server, location=location, executors=executors, architecture=architecture, pid=pid, ppid=ppid, privilege=privilege, c2=c2, exe_name=exe_name) if await self.get_service('data_svc').locate('agents', dict(paw=paw)): new_agent = await self.get_service('data_svc').store(agent) await self._add_agent_to_operation(new_agent) return new_agent agent.sleep_min = agent.sleep_max = sleep agent.group = group agent.trusted = True new_agent = await self.get_service('data_svc').store(agent) await self._add_agent_to_operation(new_agent) return new_agent
def test_task_with_facts(self, event_loop, obfuscator, init_base_world, knowledge_svc): executor = Executor(name='psh', platform='windows', command='net user #{domain.user.name} /domain') ability = Ability(ability_id='123', executors=[executor]) agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows') fact = Fact(trait='domain.user.name', value='bob') event_loop.run_until_complete(agent.task([ability], 'plain-text', [fact])) assert 1 == len(agent.links)
def test_agent(self): self.run_async(self.data_svc.store(Agent(paw='123$abc'))) self.run_async(self.data_svc.store(Agent(paw='123$abc'))) agents = self.run_async(self.data_svc.locate('agents')) self.assertEqual(1, len(agents)) for x in agents: json.dumps(x.display)
def test_agent(self): self.run_async(self.data_svc.store(Agent(sleep_min=2, sleep_max=8, watchdog=0))) self.run_async(self.data_svc.store(Agent(sleep_min=2, sleep_max=8, watchdog=0))) agents = self.run_async(self.data_svc.locate('agents')) self.assertEqual(2, len(agents)) for x in agents: json.dumps(x.display)
def test_assign_executor_change(self): agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows') executor_to_change = 'test' new_path = 'new_path' want = dict(action='update_path', executor=executor_to_change, value=new_path) agent.set_pending_executor_path_update(executor_to_change, new_path) assert agent.assign_pending_executor_change() == want assert agent.executor_change_to_assign is None
def test_task_with_facts(self, loop, obfuscator, init_base_world): ability = Ability(ability_id='123', test=BaseWorld.encode_string('net user #{domain.user.name} /domain'), variations=[], executor='psh', platform='windows') agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows') fact = Fact(trait='domain.user.name', value='bob') loop.run_until_complete(agent.task([ability], 'plain-text', [fact])) assert 1 == len(agent.links)
def test_updating_nonexistent_executor(self): agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows') agent.set_pending_executor_path_update('idontexist', 'fakepath') assert agent.executor_change_to_assign is None
def test_multiple_agents(self, loop, data_svc): loop.run_until_complete( data_svc.store(Agent(sleep_min=2, sleep_max=8, watchdog=0))) loop.run_until_complete( data_svc.store(Agent(sleep_min=2, sleep_max=8, watchdog=0))) agents = loop.run_until_complete(data_svc.locate('agents')) assert len(agents) == 2 for x in agents: json.dumps(x.display)
def test_preferred_executor_from_agent_executor(self, event_loop, ability, executor): executor_test = executor(name='test', platform='windows') executor_cmd = executor(name='cmd', platform='windows') executor_psh = executor(name='psh', platform='windows') test_ability = ability(ability_id='123', executors=[executor_test, executor_cmd, executor_psh]) agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows') preferred_executor = event_loop.run_until_complete(agent.get_preferred_executor(test_ability)) assert preferred_executor is executor_cmd # prefer agent's first executor, not ability's
def test_preferred_executor_psh(self, loop, ability, executor): executor_test = executor(name='test', platform='windows') executor_cmd = executor(name='cmd', platform='windows') executor_psh = executor(name='psh', platform='windows') test_ability = ability(ability_id='123', executors=[executor_test, executor_cmd, executor_psh]) agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['psh', 'cmd'], platform='windows') preferred_executor = loop.run_until_complete(agent.get_preferred_executor(test_ability)) assert preferred_executor is executor_psh # 'psh' preferred if available
def test_removing_nonexistent_executor(self): original_executors = ['cmd', 'test'] agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=original_executors, platform='windows') agent.set_pending_executor_removal('idontexist') assert agent.executor_change_to_assign is None assert agent.executors == original_executors
def test_task_no_facts(self, loop, data_svc, obfuscator, init_base_world): executor = Executor(name='psh', platform='windows', command='whoami') ability = Ability(ability_id='123', executors=[executor]) agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows') loop.run_until_complete(agent.task([ability], obfuscator='plain-text')) assert 1 == len(agent.links)
def test_store_new_agent(self, data_svc): agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['cmd', 'test'], platform='windows') stored_agent = agent.store(data_svc.ram) assert len(data_svc.ram['agents']) == 1 assert agent in data_svc.ram['agents'] assert stored_agent == agent
def test_task_missing_fact(self, loop, obfuscator, init_base_world): executor = Executor(name='psh', platform='windows', command='net user #{domain.user.name} /domain') ability = Ability(ability_id='123', executors=[executor]) agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows') loop.run_until_complete(agent.task([ability], obfuscator='plain-text')) assert 0 == len(agent.links)
def test_heartbeat_modification_during_pending_executor_removal( self, loop): original_executors = ['cmd', 'test'] agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=original_executors, platform='windows') agent.set_pending_executor_removal('test') loop.run_until_complete( agent.heartbeat_modification(executors=original_executors)) assert agent.executors == ['cmd']
def test_task_no_facts(self, loop, data_svc, obfuscator, init_base_world): ability = Ability(ability_id='123', test=BaseWorld.encode_string('whoami'), variations=[], executor='psh', platform='windows') agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows') loop.run_until_complete(agent.task([ability], obfuscator='plain-text')) assert 1 == len(agent.links)
def test_task_missing_fact(self, loop, init_base_world): ability = Ability(ability_id='123', test=BaseWorld.encode_string( 'net user #{domain.user.name} /domain'), variations=[], executor='psh', platform='windows') agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows') loop.run_until_complete(agent.task([ability])) assert 0 == len(agent.links)
async def start_sniffer_untrusted_agents(self): """ Cyclic function that repeatedly checks if there are agents to be marked as untrusted :return: None """ next_check = self.config['untrusted_timer'] try: while True: await asyncio.sleep(next_check + 1) trusted_agents = await self.get_service('data_svc').locate( 'agents', match=dict(trusted=1)) next_check = self.config['untrusted_timer'] for a in trusted_agents: last_trusted_seen = datetime.strptime( a.last_trusted_seen, '%Y-%m-%d %H:%M:%S') silence_time = (datetime.now() - last_trusted_seen).total_seconds() if silence_time > (self.config['untrusted_timer'] + int(a.sleep_max)): await self.get_service('data_svc').store( Agent(paw=a.paw, trusted=0)) else: trust_time_left = self.config[ 'untrusted_timer'] - silence_time if trust_time_left < next_check: next_check = trust_time_left except Exception as e: self.log.error('[!] start_sniffer_untrusted_agents: %s' % e)
def test_remove(self): a1 = self.run_async(self.data_svc.store(Agent(sleep_min=2, sleep_max=8, watchdog=0))) agents = self.run_async(self.data_svc.locate('agents', match=dict(paw=a1.paw))) self.assertEqual(1, len(agents)) self.run_async(self.data_svc.remove('agents', match=dict(paw=a1.paw))) agents = self.run_async(self.data_svc.locate('agents', match=dict(paw=a1.paw))) self.assertEqual(0, len(agents))
async def handle_heartbeat(self, **kwargs): results = kwargs.pop('results', []) old_paw = kwargs.get('paw') if old_paw: kwargs['paw'] = await self._sanitize_paw(old_paw) for agent in await self.get_service('data_svc').locate('agents', dict(paw=kwargs.get('paw', None))): await agent.heartbeat_modification(**kwargs) self.log.debug('Incoming %s beacon from %s' % (agent.contact, agent.paw)) for result in results: self.log.debug('Received result for link %s from agent %s via contact %s' % (result['id'], agent.paw, agent.contact)) await self._save(Result(**result)) operation = await self.get_service('app_svc').find_op_with_link(result['id']) access = operation.access if operation else self.Access.RED await self.get_service('event_svc').fire_event(exchange='link', queue='completed', agent=agent.display, pid=result['pid'], link_id=result['id'], access=access.value) if results: return agent, [] return agent, await self._get_instructions(agent) agent = await self.get_service('data_svc').store( Agent.load(dict(sleep_min=self.get_config(name='agents', prop='sleep_min'), sleep_max=self.get_config(name='agents', prop='sleep_max'), watchdog=self.get_config(name='agents', prop='watchdog'), **kwargs)) ) await self._add_agent_to_operation(agent) self.log.debug('First time %s beacon from %s' % (agent.contact, agent.paw)) data_svc = self.get_service('data_svc') await agent.bootstrap(data_svc) if agent.deadman_enabled: self.log.debug("Agent %s can accept deadman abilities. Will return any available deadman abilities." % agent.paw) await agent.deadman(data_svc) return agent, await self._get_instructions(agent)
async def save_results(self, id, output, status, pid): """ Save the results from a single executed link :param id: :param output: :param status: :param pid: :return: a JSON status message """ try: loop = asyncio.get_event_loop() for op in await self.data_svc.locate('operations', match=dict(finish=None)): link = next((l for l in op.chain if l.unique == id), None) if link: link.pid = int(pid) link.finish = self.data_svc.get_current_timestamp() link.status = int(status) if output: with open('data/results/%s' % id, 'w') as out: out.write(output) loop.create_task(link.parse(op)) await self.data_svc.store(Agent(paw=link.paw)) return json.dumps(dict(status=True)) except Exception as e: self.log.error('[!] save_results: %s' % e)
async def handle_heartbeat(self, **kwargs): results = kwargs.pop('results', []) for agent in await self.get_service('data_svc').locate( 'agents', dict(paw=kwargs.get('paw', None))): await agent.heartbeat_modification(**kwargs) self.log.debug('Incoming %s beacon from %s' % (agent.contact, agent.paw)) for result in results: await self._save(Result(**result)) await self.get_service('event_svc').fire_event( 'link/completed', agent=agent.display, pid=result['pid']) return agent, await self._get_instructions(agent) agent = await self.get_service('data_svc').store( Agent.load( dict(sleep_min=self.get_config(name='agents', prop='sleep_min'), sleep_max=self.get_config(name='agents', prop='sleep_max'), watchdog=self.get_config(name='agents', prop='watchdog'), **kwargs))) await self._add_agent_to_operation(agent) self.log.debug('First time %s beacon from %s' % (agent.contact, agent.paw)) await agent.bootstrap(self.get_service('data_svc')) return agent, await self._get_instructions(agent)
def setup_rest_svc_test(loop, data_svc): BaseWorld.apply_config(name='default', config={ 'app.contact.http': '0.0.0.0', 'plugins': ['sandcat', 'stockpile'], 'crypt_salt': 'BLAH', 'api_key': 'ADMIN123', 'encryption_key': 'ADMIN123', 'exfil_dir': '/tmp' }) loop.run_until_complete( data_svc.store( Ability(ability_id='123', test=BaseWorld.encode_string('curl #{app.contact.http}'), variations=[]))) loop.run_until_complete( data_svc.store( Adversary(adversary_id='123', name='test', description='test', phases=[]))) loop.run_until_complete( data_svc.store(Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0))) loop.run_until_complete( data_svc.store( Planner(planner_id='123', name='test', module='test', params=dict()))) loop.run_until_complete( data_svc.store(Source(identifier='123', name='test', facts=[])))
def test_privileged_to_run__1(self, event_loop, data_svc): """ Test ability.privilege == None """ agent = event_loop.run_until_complete( data_svc.store(Agent(sleep_min=1, sleep_max=2, watchdog=0))) ability = event_loop.run_until_complete( data_svc.store(Ability(ability_id='123', privilege=None))) assert agent.privileged_to_run(ability)
def test_agent(event_loop, mocker, mock_time): with mocker.patch('app.objects.c_agent.datetime') as mock_datetime: mock_datetime.return_value = mock_datetime mock_datetime.now.return_value = mock_time test_agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['sh'], platform='linux') event_loop.run_until_complete(BaseService.get_service('data_svc').store(test_agent)) return test_agent
async def handle_heartbeat(self, **kwargs): """ Accept all components of an agent profile and save a new agent or register an updated heartbeat. :param kwargs: key/value pairs :return: the agent object, instructions to execute """ result = kwargs.pop('result', dict()) for agent in await self.get_service('data_svc').locate( 'agents', dict(paw=kwargs.get('paw', None))): await agent.heartbeat_modification(**kwargs) self.log.debug('Incoming %s beacon from %s' % (agent.contact, agent.paw)) if result: await self._save(Result(**result)) return agent, await self._get_instructions(agent.paw) agent = await self.get_service('data_svc').store( Agent(sleep_min=self.sleep_min, sleep_max=self.sleep_max, watchdog=self.watchdog, **kwargs)) await self._add_agent_to_operation(agent) self.log.debug('First time %s beacon from %s' % (agent.contact, agent.paw)) return agent, await self._get_instructions( agent.paw) + await self._get_bootstrap_instructions(agent)