source: trunk/bin/bletchley-http2py @ 71

Last change on this file since 71 was 71, checked in by tim, 10 years ago

chosenct and http2py convenience fixes

  • Property svn:executable set to *
File size: 7.5 KB
Line 
1#!/usr/bin/env python3
2
3# Requires Python 3+
4
5'''
6This script reads a raw HTTP request and writes to stdout a Python
7script.  The generated script sends the same (or a very similar)
8request using the standard httplib/http.client library, or optionally
9using the more user friendly python-requests library.
10
11Certainly if you have a raw request, you could simply send it via TCP
12sockets, but if for some reason the server behaves oddly with flow control,
13insists on using gzip/deflate encoding, insists on using chunked encoding,
14or any number of other annoying things, then using an HTTP library is a
15lot more convenient.  This script attempts to make that conversion easy.
16
17
18Copyright (C) 2011-2013 Virtual Security Research, LLC
19Author: Timothy D. Morgan
20
21 This program is free software: you can redistribute it and/or modify
22 it under the terms of the GNU Lesser General Public License, version 3,
23 as published by the Free Software Foundation.
24
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28 GNU General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
31 along with this program.  If not, see <http://www.gnu.org/licenses/>.
32'''
33
34
35import sys
36import argparse
37
38bopen = lambda f: open(f, 'rb')
39
40parser = argparse.ArgumentParser(
41    description='A script which accepts an HTTP request and prints out a'
42    ' generated Python script which sends a similar request.  This is useful'
43    ' when one wants to automate sending a large number of requests to a'
44    ' particular page or application.'
45    ' For more information, see: http://code.google.com/p/bletchley/wiki/Overview')
46parser.add_argument(
47    'requestfile', type=bopen, nargs='?', default=sys.stdin.buffer, 
48    help='A file containing an HTTP request.  Defaults to stdin if omitted.')
49parser.add_argument(
50    '--requests', action='store_true', help='Generate a script that uses the'
51    ' python-requests module rather than httplib/http.client (experimental).')
52
53args = parser.parse_args()
54input_req = args.requestfile.read()
55
56
57if b'\r\n\r\n' in input_req:
58    raw_headers,body = input_req.split(b'\r\n\r\n', 1)
59elif b'\n\n' in input_req:
60    raw_headers,body = input_req.split(b'\n\n', 1)
61else:
62    raw_headers = input_req
63    body = b''
64
65raw_headers = raw_headers.decode('utf-8')
66
67header_lines = raw_headers.split('\n')
68method,path,version = header_lines[0].split(' ', 2)
69
70host = 'TODO'
71port = 80
72use_ssl = False
73protocol = 'http'
74
75headers = []
76for l in header_lines[1:]:
77    if len(l) < 1: 
78        break
79    # Handle header line continuations
80    if l[0] in ' \t':
81        if len(headers) == 0:
82            continue
83        name,values = headers[-1]
84        values.append(l.lstrip('\t'))
85        headers[-1] = (name,values)
86        continue
87
88    name,value = l.split(':',1)
89    value = value.lstrip(' ').rstrip('\r')
90
91    # Skip headers that have to do with transfer encodings and connection longevity
92    if name.lower() not in ['accept','accept-language',
93                            'accept-encoding','accept-charset',
94                            'connection', 'keep-alive', 'host', 
95                            'content-length', 'proxy-connection']:
96        headers.append((name,[value]))
97
98    if name.lower() == 'host':
99        if ':' in value:
100            host,port = value.split(':',1)
101            port = int(port, 10)
102            if port == 443:
103                use_ssl = True
104                protocol = 'https'
105        else:
106            host = value
107
108
109formatted_body = '\n            '.join([repr(body[i:i+40]) for i in range(0,len(body),40)])
110if formatted_body == '':
111    formatted_body = "b''"
112
113
114if args.requests:
115    print('''#!/usr/bin/env python3
116
117import sys
118try:
119    import requests
120except:
121    sys.stderr.write('ERROR: Could not import requests module.  Ensure it is installed.\\n')
122    sys.stderr.write('       Under Debian, the package name is "python3-requests"\\n.')
123    sys.exit(1)
124
125# from bletchley import blobtools,buffertools
126# from bletchley import chosenct
127# from bletchley.CBC import *
128
129
130# TODO: ensure the host, port, and SSL settings are correct.
131host = %s
132port = %s
133protocol = %s
134''' % (repr(host),repr(port),repr(protocol)))
135
136    headers = dict(headers)
137    # XXX: We don't currently support exactly formatted header
138    #      continuations with python requests, but this should be
139    #      semantically equivalent.
140    for h in headers.keys():
141        headers[h] = ' '.join(headers[h])
142
143    print('''
144session = requests.Session()
145# TODO: use "data" to supply any parameters to be included in the request
146def sendRequest(session, data=None):
147    method = %s
148    path = %s
149    headers = %s
150    url = "%%s://%%s:%%d%%s" %% (protocol,host,port,path)
151    body = (%s)
152
153    return session.request(method, url, headers=headers, data=body, allow_redirects=False)
154    ''' % (repr(method), repr(path), repr(headers), formatted_body))
155
156    print('''   
157
158def fetch(data):
159    global session
160    ret_val = None
161
162    # TODO: customize code here to retrieve what you need from the response(s)
163    # For information on the response object's interface, see:
164    #   http://docs.python-requests.org/en/latest/api/#requests.Response
165    response = sendRequest(session, data)
166    print(response.headers)
167    print(repr(response.content))
168
169    return ret_val
170
171data = ''
172fetch(data)
173''')
174
175
176
177else:
178    print('''#!/usr/bin/env python3
179
180import sys
181import http.client as httpc
182# from bletchley import blobtools,buffertools
183# from bletchley.CBC import *
184
185
186# TODO: ensure the host, port, and SSL settings are correct.
187host = %s
188port = %s
189use_ssl = %s
190''' % (repr(host),repr(port),repr(use_ssl)))
191
192    print('''
193# TODO: use "data" to supply any parameters to be included in the request
194def sendRequest(connection, data=None):
195    method = %s
196    path = %s
197    body = (%s)
198   
199    connection.putrequest(method, path)
200    ''' % (repr(method), repr(path), formatted_body))
201
202    for name,values in headers:
203        if len(values) > 1:
204            continuations = ','.join([repr(v) for v in values[1:]])
205            print('''    connection.putheader(%s, %s, %s)''' % (repr(name),repr(values[0]),continuations))
206        else:
207            print('''    connection.putheader(%s, %s)''' % (repr(name),repr(values[0])))
208
209    print('''   
210    if len(body) > 0:
211        connection.putheader('Content-Length', len(body))
212    connection.endheaders()
213    connection.send(body)
214   
215    return connection.getresponse()
216
217
218def newConnection():
219    if use_ssl:
220        return httpc.HTTPSConnection(host, port)
221    else:
222        return httpc.HTTPConnection(host, port)
223
224
225def fetch(data, other=None):
226    ret_val = False
227    connection = newConnection()
228
229    # TODO: customize code here to retrieve what you need from the response(s)
230    # For information on the response object's interface, see:
231    #   http://docs.python.org/library/httplib.html#httpresponse-objects
232    response = sendRequest(connection, data)
233    print(response.getheaders())
234    print(repr(response.read()))
235
236    connection.close()
237    return ret_val
238
239data = ''
240fetch(data)
241''')
242
243print('''
244
245# Padding Oracle Attacks
246# ciphertext = blobtools.decode('{ encoding }', data)
247# poa = POA(fetch, {block size}, ciphertext, threads=1, log_file=sys.stderr)
248# print(poa.probe_padding()) # sanity check
249# print(poa.decrypt())
250
251# Byte-by-byte probing of ciphertext
252# ciphertext = blobtools.decode('{ encoding }', data)
253# result = chosenct.probe_bytes(fetch, ciphertext, [1,2,4,8,16,32,64,128], max_threads=5)
254# print(result.toHTML())
255''')
Note: See TracBrowser for help on using the repository browser.