source: trunk/bin/bletchley-http2py @ 68

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

more crib notes

  • Property svn:executable set to *
File size: 7.4 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.CBC import *
127
128
129# TODO: ensure the host, port, and SSL settings are correct.
130host = %s
131port = %s
132protocol = %s
133''' % (repr(host),repr(port),repr(protocol)))
134
135    headers = dict(headers)
136    # XXX: We don't currently support exactly formatted header
137    #      continuations with python requests, but this should be
138    #      semantically equivalent.
139    for h in headers.keys():
140        headers[h] = ' '.join(headers[h])
141
142    print('''
143session = requests.Session()
144# TODO: use "data" to supply any parameters to be included in the request
145def sendRequest(session, data=None):
146    method = %s
147    path = %s
148    headers = %s
149    url = "%%s://%%s:%%d%%s" %% (protocol,host,port,path)
150    body = (%s)
151
152    return session.request(method, url, headers=headers, data=body)
153    ''' % (repr(method), repr(path), repr(headers), formatted_body))
154
155    print('''   
156
157def fetch(data):
158    global session
159    ret_val = None
160
161    # TODO: customize code here to retrieve what you need from the response(s)
162    # For information on the response object's interface, see:
163    #   http://docs.python-requests.org/en/latest/api/#requests.Response
164    response = sendRequest(session, data)
165    print(response.headers)
166    print(repr(response.content))
167
168    return ret_val
169
170data = ''
171fetch(data)
172''')
173
174
175
176else:
177    print('''#!/usr/bin/env python3
178
179import sys
180import http.client as httpc
181# from bletchley import blobtools,buffertools
182# from bletchley.CBC import *
183
184
185# TODO: ensure the host, port, and SSL settings are correct.
186host = %s
187port = %s
188use_ssl = %s
189''' % (repr(host),repr(port),repr(use_ssl)))
190
191    print('''
192# TODO: use "data" to supply any parameters to be included in the request
193def sendRequest(connection, data=None):
194    method = %s
195    path = %s
196    body = (%s)
197   
198    connection.putrequest(method, path)
199    ''' % (repr(method), repr(path), formatted_body))
200
201    for name,values in headers:
202        if len(values) > 1:
203            continuations = ','.join([repr(v) for v in values[1:]])
204            print('''    connection.putheader(%s, %s, %s)''' % (repr(name),repr(values[0]),continuations))
205        else:
206            print('''    connection.putheader(%s, %s)''' % (repr(name),repr(values[0])))
207
208    print('''   
209    if len(body) > 0:
210        connection.putheader('Content-Length', len(body))
211    connection.endheaders()
212    connection.send(body)
213   
214    return connection.getresponse()
215
216
217def newConnection():
218    if use_ssl:
219        return httpc.HTTPSConnection(host, port)
220    else:
221        return httpc.HTTPConnection(host, port)
222
223
224def fetch(data):
225    ret_val = False
226    connection = newConnection()
227
228    # TODO: customize code here to retrieve what you need from the response(s)
229    # For information on the response object's interface, see:
230    #   http://docs.python.org/library/httplib.html#httpresponse-objects
231    response = sendRequest(connection, data)
232    print(response.getheaders())
233    print(repr(response.read()))
234
235    connection.close()
236    return ret_val
237
238data = ''
239fetch(data)
240''')
241
242print('''
243
244# Padding Oracle Attacks
245# ciphertext = blobtools.decode('{ encoding }', data)
246# poa = POA(fetch, {block size}, ciphertext, threads=1, log_file=sys.stderr)
247# print(poa.probe_padding()) # sanity check
248# print(poa.decrypt())
249
250# Byte-by-byte probing of ciphertext
251# ciphertext = blobtools.decode('{ encoding }', data)
252# result = chosenct.probe_bytes(fetch, data, [1,2,4,8,16,32,64,128], max_threads=5)
253# print(result.toHTML())
254''')
Note: See TracBrowser for help on using the repository browser.