Beispiel #1
0
    def test_save_discover_seeded_fact_not_in_command(self, event_loop,
                                                      ability, executor,
                                                      operation,
                                                      knowledge_svc):
        test_executor = executor(name='psh', platform='windows')
        test_ability = ability(ability_id='123', executors=[test_executor])
        fact1 = Fact(trait='remote.host.fqdn', value='dc')
        fact2 = Fact(trait='domain.user.name', value='Bob')
        relationship = Relationship(source=fact1,
                                    edge='has_user',
                                    target=fact2)
        link = Link(command='net user',
                    paw='123456',
                    ability=test_ability,
                    id='111111',
                    executor=test_executor)
        operation = operation(name='test-op',
                              agents=[],
                              adversary=Adversary(name='sample',
                                                  adversary_id='XYZ',
                                                  atomic_ordering=[],
                                                  description='test'),
                              source=Source(id='test-source',
                                            facts=[fact1, fact2]))
        event_loop.run_until_complete(operation._init_source())
        event_loop.run_until_complete(
            link.save_fact(operation, fact2, 1, relationship))

        assert fact2.origin_type == OriginType.SEEDED
        assert '123456' in fact2.collected_by
Beispiel #2
0
    def test_link_knowledge_svc_synchronization(self, loop, executor, ability,
                                                knowledge_svc):
        test_executor = executor(name='psh', platform='windows')
        test_ability = ability(ability_id='123', executors=[test_executor])
        fact = Fact(trait='remote.host.fqdn', value='dc')
        fact2 = Fact(trait='domain.user.name', value='Bob')
        relationship = Relationship(source=fact,
                                    edge='has_admin',
                                    target=fact2)
        test_link = Link(command='echo "this was a triumph"',
                         paw='123456',
                         ability=test_ability,
                         id=111111,
                         executor=test_executor)

        loop.run_until_complete(
            test_link._create_relationships([relationship], None))
        checkable = [(x.trait, x.value) for x in test_link.facts]
        assert (fact.trait, fact.value) in checkable
        assert (fact2.trait, fact2.value) in checkable
        knowledge_base_f = loop.run_until_complete(
            knowledge_svc.get_facts(dict(source=test_link.id)))
        assert len(knowledge_base_f) == 2
        assert test_link.id in knowledge_base_f[0].links
        knowledge_base_r = loop.run_until_complete(
            knowledge_svc.get_relationships(dict(edge='has_admin')))
        assert len(knowledge_base_r) == 1
 def test_no_status_change_event_fired_when_setting_same_status(
         self, mock_emit_status_change_method, ability, executor):
     executor = executor('psh', 'windows')
     ability = ability(executor=executor)
     link = Link(command='net user a',
                 paw='123456',
                 ability=ability,
                 executor=executor,
                 status=-3)
     link.status = link.status
     mock_emit_status_change_method.assert_not_called()
Beispiel #4
0
 def test_status_change_event_fired_on_status_change(
         self, mock_emit_status_change_method, ability, executor):
     executor = executor('psh', 'windows')
     ability = ability(executor=executor)
     link = Link(command='net user a',
                 paw='123456',
                 ability=ability,
                 executor=executor,
                 status=-3)
     link.status = -5
     mock_emit_status_change_method.assert_called_with(from_status=-3,
                                                       to_status=-5)
Beispiel #5
0
 async def _generate_cleanup_links(self, operation, agent, link_status):
     links = []
     for link in [l for l in operation.chain if l.paw == agent.paw]:
         ability = (await self.get_service('data_svc').locate('abilities',
                                                              match=dict(unique=link.ability.unique)))[0]
         for cleanup in ability.cleanup:
             decoded_cmd = agent.replace(cleanup)
             variant, _, _ = await self._build_single_test_variant(decoded_cmd, link.used, link.ability.executor)
             lnk = Link(operation=operation.id, command=self.encode_string(variant), paw=agent.paw, cleanup=1,
                        ability=ability, score=0, jitter=2, status=link_status)
             if lnk.command not in [l.command for l in links]:
                 lnk.apply_id(agent.host)
                 links.append(lnk)
     return links
