From 9a34282aa30c0c4680fb145db31c2fe77a828e3e Mon Sep 17 00:00:00 2001 From: David Donn Date: Tue, 14 Sep 2021 17:17:21 +1000 Subject: [PATCH] get operations with query parameters --- lib/core/common.py | 51 +++++++++++++++++++++++++++++++++++++++++ lib/core/option.py | 32 +++++++++++++++++++++++--- lib/core/optiondict.py | 2 ++ lib/parse/cmdline.py | 6 +++++ lib/parse/configfile.py | 4 ++-- sqlmap.conf | 6 +++++ 6 files changed, 96 insertions(+), 5 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ec82ad733..55d8e1b44 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -36,6 +36,7 @@ import threading import time import types import unicodedata +import json from difflib import SequenceMatcher from math import sqrt @@ -5362,6 +5363,53 @@ def parseRequestFile(reqFile, checkParams=True): if not(conf.scope and not re.search(conf.scope, url, re.I)): yield (url, conf.method or method, data, cookie, tuple(headers)) + def _parseSwagger(content): + """ + Parses Swagger OpenAPI 3.x.x JSON documents + """ + + try: + swagger = json.loads(content) + logger.debug("swagger OpenAPI version '%s'" % swagger["openapi"]) + + for path in swagger["paths"]: + for operation in swagger["paths"][path]: + op = swagger["paths"][path][operation] + + tags = conf.swaggerTags.split(",") if conf.swaggerTags is not None else None + + if ((tags is None or any(tag in op["tags"] for tag in tags)) + and operation == "get"): + + url = None + method = None + data = None + cookie = None + + url = "%s%s" % (swagger["servers"][0]["url"], path) + method = operation.upper() + q = list(filter(lambda p: (p["in"] == "query"), op["parameters"])) + qs = "" + for qp in q: + qs += "&%s=%s" %(qp["name"], qp["example"]) + qs = qs.replace('&', '?', 1) + + if op["parameters"] is not None and len(q) > 0: + url += qs + + logger.debug("swagger url '%s', method '%s', data '%s', cookie '%s'" %(url, method, data, cookie)) + yield (url, method, data, cookie, None) + + else: + logger.info("excluding url '%s', method '%s' as target since there are no parameters to inject" %(url, method)) + + + except json.decoder.JSONDecodeError: + errMsg = "swagger file is not valid JSON" + raise SqlmapSyntaxException(errMsg) + + + content = readCachedFileContent(reqFile) if conf.scope: @@ -5373,6 +5421,9 @@ def parseRequestFile(reqFile, checkParams=True): for target in _parseWebScarabLog(content): yield target + for target in _parseSwagger(content): + yield target + def getSafeExString(ex, encoding=None): """ Safe way how to get the proper exception represtation as a string diff --git a/lib/core/option.py b/lib/core/option.py index 3b6a1ceab..8240f4225 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -477,6 +477,31 @@ def _setBulkMultipleTargets(): warnMsg = "no usable links found (with GET parameters)" logger.warn(warnMsg) +def _setSwaggerMultipleTargets(): + if not conf.swaggerFile: + return + + infoMsg = "parsing multiple targets from swagger '%s'" % conf.swaggerFile + logger.info(infoMsg) + + if not os.path.exists(conf.swaggerFile): + errMsg = "the specified list of targets does not exist" + raise SqlmapFilePathException(errMsg) + + if checkFile(conf.swaggerFile, False): + debugMsg = "swagger file '%s' checks out" % conf.swaggerFile + logger.debug(debugMsg) + + for target in parseRequestFile(conf.swaggerFile): + kb.targets.add(target) + + else: + errMsg = "the specified list of targets is not a file " + errMsg += "nor a directory" + raise SqlmapFilePathException(errMsg) + + + def _findPageForms(): if not conf.forms or conf.crawlDepth: return @@ -2677,7 +2702,7 @@ 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.googleDork, conf.bulkFile)): + if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile, conf.swaggerFile)): errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g' or '-m'" raise SqlmapSyntaxException(errMsg) @@ -2787,7 +2812,7 @@ def _basicOptionValidation(): errMsg = "value for option '--union-char' must be an alpha-numeric value (e.g. 1)" raise SqlmapSyntaxException(errMsg) - if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.wizard, conf.dependencies, conf.purge, conf.listTampers)): + if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.swaggerFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.wizard, conf.dependencies, conf.purge, conf.listTampers)): errMsg = "option '--crack' should be used as a standalone" raise SqlmapSyntaxException(errMsg) @@ -2855,7 +2880,7 @@ def init(): parseTargetDirect() - if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.stdinPipe)): + if any((conf.url, conf.logFile, conf.bulkFile, conf.swaggerFile, conf.requestFile, conf.googleDork, conf.stdinPipe)): _setHostname() _setHTTPTimeout() _setHTTPExtraHeaders() @@ -2871,6 +2896,7 @@ def init(): _doSearch() _setStdinPipeTargets() _setBulkMultipleTargets() + _setSwaggerMultipleTargets() _checkTor() _setCrawler() _findPageForms() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index c22b9d11e..fada113e7 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -18,6 +18,8 @@ optDict = { "requestFile": "string", "sessionFile": "string", "googleDork": "string", + "swaggerFile": "string", + "swaggerTags": "string", "configFile": "string", }, diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 5dacb84b5..36057fef2 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -141,6 +141,12 @@ def cmdLineParser(argv=None): target.add_argument("-g", dest="googleDork", help="Process Google dork results as target URLs") + target.add_argument("--swaggerFile", dest="swaggerFile", + help="Parse target(s) from a Swagger OpenAPI 3.x.x JSON file ") + + target.add_argument("--swaggerTags", dest="swaggerTags", + help="Only process swagger operations that include one of these tags") + target.add_argument("-c", dest="configFile", help="Load options from a configuration INI file") diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index a353ce8e0..b2f0fe64e 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -79,14 +79,14 @@ def configFileParser(configFile): mandatory = False - for option in ("direct", "url", "logFile", "bulkFile", "googleDork", "requestFile", "wizard"): + for option in ("direct", "url", "logFile", "bulkFile", "googleDork", "requestFile", "wizard", "swaggerFile"): if config.has_option("Target", option) and config.get("Target", option) or cmdLineOptions.get(option): mandatory = True break if not mandatory: 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, wizard or swaggerFile)" raise SqlmapMissingMandatoryOptionException(errMsg) for family, optionData in optDict.items(): diff --git a/sqlmap.conf b/sqlmap.conf index a771a4e79..e52e212fe 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -32,6 +32,12 @@ requestFile = # Example: +ext:php +inurl:"&id=" +intext:"powered by " googleDork = +# Parse target(s) for a Swagger OpenAPI 3.x.x JSON file +swaggerFile = + +# Only process swagger operations that have one of these tags (e.g. tagA,tagB) +swaggerTags = + # These options can be used to specify how to connect to the target URL. [Request]