예제 #1
0
async def create_filemeta_in_database(request, user, id):
    try:
        operation = await db_objects.get(Operation,
                                         name=user['current_operation'])
        filemeta = await db_objects.get(FileMeta, id=id, operation=operation)
    except Exception as e:
        print(e)
        return json({
            'status':
            'error',
            'error':
            'file does not exist or not part of your current operation'
        })
    status = {'status': 'success'}
    filemeta.deleted = True
    try:
        await db_objects.update(filemeta)
    except Exception as e:
        status = {'status': 'error', 'error': str(e)}
    try:
        # only remove the file if there's nothing else pointing to it
        # this could be a payload and the user is just asking to remove the hosted aspect
        file_count = await db_objects.count(
            FileMeta.select().where((FileMeta.path == filemeta.path)
                                    & (FileMeta.deleted == False)))
        file_count += await db_objects.count(
            Payload.select().where((Payload.location == filemeta.path)
                                   & (Payload.deleted == False)))
        if file_count == 0:
            os.remove(filemeta.path)
    except Exception as e:
        pass
    return json({**status, **filemeta.to_json()})
예제 #2
0
async def ws_payloads_current_operation(request, ws, user):
    try:
        async with aiopg.create_pool(apfell.config['DB_POOL_CONNECT_STRING']) as pool:
            async with pool.acquire() as conn:
                async with conn.cursor() as cur:
                    await cur.execute('LISTEN "newpayload";')
                    # BEFORE WE START GETTING NEW THINGS, UPDATE WITH ALL OF THE OLD DATA
                    if user['current_operation'] != "":
                        operation = await db_objects.get(Operation, name=user['current_operation'])
                        payloads = await db_objects.execute(Payload.select().where(Payload.operation == operation).order_by(Payload.id))
                        for p in payloads:
                            await ws.send(js.dumps(p.to_json()))
                        await ws.send("")
                        # now pull off any new payloads we got queued up while processing old data
                        while True:
                            try:
                                msg = conn.notifies.get_nowait()
                                id = (msg.payload)
                                p = await db_objects.get(Payload, id=id)
                                if p.operation == operation:
                                    await ws.send(js.dumps(p.to_json()))
                            except asyncio.QueueEmpty as e:
                                await asyncio.sleep(1)
                                await ws.send("")  # this is our test to see if the client is still there
                                continue
                            except Exception as e:
                                print(e)
                                return
    finally:
        pool.close()
예제 #3
0
async def get_all_payloads_current_operation(request, user):
    if user['current_operation'] != "":
        operation = await db_objects.get(Operation, name=user['current_operation'])
        payloads = await db_objects.execute(Payload.select().where(Payload.operation == operation))
        return json([p.to_json() for p in payloads])
    else:
        return json({"status": "error", 'error': 'must be part of a current operation'})
예제 #4
0
async def analytics_payload_tree_api(request, user):
    # each payload is the root of a tree, all of the corresponding callbacks that use it are under that tree
    dbpayloads = await db_objects.execute(Payload.select())
    display_config = {}
    display_config['inactive'] = False  # by default, include only active callbacks
    display_config['strikethrough'] = False
    if request.method == 'POST':
        data = request.json
        if 'inactive' in data:
            display_config['inactive'] = data['inactive']
        if 'strikethrough' in data:
            display_config['strikethrough'] = data['strikethrough']
    tree = []
    for p in dbpayloads:
        display = await analytics_payload_tree_api_function(p, display_config)
        ptree = Node(str(p.id), display=display)
        # now get all callbacks that have this payload tied to it
        if display_config['inactive']:
            # we want to display the inactive ones as well
            using_callbacks = await db_objects.execute(Callback.select().where(Callback.registered_payload==p))
        else:
            using_callbacks = await db_objects.execute(Callback.select().where( (Callback.registered_payload==p) &
                                                                                (Callback.active == True)))
        tree.append(ptree)
        for c in using_callbacks:
            # each of these callbacks has ptree as an associated payload
            callback_display = await analytics_callback_tree_api_function(c.to_json(), display_config)
            Node(str(c.id), parent=ptree, display=callback_display)
    output = ""
    for t in tree:
        # this is iterating over each payload-based tree
        output += str(RenderTree(t, style=DoubleStyle).by_attr("display")) + "\n"
    return text(output)
예제 #5
0
async def get_all_payloads(request, user):
    if user['current_operation'] != "":
        operation = await db_objects.get(Operation,
                                         name=user['current_operation'])
        payloads = await db_objects.execute(
            Payload.select().where(Payload.operation == operation))
        return json([p.to_json() for p in payloads])