Beispiel #6
0
    async def test_trim_links(self, setup_planning_test, planning_svc):
        """
        This test covers both remove_links_with_unset_variables and remove_links_missing_requirements.
        It uses a fact set that causes add_test_variants to create three links. One of which is the original
        that has not been populated with facts, this one gets pruned off by remove_links_with_unset_variables.
        Of the remaining two links that are populated, one is pruned off by a requirement that requires that
        the character 0 is in the link's command. The tests show that only one link is returned by trim_links
        and that the returned link is the one that is populated and adheres to the requirement.
        """
        ability, agent, operation, _ = setup_planning_test

        link = Link.load(dict(command=BaseWorld.encode_string(test_string), paw=agent.paw, ability=ability,
                              executor=next(ability.executors), status=0))
        facts = [
            Fact(trait='1_2_3', value='0'),
            Fact(trait='1_2_3', value='4'),
            Fact(trait='a.b.c', value='1'),
            Fact(trait='a.b.d', value='2'),
            Fact(trait='a.b.e', value='3'),
        ]

        operation.all_facts = async_wrapper(facts)
        operation.planner = MagicMock()
        planning_svc.load_module = async_wrapper(RequirementFake())
        link.ability.requirements = [Requirement('fake_requirement', [{'fake': 'relationship'}])]

        trimmed_links = await planning_svc.trim_links(operation, [link], agent)

        assert len(trimmed_links) == 1
        assert BaseWorld.decode_bytes(trimmed_links[0].display['command']) == target_string
 async def create_potential_link(self, operation_id: str, data: dict,
                                 access: BaseWorld.Access):
     self.validate_link_data(data)
     operation = await self.get_operation_object(operation_id, access)
     agent = await self.get_agent(operation, data)
     if data['executor']['name'] not in agent.executors:
         raise JsonHttpBadRequest(
             f'Agent {agent.paw} missing specified executor')
     encoded_command = self._encode_string(
         agent.replace(self._encode_string(data['executor']['command']),
                       file_svc=self.services['file_svc']))
     executor = self.build_executor(data=data.pop('executor', {}),
                                    agent=agent)
     ability = self.build_ability(data=data.pop('ability', {}),
                                  executor=executor)
     link = Link.load(
         dict(command=encoded_command,
              paw=agent.paw,
              ability=ability,
              executor=executor,
              status=operation.link_status(),
              score=data.get('score', 0),
              jitter=data.get('jitter', 0),
              cleanup=data.get('cleanup', 0),
              pin=data.get('pin', 0),
              host=agent.host,
              deadman=data.get('deadman', False),
              used=data.get('used', []),
              relationships=data.get('relationships', [])))
     link.apply_id(agent.host)
     await operation.apply(link)
     return link.display
Beispiel #8
0
 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 _generate_new_links(self, operation, agent, abilities,
                                  link_status):
        """Generate links with given status

        :param operation: Operation to generate links on
        :type operation: Operation
        :param agent: Agent to generate links on
        :type agent: Agent
        :param agent: Abilities to generate links for
        :type agent: list(Ability)
        :param link_status: Link status, referencing link state dict
        :type link_status: int
        :return: Links for agent
        :rtype: list(Link)
        """
        links = []
        for ability in await agent.capabilities(abilities):
            executor = await agent.get_preferred_executor(ability)
            if not executor:
                continue

            if executor.HOOKS and executor.language and executor.language in executor.HOOKS:
                await executor.HOOKS[executor.language](ability, executor)
            if executor.command:
                link = Link.load(
                    dict(command=self.encode_string(executor.test),
                         paw=agent.paw,
                         score=0,
                         ability=ability,
                         executor=executor,
                         status=link_status,
                         jitter=self.jitter(operation.jitter)))
                links.append(link)
        return links
Beispiel #10
0
    def test_filter_bs(self, loop, setup_planning_test, planning_svc):
        _, agent, operation, ability = setup_planning_test
        link = Link.load(
            dict(command=BaseWorld.encode_string(test_string),
                 paw=agent.paw,
                 ability=ability,
                 executor=next(ability.executors),
                 status=0))

        f0 = Fact(trait='1_2_3', value='0')
        f1 = Fact(trait='a.b.c', value='1')
        f2 = Fact(trait='a.b.d', value='2')
        f3 = Fact(trait='a.b.e', value='3')
        f4 = Fact(trait='a.b.e', value='4')
        f5 = Fact(trait='a.b.e', value='5')
        f6 = Fact(trait='a.b.e', value='6')

        gen = loop.run_until_complete(
            planning_svc.add_test_variants([link],
                                           agent,
                                           facts=[f0, f1, f2, f3, f4, f5, f6]))

        assert len(gen) == 4
        assert BaseWorld.decode_bytes(
            gen[1].display['command']) == target_string
