#!/usr/bin/env python

"""
    deblaze - A remote method enumeration tool for flex servers
    Created by Jon Rose
	Copyright (C) 2009-2010 Trustwave Holdings, Inc.
 
    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
sys.path.append("pyamf/")
import pyamf
from pyamf.flex import ArrayCollection
from pyamf.remoting.client import RemotingService
import sys
from optparse import OptionParser
import logging
import re
import httplib
import pprint
import urllib
import os, subprocess
import StringIO
import base64
from PIL import Image
import datetime

'''
Disclaimer: Quick and dirty hacktool for flex remoting server.  I suck at python.
'''

findings = []
htmlCodes = [
    ['&', '&amp;'],
    ['<', '&lt;'],
    ['>', '&gt;'],
    ['"', '&quot;'],
]

commonfunctions = ['activate',
'addChild',
'addEventListener',
'addHandler',
'addStyleToProtoChain',
'appendChild',
'apply',
'attribute',
'attributes',
'callLater',
'charAt',
'child',
'children',
'concat',
'contains',
'copy',
'createAutomationIDPart',
'createCursor',
'dataForFormat',
'deactivate',
'deepClone',
'descendants',
'disconnect',
'dispatchEvent',
'elements',
'end',
'execute',
'getAssociatedFactory',
'getAutomationChildAt',
'getChildAt',
'getDate',
'getDefinitionByName',
'getFullYear',
'getItemAt',
'getModule',
'getMonth',
'getSandboxRoot',
'getStyle',
'hasComplexContent',
'indexOf',
'initThemeColor',
'internalSetCredentials',
'itemToLabel',
'length',
'load',
'localName',
'localToGlobal',
'logout',
'measure',
'move',
'name',
'nodeKind',
'notification',
'notifyStyleChangeInChildren',
'onTweenEnd',
'onTweenUpdate',
'parent',
'pop',
'push',
'recordAutomatableDragCancel',
'recordAutomatableDragDrop',
'recordAutomatableDragStart',
'regenerateStyleCache',
'remove',
'removeChild',
'removeEventListener',
'replayAutomatableEvent',
'resolveAutomationIDPart',
'setActualSize',
'setCredentials',
'setNotification',
'setStyle',
'setTweenHandlers',
'setVisible',
'setWidth',
'shift',
'slice',
'splice',
'split',
'styleChanged',
'stylesInitialized',
'substr',
'text',
'toFixed',
'toLowerCase',
'toString',
'toXMLString',
'updateDisplayList',
'updateParent',
'watcherFired',
'willTrigger',]


dark_pixel_b64 = \
"""/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAUAAA/+4ADkFkb2JlAGTAAAAAAf/b

"""
bg_b64 = \
"""iVBORw0KGgoAAAANSUhEUgAAAAEAAAJYCAIAAAAsaRvIAAAABGdBTUEAAK/INwWK6QAAABl0RVh0
U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIiSURBVHjalFMJksQgCARzabL//y7LKcZk
pnZnqguCKNC2AP/94WjxFhoTEFFdlD8OgCnmuZY/5ZZy/55Q1Ba14euegooia8WAJf0xfscyoMDC
dlk4vizm63dajfO6xj5ihVXsuphVn2Orrem6roldzYZ/w/aMbStsq2OT703ttoa/mV3Nn7FvO2y7
+7v4hn2377/gcOyH2MPB/uG+xA//HlC7rRmvhlolVtm6X6vaGa02toxW1TaJNYu1Zn6rjmY4W/qG
c1g74TzZPz1+yveE64TL/Sv86+Jvw8X+ZbawtG8QrV8/F/zIGutVclBEC8QQq+uq3nkviJwlTlCI
8y2HNC57SXKI/LzcI/mgr4H3Ia8T5+pZli+PhnQfeA9jP+TnZAylfux/BWbtnOvZW3CCoL1rX1Ev
zuF+geMYc2CeZ/OA9dNzIftGmXmo/+AkObX6wSU893ziE2xGnavzQnbeyKX3aftRLVHUn3j6Arxx
8y0P+10LLwTJC0LqBd/uLniM85V/uxfyuS3uOc5J6JGmPjQW2v2qmeDJe+h9Us409Zx3AKlfknp+
92TafZ912u9+nKNvAvCh9X7now5veka9Wxx4H3X4qZe8X6tpOiPXOXZ9EuEw72dd9zsKrfhbV57H
N+X59NDYeLeQtTF5RUpO8o3G/u+axmEWq+t8vfKN/f2Q53Y+6a32WGOYA00fEO/DNR3coHPzfMfD
vb3U+YJfAQYA+QusQdBehRsAAAAASUVORK5CYII=
"""

bgcode_b64 = \
"""R0lGODlhBQAFAIABABQUFPz8/CH5BAEAAAEALAAAAAAFAAUAAAIIjAMHibz2TAEAOw==
"""

quote_b64 = \
"""R0lGODlhFAAPANUAAElJSVRUVC4uLiYmJkVFRQgICCkpKTg4OC8vLzIyMjo6OktLSywsLD8/Pyoq
Ki0tLVVVVQ4ODgsLC0FBQSQkJDMzMzc3N0hISFZWVhAQEDQ0ND09PTw8PDs7OwUFBRsbGysrKxYW
Fjk5OVlZWVBQUCcnJ1NTUxMTEzExMU9PTxkZGUdHR01NTURERFJSUigoKEJCQjY2Nj4+PldXV0ND
Q05OTkBAQDAwMDU1NQMDA////////////////////////yH5BAEAADkALAAAAAAUAA8AAAbZwJzQ
YwDUGAWh8CMLAAQSpQcHQwgImJvwloo5KjSc8kYblByCxKyQcRlKhgdqMRAGQC8QIoFbUCoyBg4P
CRY2ADknJA4MCBoKHQQHNBaNCSIyEyM5KjUPNzgcExM1FDQKN482LQsyOREmKDgNNCs1azINoQQE
NRAfQjUHIgABARUZOUUHTSMHIUoCFwo2GHVCBTMdGzViSjkSADYyBDMMYywbNibeSgMzNDY0ASdK
ODUNDSkU3znvFzYgRPiGg4SNBQL65QhxAIKWfjdm3Iii8FXFHEkuauwXBAA7
"""

bgmenu_b64 = \
"""iVBORw0KGgoAAAANSUhEUgAAAJUAAABQCAIAAACf5aAfAAAABGdBTUEAAK/INwWK6QAAABl0RVh0
U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAANeSURBVHja7JzrcqswDISDIXfCMLm8/4uS
cMQRdRSBKUwJRjPrHwwpX9zMbiRMVSspyzIRI01T5xydbP6P1+tV1/UmMAiWLwF/FSaAMDp5Pp/M
00h3u513iyF/pEGXmOudl2Zh1wF/G65/hreJ7UyKoqDXHHM8HUdh19TQkJ+DfxngWWAVS+SRZ+hI
UUiXksvlwlPwZT5yzI7/HP5dgGeHZdgR7F/SkSxM8jyXkSdPZFBy2h3O0dJvwPPC3jb2+50/yT8V
yPK8G4UDn0MufADPBat7nkqkTf6UqxU5C593V0pId4vB6hboXfRR2ObP1syf5OmPyGAR4aqqetcy
0sLkfD7ze7yF3RBEuosIy6hVjxCNVeSf90kFuApEpLtYcO9zRevR6XRSj3psLDJYdNgDBIeewpPj
8TjpkRPpbmGYmZAp6Xa7Vbc66Xx3qEuA48Jv/8ZbCHg98Id/kMMcrP2DdrbgHv+gnSG43z9oZwUO
+gftTMBD/kG79cO/+AftVg6n+/0ectiF2/9fghxG4cY/yGEXbv2DHEbhZv0COezCaZZlkMMuPLl+
BHhVMOpHtmHUj2zDqB+hfgQ4Hoz6EepHgOPBqB+hfgQ4Hoz6kW0Y9SPbMOpHtmHUj2zDWVEUcrf7
1P4v2DS0DLz57P/CfPP84PdvSrcn9X+R3xrA88K+T4E/l1tw6edpnudqwx8TcsaBr4bf1+Q3Dg5s
PwM8CVbb3tVJu1/68XjIzi/trupO8xA02okFyyiiRFqLUVVVcr/f2a1Np/8Lp92R90LsvvwGHGoe
8u7/Qv55w1QXn2Z5k2VYR8SFf+n/QvlT9X+Ry5neqQeygfoqAf4jrNYfPnNyamzjL9T/JdSFCbGy
cP8XtZD86P9yu93G9H+B0LHg3v4v3sLGv824/i+qkdZwNgA8Ixxq4dO4c71eu4ufbgstxMrysOz/
EvpTQFKW5aTbLIReGGYmZEp6OBzaTlqiTxYar6wNDpnieh8wVQru/uEH8EpgBzlMww5ymIYd5DAN
O8hhGnaQwzTsIIdp2EEO07CDHKZhBzlMww5ymIYd5DANO8hhGnaQwzTsIIdpGPUj2zDqR7Zh1I9s
w6gfoX4EOB6M+pFtGPUj2zDqR6gfAY4Ho36E+hHgeDDqR7bhfwIMAIc9EWduE4gCAAAAAElFTkSu
QmCC
"""



def buildHTML(*args):

    if len(findings) == 0:
        print "No findings to report"
        sys.exit(0)
    swfAnalysis = list(args)
    
    htmlReport = """
<html xmlns="http://www.w3.org/1999/xhtml"><head>
	<style type="text/css">
		/*
Template name: Blackbox
Template URI: http://templates.arcsin.se/blackbox-website-template/
Release date: 2006-06-01
Description: A mystic dark lightweight design.
Author: Viktor Persson
Author URI: http://arcsin.se/

This template is licensed under a Creative Commons Attribution 2.5 License:
http://templates.arcsin.se/license/
*/
*{margin:0;padding:0}a{color:white;text-decoration:none}body{background:#000 url(images/bg.png) repeat-x fixed left bottom;color:#777;font:normal 0.8em sans-serif,Arial;margin:20px 0;text-align:center}ul{margin:12px 0}li{list-style:url(images/li.gif);margin-left:18px}code{background:url(images/bgcode.gif);color:#777;display:block;font:normal 2em "Lucida Sans Unicode",serif;margin-bottom:12px;padding:3px 6px;white-space:pre}cite{background:url(images/quote.gif) no-repeat;color:#777;display:block;font:normal 1.1em "Lucida Sans Unicode",serif;margin-bottom:12px;padding-left:28px}.main{background:#000;border:3px double #EEE;border-color:#141414 #202020 #222 #202020;margin:20px auto 4px auto;text-align:left;width:600px}.gfx{background:#777 url(images/dark_pixel.jpg) no-repeat;height:240px}.gfx h1{color:#999;font:normal 2.6em Tahoma,sans-serif;padding:16px 20px}.menu a{background:#000 url(images/bgmenu.png) repeat-x;border-right:1px solid #222;border-top:1px solid #1A1A1A;color:#777;float:left;font-size:1.2em;padding-top:4px;width:149px;height:36px}.menu a span{padding-left:6px}.menu a:hover{background-position:left bottom;color:#888}.menu a#last{border-right:none;width:150px}.content{background:#1A1A1A;border-top:1px solid #1A1A1A;clear:both}.content h1{color:#999;font:bold 1.1em sans-serif,Arial;margin:0 0 4px}.content p{margin:0 0 12px}.item{background:#030303 url(images/bgitem.gif) repeat-x;padding:8px 10px}.resultTitle{font:normal 0.8em sans-serif,Arial; color:white}.result{padding:8px 10px;display:block;font:normal 0.8em sans-serif,Arial}.footer{background:#0A0A0A;color:#666;padding:5px}.footer .left,.footer .right{padding:0 12px}.footer .right,.footer .right a{color:#444}.footer .right a:hover{color:#999}.left{float:left}.right{float:right}.clearer{clear:both}
	</style>
<title>Deblaze - A remote method enumeration tool for flex servers</title>
<script>
function showhide(id){
        var divCollection = document.getElementsByTagName("div");
        for (var i=0; i < divCollection.length; i++) {
            if(divCollection[i].getAttribute("class") == "item") {
                if(divCollection[i].getAttribute("id") == id){
                divCollection[i].style.display = "";
                }
                else{divCollection[i].style.display = "none";}}}}
</script>
</head><body>
<div class="main">
<div class="gfx"><h1>Deblaze</h1><h4>A remote method enumeration tool for flex servers</h4></div>
		<div class="menu">
		<a href="#" onclick="showhide('1'); return(false);"><span>SWF Analysis</span></a>
		<a href="#" onclick="showhide('2'); return(false);"><span>Methods</span></a>
		<a href="#" onclick="showhide('3'); return(false);"><span>Errors</span></a>
		<a href="#" onclick="showhide('4'); return(false);"><span>Empty Results</span></a>
		<!--	   <a href="#" onclick="showhide('3'); return(false);"><span>About</span></a>-->
	</div>

        ---SWF---

		---METHODS---
		
		---FUZZ---
		
		---EMPTY---
		
			<script>
		showhide('1');
		</script>
<div class="footer"><span class="left">2009 deblaze-tool.appspot.com</span>
<span class="right"><a href="http://templates.arcsin.se/">Website template</a> by <a href="http://arcsin.se/">Arcsin</a></span>
<div class="clearer"><span></span></div>
</div></div></body></html>
"""

    dirname = 'report' + datetime.datetime.now().strftime("%d%B%Y-%H%M%S")
    print "Generating Report - " + dirname

    if not os.path.isdir('./' + dirname + '/'):
        os.mkdir('./' + dirname + '/')
        os.mkdir('./' + dirname + '/images/')

    res = ''
    reserr = ''
    resempty = ''

    for finding in findings:
        if(finding['error'] == 'true'):
            reserr += '<div class="item" id="3">'
            reserr += '<table>'
            reserr += '<tr><td class="resultTitle">URL</td><td class="result">' + htmlEncode(str(finding['url'])) + '</td></tr>'
            reserr += '<tr><td class="resultTitle">Service</td><td class="result">' + htmlEncode(str(finding['service'])) + '</td></tr>'
            reserr += '<tr><td class="resultTitle">Method</td><td class="result">' + htmlEncode(str(finding['method'])) + '</td></tr>'
            reserr += '<tr><td class="resultTitle">Params</td><td class="result">' + htmlEncode(str(len(finding['params'])) + ': ' + str(finding['params'])) + '</td></tr>'
            reserr += '<tr><td class="resultTitle">Result</td><td class="result">' + htmlEncode(str(finding['result'])) + '</td></tr>'
            reserr += '</table>'
            reserr += '</div>'
        elif(finding['result'] == 'None' or finding['result'] == '<flex.messaging.io.ArrayCollection []>'):
            resempty += '<div class="item" id="4">'
            resempty += '<table>'
            resempty += '<tr><td class="resultTitle">URL</td><td class="result">' + htmlEncode(str(finding['url'])) + '</td></tr>'
            resempty += '<tr><td class="resultTitle">Service</td><td class="result">' + htmlEncode(str(finding['service'])) + '</td></tr>'
            resempty += '<tr><td class="resultTitle">Method</td><td class="result">' + htmlEncode(str(finding['method'])) + '</td></tr>'
            resempty += '<tr><td class="resultTitle">Params</td><td class="result">' + htmlEncode(str(len(finding['params'])) + ': ' + str(finding['params'])) + '</td></tr>'
            resempty += '<tr><td class="resultTitle">Result</td><td class="result">' + htmlEncode(str(finding['result'])) + '</td></tr>'
            resempty += '</table>'
            resempty += '</div>' 
        else:
            res += '<div class="item" id="2">'
            res += '<table>'
            res += '<tr><td class="resultTitle">URL</td><td class="result">' + htmlEncode(str(finding['url'])) + '</td></tr>'
            res += '<tr><td class="resultTitle">Service</td><td class="result">' + htmlEncode(str(finding['service'])) + '</td></tr>'
            res += '<tr><td class="resultTitle">Method</td><td class="result">' + htmlEncode(str(finding['method'])) + '</td></tr>'
            res += '<tr><td class="resultTitle">Params</td><td class="result">' + htmlEncode(str(len(finding['params'])) + ': ' + str(finding['params'])) + '</td></tr>'

            if isinstance(finding['result'], ArrayCollection):     
                res += '<tr><td class="resultTitle">Result</td>'
                for item in range(finding['result'].length):
                    res += '<td class="result">' + htmlEncode(str(finding['result'].getItemAt(item))) + '</td>'
                res += '</td>'
            else:
                res += '<tr><td class="resultTitle">Result</td><td class="result">' + htmlEncode(str(finding['result'])) + '</td></tr>'
            res += '</table>'
            res += '</div>'
            
    if len(swfAnalysis) > 0:
        resswf = '<div class="item" id="1"><table align="center">'
        resswf += '<tr><td class="resultTitle">Gateways</td></tr>'
        for gateway in swfAnalysis[0]:
            resswf += '<tr><td class="result">' + htmlEncode(str(gateway)) + '</td></tr>'
        resswf += '<tr><td class="resultTitle">Services</td></tr>'
        for service in swfAnalysis[1]:
            resswf += '<tr><td class="result">' + htmlEncode(str(service)) + '</td></tr>'
        resswf += '<tr><td class="resultTitle">Methods</td></tr>'
        for method in swfAnalysis[2]:
            resswf += '<tr><td class="result">' + htmlEncode(str(method)) + '</td></tr>'  
        resswf += '</table></div>'
    else:
        resswf = '<div class="item" id="1"><table align="center"><tr><td class="resultTitle"><br>No SWF file</td></tr></table></div>'
      
    htmlReport = htmlReport.replace('---METHODS---',res)
    htmlReport = htmlReport.replace('---FUZZ---',reserr)
    htmlReport = htmlReport.replace('---EMPTY---',resempty)
    htmlReport = htmlReport.replace('---SWF---',resswf)    
    
    f = open(dirname + '/index.html', 'w')
    f.write(htmlReport)
    f.close()

    dark_pixel = base64.b64decode(dark_pixel_b64)
    pic = Image.open( StringIO.StringIO(dark_pixel))
    pic.save(dirname +'/images/dark_pixel.jpg', format= 'JPEG')
    
    bg = base64.b64decode(bg_b64)
    pic = Image.open( StringIO.StringIO(bg))
    pic.save(dirname +'/images/bg.png', format= 'PNG')

    bgcode = base64.b64decode(bgcode_b64)
    pic = Image.open( StringIO.StringIO(bgcode))
    pic.save(dirname +'/images/bgcode.gif', format= 'GIF')

    quote = base64.b64decode(quote_b64)
    pic = Image.open( StringIO.StringIO(quote))
    pic.save(dirname +'/images/quote.gif', format= 'GIF')

    bgmenu = base64.b64decode(bgmenu_b64)
    pic = Image.open( StringIO.StringIO(bgmenu))
    pic.save(dirname +'/images/bgmenu.png', format= 'PNG')


def htmlEncode(s, codes=htmlCodes):
    """ Returns the HTML encoded version of the given string. This is useful to
        display a plain ASCII text string on a web page."""
    for code in codes:
        s = s.replace(code[0], code[1])
    return s
    
class Deblaze:

    url = ''
    gatwayurl = ''
    service = ''
    method = ''
    creds = ''
    cookies = ''
    agent_string = ''
    params = ''
    fuzz = False
    methodsArray = []
    gatewaysArray = []
    servicesArray = []

    def __init__ (self, url = None, service = None, method = None, creds = None, cookies = None, agent_string = None, fuzz = False):

        self.url = url
        self.service = service
        self.method = method
        self.creds = creds
        self.cookies = cookies
        self.agent_string = agent_string
        self.gatewayurl = url
        self.fuzz = fuzz


    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.gatewayurl = self.url + gate
                    #print "Gateway: " + self.gatewayurl + " Service: " + self.service + " Method: " + self.method
                    self.run()
        return
        
        
############################
# Simple Fuzz Function                              #
############################
    def fuzzReq(self, dblzResult):
        fuzzstr = ["'","*","@","%","<",">","(",")","?"]
        pcount = len(dblzResult['params'])
        newparams = ''
        if pcount > 0:
            for fstr in fuzzstr:
                newparams = ''
                for i in range(pcount):
                     newparams += fstr + "|"
                newparams = parse_params(newparams[0:-1])
                d = Deblaze(dblzResult['url'], dblzResult['service'], dblzResult['method'], self.creds, self.cookies, self.agent_string, False )
                d.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.gatewayurl, user_agent=self.agent_string)
        
        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)
    
        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:
                if not options.quiet:
                    print("Empty Response - Valid service (" + self.service + ") and method (" + self.method + "), try different parameters")
                dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','false')])
                findings.append(dblzResult)
                if self.fuzz:
                    self.fuzzReq(dblzResult)
                return
        
            if isinstance(result, pyamf.flex.ArrayCollection):            
                collection = ArrayCollection(list(result))
                if collection.length == 0:
                    if not options.quiet:
                        print("Empty ArrayCollection - Valid service (" + self.service + ") and method (" + self.method + "), try different parameters - BlazeDS")
                    dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','false')])
                    findings.append(dblzResult)
                    if self.fuzz:
                        self.fuzzReq(dblzResult)
                    return
                if not options.quiet:
                    for item in range(collection.length):
                        print(str(collection.getItemAt(item)))
                dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',collection),('error','false')])
                findings.append(dblzResult)
                if self.fuzz:
                    self.fuzzReq(dblzResult)
                return
        
            if isinstance(result, (pyamf.TypedObject, list, unicode)):
                if not options.quiet:
                    print str(result)
                dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)), ('error','false')])
                findings.append(dblzResult)
                if self.fuzz:
                    self.fuzzReq(dblzResult)
                return
    
        ############################
        # Error Detection                                      #
        ############################    

            if result.code == "Server.Processing" and result.rootCause is not None:
                if not options.quiet:
                    print str(result.rootCause)
                dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result.rootCause)),('error','true')])
                findings.append(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" and not options.quiet:
                print("Service %s not found - AMFPHP Server" % (self.service))
                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"  and not options.quiet:
                m = re.findall("No destination with id '.*' is registered with any service",result.message)
                if len(m) > 0:
                    print("Service %s not found - BlazeDS" % (self.service))
                    return
         
        #GAE Python invalid services - 'description': u'Unknown service ProjectsServicze.get_by_code'
            if result.code == "Service.ResourceNotFound"  and not options.quiet:
                m = re.findall("Unknown service",result.description)
                if len(m) > 0:
                    print("Service %s not found - GAE Python" % (self.service))
                    return

       
        ############################
        # Method Detection                                  #
        ############################
        
        #GAE python
            if isinstance(result, pyamf.remoting.ErrorFault) or isinstance(result, pyamf.remoting.BaseFault):
                if result.code == "Service.MethodNotFound": #gae python 
                    if not options.quiet:
                        print("Method %s not found on service %s - GAE Python" % (self.method, self.service))
                    return
                           
        #AMFPHP The method {getServicesq} does not exist in class {Discoveryservice}.
            if result.code == "AMFPHP_INEXISTANT_METHOD":
                if not options.quiet:
                    print("Method %s not found on service %s - AMFPHP Server" % (self.method, self.service))
                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:
                    if not options.quiet:
                        print("Method %s not found on service %s - Livecycle Data Services/BlazeDS" % (self.method, self.service))
                    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]))
                    newparams = "0"
                    for i in range(int(m[0])-1):
                        newparams = newparams + "|" + str(i)
                    newparams = parse_params(newparams)
                    d = Deblaze(self.gatewayurl, self.service, self.method, self.creds, self.cookies, self.agent_string, self.fuzz )
                    d.run(*newparams)
                    return
                #check for parameter type problem
                m = re.findall("The expected argument types",result.details)
                if len(m) > 0:
                    if not options.quiet:
                        print(result.details + " - Livecycle Data Services/BlazeDS")
                    dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
                    findings.append(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:
                    if not options.quiet:
                        print("Resending with " + m[0] + " parameters - PyAMF")
                        dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
                        findings.append(dblzResult)
                    # some methods are like this     def get_by_code(self, code): and really take 1 parameter but say 2
                     #   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)
                    d = Deblaze(self.gatewayurl, self.service, self.method, self.creds, self.cookies, self.agent_string, self.fuzz )
                    d.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:
                    if not options.quiet:
                        print("AMFPHP resending with" + m[0] + "parameters")
                        dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
                        findings.append(dblzResult)
                    newparams = "0"
                    for i in range(int(m[0])-1):
                        newparams = newparams + "|" + str(i)
                    newparams = parse_params(newparams)
                    
                    d = Deblaze(self.gatewayurl, self.service, self.method, self.creds, self.cookies, self.agent_string, self.fuzz )
                    d.run(*newparams)
                else:
                    if not options.quiet:
                        print(result.__dict__)
                    dblzResult = dict([('url', self.gatewayurl), ('service', self.service), ('method', self.method),('params', params),('result',str(result)),('error','true')])
                    findings.append(dblzResult)
                return
                    

        
        #Other error, print it all out
            if isinstance(result, pyamf.remoting.ErrorFault) and result.details is not None:  
                print "Other 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:
            print("Exception in call: %s" % (str(reason)))
        return
        
        
############################
#   Extracts remoting methods from SWF    #
############################
    def findRemotingMethods(self, swf):
 
        proc = subprocess.Popen(['./swfdump', '-D',swf,], 
                        shell=False, 
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        )
                        
        stdout_value = proc.communicate()[0]

        m = re.findall("flex.messaging.io",stdout_value)
        if len(m) is 0:
            print "No Remoting Libs found!"
            os._exit(1)


        m = re.findall("endpoint uri=\"http://{server.name}:{server.port}([A-Za-z_/]*)",stdout_value)
        if len(m) > 0:
        #print "##### Gateway URLs #####"
        #print m
            self.gatewaysArray = m    

#m = re.findall("findproperty <q>\[public\]::remObj(.*)",stdout_value)
#if len(m) > 0:
#   services.extend(m)
    
        services = []
        m = re.findall("destination id=\"([\w\d]*)\"",stdout_value)
        if len(m) > 0:
            #print "##### Remoting Services #####"
            services.extend(m)
            services = unique(services)
            #print services
            self.servicesArray = services


        m = re.findall("\[staticprotected]Object}::(.*), \d params",stdout_value)
        if len(m) > 0:
            #print "##### Methods #####"
            methodnames = []
            unique_matches = unique(m)
            for name in unique_matches:
                if(name not in commonfunctions):
         #       print name
                    methodnames.append(name)
            self.methodsArray = methodnames
        #print result.methods


############################
# Returns unique list                                 #
############################
def unique(s):
     """Return a list of the elements in s, but without duplicates.

     For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
     unique("abcabc") some permutation of ["a", "b", "c"], and
     unique(([1, 2], [2, 3], [1, 2])) some permutation of
     [[2, 3], [1, 2]].

     For best speed, all sequence elements should be hashable.  Then
     unique() will usually work in linear time.

     If not possible, the sequence elements should enjoy a total
     ordering, and if list(s).sort() doesn't raise TypeError it's
     assumed that they do enjoy a total ordering.  Then unique() will
     usually work in O(N*log2(N)) time.

     If that's not possible either, the sequence elements must support
     equality-testing.  Then unique() will usually work in quadratic
     time.
     """

     n = len(s)
     if n == 0:
         return []

     # Try using a dict first, as that's the fastest and will usually
     # work.  If it doesn't work, it will usually fail quickly, so it
     # usually doesn't cost much to *try* it.  It requires that all the
     # sequence elements be hashable, and support equality comparison.
     u = {}
     try:
         for x in s:
             u[x] = 1
     except TypeError:
         del u  # move on to the next method
     else:
         return u.keys()

     # We can't hash all the elements.  Second fastest is to sort,
     # which brings the equal elements together; then duplicates are
     # easy to weed out in a single pass.
     # NOTE:  Python's list.sort() was designed to be efficient in the
     # presence of many duplicate elements.  This isn't true of all
     # sort functions in all languages or libraries, so this approach
     # is more effective in Python than it may be elsewhere.
     try:
         t = list(s)
         t.sort()
     except TypeError:
         del t  # move on to the next method
     else:
         assert n > 0
         last = t[0]
         lasti = i = 1
         while i < n:
             if t[i] != last:
                 t[lasti] = last = t[i]
                 lasti += 1
             i += 1
         return t[:lasti]

     # Brute force is all that's left.
     u = []
     for x in s:
         if x not in u:
             u.append(x)
     return u


def banner():
    print """
         .___    __________.__                        
       __| _/____\______   \  | _____  ________ ____  
      / __ |/ __ \|    |  _/  | \__  \ \___   // __ \ 
     / /_/ \  ___/|    |   \  |__/ __ \_/    /\  ___/ 
     \____ |\___  >______  /____(____  /_____ \\___  >
          \/    \/       \/          \/      \/    \/ 
    jrose@owasp.org | jrose@trustwave.com | github.com/SpiderLabs
    """

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('|'):
        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__":
    from optparse import OptionParser
    
    parser = OptionParser(description="A remote enumeration tool for Flex Servers", prog="deblaze", version="0.2", 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("-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)
    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("-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("-d", "--debug", help="Enable pyamf/AMF debugging", action="store_true")
    parser.add_option("-v", "--verbose", help="Print http request/response", action="store_true")
    parser.add_option("-r", "--report", help="Generate HTML report", action="store_true")
    parser.add_option("-n", "--nobanner", help="Do not display banner", action="store_true")
    parser.add_option("-q", "--quiet", help="Do not display messages", action="store_true")

    
    (options, args) = parser.parse_args()
    
    if not options.nobanner:
        banner()
        
    if not options.url and not options.swf:
        parser.print_help()
        sys.exit(1)
        
    if options.debug:
        logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    
    if options.verbose:
        httplib.HTTPConnection.debuglevel  = 1
        httplib.HTTPSConnection.debuglevel = 1
	
    if not options.service and not options.bruteService and not options.swf:
        print("Service or service file required")
        sys.exit(1)
	
    if not options.method and not options.bruteMethod and not options.swf:
        print("Method or method file required")
        sys.exit(1)
    
    if options.params:
        params = parse_params(options.params)
    else:
        params = ""
    
    if options.cookie:
        cookies = options.cookie
    else:
        cookies = None
    
    if options.useragent is not None:
        useragent = options.useragent
    else:
        useragent = 'PyAMF/%s' % '.'.join(map(lambda x: str(x), pyamf.__version__))
        
    if options.swf:
        if not os.path.isfile('swfdump'):
           print "Missing swfdump in current path, please copy for automated mode"
           sys.exit(1)
        swfsplit = options.swf.split('/')
        #print swfsplit[-1]
        url = swfsplit[0] + '//' + swfsplit[2] + '/'
        print "Downloading SWF for analysis: " + options.swf
        
        webFile = urllib.urlopen(options.swf)
        localFile = open(swfsplit[-1], 'w')
        localFile.write(webFile.read())
        webFile.close()
        localFile.close()

        #result = findRemotingMethods(swfsplit[-1])
        print "Running deblaze automated mode"
        bigD = Deblaze()
        bigD.findRemotingMethods(swfsplit[-1])
        bigD.url = url
        bigD.fuzz = options.fuzz
        bigD.auto()
        if options.report:
            buildHTML(bigD.gatewaysArray, bigD.servicesArray, bigD.methodsArray )
        sys.exit(1)
    
    # Run through input file
    if options.bruteService:
        fh = open(options.bruteService, 'r')
        for line in fh:
            line = line.strip()
            d = Deblaze(options.url, line, options.method, options.creds, cookies, useragent, options.fuzz)
            d.run(*params)
        fh.close()
        if options.report:
            buildHTML()
        sys.exit(1)
    elif options.bruteMethod is not None:
        fh = open(options.bruteMethod, 'r')
        for line in fh:
            line = line.strip()
            d = Deblaze(options.url, options.service, line, options.creds, cookies, useragent,  options.fuzz)
            d.run(*params)
        fh.close()
        if options.report:
            buildHTML()
        sys.exit(1)
   
    else:
        d = Deblaze(options.url, options.service, options.method, options.creds, cookies, useragent,  options.fuzz)
        d.run(*params)
        if options.report:
            buildHTML()
        sys.exit(1)

