source: trunk/bin/bletchley-http2py @ 22

Last change on this file since 22 was 14, checked in by tmorgan, 12 years ago

better script formatting for large bodies
default to port 80

  • Property svn:executable set to *
File size: 4.9 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(description='Process some integers.')
44parser.add_argument('requestfile', type=file, nargs='?', default=sys.stdin, 
45                    help='A file containing an HTTP request.  Defaults to stdin if omitted.')
46parser.add_argument('--burp', action='store_true', help='Input file is a XML export from Burp. (First request in file is used.)')
47args = parser.parse_args()
48
49if args.burp:
50    safe_parser = etree.ETCompatXMLParser(resolve_entities=False)
51    root = etree.parse(args.requestfile, parser=safe_parser)
52    input_req = root.xpath('/items/item/request')[0].text
53    root = None
54else:
55    input_req = args.requestfile.read()
56
57
58print('''#!/usr/bin/env python
59
60import sys
61# function with either Python 2.7 or 3.x
62try:
63    import http.client as httpc
64except:
65    import httplib as httpc
66''')
67
68
69if '\r\n\r\n' in input_req:
70    raw_headers,body = input_req.split('\r\n\r\n', 1)
71elif '\n\n' in input_req:
72    raw_headers,body = input_req.split('\n\n', 1)
73else:
74    raw_headers = input_req
75    body = ''
76
77header_lines = raw_headers.split('\n')
78method,path,version = header_lines[0].split(' ', 2)
79
80host = 'TODO'
81port = 80
82use_ssl = False
83
84headers = []
85for l in header_lines[1:]:
86    if len(l) < 1: 
87        break
88    # Handle header line continuations
89    if l[0] == '\t':
90        if len(headers) == 0:
91            continue
92        name,values = headers[-1]
93        values.append(l.lstrip('\t'))
94        headers[-1] = (name,values)
95        continue
96
97    name,value = l.split(':',1)
98    value = value.lstrip(' ').rstrip('\r')
99
100    # Skip headers that have to do with transfer encodings and connection longevity
101    if name.lower() not in ['accept','accept-language',
102                            'accept-encoding','accept-charset',
103                            'connection', 'keep-alive', 'host', 
104                            'content-length']:
105        headers.append((name,[value]))
106
107    if name.lower() == 'host':
108        if ':' in value:
109            host,port = value.split(':',1)
110            if port == 443:
111                use_ssl = True
112        else:
113            host = value
114
115print('''
116# TODO: ensure the host, port, and SSL settings are correct.
117host = %s
118port = %s
119use_ssl = %s
120''' % (repr(host),repr(port),repr(use_ssl)))
121
122chunked_body = '\n            '.join([repr(body[i:i+40]) for i in range(0,len(body),40)])
123
124print('''
125def sendRequest(connection):
126    method = %s
127    path = %s
128    body = (%s)
129   
130    connection.putrequest(method, path)
131    ''' % (repr(method), repr(path), chunked_body))
132
133for name,values in headers:
134    if len(values) > 1:
135        continuations = ','.join([repr(v) for v in values[1:]])
136        print('''    connection.putheader(%s, %s, %s)''' % (repr(name),repr(values[0]),continuations))
137    else:
138        print('''    connection.putheader(%s, %s)''' % (repr(name),repr(values[0])))
139
140print('''   
141    if len(body) > 0:
142        connection.putheader('Content-Length', len(body))
143    connection.endheaders()
144    connection.send(body)
145   
146    return connection.getresponse()
147
148
149connection = None
150if use_ssl:
151    connection = httpc.HTTPSConnection(host, port)
152else:
153    connection = httpc.HTTPConnection(host, port)
154
155# TODO: customize code here to retrieve what you need from the response(s)
156# For information on the response object's interface, see:
157#   http://docs.python.org/library/httplib.html#httpresponse-objects
158
159connection.connect()
160response = sendRequest(connection)
161print(response.getheaders())
162print(repr(response.read()))
163connection.close()
164''')
Note: See TracBrowser for help on using the repository browser.