Beispiel #11
0
    async def test_trait_with_multiple_variations_of_parts(
            self, setup_planning_test, planning_svc):
        _, agent, operation, ability = setup_planning_test
        encoded_command = BaseWorld.encode_string('#{a} #{a.b} #{a.b.c}')
        link = Link.load(
            dict(command=encoded_command,
                 paw=agent.paw,
                 ability=ability,
                 executor=next(ability.executors),
                 status=0))

        input_facts = [
            Fact(trait='a', value='1'),
            Fact(trait='a.b', value='2'),
            Fact(trait='a.b.c', value='3'),
            Fact(trait='server', value='5')
        ]

        new_links = await planning_svc.add_test_variants([link],
                                                         agent,
                                                         facts=input_facts)
        assert len(new_links) == 2

        found_commands = set(x.command for x in new_links)
        assert len(found_commands) == 2  # the original and the replaced
        assert encoded_command in found_commands
        assert BaseWorld.encode_string('1 2 3') in found_commands
Beispiel #12
0
    def test_get_links(self, loop, setup_planning_test, planning_svc, data_svc):
        # PART A: Don't fill in facts for "cability" so only "tability"
        #   is returned in "links"
        tability, agent, operation, cability = setup_planning_test
        operation.adversary.atomic_ordering = ["123", "321"]
        links = loop.run_until_complete(planning_svc.get_links
                                        (operation=operation, buckets=None,
                                         agent=agent))
        assert links[0].ability.ability_id == tability.ability_id

        # PART B: Fill in facts to allow "cability" to be returned in "links"
        #   in addition to "tability"
        operation.add_link(Link.load(
            dict(command='', paw=agent.paw, ability=tability, status=0)))

        operation.chain[0].facts.append(Fact(trait='1_2_3', value='0'))
        operation.chain[0].facts.append(Fact(trait='a.b.c', value='1'))
        operation.chain[0].facts.append(Fact(trait='a.b.d', value='2'))
        operation.chain[0].facts.append(Fact(trait='a.b.e', value='3'))

        links = loop.run_until_complete(planning_svc.get_links
                                        (operation=operation, buckets=None,
                                         agent=agent))

        assert links[0].ability.ability_id == cability.ability_id
        assert links[1].ability.ability_id == tability.ability_id
        assert base64.b64decode(links[0].command).decode('utf-8') == target_string
Beispiel #13
0
    async def _generate_cleanup_links(self, operation, agent, link_status):
        """Generate cleanup links with given status

        :param operation: Operation to generate cleanup links for
        :type operation: Operation
        :param agent: Agent to generate cleanup links for
        :type agent: Agent
        :param link_status: Link status, referencing link state dict
        :type link_status: int
        :return: Cleanup links for agent
        :rtype: list(Link)
        """
        links = []
        for link in [l for l in operation.chain if l.paw == agent.paw]:
            matched_abilities = await self.get_service('data_svc').locate('abilities',
                                                                          match=dict(unique=link.ability.unique))
            if matched_abilities:
                ability = matched_abilities[0]
                for cleanup in ability.cleanup:
                    decoded_cmd = agent.replace(cleanup, file_svc=self.get_service('file_svc'))
                    variant, _, _ = await self._build_single_test_variant(decoded_cmd, link.used, link.ability.executor)
                    lnk = Link.load(dict(command=self.encode_string(variant), paw=agent.paw, cleanup=1,
                                         ability=ability, score=0, jitter=2, status=link_status))
                    if lnk.command not in [l.command for l in links]:
                        lnk.apply_id(agent.host)
                        links.append(lnk)
        return links
Beispiel #14
0
    def test_wait_for_links_and_monitor(self, loop, planning_svc, fact,
                                        setup_planning_test):
        # PART A:
        ability, agent, operation, _ = setup_planning_test
        # Add a link to operation.chain
        operation.add_link(Link.load(
            dict(command='', paw=agent.paw, ability=ability, status=0)))
        # Set id to match planner.operation.chain[0].id
        operation.chain[0].id = "123"
        planner = PlannerFake(operation)
        # Create a list containing only the id used above
        link_ids = ["123"]
        # Make sure program doesn't hang in wait_for_links_completion()
        planner.operation.chain[0].finish = True
        assert loop.run_until_complete(planning_svc.wait_for_links_and_monitor(
            planner, operation, link_ids, condition_stop=True)) is False

        # PART B:
        # Make sure program hangs in wait_for_links_completion()
        planner.operation.chain[0].finish = False
        timeout = False
        try:
            loop.run_until_complete(asyncio.wait_for(
                planning_svc.wait_for_links_and_monitor(planner, operation,
                                                        link_ids,
                                                        condition_stop=True),
                timeout=5.0))
        except asyncio.TimeoutError:
            timeout = True
        assert timeout is True
