1
1
package burp ;
2
2
3
3
import java .util .List ;
4
- import java .util .Optional ;
5
4
import burp .api .montoya .http .message .HttpHeader ;
6
5
import burp .api .montoya .http .message .HttpRequestResponse ;
7
6
import burp .api .montoya .http .message .requests .HttpRequest ;
@@ -26,6 +25,7 @@ public String asPythonScript(boolean session, boolean imports, boolean main) {
26
25
27
26
if (imports ) {
28
27
if (main ) {
28
+ sb .append (buildPreamble ());
29
29
sb .append (buildImports ("aiohttp" , "asyncio" ));
30
30
} else {
31
31
sb .append (buildImports ("aiohttp" ));
@@ -42,13 +42,15 @@ public String asPythonScript(boolean session, boolean imports, boolean main) {
42
42
if (session ) {
43
43
sb .append (Utility .indent (1 ) + "async with aiohttp.ClientSession() as client:\n " );
44
44
for (int i = 0 ; i < this .messages .size (); i ++) {
45
- sb .append (Utility .indent (2 ) + "(status, headers, cookies, length, body) = await req" + i
45
+ sb .append (Utility .indent (2 )
46
+ + "(status, headers, cookies, length, body) = await req" + i
46
47
+ "_fetch(client)\n " );
47
48
sb .append (Utility .indent (2 ) + "# TODO: add your logic here\n " );
48
49
}
49
50
} else {
50
51
for (int i = 0 ; i < messages .size (); i ++) {
51
- sb .append (Utility .indent (1 ) + "(status, headers, cookies, length, body) = await req" + i
52
+ sb .append (Utility .indent (1 )
53
+ + "(status, headers, cookies, length, body) = await req" + i
52
54
+ "_fetch()\n " );
53
55
sb .append (Utility .indent (1 ) + "# TODO: add your logic here\n " );
54
56
}
@@ -62,6 +64,30 @@ public String asPythonScript(boolean session, boolean imports, boolean main) {
62
64
return sb .toString ();
63
65
}
64
66
67
+ /**
68
+ * Convert the requests to a Python script for password spraying
69
+ *
70
+ * @return The Python script as a string
71
+ */
72
+ public String asPasswordSprayingTemplate () {
73
+ StringBuilder sb = new StringBuilder ();
74
+ sb .append (buildPreamble ());
75
+ sb .append (buildImports ("aiohttp" , "aiofiles" , "aiocsv" , "argparse" , "asyncio" , "logging" ));
76
+ sb .append ("\n \n " );
77
+ sb .append (buildGlobals ());
78
+ sb .append ("\n \n " );
79
+ sb .append (parseRequests (true , 0 ));
80
+ sb .append ("\n \n " );
81
+ sb .append (buildSprayFunction (this .messages .size ()));
82
+ sb .append ("\n \n " );
83
+ sb .append (buildMainFunction ());
84
+ sb .append ("\n \n " );
85
+ sb .append ("if __name__ == '__main__':\n " );
86
+ sb .append (Utility .indent (1 ) + "loop = asyncio.get_event_loop()\n " );
87
+ sb .append (Utility .indent (1 ) + "loop.run_until_complete(main())\n " );
88
+ return sb .toString ();
89
+ }
90
+
65
91
/**
66
92
* Build the imports for the Python script
67
93
*
@@ -88,7 +114,7 @@ private String parseRequests(boolean session, int baseIndent) {
88
114
89
115
for (HttpRequestResponse message : this .messages ) {
90
116
HttpRequest request = message .request ();
91
- sb .append (requestToFunction (request , baseIndent , session , Optional . of ( reqIdx ) ));
117
+ sb .append (requestToFunction (request , baseIndent , session , reqIdx ));
92
118
// Add 2 new lines between requests
93
119
if (reqIdx < messages .size () - 1 ) {
94
120
sb .append ("\n \n " );
@@ -108,14 +134,16 @@ private String parseRequests(boolean session, int baseIndent) {
108
134
* @return The Python function
109
135
*/
110
136
private String requestToFunction (HttpRequest request , int baseIndent , boolean session ,
111
- Optional < Integer > requestIndex ) {
137
+ int requestIndex ) {
112
138
StringBuilder sb = new StringBuilder ();
113
- String prefix = requestIndex . isPresent () ? "req" + requestIndex . get () + "_" : "req_ " ;
139
+ String prefix = "req" + requestIndex + "_" ;
114
140
115
141
if (session ) {
116
- sb .append (Utility .indent (baseIndent ) + "async def " + prefix + "fetch(client):\n " );
142
+ sb .append (Utility .indent (baseIndent ) + "async def " + prefix
143
+ + "fetch(client, ssl=True, proxy=None):\n " );
117
144
} else {
118
- sb .append (Utility .indent (baseIndent ) + "async def " + prefix + "fetch():\n " );
145
+ sb .append (Utility .indent (baseIndent ) + "async def " + prefix
146
+ + "fetch(ssl=True, proxy=None):\n " );
119
147
}
120
148
sb .append (Utility .indent (baseIndent + 1 ) + "url = '" + request .url () + "'\n " );
121
149
sb .append (Utility .indent (baseIndent + 1 ) + "method = '" + request .method () + "'\n " );
@@ -133,10 +161,10 @@ private String requestToFunction(HttpRequest request, int baseIndent, boolean se
133
161
// Make the request
134
162
if (session ) {
135
163
sb .append (Utility .indent (baseIndent + 1 )
136
- + "async with client.request(method, url, headers=headers, cookies=cookies, data=data, allow_redirects=False) as response:\n " );
164
+ + "async with client.request(method, url, headers=headers, cookies=cookies, data=data, allow_redirects=False, ssl=ssl, proxy=proxy ) as response:\n " );
137
165
} else {
138
166
sb .append (Utility .indent (baseIndent + 1 )
139
- + "async with aiohttp.request(method, url, headers=headers, cookies=cookies, data=data, allow_redirects=False) as response:\n " );
167
+ + "async with aiohttp.request(method, url, headers=headers, cookies=cookies, data=data, allow_redirects=False, ssl=ssl, proxy=proxy ) as response:\n " );
140
168
}
141
169
sb .append (Utility .indent (baseIndent + 2 )
142
170
+ "return (response.status, response.headers, response.cookies, response.headers.get('content-length', 0), await response.text())\n " );
@@ -204,4 +232,147 @@ private String parseHeaders(HttpRequest request, int baseIndent) {
204
232
}
205
233
return sb .toString ();
206
234
}
235
+
236
+ /**
237
+ * Build the preamble for the Python script
238
+ *
239
+ * @return The preamble as a string
240
+ */
241
+ private String buildPreamble () {
242
+ StringBuilder sb = new StringBuilder ();
243
+ sb .append ("#!/usr/bin/env python3\n " );
244
+ sb .append ("# -*- coding: utf-8 -*-\n " );
245
+ sb .append ("\" \" \" \n " );
246
+ sb .append ("Template generated by \" Copy as Python aiohttp\" Burp extension.\n " );
247
+ sb .append ("See: https://github.com/y0k4i-1337/copy-as-python-aiohttp\n " );
248
+ sb .append ("\" \" \" \n " );
249
+ return sb .toString ();
250
+ }
251
+
252
+ /**
253
+ * Build the globals for the Python script
254
+ *
255
+ * @return The globals as a string
256
+ */
257
+ private String buildGlobals () {
258
+ StringBuilder sb = new StringBuilder ();
259
+ sb .append ("JAR_UNSAFE = True\n " );
260
+ sb .append ("CLIENT_TOTAL_TIMEOUT = 60\n " );
261
+ sb .append ("CONN_POOL_SIZE = 10\n " );
262
+ sb .append ("CONN_TTL_DNS_CACHE = 300\n " );
263
+ sb .append ("SSL_VERIFY = False\n " );
264
+ sb .append ("LOG_LEVEL = logging.INFO\n \n " );
265
+ sb .append (
266
+ "logging.basicConfig(level=LOG_LEVEL, format=\" %(asctime)s - %(levelname)s - %(message)s\" )\n " );
267
+ sb .append ("timeout = aiohttp.ClientTimeout(total=CLIENT_TOTAL_TIMEOUT)\n " );
268
+ sb .append ("conn = aiohttp.TCPConnector(\n " );
269
+ sb .append (Utility .indent (1 )
270
+ + "limit_per_host=CONN_POOL_SIZE, ttl_dns_cache=CONN_TTL_DNS_CACHE\n " );
271
+ sb .append (")\n " );
272
+ return sb .toString ();
273
+ }
274
+
275
+ /**
276
+ * Build the spray function for the Python script
277
+ *
278
+ * @param messageCount The number of messages
279
+ * @return The spray function as a string
280
+ */
281
+ private String buildSprayFunction (int messageCount ) {
282
+ StringBuilder sb = new StringBuilder ();
283
+ sb .append ("async def spray(username, password, proxy=None):\n " );
284
+ sb .append (Utility .indent (1 ) + "jar = aiohttp.CookieJar(unsafe=JAR_UNSAFE)\n " );
285
+ sb .append (Utility .indent (1 ) + "try:\n " );
286
+ sb .append (Utility .indent (2 ) + "async with aiohttp.ClientSession(\n " );
287
+ sb .append (Utility .indent (3 )
288
+ + "timeout=timeout, cookie_jar=jar, connector=conn, connector_owner=False\n " );
289
+ sb .append (Utility .indent (2 ) + ") as client:\n " );
290
+ for (int i = 0 ; i < messageCount ; i ++) {
291
+ sb .append (Utility .indent (3 ) + "(status, headers, cookies, length, body) = await req" + i
292
+ + "_fetch(client, ssl=SSL_VERIFY, proxy=proxy)\n " );
293
+ sb .append (Utility .indent (3 ) + "# TODO: add your logic here\n " );
294
+ }
295
+ sb .append (Utility .indent (3 ) + "result = {\n " );
296
+ sb .append (Utility .indent (4 ) + "'username': username,\n " );
297
+ sb .append (Utility .indent (4 ) + "'password': password,\n " );
298
+ sb .append (Utility .indent (4 ) + "'status': status,\n " );
299
+ sb .append (Utility .indent (4 ) + "'content-length': length,\n " );
300
+ sb .append (Utility .indent (3 ) + "}\n " );
301
+ sb .append (Utility .indent (1 ) + "except Exception as e:\n " );
302
+ sb .append (Utility .indent (2 ) + "result = {\n " );
303
+ sb .append (Utility .indent (3 ) + "'username': username,\n " );
304
+ sb .append (Utility .indent (3 ) + "'password': password,\n " );
305
+ sb .append (Utility .indent (3 ) + "'error': str(e),\n " );
306
+ sb .append (Utility .indent (2 ) + "}\n " );
307
+ sb .append (Utility .indent (1 ) + "return result\n " );
308
+ return sb .toString ();
309
+ }
310
+
311
+ /**
312
+ * Build the main function for the Python script
313
+ *
314
+ * @return The main function as a string
315
+ */
316
+ private String buildMainFunction () {
317
+ StringBuilder sb = new StringBuilder ();
318
+ sb .append ("async def main():\n " );
319
+ sb .append (Utility .indent (1 )
320
+ + "parser = argparse.ArgumentParser(description='Password spraying script')\n " );
321
+ sb .append (Utility .indent (1 )
322
+ + "parser.add_argument('-x', '--proxy', help='Proxy URL', default=None)\n " );
323
+ sb .append (Utility .indent (1 ) + "parser.add_argument(\n " );
324
+ sb .append (Utility .indent (2 )
325
+ + "'-e', '--errors', help='File to save errors', default='errors.txt'\n " );
326
+ sb .append (Utility .indent (1 ) + ")\n " );
327
+ sb .append (Utility .indent (1 ) + "parser.add_argument(\n " );
328
+ sb .append (Utility .indent (2 )
329
+ + "'-o', '--output', help='File to save successful requests', default='results.csv'\n " );
330
+ sb .append (Utility .indent (1 ) + ")\n " );
331
+ sb .append (Utility .indent (1 )
332
+ + "parser.add_argument('usernames', help='File containing usernames')\n " );
333
+ sb .append (Utility .indent (1 )
334
+ + "parser.add_argument('passwords', help='File containing passwords')\n " );
335
+ sb .append (Utility .indent (1 ) + "args = parser.parse_args()\n " );
336
+ sb .append (Utility .indent (1 ) + "usernames = []\n " );
337
+ sb .append (Utility .indent (1 ) + "passwords = []\n " );
338
+ sb .append (Utility .indent (1 ) + "requested = []\n " );
339
+ sb .append (Utility .indent (1 ) + "errors = []\n \n " );
340
+ sb .append (Utility .indent (1 ) + "async with aiofiles.open(args.usernames, mode='r') as f:\n " );
341
+ sb .append (Utility .indent (2 ) + "async for line in f:\n " );
342
+ sb .append (Utility .indent (3 ) + "usernames.append(line.strip())\n \n " );
343
+ sb .append (Utility .indent (1 ) + "async with aiofiles.open(args.passwords, mode='r') as f:\n " );
344
+ sb .append (Utility .indent (2 ) + "async for line in f:\n " );
345
+ sb .append (Utility .indent (3 ) + "passwords.append(line.strip())\n \n " );
346
+ sb .append (Utility .indent (1 ) + "tasks = []\n " );
347
+ sb .append (Utility .indent (1 ) + "for password in passwords:\n " );
348
+ sb .append (Utility .indent (2 ) + "for username in usernames:\n " );
349
+ sb .append (Utility .indent (3 ) + "tasks.append(spray(username, password, args.proxy))\n " );
350
+ sb .append (Utility .indent (1 ) + "logging.info('Spraying %d combinations', len(tasks))\n \n " );
351
+ sb .append (Utility .indent (1 ) + "results = await asyncio.gather(*tasks)\n " );
352
+ sb .append (Utility .indent (1 ) + "for result in results:\n " );
353
+ sb .append (Utility .indent (2 ) + "if 'error' in result.keys():\n " );
354
+ sb .append (Utility .indent (3 ) + "errors.append(result)\n " );
355
+ sb .append (Utility .indent (2 ) + "else:\n " );
356
+ sb .append (Utility .indent (3 ) + "requested.append(result)\n \n " );
357
+ sb .append (Utility .indent (1 ) + "if len(requested) > 0:\n " );
358
+ sb .append (Utility .indent (2 ) + "async with aiofiles.open(args.output, 'w') as f:\n " );
359
+ sb .append (Utility .indent (3 ) + "fieldnames = requested[0].keys()\n " );
360
+ sb .append (Utility .indent (3 )
361
+ + "writer = aiocsv.AsyncDictWriter(f, fieldnames=fieldnames, dialect='excel')\n " );
362
+ sb .append (Utility .indent (3 ) + "await writer.writeheader()\n " );
363
+ sb .append (Utility .indent (3 ) + "await writer.writerows(requested)\n " );
364
+ sb .append (Utility .indent (2 )
365
+ + "logging.info('Saved %d successful requests to %s', len(requested), args.output)\n " );
366
+ sb .append (Utility .indent (1 ) + "if len(errors) > 0:\n " );
367
+ sb .append (Utility .indent (2 ) + "async with aiofiles.open(args.errors, 'w') as f:\n " );
368
+ sb .append (Utility .indent (3 ) + "for error in errors:\n " );
369
+ sb .append (Utility .indent (4 )
370
+ + "await f.write(f\" {error['username']}:{error['password']} - {error['error']}\" )\n " );
371
+ sb .append (Utility .indent (2 )
372
+ + "logging.info('Saved %d errors to %s', len(errors), args.errors)\n \n " );
373
+ sb .append (Utility .indent (1 ) + "logging.info('Completed tasks: %d', len(requested))\n " );
374
+ sb .append (Utility .indent (1 ) + "logging.info('Errors: %d', len(errors))\n " );
375
+
376
+ return sb .toString ();
377
+ }
207
378
}
0 commit comments