From e6d0d5a1c77ecded2dabbb022ece209de6623fcf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Jul 2014 22:27:51 +0200 Subject: [PATCH] Implementation for an Issue #674 --- lib/core/option.py | 51 ++++++++++++++++++++++++++++++++++------- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 6 +++-- lib/parse/configfile.py | 3 ++- sqlmap.conf | 4 ++++ 5 files changed, 54 insertions(+), 11 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index c9fe326fa..f3177acbc 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -135,6 +135,7 @@ from lib.core.threads import getCurrentThreadData from lib.core.update import update from lib.parse.configfile import configFileParser from lib.parse.payloads import loadPayloads +from lib.parse.sitemap import parseSitemap from lib.request.basic import checkCharEncoding from lib.request.connect import Connect as Request from lib.request.dns import DNSServer @@ -504,10 +505,13 @@ def _setCrawler(): if not conf.crawlDepth: return - if not conf.bulkFile: + if not any((conf.bulkFile, conf.sitemapUrl)): crawl(conf.url) else: - targets = getFileItems(conf.bulkFile) + if conf.bulkFile: + targets = getFileItems(conf.bulkFile) + else: + targets = parseSitemap(conf.sitemapUrl) for i in xrange(len(targets)): try: target = targets[i] @@ -618,10 +622,33 @@ def _setBulkMultipleTargets(): errMsg += "does not exist" raise SqlmapFilePathException(errMsg) + found = False for line in getFileItems(conf.bulkFile): if re.match(r"[^ ]+\?(.+)", line, re.I) or CUSTOM_INJECTION_MARK_CHAR in line: + found = True kb.targets.add((line.strip(), None, None, None)) + if not found and not conf.forms and not conf.crawlDepth: + warnMsg = "no usable links found (with GET parameters)" + logger.warn(warnMsg) + +def _setSitemapTargets(): + if not conf.sitemapUrl: + return + + infoMsg = "parsing sitemap '%s'" % conf.sitemapUrl + logger.info(infoMsg) + + found = False + for item in parseSitemap(conf.sitemapUrl): + if re.match(r"[^ ]+\?(.+)", item, re.I): + found = True + kb.targets.add((item.strip(), None, None, None)) + + if not found and not conf.forms and not conf.crawlDepth: + warnMsg = "no usable links found (with GET parameters)" + logger.warn(warnMsg) + def _findPageForms(): if not conf.forms or conf.crawlDepth: return @@ -632,11 +659,14 @@ def _findPageForms(): infoMsg = "searching for forms" logger.info(infoMsg) - if not conf.bulkFile: + if not any((conf.bulkFile, conf.sitemapUrl)): page, _ = Request.queryPage(content=True) findPageForms(page, conf.url, True, True) else: - targets = getFileItems(conf.bulkFile) + if conf.bulkFile: + targets = getFileItems(conf.bulkFile) + else: + targets = parseSitemap(conf.sitemapUrl) for i in xrange(len(targets)): try: target = targets[i] @@ -1449,13 +1479,16 @@ def _cleanupOptions(): if conf.dFile: conf.dFile = ntToPosixSlashes(normalizePath(conf.dFile)) + if conf.sitemapUrl and not conf.sitemapUrl.lower().startswith("http"): + conf.sitemapUrl = "http%s://%s" % ('s' if conf.forceSSL else '', conf.sitemapUrl) + if conf.msfPath: conf.msfPath = ntToPosixSlashes(normalizePath(conf.msfPath)) if conf.tmpPath: conf.tmpPath = ntToPosixSlashes(normalizePath(conf.tmpPath)) - if conf.googleDork or conf.logFile or conf.bulkFile or conf.forms or conf.crawlDepth: + if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.forms, conf.crawlDepth)): conf.multipleTargets = True if conf.optimize: @@ -1631,6 +1664,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.extendTests = None kb.errorIsNone = True kb.fileReadMode = False + kb.followSitemapRecursion = None kb.forcedDbms = None kb.forcePartialUnion = False kb.headersFp = {} @@ -2130,8 +2164,8 @@ def _basicOptionValidation(): errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS raise SqlmapSyntaxException(errMsg) - if conf.forms and not any((conf.url, conf.bulkFile)): - errMsg = "switch '--forms' requires usage of option '-u' ('--url') or '-m'" + if conf.forms and not any((conf.url, conf.bulkFile, conf.sitemapUrl)): + errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-m' or '-x'" raise SqlmapSyntaxException(errMsg) if conf.requestFile and conf.url and conf.url != DUMMY_URL: @@ -2266,7 +2300,7 @@ def init(): parseTargetUrl() parseTargetDirect() - if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.liveTest)): + if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)): _setHTTPTimeout() _setHTTPExtraHeaders() _setHTTPCookies() @@ -2279,6 +2313,7 @@ def init(): _setSafeUrl() _setGoogleDorking() _setBulkMultipleTargets() + _setSitemapTargets() _urllib2Opener() _checkTor() _setCrawler() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 9a1c0e450..cfbd02b67 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -19,6 +19,7 @@ optDict = { "sessionFile": "string", "googleDork": "string", "configFile": "string", + "sitemapUrl": "string", }, "Request": { diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 292482348..74e06f321 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -75,6 +75,8 @@ def cmdLineParser(): target.add_option("-c", dest="configFile", help="Load options from a configuration INI file") + target.add_option("-x", dest="sitemapUrl", help="Load target URLs from remote sitemap(.xml) file") + # Request options request = OptionGroup(parser, "Request", "These options can be used " "to specify how to connect to the target URL") @@ -807,8 +809,8 @@ def cmdLineParser(): if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, \ args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, \ - args.purgeOutput, args.pickledOptions)): - errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --wizard, --update, --purge-output or --dependencies), " + args.purgeOutput, args.pickledOptions, args.sitemapUrl)): + errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --wizard, --update, --purge-output or --dependencies), " errMsg += "use -h for basic or -hh for advanced help" parser.error(errMsg) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 3715a3c97..ec8f72100 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -84,11 +84,12 @@ def configFileParser(configFile): condition &= not config.has_option("Target", "bulkFile") condition &= not config.has_option("Target", "googleDork") condition &= not config.has_option("Target", "requestFile") + condition &= not config.has_option("Target", "sitemapUrl") condition &= not config.has_option("Target", "wizard") if condition: errMsg = "missing a mandatory option in the configuration file " - errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile or wizard)" + errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile, sitemapUrl or wizard)" raise SqlmapMissingMandatoryOptionException(errMsg) for family, optionData in optDict.items(): diff --git a/sqlmap.conf b/sqlmap.conf index 984def442..9ebc3037c 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -32,6 +32,10 @@ requestFile = # Example: +ext:php +inurl:"&id=" +intext:"powered by " googleDork = +# Load target URLs from remote sitemap(.xml) file +# Example: http://192.168.1.121/sitemap.xml +sitemapUrl = + # These options can be used to specify how to connect to the target URL. [Request]