Beispiel #15
0
    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())
        ability = Ability(ability_id=ability_id, 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)
Beispiel #16
0
    async def test_global_variables_not_replaced_with_facts(
            self, setup_planning_test, planning_svc):
        _, agent, operation, ability = setup_planning_test
        encoded_command = BaseWorld.encode_string(
            '#{server} #{origin_link_id}')
        link = Link.load(
            dict(command=encoded_command,
                 paw=agent.paw,
                 ability=ability,
                 executor=next(ability.executors),
                 status=0))

        input_facts = [
            Fact(trait='server', value='bad.server'),
            Fact(trait='origin_link_id', value='bad.origin_link_id')
        ]

        planning_svc.add_global_variable_owner(Agent)  # handles #{server}
        planning_svc.add_global_variable_owner(
            Link)  # handles #{origin_link_id}

        new_links = await planning_svc.add_test_variants([link],
                                                         agent,
                                                         facts=input_facts)
        assert len(new_links) == 1
        assert new_links[0].raw_command == f'{agent.server} {link.id}'
Beispiel #17
0
 def _generate_link(operation, command, paw, ability, *args, **kwargs):
     return Link(operation=operation,
                 ability=ability,
                 command=command,
                 paw=paw,
                 *args,
                 **kwargs)
Beispiel #18
0
    def test_link_agent_reported_time_not_present_when_none_roundtrip(
            self, ability, executor):
        test_executor = executor(name='psh', platform='windows')
        test_ability = ability(ability_id='123')
        test_link = Link(
            command=
            'sc.exe \\dc create sandsvc binpath= "s4ndc4t.exe -originLinkID 111111"',
            paw='123456',
            ability=test_ability,
            executor=test_executor,
            id=111111)
        serialized_link = test_link.display
        loaded_link = Link.load(serialized_link)

        assert 'agent_reported_time' not in serialized_link
        assert loaded_link.agent_reported_time is None
Beispiel #19
0
    async def _generate_new_links(self, operation, agent, abilities,
                                  link_status):
        """Generate links with given status

        :param operation: Operation to generate links on
        :type operation: Operation
        :param agent: Agent to generate links on
        :type agent: Agent
        :param agent: Abilities to generate links for
        :type agent: list(Ability)
        :param link_status: Link status, referencing link state dict
        :type link_status: int
        :return: Links for agent
        :rtype: list(Link)
        """
        links = []
        for a in await agent.capabilities(abilities):
            if a.code and a.HOOKS:
                await a.HOOKS[a.language](a)
            if a.test:
                links.append(
                    Link.load(
                        dict(command=a.test,
                             paw=agent.paw,
                             score=0,
                             ability=a,
                             status=link_status,
                             jitter=self.jitter(operation.jitter))))
        return links
Beispiel #20
0
 async def _generate_new_links(self, operation, agent, abilities, link_status):
     links = []
     for a in await agent.capabilities(abilities):
         links.append(
             Link(operation=operation.id, command=a.test, paw=agent.paw, score=0, ability=a,
                  status=link_status, jitter=self.jitter(operation.jitter))
         )
     return links
Beispiel #21
0
 def _create_and_setup_verification_link(self, ability, link_pid):
     link = Link(command=None,
                 paw='gameboard_detection',
                 ability=ability,
                 executor=next(ability.executors))
     fact = Fact(trait='host.process.id', value=link_pid)
     link.used.append(fact)
     link.facts.append(fact)
     link.command = self.encode_string(
         'The link with the PID shown below was successfully detected.')
     link.collect = datetime.now()
     link.finish = self.data_svc.get_current_timestamp()
     link.output = True
     link.status = 0
     link.id = link.generate_number()
     link.pin = link_pid
     return link
