During Web Application Penetration tests I always need to automate requests, e.g. for fuzzing. While most of the local proxy/testing softwares (Burp, WebScarab, w3af, etc.) include a repeater/fuzzer feature, I often want to do addtional computations in python (e.g. calculating a hash and sending it as a fuzzed value or comparing parts of the response). The following script will take an entire HTTP(S) request as a string, parse it and send it to the server. As I show with the POST parameter “fuzzableParam” in this example, values can easily be fuzzed.
def send_this_request(http_request_string, remove_headers=None):
"""
Always HTTP/1.1
"""
import urllib2
if remove_headers is None:
remove_headers=['content-length', 'accept-encoding', 'accept-charset',
'accept-language', 'accept', 'keep-alive', 'connection', 'pragma',
'cache-control']
for i, remove_header in enumerate(remove_headers):
remove_headers[i] = remove_header.lower()
if '\n\n' in http_request_string:
headers, body = http_request_string.split('\n\n',1)
else:
headers = http_request_string
body = None
headers = headers.split('\n')
request_line = headers[0]
headers = headers[1:]
method, rest = request_line.split(" ", 1)
url, protocol = rest.rsplit(" ", 1)
merge_host_header_into_url = False
if url.startswith("http"):
merge_host_header_into_url = False
elif url.startswith("/"):
info("Warning: Defaulting to HTTP. Please write URL as https:// if you want SSL")
merge_host_header_into_url = True
else:
fatalError("Protocol not supported. URL must start with http or /")
header_tuples = []
for header in headers:
name, value = header.split(": ", 1)
if merge_host_header_into_url and name.lower() == 'host':
url = 'http://'+value+url
if not name.lower() in remove_headers:
header_tuples.append((name, value))
opener = urllib2.build_opener()
opener.addheaders = header_tuples
urllib2.install_opener(opener)
try:
return urllib2.urlopen(url, body, 15).read()
except urllib2.HTTPError, e:
info('The server couldn\'t fulfill the request. Error code:', e.code)
except urllib2.URLError, e:
info("URLError:", e.reason)
except Exception, e:
error("DIDNT WORK:", e)
def info(*text):
print "[PY-INFO] "+str(" ".join(str(i) for i in text))
def error(*text):
print "[PY-ERROR] "+str(" ".join(str(i) for i in text))
request = '''POST http://example.com/ HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Content-Type: application/x-www-form-urlencoded;charset=utf-8
Referer: http://example.com
Content-Length: 132
Cookie: test=somevalue; abc=123
DNT: 1
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
id=123&fuzzableParam='''
additionalValue = "&anotherParam=abc"
for i in ['78', '-1']:
print send_this_request(request+i+additionalValue)