예제 #1
0
 async def update_on_disk_object(self, obj: Any, data: dict, ram_key: str,
                                 id_property: str, obj_class: type):
     obj_id = getattr(obj, id_property)
     file_path = await self._get_existing_object_file_path(obj_id, ram_key)
     existing_obj_data = AbilitySchema().dump(obj)
     existing_obj_data.update(data)
     self._validate_ability_data(create=False, data=existing_obj_data)
     if existing_obj_data.get('tactic') not in file_path:
         await self.remove_object_from_disk_by_id(obj_id, ram_key)
         file_path = self._create_ability_filepath(data.get('tactic'),
                                                   obj_id)
     await self._save_and_reload_object(file_path, existing_obj_data,
                                        obj_class, obj.access)
     return next(self.find_objects(ram_key, {id_property: obj_id}))
예제 #2
0
def deploy_ability(test_executor, event_loop):
    ability = AbilitySchema().load(dict(ability_id='123',
                                        tactic='persistence',
                                        technique_id='auto-generated',
                                        technique_name='auto-generated',
                                        name='test deploy command',
                                        description='test ability',
                                        executors=[ExecutorSchema().dump(test_executor)]))
    event_loop.run_until_complete(BaseService.get_service('data_svc').store(ability))
    return ability
예제 #3
0
def test_ability(test_executor, loop):
    ability = AbilitySchema().load(
        dict(ability_id='123',
             tactic='discovery',
             technique_id='auto-generated',
             technique_name='auto-generated',
             name='Manual Command',
             description='test ability',
             executors=[ExecutorSchema().dump(test_executor)]))
    loop.run_until_complete(BaseService.get_service('data_svc').store(ability))
    return ability
예제 #4
0
class LinkSchema(ma.Schema):
    class Meta:
        unknown = ma.EXCLUDE

    id = ma.fields.String(missing='')
    paw = ma.fields.String()
    command = ma.fields.String()
    status = ma.fields.Integer(missing=-3)
    score = ma.fields.Integer(missing=0)
    jitter = ma.fields.Integer(missing=0)
    decide = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S')
    pin = ma.fields.Integer(missing=0)
    pid = ma.fields.String()
    facts = ma.fields.List(ma.fields.Nested(FactSchema()))
    relationships = ma.fields.List(ma.fields.Nested(RelationshipSchema()))
    used = ma.fields.List(ma.fields.Nested(FactSchema()))
    unique = ma.fields.String()
    collect = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S', default='')
    finish = ma.fields.String()
    ability = ma.fields.Nested(AbilitySchema())
    executor = ma.fields.Nested(ExecutorSchema())
    cleanup = ma.fields.Integer(missing=0)
    visibility = ma.fields.Nested(VisibilitySchema)
    host = ma.fields.String(missing=None)
    output = ma.fields.String()
    deadman = ma.fields.Boolean()
    agent_reported_time = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S',
                                             missing=None)

    @ma.pre_load()
    def fix_ability(self, link, **_):
        if 'ability' in link and isinstance(link['ability'], Ability):
            ability = link.pop('ability')
            link['ability'] = ability.schema.dump(ability)
        return link

    @ma.pre_load()
    def fix_executor(self, link, **_):
        if 'executor' in link and isinstance(link['executor'], Executor):
            executor = link.pop('executor')
            link['executor'] = executor.schema.dump(executor)
        return link

    @ma.post_load()
    def build_link(self, data, **_):
        return Link(**data)

    @ma.post_dump()
    def prepare_dump(self, data, **_):
        if data.get('agent_reported_time', None) is None:
            data.pop('agent_reported_time', None)
        return data
예제 #5
0
 def build_ability(self, data: dict, executor: Executor):
     if not data.get('ability_id'):
         data['ability_id'] = str(uuid.uuid4())
     if not data.get('tactic'):
         data['tactic'] = 'auto-generated'
     if not data.get('technique_id'):
         data['technique_id'] = 'auto-generated'
     if not data.get('technique_name'):
         data['technique_name'] = 'auto-generated'
     if not data.get('name'):
         data['name'] = 'Manual Command'
     if not data.get('description'):
         data['description'] = 'Manual command ability'
     data['executors'] = [ExecutorSchema().dump(executor)]
     ability = AbilitySchema().load(data)
     return ability
