async def get_transforms_options_func(): # reload the transform data so we can provide updated information 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 t = TransformOperation() method_list = {func: await get_type_hints(getattr(t, func).__annotations__) for func in dir(t) if callable(getattr(t, func)) and not func.startswith("__")} return method_list
async def perform_load_transforms(data, cb, operation, op): # in the end this returns a dict of status and either a final file path or an error message load_transforms = await get_transforms_func( cb.registered_payload.payload_type.ptype, "load") if load_transforms['status'] == "success": # if we need to do something like compile or put code in a specific format # we should have a temp working directory for whatever needs to be done, similar to payload creation uuid = await generate_uuid() working_path = "./app/payloads/operations/{}/{}".format( operation.name, uuid) # copy the payload type's files there shutil.copytree( "./app/payloads/{}/payload/".format( cb.registered_payload.payload_type.ptype), working_path) # now that we copied files here, do the same replacement we do for creating a payload 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 + "/" + uuid, 'w') # make sure our temp file won't exist for line in base: if 'C2PROFILE_NAME_HERE' in line: # optional directive to insert the name of the c2 profile replaced_line = line.replace( "C2PROFILE_NAME_HERE", cb.registered_payload.c2_profile.name) custom.write(replaced_line) elif 'UUID_HERE' in line: replaced_line = line.replace("UUID_HERE", uuid) custom.write(replaced_line) else: custom.write(line) base.close() custom.close() os.remove(base_file) os.rename(working_path + "/" + uuid, base_file) # also copy over and handle the c2 profile files just in case they have header files or anything needed for file in glob.glob(r'./app/c2_profiles/{}/{}/{}/*'.format( cb.registered_payload.operation.name, cb.registered_payload.c2_profile.name, cb.registered_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, cb.registered_payload) base_c2.close() base_c2_new.close() transform_output = [] # always start with a list of paths for all of the things we want to load # check if somebody submitted {'cmds':'shell,load, etc', 'file_id': 4} instead of list of commands try: replaced_params = data['params'].replace("'", '"') funcs = js.loads(replaced_params)['cmds'] except Exception as e: funcs = data['params'] data['params'] = funcs for p in data['params'].split(","): # register this command as one that we're going to have loaded into the callback try: command = await db_objects.get( Command, payload_type=cb.registered_payload.payload_type, cmd=p) try: loaded_command = await db_objects.get(LoadedCommands, callback=cb, command=command) loaded_command.version = command.version await db_objects.update(loaded_command) except Exception as e: # we couldn't find it, so we need to create it since this is a new command, not an update loaded_command = await db_objects.create( LoadedCommands, callback=cb, command=command, version=command.version, operator=op) except Exception as e: print(e) transform_output.append("./app/payloads/{}/commands/{}".format( cb.registered_payload.payload_type.ptype, p.strip())) # if we actually have transforms to do, then reload the utils to make sure we're using the latest if len(load_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 TransformOperation transforms = TransformOperation(working_dir=working_path + "/") for t in load_transforms['transforms']: try: transform_output = await getattr(transforms, t['name'])( cb.registered_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)), 'cmd': data['command'], 'params': data['params'] } # at the end, we need to make sure our final file path is not located in our current working dir # if the user selected a file outside of it, that's fine, same with if they did something that got it there # if not, handle it for them here if working_path in transform_output: new_path = "./app/payloads/operations/{}/load-{}".format( operation.name, datetime.datetime.utcnow()) shutil.copy(transform_output, new_path) transform_output = new_path # now that the file is in a good place, remove the working area shutil.rmtree(working_path) return {'status': 'success', 'path': transform_output} else: return { 'status': 'error', 'error': 'failed to get transforms for this payload type', 'cmd': data['command'], 'params': data['params'] }
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 get_transforms_options_func(): t = TransformOperation() method_list = {func: await get_type_hints(getattr(t, func).__annotations__) for func in dir(t) if callable(getattr(t, func)) and not func.startswith("__")} return method_list