async def get_one_payload_info(request, uuid, user): try: payload = await db_objects.get(Payload, uuid=uuid) except Exception as e: print(e) return json({'status': 'error', 'error': 'failed to find payload'}) if payload.operation.name in user['operations']: payloadcommands = await db_objects.execute( PayloadCommand.select().where(PayloadCommand.payload == payload)) commands = [{ "cmd": c.command.cmd, "version": c.version, "apfell_version": c.command.version } for c in payloadcommands] # now we need to get the c2 profile parameters as well c2_profile_params = await db_objects.execute( C2ProfileParametersInstance.select().where( C2ProfileParametersInstance.payload == payload)) params = [p.to_json() for p in c2_profile_params] return json({ 'status': 'success', **payload.to_json(), "commands": commands, "c2_profile_parameters_instance": params }) else: return json({ 'status': 'error', 'error': 'you need to be part of the right operation to see this' })
async def create_callback(request): data = request.json if 'user' not in data: return json({'status': 'error', 'error': 'User required'}) if 'host' not in data: return json({'status': 'error', 'error': 'Host required'}) if 'pid' not in data: return json({'status': 'error', 'error': 'PID required'}) if 'ip' not in data: return json({'status': 'error', 'error': 'IP required'}) if 'uuid' not in data: return json({'status': 'error', 'error': 'uuid required'}) # Get the corresponding Payload object based on the uuid try: payload = await db_objects.get(Payload, uuid=data['uuid']) # now that we have a uuid and payload, we should check if there's a matching parent callback if payload.pcallback: pcallback = await db_objects.get(Callback, id=payload.pcallback) else: pcallback = None except Exception as e: print(e) return json({'status': 'error', 'error': 'Failed to find payload', 'msg': str(e)}) try: cal = await db_objects.create(Callback, user=data['user'], host=data['host'], pid=data['pid'], ip=data['ip'], description=payload.tag, operator=payload.operator, registered_payload=payload, pcallback=pcallback, operation=payload.operation) if 'encryption_type' in data: cal.encryption_type = data['encryption_type'] if 'decryption_key' in data: cal.decryption_key = data['decryption_key'] if 'encryption_key' in data: cal.encryption_key = data['encryption_key'] await db_objects.update(cal) payload_commands = await db_objects.execute(PayloadCommand.select().where(PayloadCommand.payload == payload)) # now create a loaded command for each one since they are loaded by default for p in payload_commands: await db_objects.create(LoadedCommands, command=p.command, version=p.version, callback=cal, operator=payload.operator) except Exception as e: print(e) return json({'status': 'error', 'error': 'Failed to create callback', 'msg': str(e)}) cal_json = cal.to_json() status = {'status': 'success'} return response.json({**status, **cal_json}, status=201)
async def get_one_payload_info(request, uuid, user): try: payload = await db_objects.get(Payload, uuid=uuid) except Exception as e: print(e) return json({'status': 'error', 'error': 'failed to find payload'}) if payload.operation.name in user['operations']: payloadcommands = await db_objects.execute( PayloadCommand.select().where(PayloadCommand.payload == payload)) commands = [c.command.cmd for c in payloadcommands] return json({ 'status': 'success', **payload.to_json(), "commands": commands }) else: return json({ 'status': 'error', 'error': 'you need to be part of the right operation to see this' })
async def create_callback_func(data): if 'user' not in data: return {'status': 'error', 'error': 'User required'} if 'host' not in data: return {'status': 'error', 'error': 'Host required'} if 'pid' not in data: return {'status': 'error', 'error': 'PID required'} if 'ip' not in data: return {'status': 'error', 'error': 'IP required'} if 'uuid' not in data: return {'status': 'error', 'error': 'uuid required'} # Get the corresponding Payload object based on the uuid try: payload = await db_objects.get(Payload, uuid=data['uuid']) pcallback = None except Exception as e: print(e) return {} try: cal = await db_objects.create(Callback, user=data['user'], host=data['host'], pid=data['pid'], ip=data['ip'], description=payload.tag, operator=payload.operator, registered_payload=payload, pcallback=pcallback, operation=payload.operation) if 'encryption_type' in data: cal.encryption_type = data['encryption_type'] if 'decryption_key' in data: cal.decryption_key = data['decryption_key'] if 'encryption_key' in data: cal.encryption_key = data['encryption_key'] await db_objects.update(cal) payload_commands = await db_objects.execute(PayloadCommand.select().where(PayloadCommand.payload == payload)) # now create a loaded command for each one since they are loaded by default for p in payload_commands: await db_objects.create(LoadedCommands, command=p.command, version=p.version, callback=cal, operator=payload.operator) except Exception as e: print(e) return {'status': 'error', 'error': 'Failed to create callback', 'msg': str(e)} cal_json = cal.to_json() status = {'status': 'success'} return {**status, **cal_json}
async def write_payload(uuid, user): # for projects that need compiling, we should copy all of the necessary files to a temp location # do our stamping and compiling, save off the one final file to the rightful destination # then delete the temp files. They will be in a temp folder identified by the payload's UUID which should be unique try: query = await db_model.payload_query() payload = await db_objects.get(query, uuid=uuid) query = await db_model.operation_query() operation = await db_objects.get(query, name=user['current_operation']) except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) return { 'status': 'error', 'error': 'failed to get payload db object to write to disk' } try: if payload.payload_type.file_extension: extension = payload.payload_type.file_extension else: extension = "" working_path = "./app/payloads/operations/{}/{}".format( operation.name, payload.uuid) # copy the payload type's files there shutil.copytree( "./app/payloads/{}/payload/".format(payload.payload_type.ptype), working_path) # now we will work with the files from our temp directory # make sure the path and everything exists for where the final payload will go, create it if it doesn't exist payload_directory = os.path.dirname(payload.location) pathlib.Path(payload_directory).mkdir(parents=True, exist_ok=True) # wrappers won't necessarily have a c2 profile associated with them c2_path = './app/c2_profiles/{}/{}/{}{}'.format( payload.c2_profile.name, payload.payload_type.ptype, payload.c2_profile.name, extension) try: base_c2 = open(c2_path, 'r') except Exception as e: # if the wrapper doesn't have a c2 profile, that's ok if payload.payload_type.wrapper: pass # if the normal profile doesn't though, that's an issue, raise the exception else: raise e except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) shutil.rmtree(working_path) return { 'status': 'error', 'error': 'failed to open all needed files. ' + str(e) } # if we didn't actually find C2PROFILEHERE in the main code, we are probably looking at a multi-file project # in that case, keep track so that we can copy the file over to our temp directory, fill it out, and compile wrote_c2_inline = False # we will loop over all the files in the temp directory as we attempt to write out our information # this will help multi file projects as well as ones where maybe code and headers need to be in different files for base_file in glob.iglob(working_path + "/*", recursive=False): base = open(base_file, 'r') # write to the new file, then copy it over when we're done custom = open(working_path + "/" + payload.uuid, 'w') # make sure our temp file won't exist for line in base: if "C2PROFILE_HERE" in line and base_c2: # this means we need to write out the c2 profile and all parameters here await write_c2(custom, base_c2, payload) wrote_c2_inline = True elif 'C2PROFILE_NAME_HERE' in line: # optional directive to insert the name of the c2 profile replaced_line = line.replace("C2PROFILE_NAME_HERE", payload.c2_profile.name) custom.write(replaced_line) elif 'UUID_HERE' in line: replaced_line = line.replace("UUID_HERE", uuid) custom.write(replaced_line) elif 'COMMANDS_HERE' in line: # go through all the commands and write them to the payload try: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) for command in commands: # try to open up the corresponding command file cmd_file = open( './app/payloads/{}/commands/{}'.format( payload.payload_type.ptype, command.command.cmd), 'r') # we will write everything from the beginning to COMMAND_ENDS_HERE for cmdline in cmd_file: if 'COMMAND_ENDS_HERE' not in cmdline: custom.write(cmdline) else: break # stop once we find 'COMMAND_ENDS_HERE' cmd_file.close() except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk' } elif 'COMMAND_COUNT_HERE' in line: count = await db_objects.count(PayloadCommand.select().where( PayloadCommand.payload == payload)) replaced_line = line.replace('COMMAND_COUNT_HERE', str(count)) custom.write(replaced_line) elif 'COMMAND_STRING_LIST_HERE' in line: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) cmdlist = ','.join( [str('"' + cmd.command.cmd + '"') for cmd in commands]) replaced_line = line.replace('COMMAND_STRING_LIST_HERE', cmdlist) custom.write(replaced_line) elif 'COMMAND_RAW_LIST_HERE' in line: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) cmdlist = ','.join([cmd.command.cmd for cmd in commands]) replaced_line = line.replace('COMMAND_RAW_LIST_HERE', cmdlist) custom.write(replaced_line) elif 'COMMAND_HEADERS_HERE' in line: # go through all the commands and write them to the payload try: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) for command in commands: # try to open up the corresponding command file cmd_file = open( './app/payloads/{}/commands/{}'.format( payload.payload_type.ptype, command.command.cmd), 'r') found_headers = False for cmdline in cmd_file: if found_headers: custom.write(cmdline) elif 'COMMAND_ENDS_HERE' in cmdline: found_headers = True #custom.write(cmd_file.read()) cmd_file.close() except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk' } elif 'WRAPPEDPAYLOADHERE' in line and payload.payload_type.wrapper: # first we need to do the proper encoding, then we write it do the appropriate spot wrapped_payload = open(payload.wrapped_payload.location, 'rb').read() # eventually give a choice of how to encode, for now though, always base64 encode #if payload.payload_type.wrapped_encoding_type == "base64": wrapped_payload = base64.b64encode(wrapped_payload).decode( "UTF-8") replaced_line = line.replace("WRAPPEDPAYLOADHERE", str(wrapped_payload)) custom.write(replaced_line) else: custom.write(line) base.close() custom.close() os.remove(base_file) os.rename(working_path + "/" + payload.uuid, base_file) try: base_c2.close() except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) pass custom.close() if not wrote_c2_inline: # we didn't write the c2 information into the main file, so it's in another file, copy it over and fill it out for file in glob.glob(r'./app/c2_profiles/{}/{}/*'.format( payload.c2_profile.name, payload.payload_type.ptype)): # once we copy a file over, try to replace some c2 params in it try: base_c2 = open(file, 'r') base_c2_new = open( working_path + "/{}".format(file.split("/")[-1]), 'w') except Exception as e: shutil.rmtree(working_path) return {'status': 'error', 'error': 'failed to open c2 code'} await write_c2(base_c2_new, base_c2, payload) base_c2.close() base_c2_new.close() transform_request = await get_transforms_func(payload.payload_type.ptype, "create") if transform_request['status'] == "success": transform_list = transform_request['transforms'] # now we have a temporary location with everything we need # zip it all up and save it if not payload.payload_type.container_running: return {"status": "error", 'error': 'build container not running'} if payload.payload_type.last_heartbeat < datetime.utcnow() + timedelta( seconds=-30): query = await db_model.payloadtype_query() payload_type = await db_objects.get( query, ptype=payload.payload_type.ptype) payload_type.container_running = False await db_objects.update(payload_type) shutil.rmtree(working_path) return { "status": "error", 'error': 'build container not running, no heartbeat in over 30 seconds' } shutil.make_archive( "./app/payloads/operations/{}/{}".format(operation.name, payload.uuid), 'zip', working_path) file_data = open( "./app/payloads/operations/{}/{}".format( operation.name, payload.uuid) + ".zip", 'rb').read() result = await send_pt_rabbitmq_message( payload.payload_type.ptype, "create_payload_with_code.{}".format(payload.uuid), base64.b64encode( js.dumps({ "zip": base64.b64encode(file_data).decode('utf-8'), "transforms": transform_list, "extension": payload.payload_type.file_extension }).encode()).decode('utf-8')) shutil.rmtree(working_path) os.remove("./app/payloads/operations/{}/{}".format( operation.name, payload.uuid) + ".zip") return {**result, "uuid": payload.uuid} else: return {'status': 'error', 'error': 'failed to query for transforms'}
async def write_payload(uuid, user): # for projects that need compiling, we should copy all of the necessary files to a temp location # do our stamping and compiling, save off the one final file to the rightful destination # then delete the temp files. They will be in a temp folder identified by the payload's UUID which should be unique try: query = await db_model.payload_query() payload = await db_objects.get(query, uuid=uuid) query = await db_model.operation_query() operation = await db_objects.get(query, name=user['current_operation']) except Exception as e: print(e) return { 'status': 'error', 'error': 'failed to get payload db object to write to disk' } try: if payload.payload_type.file_extension: extension = payload.payload_type.file_extension else: extension = "" working_path = "./app/payloads/operations/{}/{}".format( operation.name, payload.uuid) # copy the payload type's files there shutil.copytree( "./app/payloads/{}/payload/".format(payload.payload_type.ptype), working_path) # now we will work with the files from our temp directory # make sure the path and everything exists for where the final payload will go, create it if it doesn't exist payload_directory = os.path.dirname(payload.location) pathlib.Path(payload_directory).mkdir(parents=True, exist_ok=True) # wrappers won't necessarily have a c2 profile associated with them c2_path = './app/c2_profiles/{}/{}/{}/{}{}'.format( payload.operation.name, payload.c2_profile.name, payload.payload_type.ptype, payload.c2_profile.name, extension) try: base_c2 = open(c2_path, 'r') except Exception as e: # if the wrapper doesn't have a c2 profile, that's ok if payload.payload_type.wrapper: pass # if the normal profile doesn't though, that's an issue, raise the exception else: raise e except Exception as e: print(e) shutil.rmtree(working_path) return { 'status': 'error', 'error': 'failed to open all needed files. ' + str(e) } # if we didn't actually find C2PROFILEHERE in the main code, we are probably looking at a multi-file project # in that case, keep track so that we can copy the file over to our temp directory, fill it out, and compile wrote_c2_inline = False # we will loop over all the files in the temp directory as we attempt to write out our information # this will help multi file projects as well as ones where maybe code and headers need to be in different files for base_file in glob.iglob(working_path + "/*", recursive=False): base = open(base_file, 'r') # write to the new file, then copy it over when we're done custom = open(working_path + "/" + payload.uuid, 'w') # make sure our temp file won't exist for line in base: if "C2PROFILE_HERE" in line and base_c2: # this means we need to write out the c2 profile and all parameters here await write_c2(custom, base_c2, payload) wrote_c2_inline = True elif 'C2PROFILE_NAME_HERE' in line: # optional directive to insert the name of the c2 profile replaced_line = line.replace("C2PROFILE_NAME_HERE", payload.c2_profile.name) custom.write(replaced_line) elif 'UUID_HERE' in line: replaced_line = line.replace("UUID_HERE", uuid) custom.write(replaced_line) elif 'COMMANDS_HERE' in line: # go through all the commands and write them to the payload try: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) for command in commands: # try to open up the corresponding command file cmd_file = open( './app/payloads/{}/commands/{}'.format( payload.payload_type.ptype, command.command.cmd), 'r') # we will write everything from the beginning to COMMAND_ENDS_HERE for cmdline in cmd_file: if 'COMMAND_ENDS_HERE' not in cmdline: custom.write(cmdline) else: break # stop once we find 'COMMAND_ENDS_HERE' cmd_file.close() except Exception as e: print(e) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk' } elif 'COMMAND_COUNT_HERE' in line: count = await db_objects.count(PayloadCommand.select().where( PayloadCommand.payload == payload)) replaced_line = line.replace('COMMAND_COUNT_HERE', str(count)) custom.write(replaced_line) elif 'COMMAND_STRING_LIST_HERE' in line: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) cmdlist = ','.join( [str('"' + cmd.command.cmd + '"') for cmd in commands]) replaced_line = line.replace('COMMAND_STRING_LIST_HERE', cmdlist) custom.write(replaced_line) elif 'COMMAND_RAW_LIST_HERE' in line: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) cmdlist = ','.join([cmd.command.cmd for cmd in commands]) replaced_line = line.replace('COMMAND_RAW_LIST_HERE', cmdlist) custom.write(replaced_line) elif 'COMMAND_HEADERS_HERE' in line: # go through all the commands and write them to the payload try: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) for command in commands: # try to open up the corresponding command file cmd_file = open( './app/payloads/{}/commands/{}'.format( payload.payload_type.ptype, command.command.cmd), 'r') found_headers = False for cmdline in cmd_file: if found_headers: custom.write(cmdline) elif 'COMMAND_ENDS_HERE' in cmdline: found_headers = True #custom.write(cmd_file.read()) cmd_file.close() except Exception as e: print(e) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk' } elif 'WRAPPEDPAYLOADHERE' in line and payload.payload_type.wrapper: # first we need to do the proper encoding, then we write it do the appropriate spot wrapped_payload = open(payload.wrapped_payload.location, 'rb').read() # eventually give a choice of how to encode, for now though, always base64 encode #if payload.payload_type.wrapped_encoding_type == "base64": wrapped_payload = base64.b64encode(wrapped_payload).decode( "UTF-8") replaced_line = line.replace("WRAPPEDPAYLOADHERE", str(wrapped_payload)) custom.write(replaced_line) else: custom.write(line) base.close() custom.close() os.remove(base_file) os.rename(working_path + "/" + payload.uuid, base_file) try: base_c2.close() except Exception as e: print(e) pass custom.close() if not wrote_c2_inline: # we didn't write the c2 information into the main file, so it's in another file, copy it over and fill it out for file in glob.glob(r'./app/c2_profiles/{}/{}/{}/*'.format( payload.operation.name, payload.c2_profile.name, payload.payload_type.ptype)): # once we copy a file over, try to replace some c2 params in it try: base_c2 = open(file, 'r') base_c2_new = open( working_path + "/{}".format(file.split("/")[-1]), 'w') except Exception as e: shutil.rmtree(working_path) return {'status': 'error', 'error': 'failed to open c2 code'} await write_c2(base_c2_new, base_c2, payload) base_c2.close() base_c2_new.close() # now that it's written to disk, we need to potentially do some compilation or extra transforms 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 TransformOperation transform = TransformOperation(working_dir=working_path) transform_request = await get_transforms_func(payload.payload_type.ptype, "create") if transform_request['status'] == "success": transform_list = transform_request['transforms'] # do step 0, prior_output = path of our newly written file transform_output = os.path.abspath(working_path) + "/" for t in transform_list: try: transform_output = await getattr(transform, t['name'])(payload, transform_output, t['parameter']) except Exception as e: print(e) shutil.rmtree(working_path) return { 'status': 'error', 'error': 'failed to apply transform {}, with message: {}'.format( t['name'], str(e)) } try: if transform_output != payload.location: # this means we ended up with a final file in a location other than what we specified if transform_output == os.path.abspath(working_path) + "/": transform_output += payload.payload_type.ptype + extension shutil.copy(transform_output, payload.location) shutil.rmtree(working_path) return {'status': 'success', 'path': payload.location} except Exception as e: return {'status': 'error', 'error': str(e)} try: shutil.copy(working_path + "/" + payload.uuid, payload.location) shutil.rmtree(working_path) return {'status': 'success', 'path': payload.location} except Exception as e: return {'status': 'error', 'error': str(e)}
async def create_payload(request, user): data = request.json data['operator'] = user['username'] data['current_operation'] = user['current_operation'] if 'payload' in data: try: operation = await db_objects.get(Operation, name=user['current_operation']) old_payload = await db_objects.get(Payload, uuid=data['payload'], operation=operation) db_commands = await db_objects.execute( PayloadCommand.select().where( PayloadCommand.payload == old_payload)) commands = [c.command.cmd for c in db_commands] data['payload_type'] = old_payload.payload_type.ptype data['c2_profile'] = old_payload.c2_profile.name data['commands'] = commands # we need to set the key-value pairs for the c2 profile parameters final_c2_params = {} c2_params = await db_objects.execute( C2ProfileParametersInstance.select().where( C2ProfileParametersInstance.payload == old_payload)) for param in c2_params: final_c2_params[param.c2_profile_parameters.name] = param.value data['c2_profile_parameters'] = final_c2_params if 'tag' not in data: data['tag'] = old_payload.tag if old_payload.payload_type.wrapper: data['wrapper'] = True data['wrapped_payload'] = old_payload.wrapped_payload except Exception as e: print(e) return json({ 'status': 'error', 'error': 'failed to get old payload values' }) if 'tag' not in data: if 'task' in data: if data['pcallback']: data['tag'] = user['username'] + " using " + data[ 'command'] + " from callback " + data['pcallback'] else: data['tag'] = user['username'] + " created using " + data[ 'command'] else: data['tag'] = data['payload_type'] + " payload created by " + user[ 'username'] # first we need to register the payload rsp = await register_new_payload_func(data, user) if rsp['status'] == "success": # now that it's registered, write the file, if we fail out here then we need to delete the db object payload = await db_objects.get(Payload, uuid=rsp['uuid']) create_rsp = await write_payload(rsp['uuid'], user) if create_rsp['status'] == "success": # if this was a task, we need to now issue the task to use this payload if 'task' in data: task = { 'command': data['command'], 'params': data['params'] + " " + rsp['payload_type'] + " " + rsp['uuid'], 'operator': user['username'] } task_status = await add_task_to_callback_func( task, data['pcallback'], user) return json(task_status) else: return json({'status': 'success'}) else: await db_objects.delete(payload, recursive=True) return json({'status': 'error', 'error': create_rsp['error']}) else: print(rsp['error']) return json({'status': 'error', 'error': rsp['error']})
async def write_payload(uuid, user): try: payload = await db_objects.get(Payload, uuid=uuid) except Exception as e: return { 'status': 'error', 'error': 'failed to get payload db object to write to disk' } try: if payload.payload_type.file_extension: extension = payload.payload_type.file_extension else: extension = "" base = open('./app/payloads/{}/{}{}'.format(payload.payload_type.ptype, payload.payload_type.ptype, extension)) payload_directory = os.path.dirname(payload.location) pathlib.Path(payload_directory).mkdir(parents=True, exist_ok=True) custom = open(payload.location, 'w') # wrappers won't necessarily have a c2 profile associated with them if not payload.payload_type.wrapper: base_c2 = open('./app/c2_profiles/{}/{}/{}/{}{}'.format( payload.operation.name, payload.c2_profile.name, payload.payload_type.ptype, payload.c2_profile.name, extension)) except Exception as e: print(e) return { 'status': 'error', 'error': 'failed to open all needed files. ' + str(e) } for line in base: if "C2Profile" in line and not payload.payload_type.wrapper: # this means we need to write out the c2 profile and all parameters here await write_c2(custom, base_c2, payload) # this will eventually be write_ptype_params like above, but not yet elif 'XXXX' in line: replaced_line = line.replace("XXXX", uuid) custom.write(replaced_line) elif 'COMMAND DECLARATIONS AND IMPLEMENTATIONS' in line: # go through all the commands and write them to the payload try: commands = await db_objects.execute( PayloadCommand.select().where( PayloadCommand.payload == payload)) for command in commands: # try to open up the corresponding command file cmd_file = open('./app/payloads/{}/{}'.format( payload.payload_type.ptype, command.command.cmd)) custom.write(cmd_file.read()) cmd_file.close() except Exception as e: print(e) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk' } elif 'WRAPPEDPAYLOADHERE' in line and payload.payload_type.wrapper: # first we need to do the proper encoding, then we write it do the appropriate spot wrapped_payload = open(payload.wrapped_payload.location, 'rb').read() if payload.payload_type.wrapped_encoding_type == "base64": wrapped_payload = base64.b64encode(wrapped_payload).decode( "UTF-8") replaced_line = line.replace("WRAPPEDPAYLOADHERE", str(wrapped_payload)) custom.write(replaced_line) else: custom.write(line) base.close() if not payload.payload_type.wrapper: base_c2.close() custom.close() # now that it's written to disk, we need to potentially do some compilation or extra transforms transform = TransformOperation() transform_request = await get_transforms_func(payload.payload_type.ptype, "create") if transform_request['status'] == "success": transform_list = transform_request['transforms'] # do step 0, prior_output = path of our newly written file transform_output = payload.location for t in transform_list: try: transform_output = await getattr(transform, t['name'])(payload, transform_output, t['parameter']) except Exception as e: print(e) return { 'status': 'error', 'error': 'failed to apply transform {}, with message: {}'.format( t['name'], str(e)) } if transform_output != payload.location: # this means we ended up with a final file in a location other than what we specified print(transform_output) return {'status': 'success', 'path': transform_output} return {'status': 'success', 'path': payload.location}
async def write_payload(uuid, user, data): # for projects that need compiling, we should copy all of the necessary files to a temp location # do our stamping and compiling, save off the one final file to the rightful destination # then delete the temp files. They will be in a temp folder identified by the payload's UUID which should be unique try: query = await db_model.payload_query() payload = await db_objects.get(query, uuid=uuid) query = await db_model.operation_query() operation = await db_objects.get(query, name=user['current_operation']) except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) return { 'status': 'error', 'error': 'failed to get payload db object to write to disk' } try: working_path = "./app/payloads/operations/{}/{}".format( operation.name, payload.uuid) # copy the payload type's files there await local_copytree( "./app/payloads/{}/payload/".format(payload.payload_type.ptype), working_path) # now we will work with the files from our temp directory except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) try: shutil.rmtree(working_path) except Exception as err: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(err)) pass return { 'status': 'error', 'error': 'failed to copy over all needed files. ' + str(e) } wrote_commands_inline = False wrote_c2_inline = False # we will loop over all the files in the temp directory as we attempt to write out our information # this will help multi file projects as well as ones where maybe code and headers need to be in different files # this iterates over all payload files in the working path for base_file in glob.iglob(working_path + "/**", recursive=True): try: # print("base_file: " + base_file) # print("is folder: " + str(os.path.isdir(base_file))) if os.path.isdir(base_file): continue base = open(base_file, 'r') # print("payload file: " + base_file) # write to the new file, then copy it over when we're done custom = open(base_file + payload.uuid, 'w') # make sure our temp file won't exist for line in base: # search for any of our payload_type-based pre-processing commands if "C2PROFILE_HERE" in line: # this means we need to write out the c2 profile and all parameters here try: wrote_c2_inline = True await write_c2_inline(custom, payload) except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) shutil.rmtree(working_path) return { 'status': 'error', 'error': 'failed to get and write C2 profiles to payload on disk: ' + str(e) } elif 'UUID_HERE' in line: replaced_line = line.replace("UUID_HERE", uuid) custom.write(replaced_line) elif 'COMMANDS_HERE' in line: # go through all the commands and write them to the payload wrote_commands_inline = True try: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) for command in commands: # try to open up the corresponding command file try: cmd_file = open( './app/payloads/{}/commands/{}/{}.{}'. format( payload.payload_type.ptype, command.command.cmd, command.command.cmd, payload.payload_type.file_extension), 'r') # we will write everything from the beginning to COMMAND_ENDS_HERE for cmdline in cmd_file: if 'COMMAND_ENDS_HERE' not in cmdline: custom.write(cmdline) else: break # stop once we find 'COMMAND_ENDS_HERE' cmd_file.close() except Exception as e: print( str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) print("Failed to read command: " + command.command.cmd) raise e # stop, propagate out except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) shutil.rmtree(working_path) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk: ' + str(e) } elif 'COMMAND_COUNT_HERE' in line: count = await db_objects.count( PayloadCommand.select().where( PayloadCommand.payload == payload)) replaced_line = line.replace('COMMAND_COUNT_HERE', str(count)) custom.write(replaced_line) elif 'COMMAND_STRING_LIST_HERE' in line: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) cmdlist = ','.join( [str('"' + cmd.command.cmd + '"') for cmd in commands]) replaced_line = line.replace('COMMAND_STRING_LIST_HERE', cmdlist) custom.write(replaced_line) elif 'COMMAND_RAW_LIST_HERE' in line: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) cmdlist = ','.join([cmd.command.cmd for cmd in commands]) replaced_line = line.replace('COMMAND_RAW_LIST_HERE', cmdlist) custom.write(replaced_line) elif 'COMMAND_HEADERS_HERE' in line: # go through all the commands and write them to the payload try: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) for command in commands: # try to open up the corresponding command file try: cmd_file = open( './app/payloads/{}/commands/{}/{}.{}'. format( payload.payload_type.ptype, command.command.cmd, command.command.cmd, payload.payload_type.file_extension), 'r') found_headers = False for cmdline in cmd_file: if found_headers: custom.write(cmdline) elif 'COMMAND_ENDS_HERE' in cmdline: found_headers = True cmd_file.close() except Exception as e: print( str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) print("Failed to read command: " + command.command.cmd) raise e except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) shutil.rmtree(working_path) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk: ' + str(e) } elif 'WRAPPED_PAYLOAD_HERE' in line and payload.payload_type.wrapper: # first we need to do the proper encoding, then we write it do the appropriate spot wrapped_payload = open(payload.wrapped_payload.location, 'rb').read() # eventually give a choice of how to encode, for now though, always base64 encode #if payload.payload_type.wrapped_encoding_type == "base64": wrapped_payload = base64.b64encode(wrapped_payload).decode( "UTF-8") replaced_line = line.replace("WRAPPED_PAYLOAD_HERE", str(wrapped_payload)) custom.write(replaced_line) else: custom.write(line) base.close() custom.close() os.remove(base_file) os.rename(base_file + payload.uuid, base_file) except Exception as e: # we likely got a binary file that can't be parsed like this, so move on custom.close() os.remove(base_file + payload.uuid) print( "Tried to read lines of a binary file, moving to the next file: " + str(e)) try: custom.close() except Exception as e: pass # if we didn't write the commands anywhere, then it's needed in its current separate files, copy them over if not wrote_commands_inline: query = await db_model.payloadcommand_query() commands = await db_objects.execute( query.where(PayloadCommand.payload == payload)) for command in commands: try: # copy the payload type's files there await local_copytree( './app/payloads/{}/commands/{}'.format( payload.payload_type.ptype, command.command.cmd), working_path + "/{}".format(command.command.cmd)) except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) shutil.rmtree(working_path) return { 'status': 'error', 'error': 'failed to get and write commands to payload on disk: ' + str(e) } if not wrote_c2_inline: # we didn't write c2 inline into the file, so we need to copy it over try: await write_c2(working_path, payload) except Exception as e: print(str(sys.exc_info()[-1].tb_lineno) + " " + str(e)) shutil.rmtree(working_path) return { "status": "error", 'error': 'Failed to write c2 files to temporary folder: ' + str(e) } # transform_request = await get_transforms_func(payload.payload_type.ptype, "create") transform_request = await get_payload_transforms(payload) if transform_request['status'] == "success": transform_list = transform_request['transforms'] # now we have a temporary location with everything we need # zip it all up and save it if not payload.payload_type.container_running: return {"status": "error", 'error': 'build container not running'} if payload.payload_type.last_heartbeat < datetime.utcnow() + timedelta( seconds=-30): query = await db_model.payloadtype_query() payload_type = await db_objects.get( query, ptype=payload.payload_type.ptype) payload_type.container_running = False await db_objects.update(payload_type) shutil.rmtree(working_path) return { "status": "error", 'error': 'build container not running, no heartbeat in over 30 seconds' } shutil.make_archive( "./app/payloads/operations/{}/{}".format(operation.name, payload.uuid), 'zip', working_path) file_data = open( "./app/payloads/operations/{}/{}".format( operation.name, payload.uuid) + ".zip", 'rb').read() result = await send_pt_rabbitmq_message( payload.payload_type.ptype, "create_payload_with_code.{}".format(payload.uuid), base64.b64encode( js.dumps({ "zip": base64.b64encode(file_data).decode('utf-8'), "transforms": transform_list, "extension": payload.payload_type.file_extension }).encode()).decode('utf-8'), user['username']) shutil.rmtree(working_path) os.remove("./app/payloads/operations/{}/{}".format( operation.name, payload.uuid) + ".zip") return {**result, "uuid": payload.uuid} else: return {'status': 'error', 'error': 'failed to query for transforms'}