예제 #6
0
class LinkSchema(ma.Schema):
    class Meta:
        unknown = ma.EXCLUDE

    id = ma.fields.String(missing='')
    paw = ma.fields.String()
    command = ma.fields.String()
    status = ma.fields.Integer(missing=-3)
    score = ma.fields.Integer(missing=0)
    jitter = ma.fields.Integer(missing=0)
    decide = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S')
    pin = ma.fields.Integer(missing=0)
    pid = ma.fields.String()
    facts = ma.fields.List(ma.fields.Nested(FactSchema()))
    relationships = ma.fields.List(ma.fields.Nested(RelationshipSchema()))
    used = ma.fields.List(ma.fields.Nested(FactSchema()))
    unique = ma.fields.String()
    collect = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S', default='')
    finish = ma.fields.String()
    ability = ma.fields.Nested(AbilitySchema())
    cleanup = ma.fields.Integer(missing=0)
    visibility = ma.fields.Nested(VisibilitySchema)
    host = ma.fields.String(missing=None)
    output = ma.fields.String()
    deadman = ma.fields.Boolean()

    @ma.pre_load()
    def fix_ability(self, link, **_):
        if 'ability' in link and isinstance(link['ability'], Ability):
            link_input = link.pop('ability')
            link['ability'] = link_input.schema.dump(link_input)
        return link

    @ma.post_load()
    def build_link(self, data, **_):
        return Link(**data)

    @ma.pre_dump()
    def prepare_link(self, data, **_):
        # temp - can be simplified with AbilitySchema
        data.executor = data.ability.executor if isinstance(
            data.ability, Ability) else data.ability['executor']
        return data
