async def add_manual_command(self, access, data): for parameter in ['operation', 'agent', 'executor', 'command']: if parameter not in data.keys(): return dict(error='Missing parameter: %s' % parameter) operation_search = {'id': data['operation'], **access} operation = next(iter(await self.get_service('data_svc').locate('operations', match=operation_search)), None) if not operation: return dict(error='Operation not found') agent_search = {'paw': data['agent'], **access} agent = next(iter(await self.get_service('data_svc').locate('agents', match=agent_search)), None) if not agent: return dict(error='Agent not found') if data['executor'] not in agent.executors: return dict(error='Agent missing specified executor') encoded_command = self.encode_string(data['command']) ability_id = str(uuid.uuid4()) executor = Executor(name=data['executor'], platform=agent.platform, command=data['command']) ability = Ability(ability_id=ability_id, tactic='auto-generated', technique_id='auto-generated', technique_name='auto-generated', name='Manual Command', description='Manual command ability', executors=[executor]) link = Link.load(dict(command=encoded_command, paw=agent.paw, cleanup=0, ability=ability, score=0, jitter=2, executor=executor, status=operation.link_status())) link.apply_id(agent.host) operation.add_link(link) return dict(link=link.unique)
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
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 setUp(self): # for those that are curious -- when abilities are created, commands are b64 encoded # by default. self.command = 'whoami' self.encoded_command = b64encode( self.command.strip().encode('utf-8')).decode() self.dummy_ability = Ability(ability_id=None, tactic=None, technique_id=None, technique=None, name=None, test=None, description=None, cleanup=None, executor='sh', platform=None, payload=None, variations=[], parsers=None, requirements=None, privilege=None) self.dummy_agent = Agent(paw='123', platform='linux', executors=['sh'], server='http://localhost:8888', sleep_min=0, sleep_max=0, watchdog=0) self.dummy_link = Link(id='abc', operation='123', command=self.encoded_command, paw='123', ability=self.dummy_ability)
async def _save_ability(data_svc, entries, test): ability_id = hashlib.md5(json.dumps(test).encode()).hexdigest() at_ingested = 0 at_total = 0 for p in test['supported_platforms']: at_total += 1 if test['executor']['name'] == 'manual': # this test is expected to be run manually by a human, no automation is provided continue encoded_command, encoded_cleanup = await _prepare_executor(test) await data_svc.store( Ability(ability_id=ability_id, tactic='redcanary', technique_id=entries['attack_technique'], technique=entries['display_name'], name=test['name'], description=test['description'], platform=PLATFORMS.get(p, 'unknown'), executor=EXECUTORS.get(test['executor']['name'], 'unknown'), test=encoded_command, cleanup=encoded_cleanup, requirements=[], parsers=[], variations=[])) at_ingested += 1 return (at_ingested, at_total)
def test_privileged_to_run__1(self, loop, data_svc): """ Test ability.privilege == None """ agent = loop.run_until_complete(data_svc.store(Agent(sleep_min=1, sleep_max=2, watchdog=0))) ability = loop.run_until_complete(data_svc.store( Ability(ability_id='123', privilege=None) )) assert agent.privileged_to_run(ability)
def test_privileged_to_run__6(self, loop, data_svc): """ Test ability.privilege == 'None' and agent.privilege == 'Elevated' """ agent = loop.run_until_complete(data_svc.store(Agent(sleep_min=1, sleep_max=2, watchdog=0, privilege='Elevated'))) ability = loop.run_until_complete(data_svc.store( Ability(ability_id='123') )) assert agent.privileged_to_run(ability)
def test_privileged_to_run__1(self): """ Test ability.privilege == None """ agent = self.run_async( self.data_svc.store(Agent(sleep_min=1, sleep_max=2, watchdog=0))) ability = self.run_async( self.data_svc.store( Ability(ability_id='123', privilege=None, variations=[]))) self.assertTrue(agent.privileged_to_run(ability))
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)
async def _create_ability(self, ability_id, name=None, description=None, tactic=None, technique_id=None, technique_name=None, executors=None, requirements=None, privilege=None, repeatable=False, buckets=None, access=None, singleton=False, plugin='', **kwargs): ability = Ability(ability_id=ability_id, name=name, description=description, tactic=tactic, technique_id=technique_id, technique_name=technique_name, executors=executors, requirements=requirements, privilege=privilege, repeatable=repeatable, buckets=buckets, access=access, singleton=singleton, plugin=plugin, **kwargs) return await self.store(ability)
def from_json(cls, json): ability = Ability.from_json(json['ability']) return cls(id=json['id'], pin=json['pin'], operation=json['operation'], command=json['command'], paw=json['paw'], ability=ability)
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)
async def add_manual_command(self, access, data): for parameter in ['operation', 'agent', 'executor', 'command']: if parameter not in data.keys(): return dict(error='Missing parameter: %s' % parameter) try: operation_id = int(data['operation']) except ValueError: return dict(error='Invalid operation ID') operation_search = {'id': operation_id, **access} operation = next( iter(await self.get_service('data_svc').locate('operations', match=operation_search)), None) if not operation: return dict(error='Operation not found') agent_search = {'paw': data['agent'], **access} agent = next( iter(await self.get_service('data_svc').locate('agents', match=agent_search)), None) if not agent: return dict(error='Agent not found') if data['executor'] not in agent.executors: return dict(error='Agent missing specified executor') encoded_command = self.encode_string(data['command']) ability = Ability(ability_id='auto-generated', tactic='auto-generated', technique_id='auto-generated', technique='auto-generated', name='Manual Command', description='Manual command ability', cleanup='', test=encoded_command, executor=data['executor'], platform=agent.platform, payloads=[], parsers=[], requirements=[], privilege=None, variations=[]) link = Link.load( dict(command=encoded_command, paw=agent.paw, cleanup=0, ability=ability, score=0, jitter=2, status=operation.link_status())) link.apply_id(agent.host) operation.add_link(link) return dict(link=link.unique)
def setup_rest_svc_test(loop, data_svc): BaseWorld.apply_config(name='main', 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=[], executor='psh', platform='windows')) ) loop.run_until_complete(data_svc.store( Ability(ability_id='456', test=BaseWorld.encode_string('whoami'), variations=[], executor='sh', platform='linux')) ) loop.run_until_complete(data_svc.store( Ability(ability_id='789', test=BaseWorld.encode_string('hostname'), variations=[], executor='sh', platform='linux')) ) adversary = Adversary(adversary_id='123', name='test', description='test', atomic_ordering=[]) loop.run_until_complete(data_svc.store(adversary)) agent = Agent(paw='123', sleep_min=2, sleep_max=8, watchdog=0, executors=['pwsh', 'psh'], platform='windows') loop.run_until_complete(data_svc.store(agent)) loop.run_until_complete(data_svc.store( Objective(id='495a9828-cab1-44dd-a0ca-66e58177d8cc', name='default', goals=[Goal()]) )) loop.run_until_complete(data_svc.store( Planner(planner_id='123', name='test', module='test', params=dict()) )) source = Source(id='123', name='test', facts=[], adjustments=[]) loop.run_until_complete(data_svc.store(source)) loop.run_until_complete(data_svc.store( Operation(name='test', agents=[agent], adversary=adversary, id='123', source=source) )) loop.run_until_complete(data_svc.store( Obfuscator(name='plain-text', description='Does no obfuscation to any command, instead running it in plain text', module='plugins.stockpile.app.obfuscators.plain_text') ))
def _generate_ability(ability_id=None, variations=None, *args, **kwargs): if not ability_id: ability_id = random.randint(0, 999999) if not variations: variations = [] return Ability(ability_id=ability_id, variations=variations, *args, **kwargs)
def test_privileged_to_run__3(self, loop, data_svc): """ Test ability.privilege == 'Elevated' """ agent = loop.run_until_complete( data_svc.store(Agent(sleep_min=1, sleep_max=2, watchdog=0))) ability = loop.run_until_complete( data_svc.store( Ability(ability_id='123', privilege='Elevated', variations=[]))) assert not agent.privileged_to_run(ability)
async def _create_ability(self, ability_id, tactic=None, technique_name=None, technique_id=None, name=None, test=None, description=None, executor=None, platform=None, cleanup=None, payloads=None, parsers=None, requirements=None, privilege=None, timeout=60, access=None, buckets=None, repeatable=False, code=None, language=None, build_target=None, variations=None, **kwargs): ps = [] for module in parsers: ps.append(Parser.load(dict(module=module, parserconfigs=parsers[module]))) rs = [] for requirement in requirements: for module in requirement: rs.append(Requirement.load(dict(module=module, relationship_match=requirement[module]))) ability = Ability(ability_id=ability_id, name=name, test=test, tactic=tactic, technique_id=technique_id, technique=technique_name, code=code, language=language, executor=executor, platform=platform, description=description, build_target=build_target, cleanup=cleanup, payloads=payloads, parsers=ps, requirements=rs, privilege=privilege, timeout=timeout, repeatable=repeatable, variations=variations, buckets=buckets, **kwargs) ability.access = access return await self.store(ability)
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 setUp(self): self.initialize() self.ability = Ability(ability_id='123', executor='sh', test=BaseWorld.encode_string('mkdir test'), cleanup=BaseWorld.encode_string('rm -rf test')) self.agent = Agent(sleep_min=1, sleep_max=2, watchdog=0) self.operation = Operation(name='test1', agents=self.agent, adversary='hunter') self.run_async(self.data_svc.store(self.ability))
async def _create_ability(self, ability_id, tactic=None, technique_name=None, technique_id=None, name=None, test=None, description=None, executor=None, platform=None, cleanup=None, payloads=None, parsers=None, requirements=None, privilege=None, timeout=60, access=None, repeatable=False, variations=None): ps = [] for module in parsers: pcs = [(ParserConfig(**m)) for m in parsers[module]] ps.append(Parser(module=module, parserconfigs=pcs)) rs = [] for requirement in requirements: for module in requirement: relation = [Relationship(source=r['source'], edge=r.get('edge'), target=r.get('target')) for r in requirement[module]] rs.append(Requirement(module=module, relationships=relation)) ability = Ability(ability_id=ability_id, name=name, test=test, tactic=tactic, technique_id=technique_id, technique=technique_name, executor=executor, platform=platform, description=description, cleanup=cleanup, payloads=payloads, parsers=ps, requirements=rs, privilege=privilege, timeout=timeout, repeatable=repeatable, variations=variations) ability.access = access return await self.store(ability)
def setUp(self): self.initialize() BaseWorld.apply_config({ 'app.contact.http': '0.0.0.0', 'plugins': ['sandcat', 'stockpile'] }) self.run_async( self.data_svc.store( Ability( ability_id='123', test=BaseWorld.encode_string('curl #{app.contact.http}'))))
def test_privileged_to_run__4(self): """ Test ability.privilege == 'User' and agent.privilege == 'Elevated' """ agent = self.run_async( self.data_svc.store( Agent(sleep_min=1, sleep_max=2, watchdog=0, privilege='Elevated'))) ability = self.run_async( self.data_svc.store( Ability(ability_id='123', privilege='User', variations=[]))) self.assertTrue(agent.privileged_to_run(ability))
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_ability(loop, api_v2_client, executor): executor_linux = executor(name='sh', platform='linux') ability = Ability(ability_id='123', name='Test Ability', executors=[executor_linux], technique_name='collection', technique_id='1', description='', privilege='', tactic='discovery', plugin='testplugin') loop.run_until_complete(BaseService.get_service('data_svc').store(ability)) return ability
def test_ability(self): self.run_async( self.data_svc.store( Ability(ability_id='123', tactic='discovery', technique_id='1', technique='T1033', name='test', test='d2hvYW1pCg==', description='find active user', cleanup='', executor='sh', platform='darwin', payload='wifi.sh', parsers=[], requirements=[], privilege=None, variations=[]))) self.run_async( self.data_svc.store( Ability(ability_id='123', tactic='discovery', technique_id='1', technique='T1033', name='test', test='d2hvYW1pCg==', description='find active user', cleanup='', executor='sh', platform='darwin', payload='wifi.sh', parsers=[], requirements=[], privilege=None, variations=[]))) abilities = self.run_async(self.data_svc.locate('abilities')) self.assertEqual(1, len(abilities))
def test_no_duplicate_ability(self, loop, data_svc): loop.run_until_complete( data_svc.store( Ability(ability_id='123', tactic='discovery', technique_id='1', technique='T1033', name='test', test='d2hvYW1pCg==', description='find active user', cleanup='', executor='sh', platform='darwin', payload='wifi.sh', parsers=[], requirements=[], privilege=None, variations=[]))) loop.run_until_complete( data_svc.store( Ability(ability_id='123', tactic='discovery', technique_id='1', technique='T1033', name='test', test='d2hvYW1pCg==', description='find active user', cleanup='', executor='sh', platform='darwin', payload='wifi.sh', parsers=[], requirements=[], privilege=None, variations=[]))) abilities = loop.run_until_complete(data_svc.locate('abilities')) assert len(abilities) == 1
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)
def test_ability(self): self.run_async( self.data_svc.store( Ability(ability_id='123', tactic='discovery', technique_id='1', technique='T1033', name='test', test='whoami', description='find active user', cleanup='', executor='sh', platform='darwin', payload='wifi.sh', parsers=[], requirements=[], privilege=None))) self.run_async( self.data_svc.store( Ability(ability_id='123', tactic='discovery', technique_id='1', technique='T1033', name='test', test='whoami', description='find active user', cleanup='', executor='sh', platform='darwin', payload='wifi.sh', parsers=[], requirements=[], privilege=None))) self.assertEqual(1, len(self.data_svc.ram['abilities'])) for x in self.data_svc.ram['abilities']: json.dumps(x.display)
def test_no_autogen_cleanup_cmds(self, event_loop, data_svc): cleanup_executor = Executor(name='sh', platform='linux', cleanup='rm #{payload}') event_loop.run_until_complete( data_svc.store( Ability( ability_id='4cd4eb44-29a7-4259-91ae-e457b283a880', tactic='defense-evasion', technique_id='T1070.004', technique_name='Indicator Removal on Host: File Deletion', name='Delete payload', description='Remove a downloaded payload file', privilege=None, executors=[cleanup_executor]))) executor = Executor(name='special_executor', platform='darwin', command='whoami', payloads=['wifi.sh']) event_loop.run_until_complete( data_svc.store( Ability(ability_id='123', tactic='discovery', technique_id='1', technique_name='T1033', name='test', description='find active user', privilege=None, executors=[executor]))) event_loop.run_until_complete(data_svc._verify_abilities()) abilities = event_loop.run_until_complete( data_svc.locate('abilities', dict(ability_id='123'))) for ability in abilities: for executor in ability.executors: assert not executor.cleanup