예제 #6
0
async def get_all_payloads(request, user):
    if user['admin']:
        payloads = await db_objects.execute(Payload.select())
        return json([p.to_json() for p in payloads])
    else:
        return json({
            "status": "error",
            'error': 'Must be an admin to see all payloads'
        })
예제 #7
0
async def get_payloads_by_type(request, ptype, user):
    payload_type = unquote_plus(ptype)
    try:
        payloadtype = await db_objects.get(PayloadType, ptype=payload_type)
    except Exception as e:
        return json({'status': 'error', 'error': 'failed to find payload type'})
    if user['current_operation'] != "":
        operation = await db_objects.get(Operation, name=user['current_operation'])
    else:
        return json({'status': 'error', 'error': 'must be part of an active operation'})
    payloads = await db_objects.execute(Payload.select().where((Payload.operation == operation) & (Payload.payload_type == payloadtype)))
    payloads_json = [p.to_json() for p in payloads]
    return json({'status': 'success', "payloads": payloads_json})
예제 #8
0
파일: task_api.py 프로젝트: HSIS007/Apfell
async def add_task_to_callback_func(data, cid, user):
    try:
        # first see if the operator and callback exists
        op = await db_objects.get(Operator, username=user['username'])
        cb = await db_objects.get(Callback, id=cid)
        operation = await db_objects.get(Operation,
                                         name=user['current_operation'])
        original_params = None
        task = None
        # now check the task and add it if it's valid and valid for this callback's payload type
        try:
            cmd = await db_objects.get(
                Command,
                cmd=data['command'],
                payload_type=cb.registered_payload.payload_type)
        except Exception as e:
            # it's not registered, so check the default tasks/clear
            if data['command'] == "tasks":
                # this means we're just listing out the not-completed tasks, so nothing actually goes to the agent
                task = await db_objects.create(Task,
                                               callback=cb,
                                               operator=op,
                                               params=data['command'],
                                               status="processed",
                                               original_params=data['command'])
                raw_rsp = await get_all_not_completed_tasks_for_callback_func(
                    cb.id, user)
                if raw_rsp['status'] == 'success':
                    rsp = ""
                    for t in raw_rsp['tasks']:
                        rsp += "\nOperator: " + t['operator'] + "\nTask " + str(t['id']) + ": " + t['command'] + " " + \
                               t['params'] + "\nStatus: " + t['status']
                    await db_objects.create(Response, task=task, response=rsp)
                    return {
                        'status': 'success',
                        **task.to_json(), 'command': 'tasks'
                    }
                else:
                    return {
                        'status': 'error',
                        'error': 'failed to get tasks',
                        'cmd': data['command'],
                        'params': data['params']
                    }
            elif data['command'] == "clear":
                # this means we're going to be clearing out some tasks depending on our access levels
                task = await db_objects.create(
                    Task,
                    callback=cb,
                    operator=op,
                    params="clear " + data['params'],
                    status="processed",
                    original_params="clear " + data['params'])
                raw_rsp = await clear_tasks_for_callback_func(
                    {"task": data['params']}, cb.id, user)
                if raw_rsp['status'] == 'success':
                    rsp = "Removed the following:"
                    for t in raw_rsp['tasks_removed']:
                        rsp += "\nOperator: " + t['operator'] + "\nTask " + str(
                            t['id']) + ": " + t['command'] + " " + t['params']
                    await db_objects.create(Response, task=task, response=rsp)
                    return {'status': 'success', **task.to_json()}
                else:
                    return {
                        'status': 'error',
                        'error': raw_rsp['error'],
                        'cmd': data['command'],
                        'params': data['params']
                    }
            # it's not tasks/clear, so return an error
            return {
                'status': 'error',
                'error': data['command'] + ' is not a registered command',
                'cmd': data['command'],
                'params': data['params']
            }
        file_meta = ""
        # some tasks require a bit more processing, so we'll handle that here so it's easier for the implant
        if cmd.cmd == "upload":
            upload_config = js.loads(data['params'])
            # we need to get the file into the database before we can signal for the callback to pull it down
            try:
                # see if we actually submitted "file_id /remote/path/here"
                if 'file_id' in upload_config and upload_config['file_id'] > 0:
                    f = await db_objects.get(FileMeta,
                                             id=upload_config['file_id'])
                    # we don't want to lose our tracking on this file, so we'll create a new database entry
                    file_meta = await db_objects.create(
                        FileMeta,
                        total_chunks=f.total_chunks,
                        chunks_received=f.chunks_received,
                        complete=f.complete,
                        path=f.path,
                        operation=f.operation,
                        operator=op)
                    data['file_updates_with_task'].append(file_meta)
                elif 'file' in upload_config:
                    # we just made the file for this instance, so just use it as the file_meta
                    # in this case it's already added to data['file_updates_with_task']
                    file_meta = await db_objects.get(FileMeta,
                                                     id=upload_config['file'])
                # now normalize the data for the agent since it doesn't care if it was an old or new file_id to upload
                data['params'] = js.dumps({
                    'remote_path':
                    upload_config['remote_path'],
                    'file_id':
                    file_meta.id
                })
            except Exception as e:
                print(e)
                return {
                    'status': 'error',
                    'error':
                    'failed to get file info from the database: ' + str(e),
                    'cmd': data['command'],
                    'params': data['params']
                }

        elif cmd.cmd == "download":
            if '"' in data['params']:
                data['params'] = data['params'][
                    1:
                    -1]  # remove "" around the string at this point if they are there
        elif cmd.cmd == "screencapture":
            data['params'] = datetime.datetime.utcnow().strftime(
                '%Y-%m-%d-%H:%M:%S') + ".png"
        elif cmd.cmd == "load":
            try:
                status = await perform_load_transforms(data, cb, operation, op)
                if status['status'] == 'error':
                    return {
                        **status, 'cmd': data['command'],
                        'params': data['params']
                    }
                # now create a corresponding file_meta
                file_meta = await db_objects.create(FileMeta,
                                                    total_chunks=1,
                                                    chunks_received=1,
                                                    complete=True,
                                                    path=status['path'],
                                                    operation=cb.operation)
                data['file_updates_with_task'].append(file_meta)
                data['params'] = js.dumps({
                    "cmds": data['params'],
                    "file_id": file_meta.id
                })

            except Exception as e:
                print(e)
                return {
                    'status': 'error',
                    'error': 'failed to open and encode new function',
                    'cmd': data['command'],
                    'params': data['params']
                }
        # now actually run through all of the command transforms
        original_params = data['params']
        step_output = {}  # keep track of output at each stage
        step_output["0 - initial params"] = data['params']
        cmd_transforms = await get_commandtransforms_func(
            cmd.id, operation.name)
        if cmd_transforms['status'] == 'success':
            # reload our transforms right before use if we are actually going to do some
            if len(cmd_transforms['transforms']) > 0:
                try:
                    import app.api.transforms.utils
                    importlib.reload(sys.modules['app.api.transforms.utils'])
                except Exception as e:
                    print(e)
                from app.api.transforms.utils import CommandTransformOperation
                commandTransforms = CommandTransformOperation()
            for t in cmd_transforms['transforms']:
                if data['transform_status'][str(
                        t['order'])]:  # if this is set to active, do it
                    try:
                        data['params'] = await getattr(
                            commandTransforms, t['name'])(data['params'],
                                                          t['parameter'])
                        step_output[str(t['order']) + " - " +
                                    t['name']] = data['params']
                    except Exception as e:
                        print(e)
                        return {
                            'status':
                            'error',
                            'error':
                            'failed to apply transform {}, with message: {}'.
                            format(t['name'], str(e)),
                            'cmd':
                            data['command'],
                            'params':
                            original_params
                        }
        else:
            return {
                'status':
                'error',
                'error':
                'failed to get command transforms with message: {}'.format(
                    str(cmd_transforms['error'])),
                'cmd':
                data['command'],
                'params':
                original_params
            }
        if "test_command" in data and data['test_command']:
            # we just wanted to test out how things would end up looking, but don't actually create a Task for this
            # remove all of the fileMeta objects we created in prep for this since it's not a real issuing
            for update_file in data['file_updates_with_task']:
                await db_objects.delete(update_file)
                # we only want to delete the file from disk if there are no other db objects pointing to it
                # so we need to check other FileMeta.paths and Payload.locations
                file_count = await db_objects.count(
                    FileMeta.select().where((FileMeta.path == update_file.path)
                                            & (FileMeta.deleted == False)))
                file_count += await db_objects.count(Payload.select().where(
                    (Payload.location == update_file.path)
                    & (Payload.deleted == False)))
                try:
                    if file_count == 0:
                        os.remove(update_file.path)
                except Exception as e:
                    pass
            try:
                await db_objects.delete(file_meta)
                file_count = await db_objects.count(
                    FileMeta.select().where((FileMeta.path == file_meta.path)
                                            & (FileMeta.deleted == False)))
                file_count += await db_objects.count(
                    Payload.select().where((Payload.location == file_meta.path)
                                           & (Payload.deleted == False)))
                if file_count == 0:
                    os.remove(file_meta.path)
            except Exception as e:
                pass
            return {
                'status': 'success',
                'cmd': data['command'],
                'params': original_params,
                'test_output': step_output
            }
        if original_params is None:
            original_params = data['params']
        if task is None:
            task = await db_objects.create(Task,
                                           callback=cb,
                                           operator=op,
                                           command=cmd,
                                           params=data['params'],
                                           original_params=original_params)
        await add_command_attack_to_task(task, cmd)
        for update_file in data['file_updates_with_task']:
            # now we can associate the task with the filemeta object
            update_file.task = task
            await db_objects.update(update_file)
        status = {'status': 'success'}
        task_json = task.to_json()
        task_json['task_status'] = task_json[
            'status']  # we don't want the two status keys to conflict
        task_json.pop('status')
        return {**status, **task_json}
    except Exception as e:
        print("failed to get something in add_task_to_callback_func " + str(e))
        return {
            'status': 'error',
            'error': 'Failed to create task: ' + str(e),
            'cmd': data['command'],
            'params': data['params']
        }