예제 #7
0
class AbilityApi(BaseObjectApi):
    def __init__(self, services):
        super().__init__(description='ability',
                         obj_class=Ability,
                         schema=AbilitySchema,
                         ram_key='abilities',
                         id_property='ability_id',
                         auth_svc=services['auth_svc'])
        self._api_manager = AbilityApiManager(data_svc=services['data_svc'],
                                              file_svc=services['file_svc'])

    def add_routes(self, app: web.Application):
        router = app.router
        router.add_get('/abilities', self.get_abilities)
        router.add_get('/abilities/{ability_id}', self.get_ability_by_id)
        router.add_post('/abilities', self.create_ability)
        router.add_put('/abilities/{ability_id}',
                       self.create_or_update_ability)
        router.add_patch('/abilities/{ability_id}', self.update_ability)
        router.add_delete('/abilities/{ability_id}', self.delete_ability)

    @aiohttp_apispec.docs(
        tags=['abilities'],
        summary='Get all abilities.',
        description='Provides a list of all available abilities.')
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(
        AbilitySchema(many=True, partial=True),
        description='Returns a list of all abilities.')
    async def get_abilities(self, request: web.Request):
        abilities = await self.get_all_objects(request)
        return web.json_response(abilities)

    @aiohttp_apispec.docs(
        tags=['abilities'],
        summary='Get an ability.',
        description='Provides one ability based on its ability id.',
        parameters=[{
            'in': 'path',
            'name': 'ability_id',
            'schema': {
                'type': 'string'
            },
            'required': 'true',
            'description': 'UUID of the Ability to be retrieved'
        }])
    @aiohttp_apispec.querystring_schema(BaseGetOneQuerySchema)
    @aiohttp_apispec.response_schema(
        AbilitySchema(partial=True),
        description='JSON dictionary representation of the existing Ability.')
    async def get_ability_by_id(self, request: web.Request):
        ability = await self.get_object(request)
        return web.json_response(ability)

    @aiohttp_apispec.docs(
        tags=['abilities'],
        summary='Creates a new ability.',
        description='Creates a new adversary based on the `AbilitySchema`. '
        '"name", "tactic", and "executors" are all required fields.')
    @aiohttp_apispec.request_schema(AbilitySchema)
    @aiohttp_apispec.response_schema(
        AbilitySchema,
        description='JSON dictionary representation of the created Ability.')
    async def create_ability(self, request: web.Request):
        ability = await self.create_on_disk_object(request)
        return web.json_response(ability.display)

    @aiohttp_apispec.docs(
        tags=['abilities'],
        summary='Replaces an existing ability.',
        description=
        'Replaces an ability based on the `AbilitySchema` values provided '
        'in the message body. "name", "tactic", and "executors" '
        'are all required fields.',
        parameters=[{
            'in': 'path',
            'name': 'ability_id',
            'schema': {
                'type': 'string'
            },
            'required': 'true',
            'description': 'UUID of the Ability to be retrieved'
        }])
    @aiohttp_apispec.request_schema(AbilitySchema(partial=True))
    @aiohttp_apispec.response_schema(
        AbilitySchema,
        description='JSON dictionary representation of the replaced Ability.')
    async def create_or_update_ability(self, request: web.Request):
        ability = await self.create_or_update_on_disk_object(request)
        return web.json_response(ability.display)

    @aiohttp_apispec.docs(
        tags=['abilities'],
        summary='Updates an existing ability.',
        description=
        'Updates an ability based on the `AbilitySchema` values provided '
        'in the message body.',
        parameters=[{
            'in': 'path',
            'name': 'ability_id',
            'schema': {
                'type': 'string'
            },
            'required': 'true',
            'description': 'UUID of the Ability to be retrieved'
        }])
    @aiohttp_apispec.request_schema(
        AbilitySchema(partial=True,
                      exclude=[
                          'ability_id', 'requirements', 'additional_info',
                          'access'
                      ]))
    @aiohttp_apispec.response_schema(
        AbilitySchema,
        description='JSON dictionary representation of the replaced Ability.')
    async def update_ability(self, request: web.Request):
        ability = await self.update_on_disk_object(request)
        return web.json_response(ability.display)

    @aiohttp_apispec.docs(tags=['abilities'],
                          summary='Deletes an ability.',
                          description='Deletes an existing ability.',
                          parameters=[{
                              'in':
                              'path',
                              'name':
                              'ability_id',
                              'schema': {
                                  'type': 'string'
                              },
                              'required':
                              'true',
                              'description':
                              'UUID of the Ability to be retrieved'
                          }])
    @aiohttp_apispec.response_schema(
        AbilitySchema,
        code=204,
        description='HTTP 204 Status Code (No Content)')
    async def delete_ability(self, request: web.Request):
        await self.delete_on_disk_object(request)
        return web.HTTPNoContent()
