source: trunk/bin/bletchley-http2py @ 28

Last change on this file since 28 was 28, checked in by tmorgan, 11 years ago

more documentation
added supported encodings listing

  • Property svn:executable set to *
File size: 5.2 KB
Line 
1#!/usr/bin/env python
2
3# Requires Python 2.7+
4
5'''
6This script reads a raw HTTP request from stdin and writes to stdout
7a Python script.  The generated script sends the same (or a very similar)
8request using the httplib/http.client libraries.
9
10Certainly if you have a raw request, you could simply send it via TCP
11sockets, but if for some reason the server behaves oddly with flow control,
12insists on using gzip/deflate encoding, insists on using chunked encoding,
13or any number of other annoying things, then using an HTTP library is a
14lot more convenient.  This script attempts to make that conversion easy.
15
16
17Copyright (C) 2011-2012 Virtual Security Research, LLC
18Author: Timothy D. Morgan
19
20 This program is free software: you can redistribute it and/or modify
21 it under the terms of the GNU Lesser General Public License, version 3,
22 as published by the Free Software Foundation.
23
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with this program.  If not, see <http://www.gnu.org/licenses/>.
31'''
32
33
34import sys
35import argparse
36try:
37    from lxml import etree
38except:
39    sys.stderr.write('ERROR: Could not import lxml module.  Ensure it is installed.\n')
40    sys.stderr.write('       Under Debian, the package name is "python-lxml"\n.')
41    sys.exit(1)
42
43parser = argparse.ArgumentParser(
44    description='A script which accepts an HTTP request and prints out a'
45    ' generated Python script which sends a similar request.  This is useful'
46    ' when one wants to automate sending a large number of requests to a'
47    ' particular page or application.'
48    ' For more information, see: http://code.google.com/p/bletchley/wiki/Overview')
49parser.add_argument(
50    'requestfile', type=file, nargs='?', default=sys.stdin, 
51    help='A file containing an HTTP request.  Defaults to stdin if omitted.')
52parser.add_argument(
53    '--burp', action='store_true', help='Input file is a XML export from Burp.'
54    ' (First request in file is used.)')
55args = parser.parse_args()
56
57if args.burp:
58    safe_parser = etree.ETCompatXMLParser(resolve_entities=False)
59    root = etree.parse(args.requestfile, parser=safe_parser)
60    input_req = root.xpath('/items/item/request')[0].text
61    root = None
62else:
63    input_req = args.requestfile.read()
64
65
66print('''#!/usr/bin/env python
67
68import sys
69# function with either Python 2.7 or 3.x
70try:
71    import http.client as httpc
72except:
73    import httplib as httpc
74''')
75
76
77if '\r\n\r\n' in input_req:
78    raw_headers,body = input_req.split('\r\n\r\n', 1)
79elif '\n\n' in input_req:
80    raw_headers,body = input_req.split('\n\n', 1)
81else:
82    raw_headers = input_req
83    body = ''
84
85header_lines = raw_headers.split('\n')
86method,path,version = header_lines[0].split(' ', 2)
87
88host = 'TODO'
89port = 80
90use_ssl = False
91
92headers = []
93for l in header_lines[1:]:
94    if len(l) < 1: 
95        break
96    # Handle header line continuations
97    if l[0] == '\t':
98        if len(headers) == 0:
99            continue
100        name,values = headers[-1]
101        values.append(l.lstrip('\t'))
102        headers[-1] = (name,values)
103        continue
104
105    name,value = l.split(':',1)
106    value = value.lstrip(' ').rstrip('\r')
107
108    # Skip headers that have to do with transfer encodings and connection longevity
109    if name.lower() not in ['accept','accept-language',
110                            'accept-encoding','accept-charset',
111                            'connection', 'keep-alive', 'host', 
112                            'content-length']:
113        headers.append((name,[value]))
114
115    if name.lower() == 'host':
116        if ':' in value:
117            host,port = value.split(':',1)
118            if port == 443:
119                use_ssl = True
120        else:
121            host = value
122
123print('''
124# TODO: ensure the host, port, and SSL settings are correct.
125host = %s
126port = %s
127use_ssl = %s
128''' % (repr(host),repr(port),repr(use_ssl)))
129
130chunked_body = '\n            '.join([repr(body[i:i+40]) for i in range(0,len(body),40)])
131
132print('''
133def sendRequest(connection):
134    method = %s
135    path = %s
136    body = (%s)
137   
138    connection.putrequest(method, path)
139    ''' % (repr(method), repr(path), chunked_body))
140
141for name,values in headers:
142    if len(values) > 1:
143        continuations = ','.join([repr(v) for v in values[1:]])
144        print('''    connection.putheader(%s, %s, %s)''' % (repr(name),repr(values[0]),continuations))
145    else:
146        print('''    connection.putheader(%s, %s)''' % (repr(name),repr(values[0])))
147
148print('''   
149    if len(body) > 0:
150        connection.putheader('Content-Length', len(body))
151    connection.endheaders()
152    connection.send(body)
153   
154    return connection.getresponse()
155
156
157connection = None
158if use_ssl:
159    connection = httpc.HTTPSConnection(host, port)
160else:
161    connection = httpc.HTTPConnection(host, port)
162
163# TODO: customize code here to retrieve what you need from the response(s)
164# For information on the response object's interface, see:
165#   http://docs.python.org/library/httplib.html#httpresponse-objects
166
167connection.connect()
168response = sendRequest(connection)
169print(response.getheaders())
170print(repr(response.read()))
171connection.close()
172''')
Note: See TracBrowser for help on using the repository browser.