Skip to content

Commit fe69e6b

Browse files
committed
Adding support for --openapi
1 parent d6299fc commit fe69e6b

7 files changed

Lines changed: 893 additions & 10 deletions

File tree

data/txt/sha256sums.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,15 @@ c2db614a3ce7dda889152bea8bd6d709e5d8c2b556741fdbfe44469f27ce266b lib/core/enums
181181
5387168e5dfedd94ae22af7bb255f27d6baaca50b24179c6b98f4f325f5cc7b4 lib/core/exception.py
182182
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/core/__init__.py
183183
914a13ee21fd610a6153a37cbe50830fcbd1324c7ebc1e7fc206d5e598b0f7ad lib/core/log.py
184-
4fe3ac4c0d354d1ac42ad3f5dc1b308993588f8a249ff880d273f5031d6b52b0 lib/core/optiondict.py
185-
ca3d9185aa5418cdfc79f43beb4ad6f6503496763f349ecef57fff278bcfc8c8 lib/core/option.py
184+
91cc64c3dadf05eae666fcbbb0cd44c8ed8dd60592334b419ec8748cdded5f30 lib/core/optiondict.py
185+
227716f876f3af24e2c5ae4818d1e3b9bc17627f1876d66bcefc4953e660f1af lib/core/option.py
186186
21b2b1745107c211fc7593923a3da7a808d40763c00091c28de5f7c129bcf3bc lib/core/patch.py
187187
49c0fa7e3814dfda610d665ee02b12df299b28bc0b6773815b4395514ddf8dec lib/core/profiling.py
188188
0c36a65b6237732eb001d333f80f0c58c088ff01ae80cf07e4dcc6da2a806364 lib/core/readlineng.py
189189
9bf174058f15d14e24e94f9aaf42df045119d3617c6c54bd2f3af79b462f331d lib/core/replication.py
190190
0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py
191191
888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py
192-
5fa3141353791446463a215a5481048346aa0f1dde08f1fe8fa6834a22aa23c1 lib/core/settings.py
192+
1769800f72aa1e88c885ffb641e6e816d7d569b8c4a554bf7c7de821961a5235 lib/core/settings.py
193193
c7804223319e18eb0b8e2cbf0a8b6896d1cefb7b0b1a2e9f1cf826a8a3b56750 lib/core/shell.py
194194
a2e98a94b231432736d6b304fc75525c8b5fdb4768c418387c5b4c1a610dad64 lib/core/subprocessng.py
195195
15d36cdac9389d0a54a6c33fbb89f32bb65e303f50de573773dcb6d4618bca64 lib/core/target.py
@@ -200,12 +200,13 @@ b9aacb840310173202f79c2ba125b0243003ee6b44c92eca50424f2bdfc83c02 lib/core/unesc
200200
2400e465fa4d13e4c32795910878c71ff212e4361b46428d57ce43983f5e997c lib/core/wordlist.py
201201
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/__init__.py
202202
54bfd31ebded3ffa5848df1c644f196eb704116517c7a3d860b5d081e984d821 lib/parse/banner.py
203-
2b1ccf7adab06d64784639ba4db9772cc7bd3de30ad52513d4350fbf798082ed lib/parse/cmdline.py
203+
1a67c8e0c46fb1244535d3961c35300da4aecd1872fd1fe2e3a752a5643875ed lib/parse/cmdline.py
204204
02d82e4069bd98c52755417f8b8e306d79945672656ac24f1a45e7a6eff4b158 lib/parse/configfile.py
205205
c5b258be7485089fac9d9cd179960e774fbd85e62836dc67cce76cc028bb6aeb lib/parse/handler.py
206206
5c9a9caee948843d5537745640cc7b98d70a0412cc0949f59d4ebe8b2907c06c lib/parse/headers.py
207207
ea9b195e5f5030b96d1993c106c1e13fb5c7faaf6bdc5daacfd06ec984e7f323 lib/parse/html.py
208208
1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/parse/__init__.py
209+
9cb95cc5136d5ac624860578099929fdb335face41026f79f49df4f52da9805d lib/parse/openapi.py
209210
d2e771cdacef25ee3fdc0e0355b92e7cd1b68f5edc2756ffc19f75d183ba2c73 lib/parse/payloads.py
210211
c2f34e27578742e729c2fa9c1d4f0a0d8f8f7f4cf0fc14c62ec817a260c71dec lib/parse/sitemap.py
211212
1be3da334411657461421b8a26a0f2ff28e1af1e28f1e963c6c92768f9b0847c lib/request/basicauthhandler.py
@@ -631,6 +632,7 @@ d539d0ae758b5bb91e314ab82ab4fe03d6fb2f8b377d16aefa6d7d1d77a7d5a9 tests/test_ide
631632
caa06fed7323b2bb6d0f2443ce343de94f75bf8ad012c055d5e07741d908ebad tests/test_misc.py
632633
790b78c600b61eb0bdd6e07e14b1db3eb2ddd5fc5d4edb9e975f85ced38558c7 tests/test_nosql.py
633634
88a8c7ce0ba0ca721dffbcf9351cd07f7e471ad2fe667a10608c18952b09868d tests/test_openapi_drift.py
635+
a0d173bb595ffbd2b49ee7fb1519d9898aefc262f2565923c4fe41bbc06f57e0 tests/test_openapi.py
634636
6e63ed05db0490148d1c8428d785a23b0d5d5a0f566cd397c9c4a8fe8a6ed7dc tests/test_option.py
635637
cde0bea1263ae857561f91ed2bd515e972b716743f017d31b1718a8546c72759 tests/test_pagecontent.py
636638
7554a918309cf0f2cd8a63a3bb7659708f13beffbcd5ce498ece9f9167d55c97 tests/test_parse_modules.py

lib/core/option.py

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,65 @@ def _setBulkMultipleTargets():
492492
warnMsg = "no usable links found (with GET parameters)"
493493
logger.warning(warnMsg)
494494

495+
def _setOpenApiTargets():
496+
if not conf.openApiFile:
497+
return
498+
499+
from lib.parse.openapi import openApiTargets
500+
501+
if conf.method:
502+
warnMsg = "option '--method' will override the HTTP method(s) derived from the OpenAPI/Swagger specification"
503+
logger.warning(warnMsg)
504+
505+
origin = None
506+
if re.match(r"(?i)\Ahttps?://", conf.openApiFile):
507+
infoMsg = "fetching OpenAPI/Swagger specification from '%s'" % conf.openApiFile
508+
logger.info(infoMsg)
509+
from lib.request.connect import Connect as Request
510+
content = Request.getPage(url=conf.openApiFile, raise404=True)[0]
511+
match = re.match(r"(?i)(https?://[^/]+)", conf.openApiFile)
512+
origin = match.group(1) if match else None
513+
else:
514+
conf.openApiFile = safeExpandUser(conf.openApiFile)
515+
checkFile(conf.openApiFile)
516+
infoMsg = "parsing OpenAPI/Swagger specification from '%s'" % conf.openApiFile
517+
logger.info(infoMsg)
518+
content = openFile(conf.openApiFile).read()
519+
520+
try:
521+
targets = openApiTargets(content, origin)
522+
except ValueError as ex:
523+
errMsg = "unable to parse the OpenAPI/Swagger specification ('%s')" % getSafeExString(ex)
524+
raise SqlmapSyntaxException(errMsg)
525+
526+
if re.search(r"(?i)securitySchemes|securityDefinitions", content) and not any((conf.authType, conf.authCred, conf.authFile)) and not any((_[0] or "").lower() == HTTP_HEADER.AUTHORIZATION.lower() for _ in (conf.httpHeaders or [])):
527+
warnMsg = "the OpenAPI/Swagger specification declares authentication (security schemes) but no credentials were provided. "
528+
warnMsg += "If the API requires authentication, requests are likely to be rejected. Provide credentials with "
529+
warnMsg += "'--auth-type'/'--auth-cred' or a header (e.g. --headers=\"Authorization: Bearer ...\")"
530+
logger.warning(warnMsg)
531+
532+
before = len(kb.targets) # openapi carries per-target bodies -> no conf.data fallback
533+
mutating = 0
534+
for url, method, data, headers in targets:
535+
if conf.scope and not re.search(conf.scope, url, re.I):
536+
continue
537+
if method not in ("GET", "HEAD", "OPTIONS"):
538+
mutating += 1
539+
kb.targets.add((url, method, data, conf.cookie, tuple(headers) if headers else None))
540+
541+
added = len(kb.targets) - before
542+
if added:
543+
conf.multipleTargets = True
544+
infoMsg = "derived %d target(s) from the OpenAPI/Swagger specification" % added
545+
logger.info(infoMsg)
546+
if mutating:
547+
warnMsg = "%d of the derived target(s) use state-changing HTTP methods (e.g. POST/PUT/PATCH/DELETE). " % mutating
548+
warnMsg += "Scanning them may create, modify or delete server-side data"
549+
logger.warning(warnMsg)
550+
else:
551+
warnMsg = "no usable targets derived from the OpenAPI/Swagger specification"
552+
logger.warning(warnMsg)
553+
495554
def _findPageForms():
496555
if not conf.forms or conf.crawlDepth:
497556
return
@@ -1852,7 +1911,7 @@ def _cleanupOptions():
18521911
if conf.tmpPath:
18531912
conf.tmpPath = ntToPosixSlashes(normalizePath(conf.tmpPath))
18541913

1855-
if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.forms, conf.crawlDepth, conf.stdinPipe)):
1914+
if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.forms, conf.crawlDepth, conf.stdinPipe, conf.openApiFile)):
18561915
conf.multipleTargets = True
18571916

18581917
if conf.optimize:
@@ -2728,8 +2787,8 @@ def _basicOptionValidation():
27282787
errMsg += "'SQLMAP_UNSAFE_EVAL=1' to be explicitly set"
27292788
raise SqlmapSystemException(errMsg)
27302789

2731-
if conf.chunked and not any((conf.data, conf.requestFile, conf.forms)):
2732-
errMsg = "switch '--chunked' requires usage of (POST) options/switches '--data', '-r' or '--forms'"
2790+
if conf.chunked and not any((conf.data, conf.requestFile, conf.forms, conf.openApiFile)):
2791+
errMsg = "switch '--chunked' requires usage of (POST) options/switches '--data', '-r', '--forms' or '--openapi'"
27332792
raise SqlmapSyntaxException(errMsg)
27342793

27352794
if conf.api and not conf.configFile:
@@ -3022,7 +3081,7 @@ def init():
30223081

30233082
parseTargetDirect()
30243083

3025-
if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.stdinPipe)):
3084+
if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.stdinPipe, conf.openApiFile)):
30263085
_setHostname()
30273086
_setHTTPTimeout()
30283087
_setHTTPExtraHeaders()
@@ -3038,6 +3097,7 @@ def init():
30383097
_doSearch()
30393098
_setStdinPipeTargets()
30403099
_setBulkMultipleTargets()
3100+
_setOpenApiTargets()
30413101
_checkTor()
30423102
_setCrawler()
30433103
_findPageForms()

lib/core/optiondict.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"sessionFile": "string",
2020
"googleDork": "string",
2121
"configFile": "string",
22+
"openApiFile": "string",
2223
},
2324

2425
"Request": {

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.7.16"
23+
VERSION = "1.10.7.17"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

lib/parse/cmdline.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ def cmdLineParser(argv=None):
144144
target.add_argument("-c", dest="configFile",
145145
help="Load options from a configuration INI file")
146146

147+
target.add_argument("--openapi", dest="openApiFile",
148+
help="Derive targets from an OpenAPI/Swagger specification (file or URL)")
149+
147150
# Request options
148151
request = parser.add_argument_group("Request", "These options can be used to specify how to connect to the target URL")
149152

@@ -1172,7 +1175,7 @@ def _format_action_invocation(self, action):
11721175
else:
11731176
args.stdinPipe = None
11741177

1175-
if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.fpTest, args.apiTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile, args.stdinPipe)):
1178+
if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.openApiFile, args.updateAll, args.smokeTest, args.vulnTest, args.fpTest, args.apiTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile, args.stdinPipe)):
11761179
errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --wizard, --shell, --update, --purge, --list-tampers or --dependencies). "
11771180
errMsg += "Use -h for basic and -hh for advanced help\n"
11781181
parser.error(errMsg)

0 commit comments

Comments
 (0)