예제 #8
0
class AbilityApi(BaseObjectApi):
    def __init__(self, services):
        super().__init__(description='ability',
                         obj_class=Ability,
                         schema=AbilitySchema,
                         ram_key='abilities',
                         id_property='ability_id',
                         auth_svc=services['auth_svc'])
        self._api_manager = AbilityApiManager(data_svc=services['data_svc'],
                                              file_svc=services['file_svc'])

    def add_routes(self, app: web.Application):
        router = app.router
        router.add_get('/abilities', self.get_abilities)
        router.add_get('/abilities/{ability_id}', self.get_ability_by_id)
        router.add_post('/abilities', self.create_ability)
        router.add_put('/abilities/{ability_id}',
                       self.create_or_update_ability)
        router.add_patch('/abilities/{ability_id}', self.update_ability)
        router.add_delete('/abilities/{ability_id}', self.delete_ability)

    @aiohttp_apispec.docs(tags=['abilities'])
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(AbilitySchema(many=True, partial=True))
    async def get_abilities(self, request: web.Request):
        abilities = await self.get_all_objects(request)
        return web.json_response(abilities)

    @aiohttp_apispec.docs(tags=['abilities'])
    @aiohttp_apispec.querystring_schema(BaseGetOneQuerySchema)
    @aiohttp_apispec.response_schema(AbilitySchema(partial=True))
    async def get_ability_by_id(self, request: web.Request):
        ability = await self.get_object(request)
        return web.json_response(ability)

    @aiohttp_apispec.docs(tags=['abilities'])
    @aiohttp_apispec.request_schema(AbilitySchema)
    @aiohttp_apispec.response_schema(AbilitySchema)
    async def create_ability(self, request: web.Request):
        ability = await self.create_on_disk_object(request)
        return web.json_response(ability.display)

    @aiohttp_apispec.docs(tags=['abilities'])
    @aiohttp_apispec.request_schema(AbilitySchema(partial=True))
    @aiohttp_apispec.response_schema(AbilitySchema)
    async def create_or_update_ability(self, request: web.Request):
        ability = await self.create_or_update_on_disk_object(request)
        return web.json_response(ability.display)

    @aiohttp_apispec.docs(tags=['abilities'])
    @aiohttp_apispec.request_schema(AbilitySchema(partial=True))
    @aiohttp_apispec.response_schema(AbilitySchema)
    async def update_ability(self, request: web.Request):
        ability = await self.update_on_disk_object(request)
        return web.json_response(ability.display)

    @aiohttp_apispec.docs(tags=['abilities'])
    @aiohttp_apispec.response_schema(AbilitySchema)
    async def delete_ability(self, request: web.Request):
        await self.delete_on_disk_object(request)
        return web.HTTPNoContent()
예제 #9
0
class LinkSchema(ma.Schema):
    class Meta:
        unknown = ma.EXCLUDE

    id = ma.fields.String(missing='')
    paw = ma.fields.String()
    command = ma.fields.String()
    status = ma.fields.Integer(missing=-3)
    score = ma.fields.Integer(missing=0)
    jitter = ma.fields.Integer(missing=0)
    decide = ma.fields.DateTime(format=BaseObject.TIME_FORMAT)
    pin = ma.fields.Integer(missing=0)
    pid = ma.fields.String()
    facts = ma.fields.List(ma.fields.Nested(FactSchema()))
    relationships = ma.fields.List(ma.fields.Nested(RelationshipSchema()))
    used = ma.fields.List(ma.fields.Nested(FactSchema()))
    unique = ma.fields.String()
    collect = ma.fields.DateTime(format=BaseObject.TIME_FORMAT, default='')
    finish = ma.fields.String()
    ability = ma.fields.Nested(AbilitySchema())
    executor = ma.fields.Nested(ExecutorSchema())
    cleanup = ma.fields.Integer(missing=0)
    visibility = ma.fields.Nested(VisibilitySchema())
    host = ma.fields.String(missing=None)
    output = ma.fields.String()
    deadman = ma.fields.Boolean()
    agent_reported_time = ma.fields.DateTime(format=BaseObject.TIME_FORMAT,
                                             missing=None)

    @ma.pre_load()
    def fix_ability(self, link, **_):
        if 'ability' in link and isinstance(link['ability'], Ability):
            ability = link.pop('ability')
            link['ability'] = ability.schema.dump(ability)
        return link

    @ma.pre_load()
    def fix_executor(self, link, **_):
        if 'executor' in link and isinstance(link['executor'], Executor):
            executor = link.pop('executor')
            link['executor'] = executor.schema.dump(executor)
        return link

    @ma.pre_load()
    def remove_properties(self, data, **_):
        data.pop('unique', None)
        data.pop('decide', None)
        data.pop('pid', None)
        data.pop('facts', None)
        data.pop('collect', None)
        data.pop('finish', None)
        data.pop('visibility', None)
        data.pop('output', None)
        data.pop('used.unique', None)
        return data

    @ma.post_load()
    def build_link(self, data, **kwargs):
        return None if kwargs.get('partial') is True else Link(**data)

    @ma.post_dump()
    def prepare_dump(self, data, **_):
        if data.get('agent_reported_time', None) is None:
            data.pop('agent_reported_time', None)
        return data