def do_lrun(self, argv): """Execute client-side shell command SYNOPSIS: lrun command [arg1 [arg2 [...] ] ] DESCRIPTION: Execute a shell command in your own operating system. This command works like the `exec` command in unix shells. NOTE: This core command shouldn't be confused with the `run` plugin, which does the same thing in the remotely exploited system. EXAMPLES: > lrun ls -la / > lrun htop """ if len(argv) == 1: self.interpret("help lrun") return cmd = " ".join(argv[1:]) if argv[1] != "exit": tmpfile = Path() postcmd = " ; pwd >'%s' 2>&1" % tmpfile subprocess.call(cmd + postcmd, shell=True) try: os.chdir(tmpfile.read()) finally: del tmpfile
def do_lrun(self, argv): """Execute client-side shell command SYNOPSIS: lrun command [arg1 [arg2 [...] ] ] DESCRIPTION: Execute a shell command in your own operating system. This command works like the `exec` command in unix shells. NOTE: This core command shouldn't be confused with the `run` plugin, which does the same thing in the remotely exploited system. EXAMPLES: > lrun ls -la / > lrun htop """ if len(argv) == 1: return self.interpret("help lrun") cmd = " ".join(argv[1:]) if argv[1] != "exit": tmpfile = Path() postcmd = " ; pwd >'%s' 2>&1" % tmpfile subprocess.call(cmd + postcmd, shell=True) try: os.chdir(tmpfile.read()) finally: del tmpfile
def do_lrun(self, argv): """Execute client-side shell command SYNOPSIS: lrun command [arg1 [arg2 [...] ] ] DESCRIPTION: Execute a shell command in your own operating system. This command works like the `exec` command in unix shells. NOTE: This core command shouldn't be confused with the `run` plugin, which does the same thing in the remotely exploited system. EXAMPLES: > lrun ls -la / > lrun htop """ if len(argv) == 1: self.interpret("help lrun") return False cmd = " ".join(argv[1:]) tmpfile = Path() postcmd = "\nret=$?; pwd >'%s' 2>&1; exit $ret" % tmpfile ret = os.system(cmd + postcmd) >> 8 if os.stat(tmpfile).st_size > 0: os.chdir(tmpfile.read()) return ret
def do_backlog(self, argv): """Show last command's output with $EDITOR SYNOPSIS: backlog [--save <FILE>] DESCRIPTION: Opens previous command output into the user prefered text editor ($EDITOR setting). NOTE: Last command buffer is colorless. It means that it does not contains any ANSI terminal color codes. OPTIONS: --save $file Write previous command's output to the given file instead of opening it with $EDITOR. """ if len(argv) > 1: if len(argv) == 3 and argv[1] == "--save": file = Path(argv[2]) file.write(self.stdout.backlog) del file return return self.interpret("help backlog") backlog = Path() backlog.write(self.stdout.backlog, bin_mode=True) backlog.edit() return
def __init__(self): """Initalize a plugins list instance""" self.blacklist = [] self.root_dirs = [] self.root_dirs.append(Path(core.basedir, "plugins", mode='drx')) self.root_dirs.append(Path(core.userdir, "plugins", mode='drx')) self.current_plugin = DEFAULT_PLUGIN super().__init__()
def _get_raw_payload_prefix(): """return $PAYLOAD_PREFIX setting, without php tags, in raw format """ tmpfile = Path() tmpfile.write(session.Conf.PAYLOAD_PREFIX()) payload_prefix = tmpfile.phpcode() del tmpfile return payload_prefix
def __init__(self): """Initalize a plugins list instance""" self.errors = 0 self.blacklist = [] self.root_dirs = [] self.root_dirs.append(Path(core.BASEDIR, "plugins", mode='drx')) self.root_dirs.append(Path(core.USERDIR, "plugins", mode='drx')) self.current_plugin = DEFAULT_PLUGIN super().__init__()
def do_rtfm(self, argv): """Read the fine manual SYNOPSIS: rtfm DESCRIPTION: Display phpsploit user manual. If available, the `man` command is used for display. Otherwise, a text version of the man page is displayed in phpsploit interface. """ if os.system('man ' + Path(core.basedir, 'man/phpsploit.1')) != 0: print(Path(core.basedir, 'man/phpsploit.txt').read())
def __init__(self): """Return the PhpSploit user directory. The following try order is used: 0 - $PHPSPLOIT_CONFIG_DIR/ (only if env var exists) 1 - $XDG_CONFIG_HOME/phpsploit/ (only if env var exists) 2 - ~/.config/phpsploit/ 3 - ~/.phpsploit/ If no one exists, an mkdir is tried for each one in the same order than the previous. Mkdir is not recursive, meaning that parent must already exist. If no USERDIR can be determined, a ValueError concerning last possible choice (~/.phpsploit/) is raised. """ if os.environ.get("XDG_CONFIG_HOME"): self.choices.insert(0, "$XDG_CONFIG_HOME/phpsploit") if os.environ.get("PHPSPLOIT_CONFIG_DIR"): self.choices.insert(0, "$PHPSPLOIT_CONFIG_DIR/") # normalize choices paths self.choices = [utils.path.truepath(c) for c in self.choices] # set self.path if user directory already exist for choice in self.choices: try: self.path = Path(choice, mode="drw")() break except: pass # try to create it otherwise, raise err if fails if self.path is None: for choice in self.choices: try: os.mkdir(choice) except: pass try: self.path = Path(choice, mode="drw") break except Exception as e: if choice == self.choices[-1]: raise e self.fill() # finally, fill it with default content
def do_rtfm(argv): """Read the fine manual SYNOPSIS: rtfm DESCRIPTION: Display phpsploit user manual. If available, the `man` command is used for display. Otherwise, a text version of the man page is displayed in phpsploit interface. """ man = Path(core.BASEDIR, 'man/phpsploit.1') cmd = 'man phpsploit 2>/dev/null || man %r 2>/dev/null' % man if os.system(cmd) != 0: txt_man = Path(core.BASEDIR, 'man/phpsploit.txt') print(txt_man.read())
def _list_path_dirs(self, root_dir, type="plugin"): """Returns a list of tuples representing a plugin directory. Each tuple is in the form: (basename, abspath) Example: >>> self._list_path_dirs("/plugins/system") [("ls", "/plugins/system/ls"), ("pwd", "/plugins/system/pwd")] """ errmsg = "Bad %s path" % type pattern = "^[a-zA-Z0-9_]+$" elems = [] for basename in os.listdir(root_dir): path = utils.path.truepath(root_dir, basename) try: abspath = Path(root_dir, basename, mode='drx')() except: if not basename.startswith("README"): if os.path.isdir(path): reason = "Permission denied" else: reason = "Not a directory" print("[#] %s: «%s»: %s" % (errmsg, path, reason)) self.errors += 1 continue if re.match(pattern, basename): elems.append((basename, abspath)) elif basename.endswith(".DISABLED"): continue else: reason = "Directory don't match '%s'" % pattern print("[#] %s: «%s»: %s" % (errmsg, path, reason)) self.errors += 1 return elems
def do_lrun(self, argv): """Execute client-side shell command SYNOPSIS: lrun command [arg1 [arg2 [...] ] ] DESCRIPTION: Execute a shell command in your own operating system. This command works like the `exec` command in unix shells. NOTE: This core command shouldn't be confused with the `run` plugin, which does the same thing in the remotely exploited system. EXAMPLES: > lrun ls -la / > lrun htop """ if len(argv) == 1: return self.interpret("help lrun") cmd = " ".join(argv[1:]) # on windows, we do not handle interactive commands if sys.platform.startswith("win"): postcmd = " & echo %CD%" output = subprocess.getoutput(cmd + postcmd) lines = output.splitlines() if os.path.isabs(lines[-1]): os.chdir(lines[-1]) if not lines[:-1]: return output = os.linesep.join(lines[:-1]) return print(output) # on unix, we are able to handle interactive commands # AND getting new $PWD by redirecting the `pwd` # command to a temporary file. else: tmpfile = Path() postcmd = " ; pwd >'%s' 2>&1" % tmpfile subprocess.call(cmd + postcmd, shell=True) try: os.chdir(tmpfile.read()) finally: del tmpfile
def __init__(self, filename, **kwargs): self.response = None for key in self._unherited_env_vars: self[key] = session.Env[key] for key, value in kwargs.items(): self[key] = value plugin_path = plugins.current_plugin.path self.payload = Path(plugin_path, filename, mode='fr').phpcode()
def __init__(self): """Get phpsploit configuration directory, by checking, in this order of preference: - $PHPSPLOIT_CONFIG_DIR/ (only if env var exists) - $XDG_CONFIG_HOME/phpsploit/ (only if env var exists) - ~/.config/phpsploit/ - ~/.phpsploit/ If non of the above exist, directory creation is attempted with the same order of preference. Directory creation is not recursive, to parent directory must exist. If USERDIR cannot be determined, a ValueError mentioning last tried choice (~/.phpsploit/) is raised. """ if os.environ.get("XDG_CONFIG_HOME"): self.choices.insert(0, "$XDG_CONFIG_HOME/phpsploit") if os.environ.get("PHPSPLOIT_CONFIG_DIR"): self.choices.insert(0, "$PHPSPLOIT_CONFIG_DIR/") self.choices = [utils.path.truepath(c) for c in self.choices] # try to find existing USERDIR for choice in self.choices: try: self.path = Path(choice, mode="drw")() break except ValueError: pass # try to create new valid USERDIR if self.path is None: for choice in self.choices: try: os.mkdir(choice) except OSError: pass try: self.path = Path(choice, mode="drw") break except ValueError as e: if choice == self.choices[-1]: raise e self.fill() # finally, fill it with default content
def do_rtfm(self, argv): """Read the fine manual SYNOPSIS: rtfm DESCRIPTION: Display phpsploit user manual. If available, the `man` command is used for display. Otherwise, a text version of the man page is displayed in phpsploit interface. """ txtMan = lambda: print(Path(core.basedir, 'man/phpsploit.txt').read()) if os.name == 'nt': txtMan() else: cmd = 'man ' + Path(core.basedir, 'man/phpsploit.1') return_value = os.system(cmd) if return_value is not 0: txtMan()
def __init__(self, path): if path.endswith(os.sep) or path.endswith("/"): path = path[:-1] self.path = path self.name = os.path.basename(path) self.argv = [] # redefined at runtime on run() try: Path(path, mode='drx')() except ValueError as e: print("[#] Couldn't load plugin: «%s»" % self.path) print("[#] Plugin directory error: %s" % e) print("[#] ") raise BadPlugin category = os.path.basename(os.path.dirname(path)) self.category = category.replace("_", " ").capitalize() self.help = "" try: script = Path(self.path, "plugin.py", mode='fr').read() except ValueError as e: print("[#] Couldn't load plugin: «%s»" % self.path) print("[#] File error on plugin.py: %s" % e) print("[#] ") raise BadPlugin if not script.strip(): print("[#] Couldn't load plugin: «%s»" % self.path) print("[#] File plugin.py is empty") print("[#] ") raise BadPlugin try: code = compile(script, "", "exec") except BaseException as e: print("[#] Couldn't compile plugin: «%s»" % self.path) e = traceback.format_exception(type(e), e, e.__traceback__) for line in "".join(e).splitlines(): print(colorize("[#] ", "%Red", line)) # print("[#] " + "\n[#] ".join("".join(e).splitlines())) print("[#] ") raise BadPlugin if "__doc__" in code.co_names: self.help = code.co_consts[0]
def _values_get(account_id, paths_input, aggr_level, args): if paths_input is None: return "Path(s) not specified\n\n", 400 try: paths = [Path(p, account_id).path for p in paths_input.split(',')] except: return "Path(s) not specified correctly\n\n", 400 t_from_input = args.get('t0') if t_from_input: try: t_froms = [Timestamp(t) for t in str(t_from_input).split(',')] if len(t_froms) == 1: t_froms = [t_froms[0] for _ in paths] elif len(t_froms) == len(paths): pass else: return "Number of t0 timestamps must be 1 or equal to number of paths\n\n", 400 except: return "Error parsing t0\n\n", 400 else: t_from = Measurement.get_oldest_measurement_time(account_id, paths) if not t_from: t_from = Timestamp(time.time()) t_froms = [t_from for _ in paths] t_to_input = args.get('t1') if t_to_input: try: t_to = Timestamp(t_to_input) except: return "Error parsing t1\n\n", 400 else: t_to = Timestamp(time.time()) sort_order = str(args.get('sort', 'asc')) if sort_order not in ['asc', 'desc']: return "Invalid parameter: sort (should be 'asc' or 'desc')\n\n", 400 should_sort_asc = True if sort_order == 'asc' else False try: max_records = int( args.get('limit', Measurement.MAX_DATAPOINTS_RETURNED)) if max_records > Measurement.MAX_DATAPOINTS_RETURNED: return "Invalid parameter: limit (max. value is {})\n\n".format( Measurement.MAX_DATAPOINTS_RETURNED), 400 except: return "Invalid parameter: limit\n\n", 400 # finally, return the data: paths_data = Measurement.fetch_data(account_id, paths, aggr_level, t_froms, t_to, should_sort_asc, max_records) return json.dumps({ 'paths': paths_data, }), 200
def account_path_crud(account_id, path_id): if flask.request.method in ['GET', 'HEAD']: rec = Path.get(path_id, account_id) if not rec: return "No such path", 404 return json.dumps(rec), 200 elif flask.request.method == 'PUT': record = Path.forge_from_input(flask.request.get_json(), account_id, force_id=path_id) rowcount = record.update() if not rowcount: return "No such path", 404 return "", 204 elif flask.request.method == 'DELETE': rowcount = Path.delete(path_id, account_id) if not rowcount: return "No such path", 404 return "", 204
def __init__(self, path): if path.endswith(os.sep) or path.endswith("/"): path = path[:-1] self.path = path self.name = os.path.basename(path) try: Path(path, mode='drx')() except ValueError as e: print("[#] Couldn't load plugin: «%s»" % self.path) print("[#] Plugin directory error: %s" % e) raise BadPlugin category = os.path.basename(os.path.dirname(path)) self.category = category.replace("_", " ").capitalize() self.help = "" try: script = Path(self.path, "plugin.py", mode='fr').read() except ValueError as e: print("[#] Couldn't load plugin: «%s»" % self.path) print("[#] File error on plugin.py: %s" % e) print("[#] ") raise BadPlugin if not script.strip(): print("[#] Couldn't load plugin: «%s»" % self.path) print("[#] File plugin.py is empty") print("[#] ") raise BadPlugin try: code = compile(script, "", "exec") except BaseException as e: e = traceback.format_exception(type(e), e, e.__traceback__) print("[#] Couldn't compile plugin: «%s»" % self.path) print("[#] " + "\n[#] ".join("".join(e).splitlines())) print("[#] ") raise BadPlugin if "__doc__" in code.co_names: self.help = code.co_consts[0]
def do_backlog(self, argv): """open last command output in text editor SYNOPSIS: backlog [--save <FILE>] DESCRIPTION: Open last command output with text EDITOR (`help set EDITOR`). Ansi terminal colors are automatically stripped from buffer. OPTIONS: --save <FILE> Write previous command's output to the given file instead of opening it with $EDITOR. """ if len(argv) == 1: backlog = Path() backlog.write(self.stdout.backlog, bin_mode=True) backlog.edit() elif len(argv) == 3 and argv[1] == "--save": Path(argv[2]).write(self.stdout.backlog) else: self.interpret("help backlog")
def open(self): # instanciate and configure payload handler. socket = handler.new_request() socket.is_first_payload = True socket.errmsg_request = "Could not connect to TARGET" socket.errmsg_response = "TARGET does not seem to be backdoored" # send the `connector.php` payload through created tunnel (socket). payload = Path(core.BASEDIR, 'data/tunnel/connector.php').phpcode() socket.open(payload) # build phpsploit session's environment variables from # connector.php's returned array. self.socket = socket raw_vars = self._get_vars(socket.read()) self.environ = self._build_env(raw_vars) return True
def _load_php_libs(self, code): """Replace `!import(<FOO>)` special syntax with real local library files. """ result = '' for line in code.splitlines(): comp_line = line.replace(' ', '') if not comp_line.startswith('!import('): result += line + '\n' else: libname = line[(line.find('(') + 1):line.find(')')] if line.count('(') != 1 or line.count(')') != 1 or not libname: raise BuildError('Invalid php import: ' + line.strip()) if libname not in self.loaded_phplibs: try: file_path = 'api/php-functions/%s.php' % libname lib = Path(core.COREDIR, file_path).phpcode() except ValueError: raise BuildError('Php lib not found: ' + libname) result += self._load_php_libs(lib) + '\n' self.loaded_phplibs.append(libname) return result
def _load_template(filepath): """load a PHP tunnel data template file""" file = Path(core.BASEDIR, "data/tunnel", filepath, mode='fr') return file.phpcode()
from datatypes import Path if len(plugin.argv) != 2: sys.exit(plugin.help) absolute_path = server.path.abspath(plugin.argv[1]) path_filename = server.path.basename(absolute_path) reader = server.payload.Payload("reader.php") reader["FILE"] = absolute_path # send the crafted payload to get remote file contents reader_response = reader.send() file = Path(filename=path_filename) if reader_response == "NEW_FILE": print("[*] Creating new file: %s" % absolute_path) else: # writting bytes() obj to file in binary mode file.write(base64.b64decode(reader_response), bin_mode=True) modified = file.edit() if not modified: if reader_response == "NEW_FILE": sys.exit("File creation aborted") else: sys.exit("The file was not modified") writer = server.payload.Payload("writer.php")
from datatypes import Path if len(plugin.argv) != 2: sys.exit(plugin.help) absolute_path = server.path.abspath(plugin.argv[1]) path_filename = server.path.basename(absolute_path) reader = server.payload.Payload("reader.php") reader['FILE'] = absolute_path # send the crafted payload to get remote file contents reader_response = reader.send() file = Path(filename=path_filename) if reader_response == "NEW_FILE": print("[*] Creating new file: %s" % absolute_path) else: # writting bytes() obj to file in binary mode file.write(base64.b64decode(reader_response), bin_mode=True) modified = file.edit() if not modified: if reader_response == "NEW_FILE": sys.exit("File creation aborted") else: sys.exit("The file was not modified") writer = server.payload.Payload("writer.php")
from datatypes import Path if len(plugin.argv) != 2: sys.exit(plugin.help) absolute_path = server.path.abspath(plugin.argv[1]) path_filename = server.path.basename(absolute_path) reader = server.payload.Payload("reader.php") reader['FILE'] = absolute_path # send the crafted payload to get remote file contents reader_response = reader.send() file = Path(filename=path_filename) if reader_response == "NEW_FILE": file_mtime = None print("[*] Creating new file: %s" % absolute_path) else: # writting bytes() obj to file in binary mode file_mtime, file_data = reader_response file.write(base64.b64decode(file_data), bin_mode=True) modified = file.edit() if not modified: if reader_response == "NEW_FILE": sys.exit("File creation aborted") else: sys.exit("The file was not modified")
else: sys.exit("%s: Invalid local directory" % local_dirname) try: Path(local_dirname, mode='w') except ValueError: sys.exit("%s: Local directory not writable" % local_dirname) local_abspath = os.path.join(local_dirname, local_basename) if not force and os.path.exists(local_abspath): if os.path.isfile(local_abspath): question = "Local destination %s already exists, overwrite it ?" if ui.input.Expect(False)(question % local_abspath): sys.exit("File transfer aborted") else: sys.exit("Local destination %s already exists" % local_abspath) payload = server.payload.Payload("payload.php") payload['FILE'] = abspath response = payload.send() file = Path(local_abspath) try: file.write(base64.b64decode(response), bin_mode=True) except ValueError as err: sys.exit("Couldn't download file to %s: %s" % (local_abspath, err)) print("[*] Download complete: %s -> %s" % (abspath, local_abspath))
separator += ('-' * (lens[n])) + '-+-' header += colorize("%Bold", lineify(titles[n], lens[n])[0]) + ' | ' separator = separator[:-1] header = header[:-1] print(separator + "\n" + header + "\n" + separator) for line in elements: tmp = [] for n in range(len(lens)): tmp.append(lineify(line[n], lens[n])) print(tablify(tmp)) for elem in singles: if not elements: print('+' + ('-' * (tty_cols - 2)) + '+') print(tablify([lineify(elem, (tty_cols - 4))])) print() # `phpinfo --browser` (view html output in browser) elif len(plugin.argv) == 2 and plugin.argv[1] == "--browser": html_output = server.payload.Payload("html_format.php").send() tmp_file = Path(filename="phpinfo.html") tmp_file.write(html_output) if tmp_file.browse(): print("[*] Successfully opened %r in browser" % tmp_file) else: print("[-] Failed to open %r in web browser" % tmp_file) print("[-] Try to change BROWSER environment variable") else: sys.exit(plugin.help)
local_relpath = os.getcwd() abspath = server.path.abspath(relpath) local_abspath = utils.path.truepath(local_relpath) local_dirname = local_abspath local_basename = server.path.basename(abspath) if not os.path.isdir(local_dirname): local_dirname = os.path.dirname(local_dirname) if os.path.isdir(local_dirname): local_basename = os.path.basename(local_abspath) else: sys.exit("%s: Invalid local directory!" % local_dirname) try: Path(local_dirname, mode='w') except ValueError: sys.exit("%s: Local directory not writable!" % local_dirname) local_abspath = os.path.join(local_dirname, local_basename) if not force and os.path.exists(local_abspath): if os.path.isfile(local_abspath): question = "Local destination %s already exists, overwrite it?" if ui.input.Expect(False)(question % local_abspath): sys.exit("File transfer aborted.") else: sys.exit("Local destination %s is already exists!" % local_abspath) payload = server.payload.Payload("payload.php") payload['FILE'] = abspath
header += colorize("%Bold", lineify(titles[n], lens[n])[0]) + ' | ' separator = separator[:-1] header = header[:-1] print(separator + "\n" + header + "\n" + separator) for line in elements: tmp = [] for n in range(len(lens)): tmp.append(lineify(line[n], lens[n])) print(tablify(tmp)) for elem in singles: if not elements: print('+' + ('-' * (tty_cols - 2)) + '+') print(tablify([lineify(elem, (tty_cols - 4))])) print() # `phpinfo --browser` (view html output in browser) elif len(plugin.argv) == 2 and plugin.argv[1] == "--browser": html_output = server.payload.Payload("html_format.php").send() tmp_file = Path(filename="phpinfo.html") tmp_file.write(html_output) if tmp_file.browse(): print("[*] Successfully opened %r in browser" % tmp_file) else: print("[-] Failed to open %r in web browser" % tmp_file) print("[-] Try to change BROWSER environment variable") else: sys.exit(plugin.help)
def do_set(self, argv): """View and edit settings SYNOPSIS: set [<NAME> [+] ["<VALUE>"]] DESCRIPTION: phpsploit configuration settings manager. The settings are a collection of core variables that affect the framework's core behavior. Any setting takes a default value, that can be manually modified. > set - Display all current settings > set <STRING> - Display all settings whose name starts with STRING. > set <NAME> "value" - Change the NAME setting to "value". If the value is not valid, no changes are made. > set <NAME> "file:///path/to/file" - Set NAME setting's value into a RandLine buffer whose value binds to the external file "/path/to/file". It means that the setting's effective value is dynamic, and on each call to it, the file's content will be loaded if available, and the value is a random line from the file/buffer. > set <NAME> + - Open the setting value for edition as a multiline buffer with EDITOR. The buffer can then be edited, and once saved, the setting will take the buffer's value, except if there are no valid lines. > set <NAME> + "value" - Add "value" as a setting possible choice. It converts the current setting into a RandLine buffer if it was not already. > set <NAME> + "file:///path/to/file" - Rebind NAME setting to the given file path, even if it does not exist at the moment it had been set. It means that each time the setting's value is called, a try is made to load the file's content as new buffer if it exists/is valid, and keeps the old one otherwise. BEHAVIOR - Settings are pre declared at start. It means that new ones cannot be declared. - The convention above does not apply for settings whose name starts with "HTTP_", because this kind of variable are automatically used as custom headers on http requests. For example, `set HTTP_ACCEPT_LANGUAGE "en-CA"` will set the "Accept-Language" http header to the specified value. Of course, this applies to any future HTTP request. - The default value of a setting can be restored by setting its value to the magic string "%%DEFAULT%%", e.g: > set REQ_MAX_HEADERS %%DEFAULT%% NOTE: The 'set' operating scope is limited to the current phpsploit session. It means that persistant settings value changes must be defined by hand in the user configuration file. """ # `set [<PATTERN>]` display concerned settings list if len(argv) < 3: print(session.Conf((argv + [""])[1])) # buffer edit mode elif argv[2] == "+": # `set <VAR> +`: use $EDITOR as buffer viewer in file mode if len(argv) == 3: # get a buffer obj from setting's raw buffer value file_name = argv[1].upper() file_ext = "txt" setting_obj = session.Conf[argv[1]](call=False) if isinstance(setting_obj, datatypes.PhpCode): file_ext = "php" elif isinstance(setting_obj, datatypes.ShellCmd): file_ext = "sh" buffer = Path(filename="%s.%s" % (file_name, file_ext)) buffer.write(session.Conf[argv[1]].buffer) # try to edit it through $EDITOR, and update it # if it has been modified. if buffer.edit(): session.Conf[argv[1]] = buffer.read() # `set <VAR> + "value"`: add value on setting possible choices else: session.Conf[argv[1]] += " ".join(argv[3:]) # `set <VAR> "value"`: just change VAR's "value" else: session.Conf[argv[1]] = argv[2]
def load_phpfile(filepath): file = Path(core.basedir, "data/tunnel", filepath, mode='fr') return file.phpcode()
def browser(html_string_buffer): file= Path("phpinfo.html") file.write(html_string_buffer) file.browse() del file
def load_phpfile(filepath): file = Path(core.BASEDIR, "data/tunnel", filepath, mode='fr') return file.phpcode()
class Build: """Generate final payload, ready to be injected into http requests. The returned string includes `delim`, the separation tags allowing tunnel handler to retrieve output returned from payload after remote http request execution. The payload is also encapsulated through phpsploit standard encapsulator (./data/tunnel/encapsulator.php). """ encapsulator = Path(core.BASEDIR, 'data/tunnel/encapsulator.php').phpcode() def __init__(self, php_payload, delim): self.loaded_phplibs = list() php_payload = self.encapsulate(php_payload, delim) php_payload = self._load_php_libs(php_payload) php_payload = self._php_minify(php_payload) encoded_payload = Encode(php_payload.encode(), 'noauto') self.data = encoded_payload.data self.length = encoded_payload.length self.decoder = encoded_payload.decoder def encapsulate(self, payload, delim): """Wrap `payload` with `delim` tags, so the payloads prints those tags into the page at remote php runtime, allowing tunnel handler to extract result from HTTP response body. """ # template encapsulation code = self.encapsulator.replace('%%PAYLOAD%%', payload) payload_prefix = self._get_raw_payload_prefix() code = code.replace("%%PAYLOAD_PREFIX%%", payload_prefix) code = code.rstrip(';') + ';' # delim encapsulation if delim: echo_delim = 'echo "%s";' % delim code = echo_delim + code + echo_delim return code @staticmethod def _get_raw_payload_prefix(): """return $PAYLOAD_PREFIX setting, without php tags, in raw format """ tmpfile = Path() tmpfile.write(session.Conf.PAYLOAD_PREFIX()) payload_prefix = tmpfile.phpcode() del tmpfile return payload_prefix def _load_php_libs(self, code): """Replace `!import(<FOO>)` special syntax with real local library files. """ result = '' for line in code.splitlines(): comp_line = line.replace(' ', '') if not comp_line.startswith('!import('): result += line + '\n' else: libname = line[(line.find('(') + 1):line.find(')')] if line.count('(') != 1 or line.count(')') != 1 or not libname: raise BuildError('Invalid php import: ' + line.strip()) if libname not in self.loaded_phplibs: try: file_path = 'api/php-functions/%s.php' % libname lib = Path(core.COREDIR, file_path).phpcode() except ValueError: raise BuildError('Php lib not found: ' + libname) result += self._load_php_libs(lib) + '\n' self.loaded_phplibs.append(libname) return result @staticmethod def _php_minify(code): """Basic PHP minifier, to optimize final payload size """ lines = [] for line in code.splitlines(): line = line.strip() if line and not line.startswith("//"): lines.append(line) return '\n'.join(lines)
class Build: """Generate final payload, as it can be injected into http requests. The returned string includes `parser`, the separation tags allowing tunnel handler to retrieve output returned from payload after remote http request execution. The payload is also encapsulated through phpsploit standard encapsulator (./data/tunnel/encapsulator.php). """ encapsulator = Path(core.basedir, 'data/tunnel/encapsulator.php').phpcode() def __init__(self, php_payload, parser): self.loaded_phplibs = list() php_payload = self.encapsulate(php_payload, parser) php_payload = self.loadphplibs(php_payload) php_payload = self.shorten(php_payload) encoded_payload = Encode(php_payload.encode(), 'noauto') self.data = encoded_payload.data self.length = encoded_payload.length self.decoder = encoded_payload.decoder def _get_raw_payload_prefix(self): """return $PAYLOAD_PREFIX without php tags, in raw format """ tmpfile = Path() tmpfile.write(session.Conf.PAYLOAD_PREFIX()) payload_prefix = tmpfile.phpcode() del tmpfile return payload_prefix def encapsulate(self, payload, parser): """Wrap the given payload with `parser` tags, so the payloads prints those tags into the page at remote php runtime, allowing the tunnel handler to grab payload response from returned web page. """ # template encapsulation code = self.encapsulator.replace('%%PAYLOAD%%', payload) payload_prefix = self._get_raw_payload_prefix() code = code.replace("%%PAYLOAD_PREFIX%%", payload_prefix) code = code.rstrip(';') + ';' # parser encapsulation if parser: initCode, stopCode = ['echo "%s";' % x for x in parser.split('%s')] code = initCode + code + stopCode return code def loadphplibs(self, code): """Replace `!import(<FOO>)` special syntax with real local library files. """ result = '' for line in code.splitlines(): compLine = line.replace(' ', '') if not compLine.startswith('!import('): result += line + '\n' else: libname = line[(line.find('(') + 1):line.find(')')] if line.count('(') != 1 or line.count(')') != 1 or not libname: raise BuildError('Invalid php import: ' + line.strip()) if libname not in self.loaded_phplibs: try: file_path = 'api/php-functions/%s.php' % libname lib = Path(core.coredir, file_path).phpcode() except ValueError: raise BuildError('Php lib not found: ' + libname) result += self.loadphplibs(lib) + '\n' self.loaded_phplibs.append(libname) return result def shorten(self, code): """Trivial code minifier for payload size optimization. """ lines = [] for line in code.splitlines(): line = line.strip() if line and not line.startswith("//"): lines.append(line) return '\n'.join(lines)
def do_set(argv): """view and edit configuration settings SYNOPSIS: set [<VAR> [+] ["<VALUE>"]] DESCRIPTION: Settings are a collection of editable variables that affect phpsploit's core behavior. - Their value is bound to current session. - To permanently change a setting's value at start, it must be defined by hand on phpsploit config file. > set - Display current settings > set <STRING> - Display settings whose name starts with STRING > set <VAR> <VALUE> - Assign VALUE to VAR setting (only if it's a valid value) > set <VAR> %%DEFAULT%% - Reset VAR's default value with '%%DEFAULT%%' magic string > set <VAR> "file:///path/to/file" - Bind VAR's value to a local file content > set <VAR> + - Open VAR's value in text editor. This is useful to edit values with multiple lines > set <VAR> + <LINE> - Add LINE to the end of VAR's value > set <VAR> + "file:///path/to/file" - Re-bind VAR to a local file path. Even if path doesn't exist, the setting will take the value of the file if it founds it. Otherwise, previous buffer value is kept as long as the file path is unreachable Defining HTTP Headers: You can define custom http request header fields by hand. Settings starting with 'HTTP_' are automagically treated as HTTP Request Headers values. By default, only the "User-Agent" Header is defined. It is bound by default to a local file containing common HTTP User Agents. (`help set HTTP_USER_AGENT`) * Examples: > set HTTP_ACCEPT_LANGUAGE "en-CA" - Define "Accept-Language" http request header field. > set HTTP_ACCEPT_LANGUAGE None - Remove HTTP_ACCEPT_LANGUAGE header with magic value 'None'. Use `set help <VAR>` for detailed help about a setting. """ # `set [<STRING>]` display concerned settings list if len(argv) < 3: string = (argv+[""])[1] print(session.Conf(string)) if string not in session.Conf: string = "<VAR>" print("[*] For detailed help, run `help set %s`" % string) # buffer edit mode elif argv[2] == "+": # `set <VAR> +`: use $EDITOR as buffer viewer in file mode if len(argv) == 3: # get a buffer obj from setting's raw buffer value file_name = argv[1].upper() file_ext = "txt" setting_obj = session.Conf[argv[1]](call=False) if isinstance(setting_obj, datatypes.PhpCode): file_ext = "php" elif isinstance(setting_obj, datatypes.ShellCmd): file_ext = "sh" buffer = Path(filename="%s.%s" % (file_name, file_ext)) buffer.write(session.Conf[argv[1]].buffer) # try to edit it through $EDITOR, and update it # if it has been modified. if buffer.edit(): session.Conf[argv[1]] = buffer.read() # `set <VAR> + "value"`: add value on setting possible choices else: session.Conf[argv[1]] += " ".join(argv[3:]) # `set <VAR> "value"`: just change VAR's "value" else: session.Conf[argv[1]] = " ".join(argv[2:])
def browser(html_string_buffer): file = Path("phpinfo.html") file.write(html_string_buffer) file.browse() del file
def do_set(self, argv): """View and edit settings SYNOPSIS: set [<NAME> [+] ["<VALUE>"]] DESCRIPTION: phpsploit configuration settings manager. The settings are a collection of core variables that affect the framework's core behavior. Any setting takes a default value, that can be manually modified. > set - Display all current settings > set <STRING> - Display all settings whose name starts with STRING. > set <NAME> "value" - Change the NAME setting to "value". If the value is not valid, no changes are made. > set <NAME> "file:///path/to/file" - Set NAME setting's value into a RandLine buffer whose value binds to the external file "/path/to/file". It means that the setting's effective value is dynamic, and on each call to it, the file's content will be loaded if available, and the value is a random line from the file/buffer. > set <NAME> + - Open the setting value for edition as a multiline buffer with EDITOR. The buffer can then be edited, and once saved, the setting will take the buffer's value, except if there are no valid lines. > set <NAME> + "value" - Add "value" as a setting possible choice. It converts the current setting into a RandLine buffer if it was not already. > set <NAME> + "file:///path/to/file" - Rebind NAME setting to the given file path, even if it does not exist at the moment it had been set. It means that each time the setting's value is called, a try is made to load the file's content as new buffer if it exists/is valid, and keeps the old one otherwise. BEHAVIOR - Settings are pre declared at start. It means that new ones cannot be declared. - The convention above does not apply for settings whose name starts with "HTTP_", because this kind of variable are automatically used as custom headers on http requests. For example, `set HTTP_ACCEPT_LANGUAGE "en-CA"` will set the "Accept-Language" http header to the specified value. Of course, this applies to any future HTTP request. - The default value of a setting can be restored by setting its value to the magic string "%%DEFAULT%%", e.g: > set REQ_MAX_HEADERS %%DEFAULT%% NOTE: The 'set' operating scope is limited to the current phpsploit session. It means that persistant settings value changes must be defined by hand in the user configuration file. """ # `set [<PATTERN>]` display concerned settings list if len(argv) < 3: print(session.Conf((argv+[""])[1])) # buffer edit mode elif argv[2] == "+": # `set <VAR> +`: use $EDITOR as buffer viewer in file mode if len(argv) == 3: # get a buffer obj from setting's raw buffer value file_name = argv[1].upper() file_ext = "txt" setting_obj = session.Conf[argv[1]](call=False) if isinstance(setting_obj, datatypes.PhpCode): file_ext = "php" elif isinstance(setting_obj, datatypes.ShellCmd): file_ext = "sh" buffer = Path(filename="%s.%s" % (file_name, file_ext)) buffer.write(session.Conf[argv[1]].buffer) # try to edit it through $EDITOR, and update it # if it has been modified. if buffer.edit(): session.Conf[argv[1]] = buffer.read() # `set <VAR> + "value"`: add value on setting possible choices else: session.Conf[argv[1]] += " ".join(argv[3:]) # `set <VAR> "value"`: just change VAR's "value" else: session.Conf[argv[1]] = " ".join(argv[2:])
def do_set(argv): """view and edit configuration settings SYNOPSIS: set [<VAR> [+] ["<VALUE>"]] DESCRIPTION: Settings are a collection of editable variables that affect phpsploit's core behavior. - Their value is bound to current session. - To permanently change a setting's value at start, it must be defined by hand on phpsploit config file. > set - Display current settings > set <STRING> - Display settings whose name starts with STRING > set <VAR> <VALUE> - Assign VALUE to VAR setting (only if it's a valid value) > set <VAR> %%DEFAULT%% - Reset VAR's default value with '%%DEFAULT%%' magic string > set <VAR> "file:///path/to/file" - Bind VAR's value to a local file content > set <VAR> + - Open VAR's value in text editor. This is useful to edit values with multiple lines > set <VAR> + <LINE> - Add LINE to the end of VAR's value > set <VAR> + "file:///path/to/file" - Re-bind VAR to a local file path. Even if path doesn't exist, the setting will take the value of the file if it founds it. Otherwise, previous buffer value is kept as long as the file path is unreachable Defining HTTP Headers: You can define custom http request header fields by hand. Settings starting with 'HTTP_' are automagically treated as HTTP Request Headers values. By default, only the "User-Agent" Header is defined. It is bound by default to a local file containing common HTTP User Agents. (`help set HTTP_USER_AGENT`) * Examples: > set HTTP_ACCEPT_LANGUAGE "en-CA" - Define "Accept-Language" http request header field. > set HTTP_ACCEPT_LANGUAGE None - Remove HTTP_ACCEPT_LANGUAGE header with magic value 'None'. """ # `set [<PATTERN>]` display concerned settings list if len(argv) < 3: print(session.Conf((argv+[""])[1])) # buffer edit mode elif argv[2] == "+": # `set <VAR> +`: use $EDITOR as buffer viewer in file mode if len(argv) == 3: # get a buffer obj from setting's raw buffer value file_name = argv[1].upper() file_ext = "txt" setting_obj = session.Conf[argv[1]](call=False) if isinstance(setting_obj, datatypes.PhpCode): file_ext = "php" elif isinstance(setting_obj, datatypes.ShellCmd): file_ext = "sh" buffer = Path(filename="%s.%s" % (file_name, file_ext)) buffer.write(session.Conf[argv[1]].buffer) # try to edit it through $EDITOR, and update it # if it has been modified. if buffer.edit(): session.Conf[argv[1]] = buffer.read() # `set <VAR> + "value"`: add value on setting possible choices else: session.Conf[argv[1]] += " ".join(argv[3:]) # `set <VAR> "value"`: just change VAR's "value" else: session.Conf[argv[1]] = " ".join(argv[2:])