예제 #9
0
async def get_all_payloads(request):
    payloads = await db_objects.execute(Payload.select())
    return json([p.to_json() for p in payloads])
예제 #10
0
async def database_clears(request, user):
    try:
        operator = await db_objects.get(Operator, username=user['username'])
        operation = await db_objects.get(Operation, name=user['current_operation'])
        if operation.name not in user['admin_operations']:
            return json({'status': 'error', 'error': "you must be the admin of the operation to clear the database"})
    except Exception as e:
        return json({'status': 'error', 'error': "failed to get the operation and operation: " + str(e)})
    data = request.json
    if 'object' not in data:
        return json({'status': 'error', 'error': '"object" is a required parameter'})
    deleted_obj_info = {'dbnumber': 0}
    if data['object'] == "payloads":
        payloads = await db_objects.execute(Payload.select().where(Payload.operation == operation))
        for p in payloads:
            try:
                os.remove(p.location)  # delete it from disk first
            except Exception as e:
                print(e)
            await db_objects.delete(p, recursive=True)  # then delete it and everything it relies on from the db
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1

    elif data['object'] == "callbacks":
        callbacks = await db_objects.execute(Callback.select().where(Callback.operation == operation))
        for c in callbacks:
            await db_objects.delete(c, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
    elif data['object'] == "screencaptures":
        screencaptures = await db_objects.execute(FileMeta.select().where( (FileMeta.operation == operation) & (FileMeta.path.contains("/screenshots/")) ))
        for s in screencaptures:
            try:
                os.remove(s.path)
            except Exception as e:
                print(e)
            await db_objects.delete(s, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
    elif data['object'] == "downloads":
        downloads = await db_objects.execute(FileMeta.select().where( (FileMeta.operation == operation) & (FileMeta.path.contains("/downloads/")) ))
        for d in downloads:
            try:
                os.remove(d.path)
            except Exception as e:
                print(e)
            await db_objects.delete(d, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
        shutil.rmtree("./app/files/{}/downloads".format(operation.name))  # remove the downloads folder from disk
    elif data['object'] == "uploads":
        uploads = await db_objects.execute(FileMeta.select().where( (FileMeta.operation == operation) & (FileMeta.path.contains("/{}/".format(operation.name))) & ~(FileMeta.path.contains("/downloads")) ))
        for u in uploads:
            try:
                os.remove(u.path)
            except Exception as e:
                print(e)
            await db_objects.delete(u, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
    elif data['object'] == "keylogs":
        keylogs = await db_objects.execute(Keylog.select().where(Keylog.operation == operation))
        for k in keylogs:
            await db_objects.delete(k, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
    elif data['object'] == "credentials":
        credentials = await db_objects.execute(Credential.select().where(Credential.operation == operation))
        for c in credentials:
            await db_objects.delete(c, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
    elif data['object'] == "tasks":
        callbacks = Callback.select().where(Callback.operation == operation)
        tasks = await db_objects.prefetch(Task.select(), callbacks)
        for t in tasks:
            await db_objects.delete(t, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
    elif data['object'] == "responses":
        callbacks = Callback.select().where(Callback.operation == operation)
        tasks = Task.select()
        responses = await db_objects.prefetch(Response.select(), tasks, callbacks)
        for r in responses:
            await db_objects.delete(r, recursive=True)
            deleted_obj_info['dbnumber'] = deleted_obj_info['dbnumber'] + 1
    return json({"status": "success", 'stats': deleted_obj_info})