Repository: GDSSecurity/GWT-Penetration-Testing-Toolset
Branch: master
Commit: f27a5aa2b0f3
Files: 10
Total size: 57.3 KB
Directory structure:
gitextract__7fywc2q/
├── README
├── gwtenum/
│ ├── README
│ └── gwtenum.py
├── gwtfuzzer/
│ ├── README
│ └── gwtfuzzer.py
└── gwtparse/
├── GWTParser.py
├── Parameter.py
├── README
├── __init__.py
└── gwtparse.py
================================================
FILE CONTENTS
================================================
================================================
FILE: README
================================================
================================================
FILE: gwtenum/README
================================================
EXAMPLES
python gwt_rpc_enum.py -u "http://www.whirled.com/gwt/frame/frame.nocache.js"
python gwt_rpc_enum.py -u "https://adwords.google.com/billing/ui/accountcancel/account_cancel_widget.nocache.js"
================================================
FILE: gwtenum/gwtenum.py
================================================
#!/usr/bin/env python
'''
GwtEnum v0.2
Copyright (C) 2010 Ron Gutierrez
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 urllib2
import re
import pprint
import base64
import getpass
from optparse import OptionParser
desc = "A tool for enumerating GWT RPC methods"
methods = []
proxy_url = ""
basic_auth_encoded = ""
def get_global_val( varname, html_file ):
for html_line in html_file:
match = re.match( ".*," + re.escape(varname) +
"\=\'([A-Za-z0-9_\.\!\@\#\$\%\^\&\*\(\)"
"\-\+\=\:\;\"\|\\\\/\?\>\,\<\~\`]+)\',", html_line )
if not match is None:
return match.group(1)
if __name__ == "__main__":
parser = OptionParser( usage="usage: %prog [options]",
description=desc,
version='%prog 0.10' )
parser.add_option('-p', '--proxy',
help="Proxy Host and Port (ie. -p \"http://proxy.internet.net:8080\")",
action="store" )
parser.add_option('-b', '--basicauth',
help="User Basic Authentication ( Will be prompted for creds )",
action="store_true" )
parser.add_option('-k', '--cookies',
help="Cookies to use when requesting the GWT Javascript Files (ie. -c \"JSESSIONID=AAAAAA\")",
action="store")
parser.add_option('-u', '--url',
help="Required: GWT Application Entrypoint Javascript File (ie. *.nocache.js )",
action="store")
(options, args) = parser.parse_args()
if options.url is None:
print( "\nMissing URL\n" )
parser.print_help()
exit()
url = options.url
gwt_docroot = '/'.join(url.split('/')[:-1])+'/'
req = urllib2.Request(url)
handlers = [ urllib2.HTTPHandler() ]
if url.startswith( "https://" ):
try:
import ssl
except ImportError:
print "SSL support not installed - exiting"
exit()
handlers.append( urllib2.HTTPSHandler() )
if options.proxy:
handlers.append( urllib2.ProxyHandler( {'http':'http://'+options.proxy}) )
opener = urllib2.build_opener(*handlers)
urllib2.install_opener( opener )
if options.basicauth:
username = raw_input( "Basic Auth Username: " )
password = getpass.getpass( "Basic Auth Password: " )
basic_auth_encoded = base64.encodestring( '%s:%s' % (username, password) ).strip()
req.add_header( "Authorization", "Basic %s" % basic_auth_encoded )
if options.cookies:
req.add_header( "Cookie", options.cookies )
response = urllib2.urlopen(req)
the_page = response.read()
html_files = re.findall( "([A-Z0-9]{30,35})", the_page )
if html_files is None:
print( "\nNo Cached HTML Files found\n" )
exit()
all_rpc_files = []
how_many_html_files_to_read = 1
for html_file in html_files:
if how_many_html_files_to_read == 0:
break
how_many_html_files_to_read -= 1
async_error_mess = ""
invoke_method = ""
cache_html = "%s%s.cache.html" % (gwt_docroot, html_file )
print( "Analyzing %s" % cache_html )
req = urllib2.Request( cache_html )
if options.cookies:
req.add_header( "Cookie", options.cookies )
if options.basicauth:
req.add_header( "Authorization", "Basic %s" % basic_auth_encoded )
try:
response = urllib2.urlopen(req)
except urllib2.HTTPError:
print( "404: Failed to Retrieve %s" % cache_html )
continue
the_page = response.readlines()
for line in the_page:
# Service and Method name Enumeration
rpc_method_match = re.match( "^function \w+\(.*method\:([A-Za-z0-9_\$]+),.*$", line )
if rpc_method_match:
if rpc_method_match.group(1) == "a":
continue
rpc_js_function = rpc_method_match.group(0).split(';')
service_and_method = ""
method_name = get_global_val( rpc_method_match.group(1), the_page )
if method_name is None:
continue
methods.append( "%s( " % method_name.replace( '_Proxy.', '.' ) )
# Parameter Enumeration
for i in range(0, len(rpc_js_function)):
try_match = re.match( "^try{.*$", rpc_js_function[i] )
if try_match:
i += 1
func_match = re.match( "^([A-Za-z0-9_\$]+)\(.*", rpc_js_function[i] )
payload_function = ""
if func_match:
payload_function = func_match.group(1)
i += 1
param_match = re.match( "^"+re.escape(payload_function)+
"\([A-Za-z0-9_\$]+\.[A-Za-z0-9_\$]+,([A-Za-z0-9_\$]+)\)",
rpc_js_function[i] )
num_of_params = 0
if param_match:
num_of_params = int(get_global_val( param_match.group(1), the_page ))
for j in range( 0, num_of_params ):
i += 1
param_var_match = re.match( "^"+re.escape(payload_function)+
"\([A-Za-z0-9_\$]+\.[A-Za-z0-9_\$]+,[A-Za-z0-9_\$]+\+"
"[A-Za-z0-9_\$]+\([A-Za-z0-9_\$]+,([A-Za-z0-9_\$]+)\)\)$",
rpc_js_function[i] )
if param_var_match:
param = get_global_val( param_var_match.group(1), the_page )
methods[-1] = methods[-1]+param+","
a_method = methods[-1][:-1]
methods[-1] = a_method + " )"
break
line_decor = "\n===========================\n"
print( "\n%sEnumerated Methods%s" % ( line_decor, line_decor ) )
methods = sorted(list(set(methods))) #uniq
for method in methods:
print( method )
print( "\n\n" )
================================================
FILE: gwtfuzzer/README
================================================
GwtFuzzer v0.1
This is a proof of concept fuzzer which was presented at OWASP Appsec DC. This script
utilizes the GDS Burp API and GWTParser class in order to filter out GWT RPC requests
from a Burp Proxy log and programmatically fuzz user-input. The presentation can be
downloaded at http://www.owasp.org/images/7/77/Attacking_Google_Web_Toolkit.ppt.
GDS Burp API can be downloaded here: http://mwielgoszewski.github.com/burpee/
NOTE:
Both Libraries must be added to your PYTHONPATH.
================================================
FILE: gwtfuzzer/gwtfuzzer.py
================================================
# -*- coding: utf-8 -*-
#!/usr/bin/env python
"""
GwtFuzzer v0.1
Copyright (C) 2010 Ron Gutierrez
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 urllib2
import urllib
import os
import os.path
import time
from GWTParser import GWTParser
from gds.pub.burp import parse
from itertools import product
from optparse import OptionParser
'''
Globals
'''
attacklog = []
param_manip_log = []
def replay( burpobj, fuzzified, attackstr, gwtparsed, log ):
global options
headers = burpobj.get_request_headers()
if options.cookies is not None:
headers["Cookie"] = options.cookies
headers["Content-Length"] = str(len(fuzzified))
req = urllib2.Request(burpobj.url.geturl(), fuzzified, headers )
handlers = [ urllib2.HTTPHandler() ]
if options.proxy is not None:
handlers.append( urllib2.ProxyHandler( {'http':options.proxy} ) )
opener = urllib2.build_opener(*handlers)
urllib2.install_opener( opener )
errors_found = []
try:
resp = urllib2.urlopen(req)
data = resp.read()
# Check for error messages
errors_found = check_errors(data)
# Check to see if a exception message was returned
if is_exception(data):
errors_found.append( "GWT Exception Returned" )
# Success
log.append( { 'method':gwtparsed.rpc_deserialized[2]+"."+gwtparsed.rpc_deserialized[3],
'request_url':burpobj.url.geturl(),
'request_headers':headers,
'request_payload':fuzzified,
'attack': attackstr,
'response_status':200,
'response_content':data,
'response_size':len(data),
'errors_found':errors_found })
except urllib2.HTTPError, e:
# Request did not return a 200 OK
log.append( {'method':gwtparsed.rpc_deserialized[2]+"."+gwtparsed.rpc_deserialized[3],
'request_url':burpobj.url.geturl(),
'request_headers':headers,
'request_payload':fuzzified,
'attack': attackstr,
'response_status':e.code,
'response_content':e.read(),
'response_size':len(e.read()),
'errors_found':errors_found })
except urllib2.URLError, e:
# Host could not be reached
log.append( {'method':gwtparsed.rpc_deserialized[2]+"."+gwtparsed.rpc_deserialized[3],
'request_url':burpobj.url.geturl(),
'request_headers':headers,
'request_payload':fuzzified,
'attack': attackstr,
'response_status':'Python URLError Exception',
'response_content':e.reason(),
'response_size':0,
'errors_found':errors_found})
print( "Request failed: "+burpobj.url.geturl()+"("+e.reason+")" )
def check_errors( data ):
global errors
found = []
for error in errors:
if data.find( error ) != -1:
found.append( error )
print( "found "+error )
return found
def is_exception( data ):
if data.find( "//EX[", 0, 8 ) != -1:
return True
return False
def escape( str ):
return str.replace( '<', '<' ).replace( '>', '>' ).replace( '"', '"' ).replace( '\'', ''')
def filter_gwt_reqs( parsed ):
filtered = []
for burpobj in parsed:
headers = burpobj.get_request_headers()
if "Content-Type" in headers:
if headers["Content-Type"].find("text/x-gwt-rpc") != -1:
filtered.append( burpobj )
return filtered
def get_number_range( num ):
if num < options.idrange:
return 0, num+options.idrange
begin = int(num)-int(options.idrange)
end = int(num)+int(options.idrange)
return begin, end
def load_strings( list, filename ):
if os.path.exists( filename ):
f = open( filename, 'r' )
for line in f:
if line.find( "# ", 0, 2 ) == -1: # Ignore FuzzDB comments
list.append( line.strip() )
f.close()
else:
print( "Error: "+filename+" does not exist" )
exit()
def fuzz( burpobj ):
global options, attacks, attacklog, param_manip_log
# Parse the gwt string
gwtparsed = GWTParser()
gwtparsed.deserialize( burpobj.get_request_body() )
gwtlist = burpobj.get_request_body().split('|')
# This is where the magic happens.. Special Thanks to Marcin W.
# Test all GWT requests using the attack strings submitted
for( idx, param ), fuzzy in product( enumerate(gwtlist), attacks ):
# Check to see if index was marked as a fuzzible string value by GWTParse
if idx in gwtparsed.fuzzmarked and gwtparsed.fuzzmarked[idx] == "%s":
fuzzified = "%s|%s|%s" %('|'.join(gwtlist[:idx]), fuzzy.replace('|','\!'), '|'.join(gwtlist[idx+1:]))
replay( burpobj, fuzzified, fuzzy, gwtparsed, attacklog ) # Submit the request
# Test all GWT request for Parameter Manipulation
for idx, param in enumerate( gwtlist ):
if idx in gwtparsed.fuzzmarked and gwtparsed.fuzzmarked[idx] == "%d":
begin, end = get_number_range( param )
for i in range( int(begin), int(end) ):
fuzzified = "%s|%s|%s" %('|'.join(gwtlist[:idx]), str(i), '|'.join(gwtlist[idx+1:]))
replay( burpobj, fuzzified, str(i), gwtparsed, param_manip_log ) #Submit the request
def reportfuzz( logdir ):
global attacklog
f = open( logdir+"//gwtfuzz.html", 'w' )
f.write( '''
<html>
<head>
<title>GWTFuzz Results</title>
<style type="text/css">
td, th{
font-family: sans-serif;
font-size: 12px;
border: thin solid black;
word-wrap: break-word;
border-spacing: 0;
padding: 1px 1px 1px 1px;
}
tr.error{
background-color: #FFCC66;
}
</style>
</head>
<body>
<h2>Fuzz Results</h2>
<table cellspacing=0 style="border: thin solid black;">
<tr>
<td>ID</th>
<th>Endpoint URL</th>
<th>RPC Method</th>
<th>Attack</th>
<th>Request Data</th>
<th>Resp Status</th>
<th>Resp Size</th>
<th>Resp Content</th>
<th>Errors Found</th>
</tr>''' )
for idx, entry in enumerate(attacklog):
if len(entry['errors_found']) > 0:
f.write( '<tr class="error">' )
elif entry['response_status'] != 200:
f.write( '<tr class="error">' )
else:
f.write( '<tr>' )
f.write( '<td style="max-width:300px;text-align:right">'+str(idx)+'</td>' +
'<td style="max-width:300px;">'+escape(entry['request_url'])+'</td>' +
'<td style="max-wdth:300px;">'+escape(entry['method'])+'</td>' +
'<td style="max-width:300px;text-align:center">'+escape(entry['attack'])+'</td>' +
'<td style="max-width:450px;">'+escape(entry['request_payload'])+'</td>' +
'<td style="width=10px;max-width:10px;text-align:right">'+str(entry['response_status'])+'</td>' +
'<td style="width=10px;max-width:10px;text-align:right">'+str(entry['response_size'])+'</td>' +
'<td style="max-width:150px;"><a href="responses/'+str(idx)+'.txt" target="_new">View Response</a></td>' +
'<td style="max-width:100px;">' )
errorstr = ""
for error in entry['errors_found']:
errorstr = errorstr + escape(error) + ", "
errorstr = errorstr[:-2]
f.write( errorstr+'</td></tr>' )
# Write the HTTP response into a text file
f2 = open( logdir+'//responses/'+str(idx)+'.txt', 'w' )
f2.write( entry['response_content'] )
f2.close()
f.write( '</table></body></html>' )
f.close()
print( "Results saved to "+logdir )
def reportparam( logdir ):
global param_manip_log
f = open( logdir+"//param_manip.html", 'w' )
f.write( '''
<html>
<head>
<title>GWT Parameter Manipulation Results</title>
<style type="text/css">
td, th{
font-family: sans-serif;
font-size: 12px;
border: thin solid black;
word-wrap: break-word;
border-spacing: 0;
padding: 1px 1px 1px 1px;
}
tr.status{
background-color: #FFCC66;
}
tr.error{
background-color: #FF3333;
}
</style>
</head>
<body>
<h2>GWT Parameter Manipulation Results</h2>
<table cellspacing=0 style="border: thin solid black;">
<tr>
<td>ID</th>
<th>Endpoint URL</th>
<th>RPC Method</th>
<th>Attack</th>
<th>Request Data</th>
<th>Resp Status</th>
<th>Resp Size</th>
<th>Resp Content</th>
</tr>''' )
for idx, entry in enumerate(param_manip_log):
f.write( '<tr><td style="max-width:300px;text-align:right">'+str(idx)+'</td>' +
'<td style="max-width:300px;">'+escape(entry['request_url'])+'</td>' +
'<td style="max-wdth:300px;">'+escape(entry['method'])+'</td>' +
'<td style="max-width:300px;text-align:center">'+escape(entry['attack'])+'</td>' +
'<td style="max-width:450px;">'+escape(entry['request_payload'])+'</td>' +
'<td style="width=10px;max-width:10px;text-align:right">'+str(entry['response_status'])+'</td>' +
'<td style="width=10px;max-width:10px;text-align:right">'+str(entry['response_size'])+'</td>' +
'<td style="max-width:150px;"><a href="responses/p'+str(idx)+'.txt" target="_new">View Response</a></td></tr>' +
'<td style="max-width:100px;">' )
# Write the HTTP response into a text file
f2 = open( logdir+'//responses//p'+str(idx)+'.txt', 'w' )
f2.write( entry['response_content'] )
f2.close()
f.write( '</table></body></html>' )
f.close()
if __name__ == "__main__":
global options, attacks, errors
attacks = []
errors = []
parser = OptionParser( usage="usage: %prog [options]",
description='Automates the fuzzing of GWT RPC requests',
version='%prog 0.10' )
parser.add_option('-b', '--burp',
help='Burp logfile to fuzz',
action='store' )
parser.add_option('-f', '--fuzzfile',
help='File containing attack strings',
action='store' )
parser.add_option('-e', '--errorfile',
help='File containing error messages',
action='store' )
parser.add_option('-o', '--output',
help='Directory to store results',
action='store' )
parser.add_option('-k', '--cookies',
help='Cookies to use when requesting GWT RPC pages',
action='store' )
parser.add_option('-p', '--proxy',
help='Proxy Host and Port (e.g. -p "http://proxy.internet.net:8080"',
action='store' )
parser.add_option('-i', '--idrange',
help='Range of decrements and increments to test parameter manipulation with',
action='store' )
(options, args) = parser.parse_args()
if options.burp is None:
print( "\nError: Missing Burp log file\n" )
parser.print_help()
exit()
elif options.fuzzfile is None:
print( "\nError: Missing fuzz file\n" )
parser.print_help()
exit()
if options.idrange and options.idrange < 1:
options.idrange = 100
print( "Invalid ID Range Entered: ID Range has been set to 100\n" )
elif options.idrange is None:
options.idrange = 100
print( "ID Range for Parameter Manipulation Testing has been set to 100\n" )
parsed = None
# Parse the Burp log using the GDS Burp API
if os.path.exists( options.burp ):
print( "Parsing Burp logfile" )
parsed = parse( options.burp )
else:
print( "\nBurp log file entered does not exist\n" )
exit()
logdir = ""
if options.output and os.path.exists( options.output ):
print( "Error: Output directory already exists." )
exit()
elif options.output:
logdir = options.output
else:
logdir = "gwtfuzz_results"+time.strftime("%Y%m%d%H%M%S")
os.mkdir( logdir )
os.mkdir( logdir+"//responses" )
if options.fuzzfile:
load_strings(attacks, options.fuzzfile)
if options.errorfile:
load_strings(errors, options.errorfile)
# Filter out the GWT RPC Requests from the log
filtered = filter_gwt_reqs(parsed)
print( "Fuzzing has commenced" )
# Fuzz each GWT RPC Request
for burpobj in filtered:
fuzz( burpobj )
# Generate Parameter Manipulation Report
reportparam( logdir )
reportfuzz( logdir )
================================================
FILE: gwtparse/GWTParser.py
================================================
# -*- coding: utf-8 -*-
#!/usr/bin/env python
"""
GwtParse v0.2
Copyright (C) 2010 Ron Gutierrez
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
import re
import pprint
from Parameter import Parameter
reload(sys)
sys.setdefaultencoding('utf-8')
#################################################
#
# Java Primitive Types and Object Wrappers
#
#################################################
FUZZ_STRING = "%s"
FUZZ_DIGIT = "%d"
STRING_OBJECT = "java.lang.String"
INTEGER_OBJECT = "java.lang.Integer"
DOUBLE_OBJECT = "java.lang.Double"
FLOAT_OBJECT = "java.lang.Float"
BYTE_OBJECT = "java.lang.Byte"
BOOLEAN_OBJECT = "java.lang.Boolean"
SHORT_OBJECT = "java.lang.Short"
CHAR_OBJECT = "java.lang.Char"
LONG_OBJECT = "java.lang.Long"
PRIMITIVES_WRAPPER = [ STRING_OBJECT, INTEGER_OBJECT, DOUBLE_OBJECT, FLOAT_OBJECT, BYTE_OBJECT, BOOLEAN_OBJECT, SHORT_OBJECT, CHAR_OBJECT ]
LONG = "J"
DOUBLE = "D"
FLOAT = "F"
INT = "I"
BYTE = "B"
SHORT = "S"
BOOLEAN = "Z"
CHAR = "C"
PRIMITIVES = [ "J", "D", "F", "I", "B", "S", "Z", "C" ]
NUMERICS = [ INT, CHAR, BOOLEAN, BYTE, SHORT, INTEGER_OBJECT, CHAR_OBJECT, BYTE_OBJECT, BOOLEAN_OBJECT, SHORT_OBJECT ]
ARRAYLIST = "java.util.ArrayList"
LINKEDLIST = "java.util.LinkedList"
VECTOR = "java.util.Vector"
ListTypes = [ ARRAYLIST, LINKEDLIST, VECTOR ]
prev_index = 0
INDENTATION = 15
# Indicates that obfuscated type names should be used in the RPC payload.
FLAG_ELIDE_TYPE_NAMES = 0x1
# Indicates that RPC token is included in the RPC payload.
FLAG_RPC_TOKEN_INCLUDED = 0x2
# Bit mask representing all valid flags.
VALID_FLAGS_MASK = 0x3
class GWTParser(object):
def _cleanup(self):
self.data = []
self.rpc_deserialized = []
self.rpc_list = []
self.indices = []
self.data_types = []
self.parameters = []
self.stream_version = 0
self.flags = 0
self.columns = 0
self.parameter_idx = 0
self.fuzzmarked = dict()
'''
Sets a value as fuzzable
'''
def _set_fuzzable( self, idx, fuzz_value ):
fuzz_idx = int(idx)+2
if self.surround_value:
if not fuzz_idx in self.fuzzmarked:
self.rpc_list_fuzzable[fuzz_idx] = self.surround_value+self.rpc_list_fuzzable[fuzz_idx]+self.surround_value
elif self.replace_value:
self.rpc_list_fuzzable[fuzz_idx] = self.replace_value
elif self.burp:
if not fuzz_idx in self.fuzzmarked:
self.rpc_list_fuzzable[fuzz_idx] = '§'+self.rpc_list_fuzzable[fuzz_idx]+'§'
else:
self.rpc_list_fuzzable[fuzz_idx] = fuzz_value
self.fuzzmarked[fuzz_idx] = fuzz_value
'''
Check if the next index value is an integer or a data index
'''
def _nextval_is_an_integer(self, curr_index):
if len(self.indices) == 0:
return False
# If the index is out of data array scope
if int(self.indices[0]) <= 4 or int(self.indices[0]) > self.columns:
return True
# If next index is the increment of the previous
if int(curr_index) == int(self.indices[0])-1:
if len(self.indices) > 1:
if int(self.indices[0]) == int(self.indices[1]):
return True
return False
'''
Put back the index that was pop previously
'''
def _indice_rollback(self, prev_index):
self.indices_read -= 1
self.indices.insert(0,prev_index)
'''
Is the next value an object type name
'''
def _is_an_object(self, value):
obj_check = re.compile( ".*/\d+" )
match = obj_check.match(value)
if match:
return True
return False
'''
Check to see if the type is an array of primitives
'''
def _is_a_primitive_array(self, data_type):
arr_check = re.compile( "\[(\w)/\d+" )
match = arr_check.match(data_type)
if match and match.group(1) in PRIMITIVES:
self.parameters[self.pidx].typename = match.group(1)
self.parameters[self.pidx].is_array = True
return True
return False
'''
Is the passed in value the subtype value for the current parameters typename
'''
def _is_object_subtype( self, data_type ):
typename = ""
if self.parameters[self.pidx].is_list:
typename = self.parameters[self.pidx].subtype
else:
typename = self.parameters[self.pidx].typename
subtype_check = re.compile( typename+"/\d+" )
match = subtype_check.match( data_type )
if match:
return True
return False
'''
Check to see if the type is array of objects
'''
def _is_an_object_array( self, data_type):
arr_check = re.compile( "\[L(.*);/\d+" )
match = arr_check.match(data_type)
if match:
self.parameters[self.pidx].typename = match.group(1)
self.parameters[self.pidx].is_array = True
return True
return False
'''
Check to see if data_type is a ListType
'''
def _is_list_type( self, data_type ):
if self._get_typename(data_type) in ListTypes:
return True
return False
'''
Check to see if the index passed in is an integer value
- This check needs some major work
'''
def _indice_is_intval( self, idx ):
# If the index is out of data array scope
if int(self.indices[int(idx)]) <= 4 or int(self.indices[int(idx)]) > self.columns:
return True
return False
'''
Check to see if the remaining method parameters are all numeric
'''
def _remaining_params_are_numeric( self, tracker_idx):
for i in range( self.pidx+1, len(self.parameters)):
if not self._get_typename(self.parameters[i].typename) in NUMERICS:
return False
return True
'''
Checks to see whether we should stop reading values into a custom object
'''
def _is_end_of_object( self, prev_index, value ):
tracker_idx = 0
found = False
if self._remaining_params_are_numeric( tracker_idx ):
if len(self.indices) == len(self.parameters[self.pidx+1:]):
prev_index = self.indices[0]
self._add_stringval(value)
return True
else:
return False
if len(self.parameters[self.pidx+1:]) == len(self.indices):
prev_index = self.indices[0]
self._add_stringval(value)
return True
for i in range( self.pidx+1, len( self.parameters ) ):
# Look Into the Future and see if the parameter values are still there
for j in range( tracker_idx, len(self.indices) ):
if self._get_typename(self.parameters[i].typename) in NUMERICS:
if self._indice_is_intval(j):
found = True
tracker_idx = j
continue
elif self._get_typename(self.parameters[i].typename) == STRING_OBJECT:
# If the index is out of data array scope
if int(self.indices[j]) <= 4 or int(self.indices[j]) > self.columns:
continue
if self._is_an_object( self.data[int(self.indices[j])] ) is False:
found = True
tracker_idx = j
break
else:
# If the index is out of data array scope
if int(self.indices[j]) <= 4 or int(self.indices[j]) > self.columns:
continue
# This must be a custom object. Check for the subtype..
if self._get_typename(self.data[int(self.indices[j])]) == self._get_typename(self.parameters[i].typename):
found = True
tracker_idx = j
break
if not found:
return True # Did not find the next parameter so the current value is the next method param
else:
found = False
return False
'''
Removes the "/" and digits from a typename
'''
def _get_typename( self, data_type):
subtype_check = re.compile( "(.*)/\d+" )
match = subtype_check.match(data_type)
if match:
return match.group(1)
return data_type
'''
Get the next index or integer value
'''
def _pop_index(self):
try:
self.indices_read += 1
index = int(self.indices.pop(0))
except TypeError:
print ("Invalid Integer given for indices")
sys.exit()
return index
'''
Get the next float value
'''
def _pop_float_index(self):
try:
self.indices_read += 1
index = float(self.indices.pop(0))
except TypeError:
print ("Invalid float value read")
sys.exit()
return index
'''
Pop the next index value and then return the corresponding value
from the data table
'''
def _get_nextval(self):
return self.data[self._pop_index()]
def _add_intval(self):
if self.parameters[self.pidx].flag:
self.parameters[self.pidx].values[self.aidx].values.append(self._pop_index())
elif self.parameters[self.pidx].is_list and self.parameters[self.pidx].is_custom_obj:
self.parameters[self.pidx].values[self.lidx].values.append(self._pop_index())
else:
self.parameters[self.pidx].values.append(self._pop_index())
def _add_stringval(self, value):
if self.parameters[self.pidx].flag:
self.parameters[self.pidx].values[self.aidx].values.append(value)
elif self.parameters[self.pidx].is_list and self.parameters[self.pidx].is_custom_obj:
self.parameters[self.pidx].values[self.lidx].values.append(value)
else:
self.parameters[self.pidx].values.append(value)
###################################
#
# Parsing Methods
#
####################################
def _parse_read_string(self):
self._set_fuzzable( self.indices[0], FUZZ_STRING )
if self.parameters[self.pidx].flag:
self.parameters[self.pidx].values[self.aidx].append(self._get_nextval())
else:
if self.parameters[self.pidx].is_list:
subtype = self._get_nextval()
self.parameters[self.pidx].values.append(self._get_nextval())
def _parse_read_int_byte_short_char(self, is_wrapper=False):
if is_wrapper:
subtype = self._get_nextval()
self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )
if self.parameters[self.pidx].flag:
self.parameters[self.pidx].values[self.aidx].values.append(self._pop_index())
else:
self.parameters[self.pidx].values.append(self._pop_index())
def _parse_read_long(self, is_wrapper=False):
if is_wrapper:
subtype = self._get_nextval()
value1 = self._pop_float_index()
value2 = self._pop_float_index()
self._set_fuzzable( self.indices_read+self.columns-2, FUZZ_DIGIT )
self._set_fuzzable( self.indices_read+self.columns-1, FUZZ_DIGIT )
if value2 > 0:
self.parameters[self.pidx].values.append( str(value1) + str(value2) )
else:
self.parameters[self.pidx].values.append( str(value1) )
def _parse_read_double_float(self, is_wrapper=False):
if is_wrapper:
subtype = self._get_nextval()
self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )
self.parameters[self.pidx].values.append(self._pop_float_index())
def _parse_primitive_array(self):
subtype = self._get_nextval()
how_many = self._pop_index()
for i in range(how_many):
self._parse_value( self.parameters[self.pidx].typename )
def _parse_object_array(self):
if self.parameters[self.pidx].flag is False:
subtype = self._get_nextval()
how_many = self._pop_index()
self.aidx = 0
for i in range(how_many):
self._parse_value(self.parameters[self.pidx].typename )
self.aidx += 1
def _parse_read_boolean(self):
self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )
int_value = self._pop_index()
if int_value == 1:
self.parameters[self.pidx].values.append( "true" )
else:
self.parameters[self.pidx].values.append( "false" )
def _parse_read_list(self, list_type, set_list_flag=True):
if self.parameters[self.pidx].flag is False:
if set_list_flag:
self.parameters[self.pidx].is_list = True
self.parameters[self.pidx].typename = list_type
subtype = self._get_nextval()
else:
self.parameters[self.pidx].values[self.aidx].typename = list_type
how_many = self._pop_index()
self.lidx = 0
for i in range(how_many):
prev_index = self.indices[0]
if self.parameters[self.pidx].flag: # Reading a List within a Custom Object
subtype = self._get_typename(self._get_nextval())
self.parameters[self.pidx].values[self.aidx].subtype = subtype
self._indice_rollback(prev_index)
self._parse_value(subtype)
else: # Read values within a List Method Parameter
self.parameters[self.pidx].subtype = self._get_typename(self._get_nextval())
self._indice_rollback(prev_index)
self._parse_value(self.parameters[self.pidx].subtype)
self.lidx += 1
def _parse_read_object(self,name):
if len( self.indices ) > 0:
prev_index = self.indices[0]
self.parameters[self.pidx].is_custom_obj = True
value = self._get_nextval()
if self.parameters[self.pidx].is_array and self.parameters[self.pidx].is_custom_obj:
customParam = Parameter( value )
customParam.is_custom_obj = True
self.parameters[self.pidx].values.append( customParam )
self.parameters[self.pidx].flag = True
if self.parameters[self.pidx].is_list and self.parameters[self.pidx].is_custom_obj:
customParam = Parameter(value)
customParam.is_custom_obj = True
self.parameters[self.pidx].values.append(customParam)
# If this is the final parameter just read the remaining data as member variables
if len(self.parameters)-1 == self.pidx:
while len(self.indices) > 0: # Read till the end of the index table
if self._nextval_is_an_integer( prev_index ):
prev_index = self.indices[0]
self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )
self._add_intval()
continue
else:
prev_index = self.indices[0]
value = self._get_nextval()
if self.parameters[self.pidx].is_array or self.parameters[self.pidx].is_list: # Am I reading an array of objects?
if self._is_object_subtype(value): # Did I just read in an object subtype?
self._indice_rollback(prev_index)
break # Stop reading object and move onto the next object in the array
if self._is_list_type( value ):
if self.parameters[self.pidx].flag is False:
self._indice_rollback(prev_index)
self._parse_read_list(self._get_typename(value), False)
elif self._is_an_object(value): # Is the value I just read in a subtype for another class
prev_index = self.indices[0]
else:
self._add_stringval(value)
self._set_fuzzable( prev_index, FUZZ_STRING )
else: # There are more parameters so we must be careful with the parsing
while len(self.indices) > 0:
if self._nextval_is_an_integer( prev_index ):
self._set_fuzzable( self.indices_read+self.columns, FUZZ_DIGIT )
prev_index = self.indices[0]
self._add_intval()
else:
prev_index = self.indices[0]
value = self._get_nextval()
if self.parameters[self.pidx].is_array or self.parameters[self.pidx].is_list:
if self._is_object_subtype(value):
self._indice_rollback(prev_index)
break;
if self._is_end_of_object( prev_index, value ):
if self.parameters[self.pidx].is_list or self.parameters[self.pidx].is_array:
self.pidx += 1
self._indice_rollback(prev_index)
self._parse_value( self.parameters[self.pidx].typename )
else:
if not self._get_typename(self.parameters[self.pidx+1].typename) in NUMERICS:
self._indice_rollback(prev_index)
break
elif self._is_an_object( value ):
continue
else: # store value
self._set_fuzzable( prev_index, FUZZ_STRING )
prev_index = self.indices[0]
self._add_stringval(value)
self.parameters[self.pidx].flag = False
'''
Split the object into a list and remove the last element
The RPC String ends with a '|' so this will create an empty element
'''
def _read_string_into_list(self):
# This copy is used to keep track of fuzzable values
self.rpc_list_fuzzable = list(self.rpc_string.split('|'))
self.rpc_list_fuzzable.pop()
# This copy is used to parsing and will have values removed during parsing
self.rpc_list = self.rpc_string.split('|')
self.rpc_list.pop()
'''
Store and remove the first three elements of the list
'''
def _get_headers(self):
try:
self.stream_version = int(self.rpc_list.pop(0))
self.flags = int(self.rpc_list.pop(0))
self.columns = int(self.rpc_list.pop(0))
except TypeError:
print ("Invalid Integer given for the stream_version or number of columns")
sys.exit()
'''
Store the data inside of the serialized object
I add in an empty string in the 0 Element in order to
stay uniform with the indices table in the RPC Object
'''
def _get_data(self):
self.data = self.rpc_list[0:self.columns]
self.data.insert(0,"")
'''
Store the indices that are found at the end of the RPC serialized object
'''
def _get_indices(self):
self.indices = self.rpc_list[self.columns:]
'''
Parses a value from the string table
'''
def _parse_value(self, data_type):
if self._get_typename(data_type) == STRING_OBJECT:
self._parse_read_string()
elif self._get_typename(data_type) == INT or data_type == BYTE or data_type == SHORT or data_type == CHAR:
self._parse_read_int_byte_short_char()
elif self._get_typename(data_type) == INTEGER_OBJECT or data_type == BYTE_OBJECT or data_type == SHORT_OBJECT or data_type == CHAR_OBJECT:
self._parse_read_int_byte_short_char(True)
elif self._get_typename(data_type) == LONG:
self._parse_read_long()
elif self._get_typename(data_type) == LONG_OBJECT:
self._parse_read_long(True)
elif self._get_typename(data_type) == DOUBLE or data_type == FLOAT:
self._parse_read_double_float()
elif self._get_typename(data_type) == DOUBLE_OBJECT or data_type == FLOAT_OBJECT:
self._parse_read_double_float(True)
elif self._is_a_primitive_array(data_type):
self._parse_primitive_array()
elif self._is_an_object_array(data_type):
self._parse_object_array()
elif self._get_typename(data_type) == BOOLEAN:
self._parse_read_boolean()
elif self._is_list_type(data_type):
self._parse_read_list(data_type)
else:
self._parse_read_object(data_type)
'''
Parses the GWT-RPC Request Payload
'''
def _parse(self):
self.rpc_deserialized = []
self.parameters = [] # Stores Parameter names and values read in from the request
self.pidx = 0 # Index value used to know which Parameter we are currently writing into
self.indices_read = 1 # Keeps track how many indices we have read
'''
Store the first four values
Hostname, Hash, Class Name, Method
'''
# rpc request has an rpc/xsrf token
if (self.flags & FLAG_RPC_TOKEN_INCLUDED):
self.rpc_deserialized.append(self._get_nextval()) # hostname
self.rpc_deserialized.append(self._get_nextval()) # strong name
self.rpc_token = {}
self.rpc_token[self._get_nextval()] = self._get_nextval()
self.rpc_deserialized.append(self._get_nextval()) # interface
self.rpc_deserialized.append(self._get_nextval()) # method
else:
for i in range(4):
self.rpc_deserialized.append(self._get_nextval())
for index in self.indices:
num_of_params = self._pop_index() # Number of Method parameters
for i in range(num_of_params):
self.parameters.append( Parameter(self._get_nextval()) )
for param in self.parameters:
if num_of_params > self.pidx: # If parameter index is greater than number of params then we are done
self._parse_value(param.typename)
self.pidx += 1
'''
Handles the parsing of the RPC string
'''
def deserialize(self, rpc_string):
self._cleanup()
self.rpc_string = rpc_string
self._read_string_into_list()
self._get_headers()
self._get_data()
self._get_indices()
try:
self._parse()
except IndexError:
print( "Encountered Error During Parsing" )
def get_fuzzstr(self):
fuzzstr = "|".join( self.rpc_list_fuzzable )+"|"
if self.fout:
self.fout.write( fuzzstr+"\n" )
else:
print( "\nGWT RPC Payload Fuzz String\n" )
print( fuzzstr+"\n" )
'''
Prints out the deserialized method call in a user friendly format
'''
def display(self):
if self.fout:
self.fout.write("==================================\n")
self.fout.write(str("Serialized Object:").rjust(INDENTATION) + "\n" + self.rpc_string + "\n\n")
self.fout.write(str("Stream Version:").rjust(INDENTATION) + "\t" + str(self.stream_version)+"\n")
self.fout.write(str("Flags:").rjust(INDENTATION) + "\t" + str(self.flags+"\n"))
self.fout.write(str("Column Numbers:").rjust(INDENTATION) + "\t" + str(self.columns)+"\n")
self.fout.write(str("Host:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[0]+"\n")
self.fout.write(str("Hash:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[1]+"\n")
self.fout.write(str("Class Name:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[2]+"\n")
self.fout.write(str("Method:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[3] + "\n")
self.fout.write(str("# of Params:").rjust(INDENTATION) + "\t" + str(len(self.parameters)) + "\n")
self.fout.write(str("Parameters:").rjust(INDENTATION)+"\n")
else:
print (str("\nSerialized Object:").rjust(INDENTATION) + "\n" + self.rpc_string + "\n")
print (str("Stream Version:").rjust(INDENTATION) + "\t" + str(self.stream_version))
print (str("Flags:").rjust(INDENTATION) + "\t" + str(self.flags))
print (str("Column Numbers:").rjust(INDENTATION) + "\t" + str(self.columns))
print (str("Host:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[0])
print (str("Hash:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[1])
print (str("Class Name:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[2])
print (str("Method:").rjust(INDENTATION) + "\t" + self.rpc_deserialized[3])
print (str("# of Params:").rjust(INDENTATION) + "\t" + str(len(self.parameters)) + "\n")
print (str("Parameters:").rjust(INDENTATION))
for parameter in self.parameters:
if self.fout:
pprint.pprint(parameter.__dict__, stream=self.fout, indent="1")
else:
pprint.pprint(parameter.__dict__, indent="1")
print( "\n" )
if self.fout:
self.fout.write( "\n" )
else:
print ("\n")
def __init__( self ):
self.burp = False
self.surround_value = ""
self.replace_value = ""
self.fout = None
================================================
FILE: gwtparse/Parameter.py
================================================
#!/usr/bin/env python
class Parameter(object):
def __init__(self, tn ):
self.typename = tn
self.values = []
self.flag = False
self.is_custom_obj = False
self.is_list = False
self.is_array = False
def _add_value(self, val):
values.append( val )
def _set_flag(self, flag_value ):
self.flag = flag_value
def __repr__(self):
return "<Parameter %r>" % self.__dict__
================================================
FILE: gwtparse/README
================================================
GwtParse v0.2
For a detailed explaination of the tool see http://www.gdssecurity.com/l/b/2010/05/03/fuzzing-gwt-rpc-requests/
Any issues encountered when using the tool should be sent to rgutierrez@gdssecurity.com
Files Included:
gwtparse.py: Command Line interface to parse GWT RPC request and identify all fuzzable values
GWTParse.py: Modules containing all parsing logic which can be used within your own python code
Parameter.py: Simple Python object which is used by GWTParse to store RPC method parameters
Important Note:
When using the -b flag it is noted that the proper unicode symbol will only be properly display within
terminals which allow unicode characters ( Linux, Cygwin rxvt ). When using on windows the the output
can be written to a text file using the "-w" or "-a" options in order to properly view the output.
===============================
Command Line Usage Examples
===============================
Identify all fuzzable values in the GWT RPC Request Payload using the default output format
> python gwtparse.py -i "<RPC STRING>"
Surrounds fuzzable value with the "Section" symbol for use within Burp Intruder
> python gwtparse.py -i "<RPC STRING>" -b
Surrounds fuzzable value with the user defined value for use within custom fuzzer
> python gwtparse.py -i "<RPC STRING>" -s "<fuzz me>"
Replaces fuzzable value with the user defined value for use within custom fuzzer
> python gwtparse.py -i "<RPC STRING>" -r "<fuzz me>"
Parses RPC String and displays output in a pretty format in order to better understand the values passed
> python gwtparse.py -i "<RPC STRING>" -p
Writes output to file ( File must not already exist )
> python gwtparse.py -i "<RPC STRING>" -w "outfile.txt"
Appends output to file
> python gwtparse.py -i "<RPC STRING>" -a "outfile.txt"
================================
GWTParse Module Usage
================================
The GWTParser.py and Parameter.py files should be placed in a directory within your python path and imported into your python code
These must be performed in the following order.
Create Object
gwt = GWTParser()
Specify surround, replace or burp output format ( If none specified the default format is used )
gwt.surround_value = <STRING VALUE>
or
gwt.replace_value = <STRING VALUE>
or
gwt.burp = True
Specify file output handler ( If you want to write to output, default is standard output )
gwt.fout = < FILE OUTPUT HANDLER >
Deserialize the rpc request
gwt.deserialize( <ADD RPC PAYLOAD HERE> )
Display Pretty Output
gwt.display()
Return fuzz string
gwt.get_fuzzstr()
Data Access
Service Name: gwt.rpc_deserialized[2]
Method Name: gwt.rpc_deserialize[3]
Method Parameters: gwt.parameters
================================================
FILE: gwtparse/__init__.py
================================================
================================================
FILE: gwtparse/gwtparse.py
================================================
# -*- coding: utf-8 -*-
#!/usr/bin/env python
"""
GwtParse v0.2
Copyright (C) 2010 Ron Gutierrez
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 os
from optparse import OptionParser
from GWTParser import GWTParser
desc = "A tool for parsing GWT RPC Requests"
if __name__ == "__main__":
parser = OptionParser(usage='usage: %prog [options]', description=desc, version='%prog 0.10')
parser.add_option('-p', '--pretty', help="Output the GWT RPC Request in a human readible format", action="store_true")
parser.add_option('-s', '--surround', help="String used to surround all fuzzable values", action="store", dest="surround_value")
parser.add_option('-r', '--replace', help="String used to replace all fuzzable values", action="store", dest="replace_value")
parser.add_option('-b', '--burp', help="Generates Burp Intruder Output", default=False, action="store_true")
parser.add_option('-i', '--input', help="RPC Request Payload (Required)", action="store", dest="rpc_request")
parser.add_option('-w', '--write', help="Writes Fuzz String to a new output file", action="store" )
parser.add_option('-a', '--append', help="Appends Fuzz String to an existing output file", action="store" )
(options, args) = parser.parse_args()
if options.rpc_request:
if options.surround_value and options.replace_value and options.burp:
print( "\nCannot choose more then one output format.\n" )
parser.print_help()
exit()
if options.surround_value and options.replace_value:
print( "\nCannot choose more then one output format.\n" )
parser.print_help()
exit()
if options.surround_value and options.burp:
print( "\nCannot choose more then one output format.\n" )
parser.print_help()
exit()
if options.replace_value and options.burp:
print( "\nCannot choose more then one output format.\n" )
parser.print_help()
exit()
gwt = GWTParser()
if options.surround_value:
gwt.surround_value = options.surround_value
elif options.replace_value:
gwt.replace_value = options.replace_value
elif options.burp:
gwt.burp = options.burp
if options.write:
if os.path.exists(options.write):
print( "Output file entered already exists" )
exit()
fout = open( options.write, "w" )
gwt.fout = fout
elif options.append:
fout = open( options.append, "a" )
gwt.fout = fout
gwt.deserialize( options.rpc_request )
if options.pretty:
gwt.display()
gwt.get_fuzzstr()
if gwt.fout:
gwt.fout.close()
else:
print( "\nMissing RPC Request Payload\n" )
parser.print_help()
gitextract__7fywc2q/
├── README
├── gwtenum/
│ ├── README
│ └── gwtenum.py
├── gwtfuzzer/
│ ├── README
│ └── gwtfuzzer.py
└── gwtparse/
├── GWTParser.py
├── Parameter.py
├── README
├── __init__.py
└── gwtparse.py
SYMBOL INDEX (54 symbols across 4 files)
FILE: gwtenum/gwtenum.py
function get_global_val (line 35) | def get_global_val( varname, html_file ):
FILE: gwtfuzzer/gwtfuzzer.py
function replay (line 42) | def replay( burpobj, fuzzified, attackstr, gwtparsed, log ):
function check_errors (line 111) | def check_errors( data ):
function is_exception (line 123) | def is_exception( data ):
function escape (line 130) | def escape( str ):
function filter_gwt_reqs (line 134) | def filter_gwt_reqs( parsed ):
function get_number_range (line 145) | def get_number_range( num ):
function load_strings (line 154) | def load_strings( list, filename ):
function fuzz (line 168) | def fuzz( burpobj ):
function reportfuzz (line 195) | def reportfuzz( logdir ):
function reportparam (line 271) | def reportparam( logdir ):
FILE: gwtparse/GWTParser.py
class GWTParser (line 84) | class GWTParser(object):
method _cleanup (line 86) | def _cleanup(self):
method _set_fuzzable (line 102) | def _set_fuzzable( self, idx, fuzz_value ):
method _nextval_is_an_integer (line 125) | def _nextval_is_an_integer(self, curr_index):
method _indice_rollback (line 145) | def _indice_rollback(self, prev_index):
method _is_an_object (line 153) | def _is_an_object(self, value):
method _is_a_primitive_array (line 165) | def _is_a_primitive_array(self, data_type):
method _is_object_subtype (line 179) | def _is_object_subtype( self, data_type ):
method _is_an_object_array (line 198) | def _is_an_object_array( self, data_type):
method _is_list_type (line 212) | def _is_list_type( self, data_type ):
method _indice_is_intval (line 223) | def _indice_is_intval( self, idx ):
method _remaining_params_are_numeric (line 234) | def _remaining_params_are_numeric( self, tracker_idx):
method _is_end_of_object (line 245) | def _is_end_of_object( self, prev_index, value ):
method _get_typename (line 307) | def _get_typename( self, data_type):
method _pop_index (line 320) | def _pop_index(self):
method _pop_float_index (line 334) | def _pop_float_index(self):
method _get_nextval (line 349) | def _get_nextval(self):
method _add_intval (line 353) | def _add_intval(self):
method _add_stringval (line 362) | def _add_stringval(self, value):
method _parse_read_string (line 377) | def _parse_read_string(self):
method _parse_read_int_byte_short_char (line 389) | def _parse_read_int_byte_short_char(self, is_wrapper=False):
method _parse_read_long (line 401) | def _parse_read_long(self, is_wrapper=False):
method _parse_read_double_float (line 418) | def _parse_read_double_float(self, is_wrapper=False):
method _parse_primitive_array (line 425) | def _parse_primitive_array(self):
method _parse_object_array (line 432) | def _parse_object_array(self):
method _parse_read_boolean (line 443) | def _parse_read_boolean(self):
method _parse_read_list (line 452) | def _parse_read_list(self, list_type, set_list_flag=True):
method _parse_read_object (line 485) | def _parse_read_object(self,name):
method _read_string_into_list (line 584) | def _read_string_into_list(self):
method _get_headers (line 597) | def _get_headers(self):
method _get_data (line 612) | def _get_data(self):
method _get_indices (line 620) | def _get_indices(self):
method _parse_value (line 627) | def _parse_value(self, data_type):
method _parse (line 668) | def _parse(self):
method deserialize (line 708) | def deserialize(self, rpc_string):
method get_fuzzstr (line 720) | def get_fuzzstr(self):
method display (line 733) | def display(self):
method __init__ (line 772) | def __init__( self ):
FILE: gwtparse/Parameter.py
class Parameter (line 3) | class Parameter(object):
method __init__ (line 5) | def __init__(self, tn ):
method _add_value (line 13) | def _add_value(self, val):
method _set_flag (line 16) | def _set_flag(self, flag_value ):
method __repr__ (line 19) | def __repr__(self):
Condensed preview — 10 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (63K chars).
[
{
"path": "README",
"chars": 0,
"preview": ""
},
{
"path": "gwtenum/README",
"chars": 203,
"preview": "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 \"ht"
},
{
"path": "gwtenum/gwtenum.py",
"chars": 7416,
"preview": "#!/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 softw"
},
{
"path": "gwtfuzzer/README",
"chars": 503,
"preview": "\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 "
},
{
"path": "gwtfuzzer/gwtfuzzer.py",
"chars": 14782,
"preview": "# -*- 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 "
},
{
"path": "gwtparse/GWTParser.py",
"chars": 28728,
"preview": "# -*- 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 "
},
{
"path": "gwtparse/Parameter.py",
"chars": 489,
"preview": "#!/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 "
},
{
"path": "gwtparse/README",
"chars": 2801,
"preview": "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-r"
},
{
"path": "gwtparse/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "gwtparse/gwtparse.py",
"chars": 3796,
"preview": "# -*- 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 "
}
]
About this extraction
This page contains the full source code of the GDSSecurity/GWT-Penetration-Testing-Toolset GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 10 files (57.3 KB), approximately 13.3k tokens, and a symbol index with 54 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.