Advertisement
Guest User

pedrolagunapenturacom

a guest
Feb 22nd, 2010
655
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 22.70 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. """
  4.    deblaze - A remote method enumeration tool for flex servers
  5.    Copyright (C) 2009 Jon Rose
  6.  
  7.    This program is free software: you can redistribute it and/or modify
  8.    it under the terms of the GNU General Public License as published by
  9.    the Free Software Foundation, either version 3 of the License, or
  10.    (at your option) any later version.
  11.  
  12.    This program is distributed in the hope that it will be useful,
  13.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.    GNU General Public License for more details.
  16.  
  17.    You should have received a copy of the GNU General Public License
  18.    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19.  
  20. """
  21. import sys
  22. from optparse import OptionParser
  23. import logging
  24. import re
  25. import httplib
  26. import pprint
  27. import os
  28. import urlparse
  29.  
  30. sys.path.append("pyamf/")
  31. import pyamf
  32. from pyamf.flex import ArrayCollection
  33. from pyamf.remoting.client import RemotingService
  34.  
  35. import swfdecompiler
  36. from ReactorThread import ReactorThread
  37. from outputManager import outputManager
  38. import time
  39.  
  40.  
  41.  
  42. '''
  43. Disclaimer: Quick and dirty hacktool for flex remoting servers.  
  44. '''
  45.  
  46.  
  47. class Deblaze:
  48.  
  49.     def __init__ (self, url = None, service = None, method = None, creds = None, cookies = None, agent_string = None, fuzz = False, proxy=None):
  50.         self.url = url
  51.         self.service = service
  52.         self.method = method
  53.         self.creds = creds
  54.         self.cookies = cookies
  55.         self.agent_string = agent_string
  56.         self.fuzz = fuzz
  57.         self.proxy = proxy
  58.  
  59.  
  60.     def auto(self):
  61.         for gate in self.gatewaysArray:
  62.             for serv in self.servicesArray:
  63.                 for meth in self.methodsArray:
  64.                     self.method = meth
  65.                     self.service = serv
  66.                     self.url = gate
  67.                     logging.info("Gateway: " + self.url + " Service: " + self.service + " Method: " + self.method)
  68.                     self.run()
  69.         return
  70.        
  71.        
  72. ############################
  73. # Simple Fuzz Function                              #
  74. ############################
  75.     def fuzzReq(self, dblzResult):
  76.         pcount = len(dblzResult['params'])
  77.         newparams = ''
  78.         if pcount > 0:
  79.            
  80.             fuzzstr = ["*","@","%","<",">","(",")","?","'"]
  81.             for fstr in fuzzstr:
  82.                 newparams = ''
  83.                 for z in range(pcount):
  84.                     newparams += fstr + "|"
  85.                 newparams = parse_params(newparams[0:-1])
  86.                 self.fuzz = False
  87.                 self.run(*newparams)            
  88.  
  89.    
  90.     def run(self, *params):
  91.         """
  92.        Takes a url, service, method, credentials, cookies, useragent and parameters and makes
  93.        the Flash remoting call.   Evaluates the response for enumerating
  94.        valid services and methods.  Fingerprints Flex technology based on
  95.        responses.
  96.  
  97.        @return: Nothing
  98.    
  99.        """    
  100.         gw = RemotingService(self.url, user_agent=self.agent_string, proxy=self.proxy)
  101.        
  102.        
  103.         #amf_server_debug = {"amf": "true","error": "true","trace": "true","coldfusion": "true","m_debug": "true","httpheaders": "true","amfheaders": "true","recordset": "true",}
  104.         #gw.addHeader('amf_server_debug', amf_server_debug)
  105.  
  106.         if self.cookies:
  107.             gw.addHTTPHeader("Cookie", self.cookies)
  108.            
  109.         # Add proxy support here
  110.    
  111.         if self.creds:
  112.             self.creds = self.creds.split(':')
  113.             gw.setCredentials(self.creds[0], self.creds[1])
  114.            
  115.    
  116.         targetService = gw.getService(self.service)
  117.        
  118.    
  119.         try:
  120.             methodcall = getattr(targetService, self.method)
  121.             result = methodcall(*params)
  122.        
  123.             if result is None:
  124.                 logging.info("Empty Response - Valid service (" + self.service + ") and method (" + self.method + "), try different parameters")
  125.                 om.addMethod(self.method)
  126.                 om.addService(self.service)
  127.                 dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','false')])
  128.                 om.addFinding(dblzResult)
  129.                 if self.fuzz:
  130.                     self.fuzzReq(dblzResult)
  131.                 return
  132.        
  133.             if isinstance(result, pyamf.flex.ArrayCollection):            
  134.                 collection = ArrayCollection(list(result))
  135.                 if collection.length == 0:
  136.                     logging.info("Empty ArrayCollection - Valid service (" + self.service + ") and method (" + self.method + "), try different parameters - BlazeDS")
  137.                     om.addMethod(self.method)
  138.                     om.addService(self.service)
  139.                     om.addFingerprint('BlazeDS')
  140.                     dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','false')])
  141.                     om.addFinding(dblzResult)
  142.                     if self.fuzz:
  143.                         self.fuzzReq(dblzResult)
  144.                     return
  145.                 for item in range(collection.length):
  146.                     om.out(str(collection.getItemAt(item)))
  147.                 dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',collection),('error','false')])
  148.                 om.addFinding(dblzResult)
  149.                 if self.fuzz:
  150.                     self.fuzzReq(dblzResult)
  151.                 return
  152.        
  153.             if isinstance(result, (pyamf.TypedObject, list, unicode)):
  154.                 om.out(str(result))
  155.                 om.addMethod(self.method)
  156.                 om.addService(self.service)
  157.                 dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)), ('error','false')])
  158.                 om.addFinding(dblzResult)
  159.                 if self.fuzz:
  160.                     self.fuzzReq(dblzResult)
  161.                 return
  162.    
  163.         ############################
  164.         # Error Detection                                      #
  165.         ############################    
  166.  
  167.             if result.code == "Server.Processing" and result.rootCause is not None:
  168.                 logging.info(str(result.rootCause))
  169.                 dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result.rootCause)),('error','true')])
  170.                 om.addFinding(dblzResult)
  171.                 if self.fuzz:
  172.                     self.fuzzReq(dblzResult)
  173.  
  174.         ############################
  175.         # Service Detection                                  #
  176.         ############################
  177.        
  178.         #AMFPHP server error message for incorrect Service - missing class/file
  179.             if result.code == "AMFPHP_FILE_NOT_FOUND":
  180.                 logging.info("Service %s not found - AMFPHP Server" % (self.service))
  181.                 om.addFingerprint('AMFPHP Server')
  182.                 return
  183.            
  184.         #BlazeDS invalid services - No destination with id 'products' is registered with any service
  185.         # Service is Case Sensitive for blaze!!!
  186.             if result.code == "Server.Processing":
  187.                 m = re.findall("No destination with id '.*' is registered with any service",result.message)
  188.                 if len(m) > 0:
  189.                     logging.info("Service %s not found - Livecycle Data Services/BlazeDS" % (self.service))
  190.                     om.addFingerprint('Livecycle Data Services/BlazeDS')
  191.                     return
  192.          
  193.         #GAE Python invalid services - 'description': u'Unknown service ProjectsServicze.get_by_code'
  194.             if result.code == "Service.ResourceNotFound":
  195.                 m = re.findall("Unknown service",result.description)
  196.                 if len(m) > 0:
  197.                     logging.info("Service %s not found - GAE Python" % (self.service))
  198.                     om.addFingerprint('GAE Python')
  199.                     return
  200.  
  201.        
  202.         ############################
  203.         # Method Detection                                  #
  204.         ############################
  205.        
  206.         #GAE python
  207.             if isinstance(result, pyamf.remoting.ErrorFault) or isinstance(result, pyamf.remoting.BaseFault):
  208.                 if result.code == "Service.MethodNotFound": #gae python
  209.                     logging.info("Method %s not found on service %s - GAE Python" % (self.method, self.service))
  210.                     om.addService(self.service)
  211.                     om.addFingerprint('GAE Python')
  212.                     return
  213.                            
  214.         #AMFPHP The method {getServicesq} does not exist in class {Discoveryservice}.
  215.             if result.code == "AMFPHP_INEXISTANT_METHOD":
  216.                 logging.info("Method %s not found on service %s - AMFPHP Server" % (self.method, self.service))
  217.                 om.addService(self.service)
  218.                 om.addFingerprint('AMFPHP Server')
  219.                 return
  220.            
  221.         #Blazeds wrong method
  222.             if result.code == "Server.ResourceUnavailable" and result.level == "error" and result.details is not None:
  223.                 m = re.findall("Method '.*' not found",result.details)
  224.                 if len(m) > 0:
  225.                     logging.info("Method %s not found on service %s - Livecycle Data Services/BlazeDS" % (self.method, self.service))
  226.                     om.addService(self.service)
  227.                     om.addFingerprint('Livecycle Data Services/BlazeDS')
  228.                     return
  229.        
  230.         ############################
  231.         # Parameter Detection                              #
  232.         ############################
  233.        
  234.         #Livecycle Data Services/BlazeDS
  235.             if result.code == "Server.ResourceUnavailable" and result.details is not None: #Blazeds params wrong
  236.                 m = re.findall(" arguments were sent but ([0-9]) were expected",result.details)
  237.                 if len(m) > 0:
  238.                     #if not options.swf:
  239.                     #    print("Error method %s requires %s params - Livecycle Data Services/BlazeDS" % (self.method, m[0]))
  240.                     om.addFingerprint('Livecycle Data Services/BlazeDS')
  241.                     newparams = "0"
  242.                     for i in range(int(m[0])-1):
  243.                         newparams = newparams + "|" + str(i)
  244.                     newparams = parse_params(newparams)
  245.                     self.run(*newparams)
  246.                     return
  247.                 #check for parameter type problem
  248.                 m = re.findall("The expected argument types",result.details)
  249.                 if len(m) > 0:
  250.                     logging.info(result.details + " - Livecycle Data Services/BlazeDS")
  251.                     om.addFingerprint('Livecycle Data Services/BlazeDS')
  252.                     dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
  253.                     om.addFinding(dblzResult)
  254.                     return
  255.  
  256.         #PyAMF server error message for parameters
  257.             if result.code == "TypeError":
  258.                 m = re.findall("takes exactly ([0-9]) arguments",result.description)
  259.                 if len(m) > 0:
  260.                     logging.info("Resending with " + m[0] + " parameters - PyAMF")
  261.                     om.addFingerprint('PyAMF')
  262.                     dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
  263.                     om.addFinding(dblzResult)
  264.                     # Python - def get_by_code(self, code): really takes 1 parameter but say 2 (includes self)
  265.                      #   print("Error method %s requires %s params - PyAMF Server" % (self.method, m[0]))
  266.                     newparams = "0"
  267.                     for i in range(int(m[0])-2):
  268.                         newparams = newparams + "|" + str(i)
  269.                     newparams = parse_params(newparams)
  270.                     self.run(*newparams)
  271.                 return
  272.  
  273.         #AMFPHP server error message for parameters"
  274.             if result.code == "AMFPHP_RUNTIME_ERROR":
  275.                 m = re.findall("Missing argument ([0-9])",result.description)
  276.                 # not just for param errors
  277.                 if len(m) > 0:
  278.                     logging.info("AMFPHP resending with" + m[0] + "parameters")
  279.                     om.addFingerprint('AMFPHP')
  280.                     dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
  281.                     om.addFinding(dblzResult)
  282.                     newparams = "0"
  283.                     for i in range(int(m[0])-1):
  284.                         newparams = newparams + "|" + str(i)
  285.                     newparams = parse_params(newparams)
  286.                    
  287.                     self.run(*newparams)
  288.                 else:
  289.                     logging.info(result.__dict__)
  290.                     dblzResult = dict([('url', self.url), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
  291.                     om.addFinding(dblzResult)
  292.                 return
  293.        
  294.         #Other error, print it all out
  295.             if isinstance(result, pyamf.remoting.ErrorFault) and result.details is not None:  
  296.                 logging.info("Error: " + str(result.details))
  297.              #   dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
  298.               #  findings.append(dblzResult)
  299.        
  300.         except KeyboardInterrupt:
  301.             choice = raw_input("Do you wish to quit? (y/n): ")
  302.             if choice.lower().startswith("y"):
  303.                 sys.exit(1)
  304.                
  305.             elif choice.lower().startswith("n"):
  306.                 return
  307.             else:
  308.                 sys.exit(1)
  309.        
  310.         except Exception, reason:
  311.             logging.warn("Exception in call: %s" % (str(reason)))
  312.             return
  313.        
  314.  
  315.  
  316. def banner():
  317.     print """
  318.         .___    __________.__                        
  319.       __| _/____\______   \ | _____  ________ ____  
  320.      / __ |/ __ \|    |  _/  | \__  \ \___   // __ \
  321.     / /_/ \ ___/|    |   \ |__/ __ \_/    /\ ___/
  322.     \____ |\___  >______  /____(____  /_____ \\___  >
  323.          \/    \/       \/          \/      \/    \/
  324.    jrose@owasp.org | jrose@trustwave.com | deblaze-tool.appspot.com
  325.    """
  326.  
  327. def parse_params(params):
  328.     """
  329.    Takes a string parameter and splits on '|' and attemps to convert to basic
  330.    types - int, float and string. More complex object notations are not
  331.    accounted for (dicts, lists etc.)
  332.  
  333.    @return: A list of params converted into their scalar types.
  334.    """
  335.     p = []
  336.     for x in params.split('|'):
  337.    
  338.     if x.lower() == "true" or x.lower() == "false":
  339.             try:
  340.                 if x.lower() == "true":
  341.                     p.append(True)
  342.                     continue
  343.                 elif x.lower() == "false":
  344.                     p.append(False)
  345.                     continue
  346.             except ValueError:
  347.                 pass
  348.             else:
  349.                 continue
  350.        
  351.     else:
  352.         try:
  353.             p.append(int(x))
  354.         except ValueError:
  355.             pass
  356.         else:
  357.             continue
  358.  
  359.         try:
  360.             p.append(float(x))
  361.         except ValueError:
  362.             pass
  363.         else:
  364.             continue
  365.  
  366.         p.append(x)
  367.    
  368.     return eval('%s' % (p,))
  369.    
  370.  
  371. if __name__ == "__main__":
  372.    
  373.     banner()
  374.    
  375.     from optparse import OptionParser
  376.    
  377.     parser = OptionParser(description="A remote enumeration tool for Flex Servers", prog="deblaze", version="0.3", usage="%prog [option]")
  378.     parser.add_option("-u", "--url", help="URL for AMF Gateway", dest="url")
  379.     parser.add_option("-s", "--service", help="Remote service to call")
  380.     parser.add_option("-m", "--method", help="Method to call")
  381.     parser.add_option("-p", "--params", help="Parameters to send pipe seperated 'param1|param2|param3'")
  382.     parser.add_option("-1", "--bruteService", help="File to load services for brute forcing (mutually exclusive to -s)")
  383.     parser.add_option("-2", "--bruteMethod", help="File to load methods for brute forcing (mutually exclusive to -m)")
  384.     parser.add_option("-o", "--output", help="Output: console, html, text", dest="output")
  385.     parser.add_option("-P", "--proxy", help="Run local proxy, -P localport:targetIP:targetport", dest="proxy")
  386.     parser.add_option("-d", "--defaultproxy", help="Run default local proxy, 8080:targetIP:targetport",action="store_true")    
  387.    
  388.     parser.add_option("-c", "--creds", help="Username and password for service in u:p format", dest="creds")
  389.     parser.add_option("-b", "--cookie", dest="cookie", help="Send cookies with request")
  390.     parser.add_option("-A", "--user-agent", help="User-Agent string to send to the server", dest="useragent")
  391.     parser.add_option("-r", "--report", help="console, html, defaults to console", dest="report")
  392.     parser.add_option('-v', '--verbose', dest='verbose', action='count',
  393.                     help="Increase verbosity (specify multiple times for more)")
  394.    
  395.     parser.add_option("-t", "--testswf", help="URL to SWF - Download SWF and find remoting services and methods only", dest="testswf")
  396.     parser.add_option("-f", "--fullauto", help="URL to SWF - Download SWF, find remoting services, methods,and parameters", dest="swf")
  397.     parser.add_option("--fuzz", help="Fuzz parameter values", action="store_true", dest="fuzz", default=False)
  398.    
  399.     (options, args) = parser.parse_args()
  400.    
  401.     log_level = logging.WARNING # default
  402.     if options.verbose == 1:
  403.         log_level = logging.INFO
  404.     elif options.verbose == 2:
  405.         #enable http logging
  406.         #httplib.HTTPConnection.debuglevel  = 1
  407.         #httplib.HTTPSConnection.debuglevel = 1
  408.         log_level = logging.DEBUG
  409.     logging.basicConfig(level=log_level, format='%(message)s')    
  410.    
  411.     om = outputManager()
  412.  
  413.     if options.defaultproxy and options.url:
  414.         url = urlparse.urlparse(options.url)
  415.         options.proxy = '8080:' + url[1]
  416.  
  417.     if not options.url and not options.proxy and not options.testswf and not options.swf:
  418.         print("URL or Proxy Mode required")
  419.         sys.exit(1)
  420.    
  421.     try:
  422.         #Run proxy mode to hanlde AMF req/response
  423.         if options.proxy is not None:
  424.             rt = ReactorThread(options.proxy.split(':')[0],options.proxy.split(':')[1], options.proxy.split(':')[2])
  425.             rt.startReactor()
  426.             time.sleep(1)
  427.             logging.info('Proxy Mode enabled')
  428.            
  429.  
  430.         log_level = logging.WARNING # default
  431.         if options.verbose == 1:
  432.             log_level = logging.INFO
  433.         elif options.verbose == 2:
  434.             #enable http logging
  435.             #httplib.HTTPConnection.debuglevel  = 1
  436.             #httplib.HTTPSConnection.debuglevel = 1
  437.             log_level = logging.DEBUG
  438.         logging.basicConfig(level=log_level, format='%(message)s')
  439.        
  440.         if options.report is None:
  441.              options.report = "console"
  442.              logging.info("Reporting to console")
  443.  
  444.        
  445.         #init deblaze
  446.         bigD = Deblaze()
  447.         if options.cookie:
  448.             bigD.cookies = options.cookie
  449.        
  450.         if options.useragent is not None:
  451.             bigD.agent_string = options.useragent
  452.         else:
  453.             bigD.agent_string = 'deblaze/0.3'
  454.            
  455.         if options.proxy is not None:
  456.             bigD.proxy = options.proxy
  457.            
  458.         if options.url is not None:
  459.             bigD.url = options.url
  460.            
  461.         if options.creds is not None:
  462.             bigD.creds = options.creds
  463.        
  464.         if options.fuzz is not None:
  465.             bigD.fuzz = options.fuzz  
  466.        
  467.         if  options.method is not None:
  468.             bigD.method =  options.method
  469.            
  470.         if  options.service is not None:
  471.             bigD.service =  options.service
  472.        
  473.        
  474.         # If we are running auto against a swf file
  475.         if options.swf:
  476.             swfd = swfdecompiler.swfdecompiler()
  477.             swfd.findRemotingMethods(options.swf)
  478.             print "Running deblaze automated mode"
  479.             bigD.fuzz = options.fuzz
  480.             bigD.gatewaysArray = swfd.gatewaysArray
  481.             bigD.servicesArray = swfd.servicesArray
  482.             bigD.methodsArray = swfd.methodsArray
  483.             bigD.auto()
  484.             om.genReport(options.report)
  485.             sys.exit(1)
  486.            
  487.         if options.testswf:
  488.             swfd = swfdecompiler.swfdecompiler()
  489.             swfd.findRemotingMethods(options.testswf)
  490.             om.genReport(options.report)
  491.             sys.exit()
  492.                    
  493.         if not options.service and not options.bruteService and not options.proxy:
  494.             print("Service or service file required")
  495.             sys.exit(1)
  496.        
  497.         if not options.method and not options.bruteMethod and not options.proxy:
  498.             print("Method or method file required")
  499.             sys.exit(1)
  500.        
  501.         if options.params:
  502.             params = parse_params(options.params)
  503.         else:
  504.             params = ""
  505.        
  506.         # Run through input file
  507.         if options.bruteService:
  508.             fh = open(options.bruteService, 'r')
  509.             for line in fh:
  510.                 line = line.strip()
  511.                 bigD.service = line
  512.                 bigD.run(*params)
  513.             fh.close()
  514.             om.addGateway(options.url)
  515.             om.genReport(options.report)
  516.             sys.exit(1)
  517.         elif options.bruteMethod is not None:
  518.             fh = open(options.bruteMethod, 'r')
  519.             for line in fh:
  520.                 line = line.strip()
  521.                 bigD.method=line
  522.                 bigD.run(*params)
  523.             fh.close()
  524.             om.addGateway(options.url)
  525.             om.genReport(options.report)
  526.             sys.exit(1)
  527.         elif options.url:
  528.             bigD.run(*params)
  529.             om.addMethod(options.method)
  530.             om.addService(options.service)
  531.             om.addGateway(options.url)
  532.             om.genReport(options.report)
  533.             sys.exit(1)
  534.        
  535.         #spin the proxy
  536.         while(True):
  537.             time.sleep(60)
  538.            
  539.            
  540.             #    om.addFinding('test2')
  541. #    print om.getFindings()
  542.        
  543.     except KeyboardInterrupt:
  544.         rt.stopReactor()
  545.         rt.join()
  546.         om.genReport()
  547.         sys.exit(1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement