Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- """
- deblaze - A remote method enumeration tool for flex servers
- Copyright (C) 2009 Jon Rose
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- import sys
- from optparse import OptionParser
- import logging
- import re
- import httplib
- import pprint
- import os
- import urlparse
- sys.path.append("pyamf/")
- import pyamf
- from pyamf.flex import ArrayCollection
- from pyamf.remoting.client import RemotingService
- import swfdecompiler
- from ReactorThread import ReactorThread
- from outputManager import outputManager
- import time
- '''
- Disclaimer: Quick and dirty hacktool for flex remoting servers.
- '''
- class Deblaze:
- def __init__ (self, url = None, service = None, method = None, creds = None, cookies = None, agent_string = None, fuzz = False, proxy=None):
- self.url = url
- self.service = service
- self.method = method
- self.creds = creds
- self.cookies = cookies
- self.agent_string = agent_string
- self.fuzz = fuzz
- self.proxy = proxy
- def auto(self):
- for gate in self.gatewaysArray:
- for serv in self.servicesArray:
- for meth in self.methodsArray:
- self.method = meth
- self.service = serv
- self.url = gate
- logging.info("Gateway: " + self.url + " Service: " + self.service + " Method: " + self.method)
- self.run()
- return
- ############################
- # Simple Fuzz Function #
- ############################
- def fuzzReq(self, dblzResult):
- pcount = len(dblzResult['params'])
- newparams = ''
- if pcount > 0:
- fuzzstr = ["*","@","%","<",">","(",")","?","'"]
- for fstr in fuzzstr:
- newparams = ''
- for z in range(pcount):
- newparams += fstr + "|"
- newparams = parse_params(newparams[0:-1])
- self.fuzz = False
- self.run(*newparams)
- def run(self, *params):
- """
- Takes a url, service, method, credentials, cookies, useragent and parameters and makes
- the Flash remoting call. Evaluates the response for enumerating
- valid services and methods. Fingerprints Flex technology based on
- responses.
- @return: Nothing
- """
- gw = RemotingService(self.url, user_agent=self.agent_string, proxy=self.proxy)
- #amf_server_debug = {"amf": "true","error": "true","trace": "true","coldfusion": "true","m_debug": "true","httpheaders": "true","amfheaders": "true","recordset": "true",}
- #gw.addHeader('amf_server_debug', amf_server_debug)
- if self.cookies:
- gw.addHTTPHeader("Cookie", self.cookies)
- # Add proxy support here
- if self.creds:
- self.creds = self.creds.split(':')
- gw.setCredentials(self.creds[0], self.creds[1])
- targetService = gw.getService(self.service)
- try:
- methodcall = getattr(targetService, self.method)
- result = methodcall(*params)
- if result is None:
- logging.info("Empty Response - Valid service (" + self.service + ") and method (" + self.method + "), try different parameters")
- om.addMethod(self.method)
- om.addService(self.service)
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','false')])
- om.addFinding(dblzResult)
- if self.fuzz:
- self.fuzzReq(dblzResult)
- return
- if isinstance(result, pyamf.flex.ArrayCollection):
- collection = ArrayCollection(list(result))
- if collection.length == 0:
- logging.info("Empty ArrayCollection - Valid service (" + self.service + ") and method (" + self.method + "), try different parameters - BlazeDS")
- om.addMethod(self.method)
- om.addService(self.service)
- om.addFingerprint('BlazeDS')
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','false')])
- om.addFinding(dblzResult)
- if self.fuzz:
- self.fuzzReq(dblzResult)
- return
- for item in range(collection.length):
- om.out(str(collection.getItemAt(item)))
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',collection),('error','false')])
- om.addFinding(dblzResult)
- if self.fuzz:
- self.fuzzReq(dblzResult)
- return
- if isinstance(result, (pyamf.TypedObject, list, unicode)):
- om.out(str(result))
- om.addMethod(self.method)
- om.addService(self.service)
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)), ('error','false')])
- om.addFinding(dblzResult)
- if self.fuzz:
- self.fuzzReq(dblzResult)
- return
- ############################
- # Error Detection #
- ############################
- if result.code == "Server.Processing" and result.rootCause is not None:
- logging.info(str(result.rootCause))
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result.rootCause)),('error','true')])
- om.addFinding(dblzResult)
- if self.fuzz:
- self.fuzzReq(dblzResult)
- ############################
- # Service Detection #
- ############################
- #AMFPHP server error message for incorrect Service - missing class/file
- if result.code == "AMFPHP_FILE_NOT_FOUND":
- logging.info("Service %s not found - AMFPHP Server" % (self.service))
- om.addFingerprint('AMFPHP Server')
- return
- #BlazeDS invalid services - No destination with id 'products' is registered with any service
- # Service is Case Sensitive for blaze!!!
- if result.code == "Server.Processing":
- m = re.findall("No destination with id '.*' is registered with any service",result.message)
- if len(m) > 0:
- logging.info("Service %s not found - Livecycle Data Services/BlazeDS" % (self.service))
- om.addFingerprint('Livecycle Data Services/BlazeDS')
- return
- #GAE Python invalid services - 'description': u'Unknown service ProjectsServicze.get_by_code'
- if result.code == "Service.ResourceNotFound":
- m = re.findall("Unknown service",result.description)
- if len(m) > 0:
- logging.info("Service %s not found - GAE Python" % (self.service))
- om.addFingerprint('GAE Python')
- return
- ############################
- # Method Detection #
- ############################
- #GAE python
- if isinstance(result, pyamf.remoting.ErrorFault) or isinstance(result, pyamf.remoting.BaseFault):
- if result.code == "Service.MethodNotFound": #gae python
- logging.info("Method %s not found on service %s - GAE Python" % (self.method, self.service))
- om.addService(self.service)
- om.addFingerprint('GAE Python')
- return
- #AMFPHP The method {getServicesq} does not exist in class {Discoveryservice}.
- if result.code == "AMFPHP_INEXISTANT_METHOD":
- logging.info("Method %s not found on service %s - AMFPHP Server" % (self.method, self.service))
- om.addService(self.service)
- om.addFingerprint('AMFPHP Server')
- return
- #Blazeds wrong method
- if result.code == "Server.ResourceUnavailable" and result.level == "error" and result.details is not None:
- m = re.findall("Method '.*' not found",result.details)
- if len(m) > 0:
- logging.info("Method %s not found on service %s - Livecycle Data Services/BlazeDS" % (self.method, self.service))
- om.addService(self.service)
- om.addFingerprint('Livecycle Data Services/BlazeDS')
- return
- ############################
- # Parameter Detection #
- ############################
- #Livecycle Data Services/BlazeDS
- if result.code == "Server.ResourceUnavailable" and result.details is not None: #Blazeds params wrong
- m = re.findall(" arguments were sent but ([0-9]) were expected",result.details)
- if len(m) > 0:
- #if not options.swf:
- # print("Error method %s requires %s params - Livecycle Data Services/BlazeDS" % (self.method, m[0]))
- om.addFingerprint('Livecycle Data Services/BlazeDS')
- newparams = "0"
- for i in range(int(m[0])-1):
- newparams = newparams + "|" + str(i)
- newparams = parse_params(newparams)
- self.run(*newparams)
- return
- #check for parameter type problem
- m = re.findall("The expected argument types",result.details)
- if len(m) > 0:
- logging.info(result.details + " - Livecycle Data Services/BlazeDS")
- om.addFingerprint('Livecycle Data Services/BlazeDS')
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
- om.addFinding(dblzResult)
- return
- #PyAMF server error message for parameters
- if result.code == "TypeError":
- m = re.findall("takes exactly ([0-9]) arguments",result.description)
- if len(m) > 0:
- logging.info("Resending with " + m[0] + " parameters - PyAMF")
- om.addFingerprint('PyAMF')
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
- om.addFinding(dblzResult)
- # Python - def get_by_code(self, code): really takes 1 parameter but say 2 (includes self)
- # print("Error method %s requires %s params - PyAMF Server" % (self.method, m[0]))
- newparams = "0"
- for i in range(int(m[0])-2):
- newparams = newparams + "|" + str(i)
- newparams = parse_params(newparams)
- self.run(*newparams)
- return
- #AMFPHP server error message for parameters"
- if result.code == "AMFPHP_RUNTIME_ERROR":
- m = re.findall("Missing argument ([0-9])",result.description)
- # not just for param errors
- if len(m) > 0:
- logging.info("AMFPHP resending with" + m[0] + "parameters")
- om.addFingerprint('AMFPHP')
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
- om.addFinding(dblzResult)
- newparams = "0"
- for i in range(int(m[0])-1):
- newparams = newparams + "|" + str(i)
- newparams = parse_params(newparams)
- self.run(*newparams)
- else:
- logging.info(result.__dict__)
- dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
- om.addFinding(dblzResult)
- return
- #Other error, print it all out
- if isinstance(result, pyamf.remoting.ErrorFault) and result.details is not None:
- logging.info("Error: " + str(result.details))
- # dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
- # findings.append(dblzResult)
- except KeyboardInterrupt:
- choice = raw_input("Do you wish to quit? (y/n): ")
- if choice.lower().startswith("y"):
- sys.exit(1)
- elif choice.lower().startswith("n"):
- return
- else:
- sys.exit(1)
- except Exception, reason:
- logging.warn("Exception in call: %s" % (str(reason)))
- return
- def banner():
- print """
- .___ __________.__
- __| _/____\______ \ | _____ ________ ____
- / __ |/ __ \| | _/ | \__ \ \___ // __ \
- / /_/ \ ___/| | \ |__/ __ \_/ /\ ___/
- \____ |\___ >______ /____(____ /_____ \\___ >
- \/ \/ \/ \/ \/ \/
- jrose@owasp.org | jrose@trustwave.com | deblaze-tool.appspot.com
- """
- def parse_params(params):
- """
- Takes a string parameter and splits on '|' and attemps to convert to basic
- types - int, float and string. More complex object notations are not
- accounted for (dicts, lists etc.)
- @return: A list of params converted into their scalar types.
- """
- p = []
- for x in params.split('|'):
- if x.lower() == "true" or x.lower() == "false":
- try:
- if x.lower() == "true":
- p.append(True)
- continue
- elif x.lower() == "false":
- p.append(False)
- continue
- except ValueError:
- pass
- else:
- continue
- else:
- try:
- p.append(int(x))
- except ValueError:
- pass
- else:
- continue
- try:
- p.append(float(x))
- except ValueError:
- pass
- else:
- continue
- p.append(x)
- return eval('%s' % (p,))
- if __name__ == "__main__":
- banner()
- from optparse import OptionParser
- parser = OptionParser(description="A remote enumeration tool for Flex Servers", prog="deblaze", version="0.3", usage="%prog [option]")
- parser.add_option("-u", "--url", help="URL for AMF Gateway", dest="url")
- parser.add_option("-s", "--service", help="Remote service to call")
- parser.add_option("-m", "--method", help="Method to call")
- parser.add_option("-p", "--params", help="Parameters to send pipe seperated 'param1|param2|param3'")
- parser.add_option("-1", "--bruteService", help="File to load services for brute forcing (mutually exclusive to -s)")
- parser.add_option("-2", "--bruteMethod", help="File to load methods for brute forcing (mutually exclusive to -m)")
- parser.add_option("-o", "--output", help="Output: console, html, text", dest="output")
- parser.add_option("-P", "--proxy", help="Run local proxy, -P localport:targetIP:targetport", dest="proxy")
- parser.add_option("-d", "--defaultproxy", help="Run default local proxy, 8080:targetIP:targetport",action="store_true")
- parser.add_option("-c", "--creds", help="Username and password for service in u:p format", dest="creds")
- parser.add_option("-b", "--cookie", dest="cookie", help="Send cookies with request")
- parser.add_option("-A", "--user-agent", help="User-Agent string to send to the server", dest="useragent")
- parser.add_option("-r", "--report", help="console, html, defaults to console", dest="report")
- parser.add_option('-v', '--verbose', dest='verbose', action='count',
- help="Increase verbosity (specify multiple times for more)")
- parser.add_option("-t", "--testswf", help="URL to SWF - Download SWF and find remoting services and methods only", dest="testswf")
- parser.add_option("-f", "--fullauto", help="URL to SWF - Download SWF, find remoting services, methods,and parameters", dest="swf")
- parser.add_option("--fuzz", help="Fuzz parameter values", action="store_true", dest="fuzz", default=False)
- (options, args) = parser.parse_args()
- log_level = logging.WARNING # default
- if options.verbose == 1:
- log_level = logging.INFO
- elif options.verbose == 2:
- #enable http logging
- #httplib.HTTPConnection.debuglevel = 1
- #httplib.HTTPSConnection.debuglevel = 1
- log_level = logging.DEBUG
- logging.basicConfig(level=log_level, format='%(message)s')
- om = outputManager()
- if options.defaultproxy and options.url:
- url = urlparse.urlparse(options.url)
- options.proxy = '8080:' + url[1]
- if not options.url and not options.proxy and not options.testswf and not options.swf:
- print("URL or Proxy Mode required")
- sys.exit(1)
- try:
- #Run proxy mode to hanlde AMF req/response
- if options.proxy is not None:
- rt = ReactorThread(options.proxy.split(':')[0],options.proxy.split(':')[1], options.proxy.split(':')[2])
- rt.startReactor()
- time.sleep(1)
- logging.info('Proxy Mode enabled')
- log_level = logging.WARNING # default
- if options.verbose == 1:
- log_level = logging.INFO
- elif options.verbose == 2:
- #enable http logging
- #httplib.HTTPConnection.debuglevel = 1
- #httplib.HTTPSConnection.debuglevel = 1
- log_level = logging.DEBUG
- logging.basicConfig(level=log_level, format='%(message)s')
- if options.report is None:
- options.report = "console"
- logging.info("Reporting to console")
- #init deblaze
- bigD = Deblaze()
- if options.cookie:
- bigD.cookies = options.cookie
- if options.useragent is not None:
- bigD.agent_string = options.useragent
- else:
- bigD.agent_string = 'deblaze/0.3'
- if options.proxy is not None:
- bigD.proxy = options.proxy
- if options.url is not None:
- bigD.url = options.url
- if options.creds is not None:
- bigD.creds = options.creds
- if options.fuzz is not None:
- bigD.fuzz = options.fuzz
- if options.method is not None:
- bigD.method = options.method
- if options.service is not None:
- bigD.service = options.service
- # If we are running auto against a swf file
- if options.swf:
- swfd = swfdecompiler.swfdecompiler()
- swfd.findRemotingMethods(options.swf)
- print "Running deblaze automated mode"
- bigD.fuzz = options.fuzz
- bigD.gatewaysArray = swfd.gatewaysArray
- bigD.servicesArray = swfd.servicesArray
- bigD.methodsArray = swfd.methodsArray
- bigD.auto()
- om.genReport(options.report)
- sys.exit(1)
- if options.testswf:
- swfd = swfdecompiler.swfdecompiler()
- swfd.findRemotingMethods(options.testswf)
- om.genReport(options.report)
- sys.exit()
- if not options.service and not options.bruteService and not options.proxy:
- print("Service or service file required")
- sys.exit(1)
- if not options.method and not options.bruteMethod and not options.proxy:
- print("Method or method file required")
- sys.exit(1)
- if options.params:
- params = parse_params(options.params)
- else:
- params = ""
- # Run through input file
- if options.bruteService:
- fh = open(options.bruteService, 'r')
- for line in fh:
- line = line.strip()
- bigD.service = line
- bigD.run(*params)
- fh.close()
- om.addGateway(options.url)
- om.genReport(options.report)
- sys.exit(1)
- elif options.bruteMethod is not None:
- fh = open(options.bruteMethod, 'r')
- for line in fh:
- line = line.strip()
- bigD.method=line
- bigD.run(*params)
- fh.close()
- om.addGateway(options.url)
- om.genReport(options.report)
- sys.exit(1)
- elif options.url:
- bigD.run(*params)
- om.addMethod(options.method)
- om.addService(options.service)
- om.addGateway(options.url)
- om.genReport(options.report)
- sys.exit(1)
- #spin the proxy
- while(True):
- time.sleep(60)
- # om.addFinding('test2')
- # print om.getFindings()
- except KeyboardInterrupt:
- rt.stopReactor()
- rt.join()
- om.genReport()
- sys.exit(1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement