[
  {
    "path": "README",
    "content": ""
  },
  {
    "path": "gwtenum/README",
    "content": "EXAMPLES\r\n\r\npython gwt_rpc_enum.py -u \"http://www.whirled.com/gwt/frame/frame.nocache.js\"\r\npython gwt_rpc_enum.py -u \"https://adwords.google.com/billing/ui/accountcancel/account_cancel_widget.nocache.js\""
  },
  {
    "path": "gwtenum/gwtenum.py",
    "content": "#!/usr/bin/env python\r\n\r\n'''\r\n\r\n    GwtEnum v0.2\r\n    Copyright (C) 2010 Ron Gutierrez\r\n\r\n    This program is free software: you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation, either version 3 of the License, or\r\n    (at your option) any later version.\r\n\r\n    This program is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n\r\n    You should have received a copy of the GNU General Public License\r\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r\n\r\n'''\r\n\r\nimport urllib2\r\nimport re\r\nimport pprint\r\nimport base64\r\nimport getpass\r\nfrom optparse import OptionParser\r\n\r\ndesc = \"A tool for enumerating GWT RPC methods\"\r\nmethods = []\r\nproxy_url = \"\"\r\nbasic_auth_encoded = \"\"\r\n        \r\ndef get_global_val( varname, html_file ):\r\n    for html_line in html_file:\r\n        match = re.match( \".*,\" + re.escape(varname) +\r\n            \"\\=\\'([A-Za-z0-9_\\.\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\" \r\n            \"\\-\\+\\=\\:\\;\\\"\\|\\\\\\\\/\\?\\>\\,\\<\\~\\`]+)\\',\", html_line )\r\n        if not match is None:\r\n            return match.group(1)\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    parser = OptionParser( usage=\"usage: %prog [options]\", \r\n        description=desc, \r\n        version='%prog 0.10' )\r\n    \r\n    parser.add_option('-p', '--proxy', \r\n        help=\"Proxy Host and Port (ie. -p \\\"http://proxy.internet.net:8080\\\")\", \r\n        action=\"store\" )\r\n        \r\n    parser.add_option('-b', '--basicauth', \r\n        help=\"User Basic Authentication ( Will be prompted for creds )\", \r\n        action=\"store_true\" )\r\n        \r\n    parser.add_option('-k', '--cookies', \r\n        help=\"Cookies to use when requesting the GWT Javascript Files (ie. -c \\\"JSESSIONID=AAAAAA\\\")\", \r\n        action=\"store\")\r\n        \r\n    parser.add_option('-u', '--url', \r\n        help=\"Required: GWT Application Entrypoint Javascript File (ie. *.nocache.js )\", \r\n        action=\"store\")\r\n    \r\n    (options, args) = parser.parse_args()\r\n        \r\n    if options.url is None:\r\n        print( \"\\nMissing URL\\n\" )\r\n        parser.print_help()\r\n        exit()\r\n            \r\n    url = options.url\r\n    gwt_docroot = '/'.join(url.split('/')[:-1])+'/'\t\r\n            \r\n    req = urllib2.Request(url)\r\n    \r\n    handlers = [ urllib2.HTTPHandler() ]\r\n    \r\n    if url.startswith( \"https://\" ):\r\n        try:\r\n            import ssl\r\n        except ImportError:\r\n            print \"SSL support not installed - exiting\"\r\n            exit()\r\n            \r\n        handlers.append( urllib2.HTTPSHandler() )\r\n    \r\n    if options.proxy:\r\n        handlers.append( urllib2.ProxyHandler( {'http':'http://'+options.proxy}) )\r\n        \r\n    opener = urllib2.build_opener(*handlers)\r\n    urllib2.install_opener( opener )\r\n    \r\n    if options.basicauth:\r\n        username = raw_input( \"Basic Auth Username: \" )\r\n        password = getpass.getpass( \"Basic Auth Password: \" )\r\n        basic_auth_encoded = base64.encodestring( '%s:%s' % (username, password) ).strip()\r\n        req.add_header( \"Authorization\", \"Basic %s\" % basic_auth_encoded )\r\n    \r\n    if options.cookies:\r\n        req.add_header( \"Cookie\", options.cookies )\r\n        \r\n    response = urllib2.urlopen(req)\r\n    the_page = response.read()\r\n    \r\n    html_files = re.findall( \"([A-Z0-9]{30,35})\", the_page )\r\n    if html_files is None:\r\n        print( \"\\nNo Cached HTML Files found\\n\" )\r\n        exit()\r\n        \r\n    all_rpc_files = []\r\n    how_many_html_files_to_read = 1\r\n    \r\n    for html_file in html_files:\r\n        if how_many_html_files_to_read == 0:\r\n           break\r\n        how_many_html_files_to_read -= 1\r\n\r\n        async_error_mess = \"\"\r\n        invoke_method = \"\"\r\n        cache_html = \"%s%s.cache.html\" % (gwt_docroot, html_file )\r\n        print( \"Analyzing %s\" % cache_html )\r\n        \r\n        req = urllib2.Request( cache_html )\r\n        \r\n        if options.cookies:\r\n            req.add_header( \"Cookie\", options.cookies )\r\n            \r\n        if options.basicauth:\r\n            req.add_header( \"Authorization\", \"Basic %s\" % basic_auth_encoded )\r\n                \r\n        try:       \r\n            response = urllib2.urlopen(req)     \r\n        except urllib2.HTTPError:\r\n            print( \"404: Failed to Retrieve %s\" % cache_html )\r\n            continue\r\n            \r\n        the_page = response.readlines()\r\n \r\n        for line in the_page:\r\n        \r\n            # Service and Method name Enumeration\r\n            rpc_method_match = re.match( \"^function \\w+\\(.*method\\:([A-Za-z0-9_\\$]+),.*$\", line )\r\n            \r\n            if rpc_method_match:\r\n                if rpc_method_match.group(1) == \"a\":\r\n                    continue\r\n                  \r\n                rpc_js_function = rpc_method_match.group(0).split(';')\r\n                service_and_method = \"\"\r\n                \r\n                method_name = get_global_val( rpc_method_match.group(1), the_page )\r\n                if method_name is None:\r\n                    continue\r\n                    \r\n                methods.append(  \"%s( \" % method_name.replace( '_Proxy.', '.' ) )\r\n                \r\n                # Parameter Enumeration\r\n                for i in range(0, len(rpc_js_function)):\r\n                    try_match = re.match( \"^try{.*$\", rpc_js_function[i] )\r\n                    if try_match:\r\n                        i += 1\r\n                        func_match = re.match( \"^([A-Za-z0-9_\\$]+)\\(.*\", rpc_js_function[i] )\r\n                        payload_function = \"\"\r\n                        if func_match:\r\n                            payload_function = func_match.group(1)\r\n                        \r\n                        i += 1\r\n                        param_match = re.match( \"^\"+re.escape(payload_function)+\r\n                            \"\\([A-Za-z0-9_\\$]+\\.[A-Za-z0-9_\\$]+,([A-Za-z0-9_\\$]+)\\)\", \r\n                            rpc_js_function[i] )\r\n                            \r\n                        num_of_params = 0\r\n                        if param_match:\r\n                            num_of_params = int(get_global_val( param_match.group(1), the_page ))\r\n                        \r\n                        for j in range( 0, num_of_params ):\r\n                            i += 1\r\n                            \r\n                            param_var_match = re.match( \"^\"+re.escape(payload_function)+\r\n                                \"\\([A-Za-z0-9_\\$]+\\.[A-Za-z0-9_\\$]+,[A-Za-z0-9_\\$]+\\+\"\r\n                                \"[A-Za-z0-9_\\$]+\\([A-Za-z0-9_\\$]+,([A-Za-z0-9_\\$]+)\\)\\)$\", \r\n                                rpc_js_function[i] )\r\n                                \r\n                            if param_var_match:\r\n                                param = get_global_val( param_var_match.group(1), the_page )\r\n                                methods[-1] = methods[-1]+param+\",\"\r\n                             \r\n                        a_method = methods[-1][:-1]\r\n                        methods[-1] = a_method + \" )\"\r\n                        break\r\n    \r\n    line_decor = \"\\n===========================\\n\"\r\n    print( \"\\n%sEnumerated Methods%s\" % ( line_decor, line_decor ) )\r\n    methods = sorted(list(set(methods))) #uniq\r\n        \r\n    for method in methods:\r\n        print( method )\r\n    \r\n    print( \"\\n\\n\" )\r\n"
  },
  {
    "path": "gwtfuzzer/README",
    "content": "\r\nGwtFuzzer v0.1\r\n\r\nThis is a proof of concept fuzzer which was presented at OWASP Appsec DC. This script\r\nutilizes the GDS Burp API and GWTParser class in order to filter out GWT RPC requests\r\nfrom a Burp Proxy log and programmatically fuzz user-input. The presentation can be\r\ndownloaded at http://www.owasp.org/images/7/77/Attacking_Google_Web_Toolkit.ppt.\r\n\r\nGDS Burp API can be downloaded here: http://mwielgoszewski.github.com/burpee/\r\n\r\n\r\nNOTE:\r\nBoth Libraries must be added to your PYTHONPATH.\r\n"
  },
  {
    "path": "gwtfuzzer/gwtfuzzer.py",
    "content": "# -*- coding: utf-8 -*-\r\n#!/usr/bin/env python\r\n\r\n\"\"\"\r\n\r\n    GwtFuzzer v0.1\r\n    Copyright (C) 2010 Ron Gutierrez\r\n\r\n    This program is free software: you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation, either version 3 of the License, or\r\n    (at your option) any later version.\r\n\r\n    This program is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n\r\n    You should have received a copy of the GNU General Public License\r\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r\n\r\n\"\"\"\r\n\r\nimport urllib2\r\nimport urllib\r\nimport os\r\nimport os.path\r\nimport time\r\nfrom GWTParser import GWTParser\r\nfrom gds.pub.burp import parse\r\nfrom itertools import product\r\nfrom optparse import OptionParser\r\n\r\n'''\r\n    Globals\r\n'''\r\n\r\nattacklog = []\r\nparam_manip_log = []\r\n\r\n\r\ndef replay( burpobj, fuzzified, attackstr, gwtparsed, log ):\r\n    global options\r\n    \r\n    headers = burpobj.get_request_headers()\r\n\r\n    if options.cookies is not None:   \r\n        headers[\"Cookie\"] = options.cookies\r\n        \r\n    headers[\"Content-Length\"] = str(len(fuzzified))\r\n    \r\n    req = urllib2.Request(burpobj.url.geturl(), fuzzified, headers )\r\n    \r\n    handlers = [ urllib2.HTTPHandler() ]\r\n\r\n    if options.proxy is not None:    \r\n        handlers.append( urllib2.ProxyHandler( {'http':options.proxy} ) )\r\n        \r\n    opener = urllib2.build_opener(*handlers)\r\n    urllib2.install_opener( opener )\r\n\r\n    errors_found = []    \r\n    try:    \r\n        resp = urllib2.urlopen(req)\r\n        data = resp.read()\r\n        \r\n        # Check for error messages\r\n        errors_found = check_errors(data)\r\n\r\n        # Check to see if a exception message was returned        \r\n        if is_exception(data):\r\n            errors_found.append( \"GWT Exception Returned\" )\r\n        \r\n        # Success        \r\n        log.append( { 'method':gwtparsed.rpc_deserialized[2]+\".\"+gwtparsed.rpc_deserialized[3],\r\n            'request_url':burpobj.url.geturl(),\r\n                            'request_headers':headers,\r\n                           'request_payload':fuzzified,\r\n                           'attack': attackstr,\r\n                           'response_status':200,\r\n                           'response_content':data,\r\n                           'response_size':len(data),\r\n                           'errors_found':errors_found })\r\n        \r\n    except urllib2.HTTPError, e:\r\n        # Request did not return a 200 OK\r\n        log.append( {'method':gwtparsed.rpc_deserialized[2]+\".\"+gwtparsed.rpc_deserialized[3],\r\n            'request_url':burpobj.url.geturl(),\r\n                           'request_headers':headers,\r\n                           'request_payload':fuzzified,\r\n                           'attack': attackstr,\r\n                           'response_status':e.code,\r\n                           'response_content':e.read(),\r\n                           'response_size':len(e.read()),\r\n                           'errors_found':errors_found })\r\n    except urllib2.URLError, e:\r\n        # Host could not be reached\r\n        log.append( {'method':gwtparsed.rpc_deserialized[2]+\".\"+gwtparsed.rpc_deserialized[3],\r\n            'request_url':burpobj.url.geturl(),\r\n                           'request_headers':headers,\r\n                           'request_payload':fuzzified,\r\n                           'attack': attackstr,\r\n                           'response_status':'Python URLError Exception',\r\n                           'response_content':e.reason(),\r\n                           'response_size':0,\r\n                           'errors_found':errors_found})\r\n        \r\n        print( \"Request failed: \"+burpobj.url.geturl()+\"(\"+e.reason+\")\" )\r\n\r\n\r\ndef check_errors( data ):\r\n    global errors\r\n    found = []\r\n\r\n    for error in errors:\r\n        if data.find( error ) != -1:\r\n            found.append( error )\r\n            print( \"found \"+error )\r\n\r\n    return found\r\n\r\n\r\ndef is_exception( data ):\r\n    if data.find( \"//EX[\", 0, 8 ) != -1:\r\n        return True\r\n\r\n    return False\r\n\r\n\r\ndef escape( str ):\r\n    return str.replace( '<', '&lt;' ).replace( '>', '&gt;' ).replace( '\"', '&quot' ).replace( '\\'', '&#39;')\r\n\r\n\r\ndef filter_gwt_reqs( parsed ):\r\n    filtered = []\r\n    for burpobj in parsed:\r\n        headers = burpobj.get_request_headers()\r\n        if \"Content-Type\" in headers:\r\n            if headers[\"Content-Type\"].find(\"text/x-gwt-rpc\") != -1:\r\n                filtered.append( burpobj )\r\n            \r\n    return filtered    \r\n\r\n\r\ndef get_number_range( num ):\r\n    if num < options.idrange:\r\n        return 0, num+options.idrange\r\n\r\n    begin = int(num)-int(options.idrange)\r\n    end = int(num)+int(options.idrange) \r\n    return begin, end \r\n\r\n\r\ndef load_strings( list, filename ):\r\n    if os.path.exists( filename ):\r\n        f = open( filename, 'r' )\r\n\r\n        for line in f:\r\n            if line.find( \"# \", 0, 2 ) == -1: # Ignore FuzzDB comments\r\n                list.append( line.strip() )\r\n               \r\n        f.close()\r\n    else:\r\n        print( \"Error: \"+filename+\" does not exist\" )\r\n        exit()\r\n\r\n      \r\ndef fuzz( burpobj ):\r\n    global options, attacks, attacklog, param_manip_log\r\n\r\n    # Parse the gwt string\r\n    gwtparsed = GWTParser()\r\n    gwtparsed.deserialize( burpobj.get_request_body() )\r\n    \r\n    gwtlist = burpobj.get_request_body().split('|')\r\n\r\n    # This is where the magic happens.. Special Thanks to Marcin W.\r\n\r\n    # Test all GWT requests using the attack strings submitted\r\n    for( idx, param ), fuzzy in product( enumerate(gwtlist), attacks ):\r\n        # Check to see if index was marked as a fuzzible string value by GWTParse\r\n        if idx in gwtparsed.fuzzmarked and gwtparsed.fuzzmarked[idx] == \"%s\":\r\n            fuzzified = \"%s|%s|%s\" %('|'.join(gwtlist[:idx]), fuzzy.replace('|','\\!'), '|'.join(gwtlist[idx+1:]))\r\n            replay( burpobj, fuzzified, fuzzy, gwtparsed, attacklog ) # Submit the request\r\n\r\n    # Test all GWT request for Parameter Manipulation\r\n    for idx, param in enumerate( gwtlist ):\r\n        if idx in gwtparsed.fuzzmarked and gwtparsed.fuzzmarked[idx] == \"%d\":\r\n            begin, end = get_number_range( param )\r\n            for i in range( int(begin), int(end) ):\r\n                fuzzified = \"%s|%s|%s\" %('|'.join(gwtlist[:idx]), str(i), '|'.join(gwtlist[idx+1:]))\r\n                replay( burpobj, fuzzified, str(i), gwtparsed, param_manip_log ) #Submit the request\r\n            \r\n\r\ndef reportfuzz( logdir ):\r\n    global attacklog\r\n        \r\n    f = open( logdir+\"//gwtfuzz.html\", 'w' )\r\n\r\n    f.write( '''\r\n    <html>\r\n    <head>\r\n        <title>GWTFuzz Results</title>\r\n        <style type=\"text/css\">\r\n            td, th{\r\n                font-family: sans-serif;\r\n                font-size: 12px;\r\n                border: thin solid black;\r\n                word-wrap: break-word;\r\n                border-spacing: 0;\r\n                padding: 1px 1px 1px 1px;\r\n            }\r\n\r\n            tr.error{\r\n                background-color: #FFCC66;\r\n            }\r\n        </style>        \r\n    </head>\r\n    <body>\r\n    <h2>Fuzz Results</h2>\r\n    <table cellspacing=0 style=\"border: thin solid black;\">\r\n    <tr>\r\n        <td>ID</th>\r\n        <th>Endpoint URL</th>\r\n        <th>RPC Method</th>\r\n        <th>Attack</th>\r\n        <th>Request Data</th>\r\n        <th>Resp Status</th>\r\n        <th>Resp Size</th>\r\n        <th>Resp Content</th>\r\n        <th>Errors Found</th>\r\n    </tr>''' )\r\n    \r\n    for idx, entry in enumerate(attacklog):\r\n        if len(entry['errors_found']) > 0:\r\n            f.write( '<tr class=\"error\">' )\r\n        elif entry['response_status'] != 200:\r\n            f.write( '<tr class=\"error\">' )\r\n        else:\r\n            f.write( '<tr>' )\r\n            \r\n        f.write( '<td style=\"max-width:300px;text-align:right\">'+str(idx)+'</td>' +\r\n                 '<td style=\"max-width:300px;\">'+escape(entry['request_url'])+'</td>' +\r\n                 '<td style=\"max-wdth:300px;\">'+escape(entry['method'])+'</td>' +\r\n                 '<td style=\"max-width:300px;text-align:center\">'+escape(entry['attack'])+'</td>' +\r\n               '<td style=\"max-width:450px;\">'+escape(entry['request_payload'])+'</td>' +\r\n               '<td style=\"width=10px;max-width:10px;text-align:right\">'+str(entry['response_status'])+'</td>' +\r\n               '<td style=\"width=10px;max-width:10px;text-align:right\">'+str(entry['response_size'])+'</td>' +\r\n               '<td style=\"max-width:150px;\"><a href=\"responses/'+str(idx)+'.txt\" target=\"_new\">View Response</a></td>' +\r\n                '<td style=\"max-width:100px;\">' )\r\n\r\n        errorstr = \"\"\r\n        \r\n        for error in entry['errors_found']:\r\n            errorstr = errorstr + escape(error) + \", \"\r\n\r\n        errorstr = errorstr[:-2]\r\n\r\n        f.write( errorstr+'</td></tr>' )\r\n\r\n        # Write the HTTP response into a text file\r\n        f2 = open( logdir+'//responses/'+str(idx)+'.txt', 'w' ) \r\n        f2.write( entry['response_content'] )\r\n        f2.close()\r\n    \r\n    f.write( '</table></body></html>' )\r\n    f.close()\r\n\r\n    print( \"Results saved to \"+logdir )    \r\n\r\ndef reportparam( logdir ):\r\n    global param_manip_log\r\n    \r\n    f = open( logdir+\"//param_manip.html\", 'w' )\r\n\r\n    f.write( '''\r\n    <html>\r\n    <head>\r\n        <title>GWT Parameter Manipulation Results</title>\r\n        <style type=\"text/css\">\r\n            td, th{\r\n                font-family: sans-serif;\r\n                font-size: 12px;\r\n                border: thin solid black;\r\n                word-wrap: break-word;\r\n                border-spacing: 0;\r\n                padding: 1px 1px 1px 1px;\r\n            }\r\n            tr.status{\r\n                background-color: #FFCC66;\r\n            }\r\n\r\n            tr.error{\r\n                background-color: #FF3333;\r\n            }\r\n        </style>        \r\n    </head>\r\n    <body>\r\n    <h2>GWT Parameter Manipulation Results</h2>\r\n    <table cellspacing=0 style=\"border: thin solid black;\">\r\n    <tr>\r\n        <td>ID</th>\r\n        <th>Endpoint URL</th>\r\n        <th>RPC Method</th>\r\n        <th>Attack</th>\r\n        <th>Request Data</th>\r\n        <th>Resp Status</th>\r\n        <th>Resp Size</th>\r\n        <th>Resp Content</th>\r\n    </tr>''' )\r\n    \r\n    for idx, entry in enumerate(param_manip_log):     \r\n        f.write( '<tr><td style=\"max-width:300px;text-align:right\">'+str(idx)+'</td>' +\r\n                 '<td style=\"max-width:300px;\">'+escape(entry['request_url'])+'</td>' +\r\n                 '<td style=\"max-wdth:300px;\">'+escape(entry['method'])+'</td>' +\r\n                 '<td style=\"max-width:300px;text-align:center\">'+escape(entry['attack'])+'</td>' +\r\n               '<td style=\"max-width:450px;\">'+escape(entry['request_payload'])+'</td>' +\r\n               '<td style=\"width=10px;max-width:10px;text-align:right\">'+str(entry['response_status'])+'</td>' +\r\n               '<td style=\"width=10px;max-width:10px;text-align:right\">'+str(entry['response_size'])+'</td>' +\r\n               '<td style=\"max-width:150px;\"><a href=\"responses/p'+str(idx)+'.txt\" target=\"_new\">View Response</a></td></tr>' +\r\n                '<td style=\"max-width:100px;\">' )\r\n            \r\n        # Write the HTTP response into a text file\r\n        f2 = open( logdir+'//responses//p'+str(idx)+'.txt', 'w' ) \r\n        f2.write( entry['response_content'] )\r\n        f2.close()\r\n    \r\n    f.write( '</table></body></html>' )\r\n    f.close()    \r\n\r\n      \r\nif __name__ == \"__main__\":\r\n    global options, attacks, errors\r\n    attacks = []\r\n    errors = []\r\n    \r\n    parser = OptionParser( usage=\"usage: %prog [options]\",\r\n                           description='Automates the fuzzing of GWT RPC requests',\r\n                           version='%prog 0.10' )\r\n\r\n    parser.add_option('-b', '--burp',\r\n                      help='Burp logfile to fuzz',\r\n                      action='store' )\r\n\r\n    parser.add_option('-f', '--fuzzfile',\r\n                      help='File containing attack strings',\r\n                      action='store' )\r\n\r\n    parser.add_option('-e', '--errorfile',\r\n                      help='File containing error messages',\r\n                      action='store' )\r\n\r\n    parser.add_option('-o', '--output',\r\n                      help='Directory to store results',\r\n                      action='store' )\r\n\r\n    parser.add_option('-k', '--cookies',\r\n                      help='Cookies to use when requesting GWT RPC pages',\r\n                      action='store' )\r\n\r\n    parser.add_option('-p', '--proxy',\r\n                      help='Proxy Host and Port (e.g. -p \"http://proxy.internet.net:8080\"',\r\n                      action='store' )\r\n\r\n    parser.add_option('-i', '--idrange',\r\n                      help='Range of decrements and increments to test parameter manipulation with',\r\n                      action='store' )\r\n\r\n    (options, args) = parser.parse_args()\r\n\r\n    if options.burp is None:\r\n        print( \"\\nError: Missing Burp log file\\n\" )\r\n        parser.print_help()\r\n        exit()\r\n    elif options.fuzzfile is None:\r\n        print( \"\\nError: Missing fuzz file\\n\" )\r\n        parser.print_help()\r\n        exit()\r\n\r\n    if options.idrange and options.idrange < 1:\r\n        options.idrange = 100\r\n        print( \"Invalid ID Range Entered: ID Range has been set to 100\\n\" )\r\n    elif options.idrange is None:\r\n        options.idrange = 100\r\n        print( \"ID Range for Parameter Manipulation Testing has been set to 100\\n\" )\r\n        \r\n    parsed = None\r\n\r\n    # Parse the Burp log using the GDS Burp API    \r\n    if os.path.exists( options.burp ):\r\n        print( \"Parsing Burp logfile\" )\r\n        parsed = parse( options.burp )\r\n    else:\r\n        print( \"\\nBurp log file entered does not exist\\n\" )\r\n        exit()\r\n\r\n    logdir = \"\"\r\n    \r\n    if options.output and os.path.exists( options.output ):\r\n        print( \"Error: Output directory already exists.\" )\r\n        exit()\r\n    elif options.output:\r\n        logdir = options.output\r\n    else:\r\n        logdir = \"gwtfuzz_results\"+time.strftime(\"%Y%m%d%H%M%S\")\r\n\r\n    os.mkdir( logdir )\r\n    os.mkdir( logdir+\"//responses\" )\r\n\r\n    if options.fuzzfile:\r\n        load_strings(attacks, options.fuzzfile)\r\n\r\n    if options.errorfile:\r\n        load_strings(errors, options.errorfile)\r\n        \r\n    # Filter out the GWT RPC Requests from the log    \r\n    filtered = filter_gwt_reqs(parsed)\r\n\r\n    print( \"Fuzzing has commenced\" )    \r\n    # Fuzz each GWT RPC Request\r\n    for burpobj in filtered:\r\n        fuzz( burpobj )\r\n\r\n    # Generate Parameter Manipulation Report\r\n    reportparam( logdir )\r\n    \r\n    reportfuzz( logdir )\r\n    \r\n"
  },
  {
    "path": "gwtparse/GWTParser.py",
    "content": "# -*- coding: utf-8 -*-\r\n#!/usr/bin/env python\r\n\r\n\"\"\"\r\n\r\n    GwtParse v0.2\r\n    Copyright (C) 2010 Ron Gutierrez\r\n\r\n    This program is free software: you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation, either version 3 of the License, or\r\n    (at your option) any later version.\r\n\r\n    This program is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n\r\n    You should have received a copy of the GNU General Public License\r\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r\n\r\n\"\"\"\r\n\r\nimport sys\r\nimport re\r\nimport pprint\r\nfrom Parameter import Parameter\r\n\r\nreload(sys)\r\nsys.setdefaultencoding('utf-8')\r\n\r\n\r\n#################################################\r\n#\r\n#   Java Primitive Types and Object Wrappers\r\n#\r\n#################################################\r\n\r\nFUZZ_STRING = \"%s\"\r\nFUZZ_DIGIT = \"%d\"\r\n\r\nSTRING_OBJECT = \"java.lang.String\"\r\nINTEGER_OBJECT = \"java.lang.Integer\"\r\nDOUBLE_OBJECT = \"java.lang.Double\"\r\nFLOAT_OBJECT = \"java.lang.Float\"\r\nBYTE_OBJECT = \"java.lang.Byte\"\r\nBOOLEAN_OBJECT = \"java.lang.Boolean\"\r\nSHORT_OBJECT = \"java.lang.Short\"\r\nCHAR_OBJECT = \"java.lang.Char\"\r\nLONG_OBJECT = \"java.lang.Long\"\r\n\r\nPRIMITIVES_WRAPPER = [ STRING_OBJECT, INTEGER_OBJECT, DOUBLE_OBJECT, FLOAT_OBJECT, BYTE_OBJECT, BOOLEAN_OBJECT, SHORT_OBJECT, CHAR_OBJECT ]\r\n\r\nLONG = \"J\"\r\nDOUBLE = \"D\"\r\nFLOAT = \"F\"\r\nINT = \"I\"\r\nBYTE = \"B\"\r\nSHORT = \"S\"\r\nBOOLEAN = \"Z\"\r\nCHAR = \"C\"\r\n\r\nPRIMITIVES = [ \"J\", \"D\", \"F\", \"I\", \"B\", \"S\", \"Z\", \"C\" ] \r\nNUMERICS = [ INT, CHAR, BOOLEAN, BYTE, SHORT, INTEGER_OBJECT, CHAR_OBJECT, BYTE_OBJECT, BOOLEAN_OBJECT, SHORT_OBJECT ]\r\n\r\nARRAYLIST = \"java.util.ArrayList\"\r\nLINKEDLIST = \"java.util.LinkedList\"\r\nVECTOR = \"java.util.Vector\"\r\n\r\nListTypes = [ ARRAYLIST, LINKEDLIST, VECTOR ]\r\n\r\nprev_index = 0\r\nINDENTATION = 15\r\n\r\n# Indicates that obfuscated type names should be used in the RPC payload.\r\nFLAG_ELIDE_TYPE_NAMES = 0x1\r\n\r\n# Indicates that RPC token is included in the RPC payload.\r\nFLAG_RPC_TOKEN_INCLUDED = 0x2\r\n\r\n# Bit mask representing all valid flags.\r\nVALID_FLAGS_MASK = 0x3\r\n\r\nclass GWTParser(object):\r\n    \r\n    def _cleanup(self):\r\n        self.data = []\r\n        self.rpc_deserialized = []\r\n        self.rpc_list = []\r\n        self.indices = []\r\n        self.data_types = []\r\n        self.parameters = []\r\n        self.stream_version = 0\r\n        self.flags = 0\r\n        self.columns = 0\r\n        self.parameter_idx = 0\r\n        self.fuzzmarked = dict()\r\n        \r\n    '''\r\n    Sets a value as fuzzable\r\n    '''\r\n    def _set_fuzzable( self, idx, fuzz_value ):\r\n        fuzz_idx = int(idx)+2\r\n        \r\n        if self.surround_value:\r\n            if not fuzz_idx in self.fuzzmarked:\r\n                self.rpc_list_fuzzable[fuzz_idx] = self.surround_value+self.rpc_list_fuzzable[fuzz_idx]+self.surround_value\r\n        \r\n        elif self.replace_value:\r\n            self.rpc_list_fuzzable[fuzz_idx] = self.replace_value\r\n        \r\n        elif self.burp:\r\n            if not fuzz_idx in self.fuzzmarked:\r\n                self.rpc_list_fuzzable[fuzz_idx] = '§'+self.rpc_list_fuzzable[fuzz_idx]+'§'\r\n                \r\n        else:\r\n            self.rpc_list_fuzzable[fuzz_idx] = fuzz_value\r\n        \r\n        self.fuzzmarked[fuzz_idx] = fuzz_value\r\n        \r\n            \r\n    '''\r\n    Check if the next index value is an integer or a data index\r\n    '''\r\n    def _nextval_is_an_integer(self, curr_index):\r\n        if len(self.indices) == 0:\r\n            return False\r\n        \r\n        # If the index is out of data array scope\r\n        if int(self.indices[0]) <= 4 or int(self.indices[0]) > self.columns:\r\n            return True\r\n            \r\n        # If next index is the increment of the previous    \r\n        if int(curr_index) == int(self.indices[0])-1:\r\n            if len(self.indices) > 1:\r\n                if int(self.indices[0]) == int(self.indices[1]):\r\n                        return True\r\n        \r\n        return False\r\n        \r\n\r\n    '''\r\n    Put back the index that was pop previously\r\n    '''\r\n    def _indice_rollback(self, prev_index):\r\n        self.indices_read -= 1\r\n        self.indices.insert(0,prev_index)\r\n        \r\n        \r\n    ''' \r\n    Is the next value an object type name\r\n    '''\r\n    def _is_an_object(self, value):\r\n        obj_check = re.compile( \".*/\\d+\" )\r\n        match = obj_check.match(value)\r\n        \r\n        if match:\r\n            return True\r\n\r\n        return False\r\n\r\n    ''' \r\n    Check to see if the type is an array of primitives\r\n    '''\r\n    def _is_a_primitive_array(self, data_type):\r\n        arr_check = re.compile( \"\\[(\\w)/\\d+\" )\r\n        match = arr_check.match(data_type)\r\n        \r\n        if match and match.group(1) in PRIMITIVES:\r\n            self.parameters[self.pidx].typename = match.group(1)\r\n            self.parameters[self.pidx].is_array = True\r\n            return True\r\n            \r\n        return False\r\n    \r\n    '''\r\n    Is the passed in value the subtype value for the current parameters typename\r\n    '''\r\n    def _is_object_subtype( self, data_type ):\r\n        typename = \"\"\r\n        \r\n        if self.parameters[self.pidx].is_list:\r\n            typename = self.parameters[self.pidx].subtype\r\n        else:\r\n            typename = self.parameters[self.pidx].typename\r\n    \r\n        subtype_check = re.compile( typename+\"/\\d+\" )\r\n        match = subtype_check.match( data_type )\r\n\r\n        if match:\r\n            return True\r\n        \r\n        return False\r\n    \r\n    '''\r\n    Check to see if the type is array of objects\r\n    '''\r\n    def _is_an_object_array( self, data_type):\r\n        arr_check = re.compile( \"\\[L(.*);/\\d+\" )\r\n        match = arr_check.match(data_type)\r\n\r\n        if match:\r\n            self.parameters[self.pidx].typename = match.group(1)\r\n            self.parameters[self.pidx].is_array = True\r\n            return True\r\n            \r\n        return False\r\n        \r\n    '''\r\n    Check to see if data_type is a ListType\r\n    '''\r\n    def _is_list_type( self, data_type ):\r\n        if self._get_typename(data_type) in ListTypes:\r\n            return True\r\n            \r\n        return False\r\n        \r\n    \r\n    '''\r\n    Check to see if the index passed in is an integer value\r\n        - This check needs some major work\r\n    '''\r\n    def _indice_is_intval( self, idx ):\r\n        # If the index is out of data array scope\r\n        if int(self.indices[int(idx)]) <= 4 or int(self.indices[int(idx)]) > self.columns:\r\n            return True\r\n        \r\n        return False\r\n                    \r\n    \r\n    '''\r\n    Check to see if the remaining method parameters are all numeric\r\n    '''\r\n    def _remaining_params_are_numeric( self, tracker_idx):      \r\n        for i in range( self.pidx+1, len(self.parameters)):\r\n            if not self._get_typename(self.parameters[i].typename) in NUMERICS:\r\n                return False\r\n                \r\n        return True\r\n        \r\n    \r\n    '''\r\n    Checks to see whether we should stop reading values into a custom object\r\n    '''\r\n    def _is_end_of_object( self, prev_index, value ):   \r\n        tracker_idx = 0\r\n        found = False\r\n        \r\n        if self._remaining_params_are_numeric( tracker_idx ):\r\n\r\n            if len(self.indices) == len(self.parameters[self.pidx+1:]):\r\n                prev_index = self.indices[0]\r\n                self._add_stringval(value)\r\n                return True\r\n            else:\r\n                return False\r\n                \r\n        if len(self.parameters[self.pidx+1:]) == len(self.indices):\r\n            prev_index = self.indices[0]\r\n            self._add_stringval(value)\r\n            return True\r\n            \r\n        for i in range( self.pidx+1, len( self.parameters ) ):\r\n                    \r\n            # Look Into the Future and see if the parameter values are still there\r\n            for j in range( tracker_idx, len(self.indices) ):\r\n                \r\n                if self._get_typename(self.parameters[i].typename) in NUMERICS: \r\n                    \r\n                    if self._indice_is_intval(j):\r\n                        found = True\r\n                        tracker_idx = j\r\n                        continue\r\n                    \r\n                elif self._get_typename(self.parameters[i].typename) == STRING_OBJECT:\r\n                    \r\n                    # If the index is out of data array scope\r\n                    if int(self.indices[j]) <= 4 or int(self.indices[j]) > self.columns:\r\n                        continue\r\n                                            \r\n                    if self._is_an_object( self.data[int(self.indices[j])] ) is False:\r\n                        found = True\r\n                        tracker_idx = j\r\n                        break\r\n                    \r\n                else:\r\n                    # If the index is out of data array scope\r\n                    if int(self.indices[j]) <= 4 or int(self.indices[j]) > self.columns:\r\n                        continue\r\n                        \r\n                    # This must be a custom object. Check for the subtype..\r\n                    if self._get_typename(self.data[int(self.indices[j])]) == self._get_typename(self.parameters[i].typename):\r\n                        found = True\r\n                        tracker_idx = j\r\n                        break\r\n                        \r\n            if not found:\r\n                return True # Did not find the next parameter so the current value is the next method param\r\n            else:\r\n                found = False \r\n                \r\n        return False\r\n            \r\n    '''\r\n    Removes the \"/\" and digits from a typename\r\n    '''\r\n    def _get_typename( self, data_type):\r\n        subtype_check = re.compile( \"(.*)/\\d+\" )\r\n        match = subtype_check.match(data_type)\r\n        \r\n        if match:\r\n            return match.group(1)\r\n        \r\n        return data_type\r\n    \r\n    \r\n    '''\r\n    Get the next index or integer value\r\n    '''\r\n    def _pop_index(self):\r\n        try:\r\n            self.indices_read += 1\r\n            index = int(self.indices.pop(0))\r\n        except TypeError:\r\n            print (\"Invalid Integer given for indices\")\r\n            sys.exit()\r\n    \r\n        return index\r\n    \r\n    \r\n    '''\r\n    Get the next float value\r\n    '''\r\n    def _pop_float_index(self):\r\n        try:\r\n            self.indices_read += 1\r\n            index = float(self.indices.pop(0))\r\n        except TypeError:\r\n            print (\"Invalid float value read\")\r\n            sys.exit()\r\n    \r\n        return index\r\n    \r\n    \r\n    '''\r\n    Pop the next index value and then return the corresponding value\r\n    from the data table\r\n    '''\r\n    def _get_nextval(self):\r\n        return self.data[self._pop_index()]\r\n    \r\n    \r\n    def _add_intval(self):\r\n        if self.parameters[self.pidx].flag:\r\n            self.parameters[self.pidx].values[self.aidx].values.append(self._pop_index())\r\n        elif self.parameters[self.pidx].is_list and self.parameters[self.pidx].is_custom_obj:\r\n            self.parameters[self.pidx].values[self.lidx].values.append(self._pop_index())\r\n        else:\r\n            self.parameters[self.pidx].values.append(self._pop_index())\r\n    \r\n    \r\n    def _add_stringval(self, value):\r\n        if self.parameters[self.pidx].flag:\r\n            self.parameters[self.pidx].values[self.aidx].values.append(value)\r\n        elif self.parameters[self.pidx].is_list and self.parameters[self.pidx].is_custom_obj:\r\n            self.parameters[self.pidx].values[self.lidx].values.append(value)\r\n        else:\r\n            self.parameters[self.pidx].values.append(value)\r\n        \r\n        \r\n    ###################################\r\n    #\r\n    # Parsing Methods\r\n    #\r\n    ####################################\r\n    \r\n    def _parse_read_string(self):\r\n        self._set_fuzzable( self.indices[0], FUZZ_STRING )\r\n        \r\n        if self.parameters[self.pidx].flag:\r\n            self.parameters[self.pidx].values[self.aidx].append(self._get_nextval())\r\n        else:\r\n            if self.parameters[self.pidx].is_list:\r\n                subtype = self._get_nextval()\r\n                \r\n            self.parameters[self.pidx].values.append(self._get_nextval())\r\n    \r\n    \r\n    def _parse_read_int_byte_short_char(self, is_wrapper=False):\r\n        if is_wrapper:\r\n            subtype = self._get_nextval()\r\n        \r\n        self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )\r\n        \r\n        if self.parameters[self.pidx].flag:\r\n            self.parameters[self.pidx].values[self.aidx].values.append(self._pop_index())\r\n        else:\r\n            self.parameters[self.pidx].values.append(self._pop_index())\r\n        \r\n        \r\n    def _parse_read_long(self, is_wrapper=False):\r\n        if is_wrapper:\r\n            subtype = self._get_nextval()\r\n            \r\n        value1 = self._pop_float_index()\r\n        value2 = self._pop_float_index()\r\n        \r\n        self._set_fuzzable( self.indices_read+self.columns-2, FUZZ_DIGIT )\r\n        self._set_fuzzable( self.indices_read+self.columns-1, FUZZ_DIGIT )\r\n        \r\n        \r\n        if value2 > 0:  \r\n            self.parameters[self.pidx].values.append( str(value1) + str(value2) )\r\n        else:\r\n            self.parameters[self.pidx].values.append( str(value1) )\r\n    \r\n    \r\n    def _parse_read_double_float(self, is_wrapper=False):\r\n        if is_wrapper:\r\n            subtype = self._get_nextval()\r\n            \r\n        self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )\r\n        self.parameters[self.pidx].values.append(self._pop_float_index())\r\n    \r\n    def _parse_primitive_array(self):\r\n        subtype = self._get_nextval()\r\n        how_many = self._pop_index()\r\n        \r\n        for i in range(how_many):\r\n            self._parse_value( self.parameters[self.pidx].typename )\r\n        \r\n    def _parse_object_array(self):\r\n        if self.parameters[self.pidx].flag is False:\r\n            subtype = self._get_nextval()\r\n            \r\n        how_many = self._pop_index()\r\n        \r\n        self.aidx = 0 \r\n        for i in range(how_many):\r\n            self._parse_value(self.parameters[self.pidx].typename )\r\n            self.aidx += 1\r\n        \r\n    def _parse_read_boolean(self):      \r\n        self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )\r\n        int_value = self._pop_index()\r\n            \r\n        if int_value == 1:\r\n            self.parameters[self.pidx].values.append( \"true\" )\r\n        else:\r\n            self.parameters[self.pidx].values.append( \"false\" )\r\n                \r\n    def _parse_read_list(self, list_type, set_list_flag=True):\r\n        if self.parameters[self.pidx].flag is False:\r\n            \r\n            if set_list_flag:\r\n                self.parameters[self.pidx].is_list = True\r\n                \r\n            self.parameters[self.pidx].typename = list_type\r\n            subtype = self._get_nextval()\r\n            \r\n        else:\r\n            self.parameters[self.pidx].values[self.aidx].typename = list_type\r\n            \r\n        how_many = self._pop_index()\r\n        self.lidx = 0\r\n        \r\n        for i in range(how_many):\r\n        \r\n            prev_index = self.indices[0]\r\n            \r\n            if self.parameters[self.pidx].flag: # Reading a List within a Custom Object\r\n                subtype = self._get_typename(self._get_nextval())\r\n                self.parameters[self.pidx].values[self.aidx].subtype = subtype\r\n                self._indice_rollback(prev_index)\r\n                self._parse_value(subtype)\r\n                \r\n            else: # Read values within a List Method Parameter\r\n                self.parameters[self.pidx].subtype = self._get_typename(self._get_nextval())\r\n                self._indice_rollback(prev_index)\r\n                self._parse_value(self.parameters[self.pidx].subtype)\r\n    \r\n            self.lidx += 1\r\n    \r\n    \r\n    def _parse_read_object(self,name):\r\n        \r\n        if len( self.indices ) > 0:\r\n            prev_index = self.indices[0]\r\n        \r\n        self.parameters[self.pidx].is_custom_obj = True\r\n        value = self._get_nextval()\r\n        \r\n        if self.parameters[self.pidx].is_array and self.parameters[self.pidx].is_custom_obj:\r\n            customParam = Parameter( value )\r\n            customParam.is_custom_obj = True\r\n            self.parameters[self.pidx].values.append( customParam )\r\n            self.parameters[self.pidx].flag = True\r\n        \r\n        if self.parameters[self.pidx].is_list and self.parameters[self.pidx].is_custom_obj:\r\n            customParam = Parameter(value)\r\n            customParam.is_custom_obj = True\r\n            self.parameters[self.pidx].values.append(customParam)\r\n        \r\n        # If this is the final parameter just read the remaining data as member variables\r\n        if len(self.parameters)-1 == self.pidx:\r\n\r\n            while len(self.indices) > 0: # Read till the end of the index table\r\n                \r\n                if self._nextval_is_an_integer( prev_index ):\r\n\r\n                    prev_index = self.indices[0]\r\n                    self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )\r\n                    self._add_intval()\r\n                    continue\r\n                    \r\n                else:\r\n                    prev_index = self.indices[0]\r\n                    value = self._get_nextval()\r\n                    \r\n                if self.parameters[self.pidx].is_array or self.parameters[self.pidx].is_list: # Am I reading an array of objects?   \r\n                \r\n                    if self._is_object_subtype(value): # Did I just read in an object subtype?\r\n                        self._indice_rollback(prev_index)\r\n                        break # Stop reading object and move onto the next object in the array\r\n                    \r\n                if self._is_list_type( value ):\r\n                \r\n                    if self.parameters[self.pidx].flag is False:\r\n                        self._indice_rollback(prev_index)\r\n                        \r\n                    self._parse_read_list(self._get_typename(value), False)\r\n                    \r\n                elif self._is_an_object(value): # Is the value I just read in a subtype for another class\r\n                    prev_index = self.indices[0]\r\n\r\n                else:\r\n                    self._add_stringval(value)\r\n                    self._set_fuzzable( prev_index, FUZZ_STRING )\r\n                    \r\n        else: # There are more parameters so we must be careful with the parsing\r\n\r\n            while len(self.indices) > 0: \r\n            \r\n                if self._nextval_is_an_integer( prev_index ):\r\n                    self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )\r\n                    prev_index = self.indices[0]\r\n                    self._add_intval()\r\n                else:\r\n                    prev_index = self.indices[0]\r\n                    value = self._get_nextval()\r\n                            \r\n                    if self.parameters[self.pidx].is_array or self.parameters[self.pidx].is_list:\r\n                    \r\n                        if self._is_object_subtype(value):\r\n                            self._indice_rollback(prev_index)\r\n                            break;\r\n                \r\n                    if self._is_end_of_object( prev_index, value ):\r\n\r\n                        if self.parameters[self.pidx].is_list or self.parameters[self.pidx].is_array:\r\n                            self.pidx += 1\r\n                            self._indice_rollback(prev_index)\r\n                            self._parse_value( self.parameters[self.pidx].typename )\r\n                            \r\n                        else:\r\n                            if not self._get_typename(self.parameters[self.pidx+1].typename) in NUMERICS:\r\n                                self._indice_rollback(prev_index)\r\n                            break\r\n                            \r\n                    elif self._is_an_object( value ):\r\n                        continue\r\n                        \r\n                    else: # store value\r\n                        self._set_fuzzable( prev_index, FUZZ_STRING )\r\n                        prev_index = self.indices[0]\r\n                        self._add_stringval(value)                      \r\n            \r\n        self.parameters[self.pidx].flag = False\r\n                            \r\n    '''\r\n    Split the object into a list and remove the last element\r\n    The RPC String ends with a '|' so this will create an empty element\r\n    '''\r\n    def _read_string_into_list(self):\r\n        # This copy is used to keep track of fuzzable values\r\n        self.rpc_list_fuzzable = list(self.rpc_string.split('|'))\r\n        self.rpc_list_fuzzable.pop()\r\n        \r\n        # This copy is used to parsing and will have values removed during parsing\r\n        self.rpc_list = self.rpc_string.split('|')\r\n        self.rpc_list.pop()\r\n    \r\n    \r\n    '''\r\n    Store and remove the first three elements of the list\r\n    '''\r\n    def _get_headers(self):\r\n        try:\r\n            self.stream_version = int(self.rpc_list.pop(0))\r\n            self.flags = int(self.rpc_list.pop(0))\r\n            self.columns = int(self.rpc_list.pop(0))\r\n        except TypeError:\r\n            print (\"Invalid Integer given for the stream_version or number of columns\")\r\n            sys.exit()\r\n    \r\n    \r\n    '''\r\n    Store the data inside of the serialized object\r\n    I add in an empty string in the 0 Element in order to\r\n    stay uniform with the indices table in the RPC Object\r\n    '''\r\n    def _get_data(self):\r\n        self.data = self.rpc_list[0:self.columns]\r\n        self.data.insert(0,\"\")\r\n    \r\n    \r\n    '''\r\n    Store the indices that are found at the end of the RPC serialized object\r\n    '''\r\n    def _get_indices(self):\r\n        self.indices = self.rpc_list[self.columns:]\r\n    \r\n    \r\n    '''\r\n    Parses a value from the string table\r\n    '''\r\n    def _parse_value(self, data_type):\r\n        \r\n        if self._get_typename(data_type) == STRING_OBJECT:\r\n            self._parse_read_string()\r\n                    \r\n        elif self._get_typename(data_type) == INT or data_type == BYTE or data_type == SHORT or data_type == CHAR:\r\n            self._parse_read_int_byte_short_char()\r\n            \r\n        elif self._get_typename(data_type) == INTEGER_OBJECT or data_type == BYTE_OBJECT or data_type == SHORT_OBJECT or data_type == CHAR_OBJECT:\r\n            self._parse_read_int_byte_short_char(True)\r\n            \r\n        elif self._get_typename(data_type) == LONG:\r\n            self._parse_read_long()\r\n            \r\n        elif self._get_typename(data_type) == LONG_OBJECT:\r\n            self._parse_read_long(True)\r\n            \r\n        elif self._get_typename(data_type) == DOUBLE or data_type == FLOAT:\r\n            self._parse_read_double_float()\r\n        \r\n        elif self._get_typename(data_type) == DOUBLE_OBJECT or data_type == FLOAT_OBJECT:\r\n            self._parse_read_double_float(True)\r\n            \r\n        elif self._is_a_primitive_array(data_type):\r\n            self._parse_primitive_array()\r\n            \r\n        elif self._is_an_object_array(data_type):\r\n            self._parse_object_array()\r\n\r\n        elif self._get_typename(data_type) == BOOLEAN:\r\n            self._parse_read_boolean()\r\n        \r\n        elif self._is_list_type(data_type):\r\n            self._parse_read_list(data_type)\r\n            \r\n        else:\r\n            self._parse_read_object(data_type)\r\n            \r\n    '''\r\n    Parses the GWT-RPC Request Payload\r\n    '''\r\n    def _parse(self):\r\n        self.rpc_deserialized = []\r\n        self.parameters = [] # Stores Parameter names and values read in from the request\r\n        self.pidx = 0 # Index value used to know which Parameter we are currently writing into\r\n        self.indices_read = 1 # Keeps track how many indices we have read\r\n        \r\n        '''\r\n        Store the first four values\r\n        Hostname, Hash, Class Name, Method\r\n        '''\r\n        # rpc request has an rpc/xsrf token\r\n        if (self.flags & FLAG_RPC_TOKEN_INCLUDED):\r\n            self.rpc_deserialized.append(self._get_nextval()) # hostname\r\n            self.rpc_deserialized.append(self._get_nextval()) # strong name\r\n            \r\n            self.rpc_token = {}\r\n            self.rpc_token[self._get_nextval()] = self._get_nextval()\r\n            \r\n            self.rpc_deserialized.append(self._get_nextval()) # interface\r\n            self.rpc_deserialized.append(self._get_nextval()) # method\r\n        \r\n        else:\r\n            for i in range(4):\r\n                self.rpc_deserialized.append(self._get_nextval())\r\n        \r\n        for index in self.indices:\r\n            num_of_params = self._pop_index() # Number of Method parameters\r\n            \r\n            for i in range(num_of_params):\r\n                self.parameters.append( Parameter(self._get_nextval()) )\r\n                \r\n            for param in self.parameters:\r\n                if num_of_params > self.pidx: # If parameter index is greater than number of params then we are done\r\n                    self._parse_value(param.typename)\r\n                    self.pidx += 1\r\n    \r\n    \r\n    '''\r\n    Handles the parsing of the RPC string\r\n    '''\r\n    def deserialize(self, rpc_string):\r\n        self._cleanup()\r\n        self.rpc_string = rpc_string\r\n        self._read_string_into_list()\r\n        self._get_headers()\r\n        self._get_data()\r\n        self._get_indices()\r\n        try:\r\n            self._parse()\r\n        except IndexError:\r\n            print( \"Encountered Error During Parsing\" )\r\n    \r\n    def get_fuzzstr(self):\r\n        fuzzstr = \"|\".join( self.rpc_list_fuzzable )+\"|\"\r\n        \r\n        if self.fout:   \r\n            self.fout.write( fuzzstr+\"\\n\" )\r\n            \r\n        else:\r\n            print( \"\\nGWT RPC Payload Fuzz String\\n\" )\r\n            print( fuzzstr+\"\\n\" )\r\n        \r\n    '''\r\n    Prints out the deserialized method call in a user friendly format\r\n    '''\r\n    def display(self):\r\n    \r\n        if self.fout:\r\n            self.fout.write(\"==================================\\n\")\r\n            self.fout.write(str(\"Serialized Object:\").rjust(INDENTATION) + \"\\n\" + self.rpc_string + \"\\n\\n\")\r\n            self.fout.write(str(\"Stream Version:\").rjust(INDENTATION) + \"\\t\" + str(self.stream_version)+\"\\n\")\r\n            self.fout.write(str(\"Flags:\").rjust(INDENTATION) + \"\\t\" + str(self.flags+\"\\n\"))\r\n            self.fout.write(str(\"Column Numbers:\").rjust(INDENTATION) + \"\\t\" + str(self.columns)+\"\\n\")\r\n            self.fout.write(str(\"Host:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[0]+\"\\n\")\r\n            self.fout.write(str(\"Hash:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[1]+\"\\n\")\r\n            self.fout.write(str(\"Class Name:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[2]+\"\\n\")\r\n            self.fout.write(str(\"Method:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[3] + \"\\n\")\r\n            self.fout.write(str(\"# of Params:\").rjust(INDENTATION) + \"\\t\" + str(len(self.parameters)) + \"\\n\")\r\n            self.fout.write(str(\"Parameters:\").rjust(INDENTATION)+\"\\n\")\r\n        else:   \r\n            print (str(\"\\nSerialized Object:\").rjust(INDENTATION) + \"\\n\" + self.rpc_string + \"\\n\")\r\n            print (str(\"Stream Version:\").rjust(INDENTATION) + \"\\t\" + str(self.stream_version))\r\n            print (str(\"Flags:\").rjust(INDENTATION) + \"\\t\" + str(self.flags))\r\n            print (str(\"Column Numbers:\").rjust(INDENTATION) + \"\\t\" + str(self.columns))\r\n            print (str(\"Host:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[0])\r\n            print (str(\"Hash:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[1])\r\n            print (str(\"Class Name:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[2])\r\n            print (str(\"Method:\").rjust(INDENTATION) + \"\\t\" + self.rpc_deserialized[3])\r\n            print (str(\"# of Params:\").rjust(INDENTATION) + \"\\t\" + str(len(self.parameters)) + \"\\n\")\r\n            print (str(\"Parameters:\").rjust(INDENTATION))\r\n        \r\n        for parameter in self.parameters:\r\n            if self.fout:\r\n                pprint.pprint(parameter.__dict__, stream=self.fout, indent=\"1\")\r\n            else:\r\n                pprint.pprint(parameter.__dict__, indent=\"1\")\r\n             \r\n        print( \"\\n\" )\r\n             \r\n        if self.fout:\r\n            self.fout.write( \"\\n\" )\r\n        else:\r\n            print (\"\\n\")\r\n            \r\n    def __init__( self ):\r\n        self.burp = False\r\n        self.surround_value = \"\"\r\n        self.replace_value = \"\"\r\n        self.fout = None\r\n"
  },
  {
    "path": "gwtparse/Parameter.py",
    "content": "#!/usr/bin/env python\r\n\r\nclass Parameter(object):\r\n    \r\n    def __init__(self, tn ):\r\n        self.typename = tn\r\n        self.values = []\r\n        self.flag = False\r\n        self.is_custom_obj = False\r\n        self.is_list = False\r\n        self.is_array = False\r\n        \r\n    def _add_value(self, val):\r\n        values.append( val )\r\n    \r\n    def _set_flag(self, flag_value ):\r\n        self.flag = flag_value\r\n\r\n    def __repr__(self):\r\n        return \"<Parameter %r>\" % self.__dict__ "
  },
  {
    "path": "gwtparse/README",
    "content": "GwtParse v0.2\r\n\r\nFor a detailed explaination of the tool see http://www.gdssecurity.com/l/b/2010/05/03/fuzzing-gwt-rpc-requests/\r\n\r\nAny issues encountered when using the tool should be sent to rgutierrez@gdssecurity.com\r\n\r\nFiles Included:\r\n\r\n\tgwtparse.py: Command Line interface to parse GWT RPC request and identify all fuzzable values\r\n\tGWTParse.py: Modules containing all parsing logic which can be used within your own python code\r\n\tParameter.py: Simple Python object which is used by GWTParse to store RPC method parameters\r\n\t\r\nImportant Note:\r\n\tWhen using the -b flag it is noted that the proper unicode symbol will only be properly display within\r\n\tterminals which allow unicode characters ( Linux, Cygwin rxvt ). When using on windows the the output\r\n\tcan be written to a text file using the \"-w\" or \"-a\" options in order to properly view the output.\r\n\t\r\n\t\r\n===============================\t\r\nCommand Line Usage Examples\r\n===============================\r\n\r\nIdentify all fuzzable values in the GWT RPC Request Payload using the default output format\r\n> python gwtparse.py -i \"<RPC STRING>\"\r\n\r\nSurrounds fuzzable value with the \"Section\" symbol for use within Burp Intruder\r\n> python gwtparse.py -i \"<RPC STRING>\" -b\r\n\r\nSurrounds fuzzable value with the user defined value for use within custom fuzzer\r\n> python gwtparse.py -i \"<RPC STRING>\" -s \"<fuzz me>\"\r\n\r\nReplaces fuzzable value with the user defined value for use within custom fuzzer\r\n> python gwtparse.py -i \"<RPC STRING>\" -r \"<fuzz me>\"\r\n\r\nParses RPC String and displays output in a pretty format in order to better understand the values passed\r\n> python gwtparse.py -i \"<RPC STRING>\" -p \r\n\r\nWrites output to file ( File must not already exist )\r\n> python gwtparse.py -i \"<RPC STRING>\" -w \"outfile.txt\"\r\n\r\nAppends output to file\r\n> python gwtparse.py -i \"<RPC STRING>\" -a \"outfile.txt\"\r\n\r\n\r\n\r\n================================\r\n\tGWTParse Module Usage\r\n================================\r\n\r\nThe GWTParser.py and Parameter.py files should be placed in a directory within your python path and imported into your python code\r\n\r\nThese must be performed in the following order.\r\n\r\nCreate Object\r\n\tgwt = GWTParser()\r\n\t\r\nSpecify surround, replace or burp output format ( If none specified the default format is used )\r\n\tgwt.surround_value = <STRING VALUE>\r\n\tor\r\n\tgwt.replace_value = <STRING VALUE>\r\n\tor\r\n\tgwt.burp = True\r\n\t\r\nSpecify file output handler ( If you want to write to output, default is standard output )\r\n\tgwt.fout = < FILE OUTPUT HANDLER >\r\n\t\r\nDeserialize the rpc request\r\n\tgwt.deserialize( <ADD RPC PAYLOAD HERE> )\r\n\r\nDisplay Pretty Output\r\n\tgwt.display()\r\n\t\r\nReturn fuzz string\r\n\tgwt.get_fuzzstr()\r\n\t\r\nData Access\r\n\tService Name:   gwt.rpc_deserialized[2]\r\n\tMethod Name:\tgwt.rpc_deserialize[3]\r\n\tMethod Parameters:\tgwt.parameters\r\n\r\n"
  },
  {
    "path": "gwtparse/__init__.py",
    "content": ""
  },
  {
    "path": "gwtparse/gwtparse.py",
    "content": "# -*- coding: utf-8 -*-\r\n#!/usr/bin/env python\r\n\r\n\"\"\"\r\n\r\n    GwtParse v0.2\r\n    Copyright (C) 2010 Ron Gutierrez\r\n\r\n    This program is free software: you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation, either version 3 of the License, or\r\n    (at your option) any later version.\r\n\r\n    This program is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n\r\n    You should have received a copy of the GNU General Public License\r\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r\n\r\n\"\"\"\r\n\r\nimport os\r\nfrom optparse import OptionParser\r\nfrom GWTParser import GWTParser\r\n\r\ndesc = \"A tool for parsing GWT RPC Requests\"\r\n\r\nif __name__ == \"__main__\":\r\n    parser = OptionParser(usage='usage: %prog [options]', description=desc, version='%prog 0.10')\r\n    \r\n    parser.add_option('-p', '--pretty', help=\"Output the GWT RPC Request in a human readible format\", action=\"store_true\")\r\n    parser.add_option('-s', '--surround', help=\"String used to surround all fuzzable values\", action=\"store\", dest=\"surround_value\")\r\n    parser.add_option('-r', '--replace', help=\"String used to replace all fuzzable values\", action=\"store\", dest=\"replace_value\")\r\n    parser.add_option('-b', '--burp', help=\"Generates Burp Intruder Output\", default=False, action=\"store_true\")\r\n    parser.add_option('-i', '--input', help=\"RPC Request Payload (Required)\", action=\"store\", dest=\"rpc_request\")\r\n    parser.add_option('-w', '--write', help=\"Writes Fuzz String to a new output file\", action=\"store\" )\r\n    parser.add_option('-a', '--append', help=\"Appends Fuzz String to an existing output file\", action=\"store\" )\r\n    \r\n    (options, args) = parser.parse_args()\r\n\r\n    if options.rpc_request:\r\n    \r\n        if options.surround_value and options.replace_value and options.burp:\r\n            print( \"\\nCannot choose more then one output format.\\n\" )\r\n            parser.print_help()\r\n            exit()\r\n        \r\n        if options.surround_value and options.replace_value:\r\n            print( \"\\nCannot choose more then one output format.\\n\" )\r\n            parser.print_help()\r\n            exit()\r\n            \r\n        if options.surround_value and options.burp:\r\n            print( \"\\nCannot choose more then one output format.\\n\" )\r\n            parser.print_help()\r\n            exit()\r\n            \r\n        if options.replace_value and options.burp:\r\n            print( \"\\nCannot choose more then one output format.\\n\" )\r\n            parser.print_help()\r\n            exit()\r\n            \r\n        gwt = GWTParser()\r\n        \r\n        if options.surround_value:\r\n            gwt.surround_value = options.surround_value\r\n        elif options.replace_value:\r\n            gwt.replace_value = options.replace_value\r\n        elif options.burp:\r\n            gwt.burp = options.burp\r\n        \r\n        \r\n        if options.write:\r\n            if os.path.exists(options.write):\r\n                print( \"Output file entered already exists\" )\r\n                exit()\r\n                \r\n            fout = open( options.write, \"w\" )\r\n            gwt.fout = fout\r\n            \r\n        elif options.append:\r\n            fout = open( options.append, \"a\" )\r\n            gwt.fout = fout\r\n        \r\n        gwt.deserialize( options.rpc_request )\r\n        \r\n        if options.pretty:\r\n            gwt.display()\r\n        \r\n        gwt.get_fuzzstr()\r\n        \r\n        if gwt.fout:\r\n            gwt.fout.close()\r\n        \r\n    else:\r\n        print( \"\\nMissing RPC Request Payload\\n\" )\r\n        parser.print_help()\r\n        \r\n    \r\n    \r\n"
  }
]