Beispiel #22
0
 async def test_get_cleanup_links(self, setup_planning_test, planning_svc):
     ability, agent, operation, _ = setup_planning_test
     executor = next(ability.executors)
     operation.add_link(Link.load(dict(command='', paw=agent.paw, ability=ability, executor=executor, status=0)))
     links = await planning_svc.get_cleanup_links(operation=operation, agent=agent)
     link_list = list(links)
     assert len(link_list) == 1
     assert BaseWorld.decode_bytes(link_list[0].command) == executor.cleanup[0]
Beispiel #23
0
 def _generate_link(command, paw, ability, executor, *args, **kwargs):
     return Link.load(
         dict(ability=ability,
              executor=executor,
              command=command,
              paw=paw,
              *args,
              **kwargs))
Beispiel #24
0
 async def handle_potential_links(self, request):
     data = dict(await request.json())
     options = dict(
         PUT=dict(func=lambda d: self.rest_svc.apply_potential_link(
             Link.from_json(d))),
         POST=dict(func=lambda d: self.rest_svc.get_potential_links(**d)))
     resp = await options[request.method]['func'](data)
     return web.json_response(resp)
Beispiel #25
0
 def test_get_cleanup_links(self, loop, setup_planning_test, planning_svc):
     ability, agent, operation, _ = setup_planning_test
     operation.add_link(Link.load(dict(command='', paw=agent.paw, ability=ability, status=0)))
     links = loop.run_until_complete(
         planning_svc.get_cleanup_links(operation=operation, agent=agent)
     )
     link_list = list(links)
     assert len(link_list) == 1
     assert link_list[0].command == ability.cleanup[0]
Beispiel #26
0
    async def test_remove_links_does_not_ignore_global_variables(self, planning_svc, ability, executor):
        cmd = 'a -b --foo=#{server} --bar=#{origin_link_id}'
        links = [Link(command=BaseWorld.encode_string(cmd), paw='1', ability=ability(), executor=executor())]

        planning_svc.add_global_variable_owner(Agent)  # handles #{server}
        planning_svc.add_global_variable_owner(Link)  # handles #{origin_link_id}

        await planning_svc.remove_links_with_unset_variables(links)
        assert len(links) == 0
Beispiel #27
0
    def test_emit_status_change_event(self, loop, fake_event_svc, ability):
        link = Link(command='net user a', paw='123456', ability=ability(), status=-3)
        fake_event_svc.reset()

        loop.run_until_complete(
            link._emit_status_change_event(
                from_status=-3,
                to_status=-5
            )
        )

        expected_key = (Link.EVENT_EXCHANGE, Link.EVENT_QUEUE_STATUS_CHANGED)
        assert expected_key in fake_event_svc.fired

        event_kwargs = fake_event_svc.fired[expected_key]
        assert event_kwargs['link'] == link.id
        assert event_kwargs['from_status'] == -3
        assert event_kwargs['to_status'] == -5
Beispiel #28
0
 def test_no_status_change_event_on_instantiation(
         self, mock_emit_status_change_method, ability, executor):
     executor = executor('psh', 'windows')
     ability = ability(executor=executor)
     Link(command='net user a',
          paw='123456',
          ability=ability,
          executor=executor)
     mock_emit_status_change_method.assert_not_called()
Beispiel #29
0
 async def task(self, abilities, obfuscator, facts=(), deadman=False):
     bps = BasePlanningService()
     potential_links = [Link.load(dict(command=i.test, paw=self.paw, ability=i, deadman=deadman)) for i in await self.capabilities(abilities)]
     links = []
     for valid in await bps.remove_links_missing_facts(
             await bps.add_test_variants(links=potential_links, agent=self, facts=facts)):
         links.append(valid)
     links = await bps.obfuscate_commands(self, obfuscator, links)
     self.links.extend(links)
     return links
Beispiel #30
0
    def test_link_agent_reported_time_present_when_set_roundtrip(
            self, ability, executor):
        test_executor = executor(name='psh', platform='windows')
        test_ability = ability(ability_id='123')
        test_link = Link(
            command=
            'sc.exe \\dc create sandsvc binpath= "s4ndc4t.exe -originLinkID 111111"',
            paw='123456',
            ability=test_ability,
            executor=test_executor,
            id=111111,
            agent_reported_time=BaseService.get_timestamp_from_string(
                '2021-02-23 11:50:16'))
        serialized_link = test_link.display
        loaded_link = Link.load(serialized_link)

        assert serialized_link['agent_reported_time'] == '2021-02-23 11:50:16'
        assert loaded_link.agent_reported_time == BaseService.get_timestamp_from_string(
            '2021-02-23 11:50:16')