2021-09-17 08:06:20 +03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
"""
|
|
|
|
Copyright (c) 2006-2021 sqlmap developers (https://sqlmap.org/)
|
|
|
|
See the file 'LICENSE' for copying permission
|
|
|
|
"""
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
from lib.core.data import logger
|
|
|
|
from lib.core.exception import SqlmapSyntaxException
|
2021-09-17 11:44:18 +03:00
|
|
|
from lib.core.exception import SqlmapSkipTargetException
|
2021-09-17 08:06:20 +03:00
|
|
|
|
2021-09-17 08:49:06 +03:00
|
|
|
class Operation:
|
2021-09-17 08:06:20 +03:00
|
|
|
|
2021-09-17 11:44:18 +03:00
|
|
|
def __init__(self, name, method, props):
|
|
|
|
self.name = name
|
|
|
|
self.method = method
|
|
|
|
self.props = props
|
2021-09-17 08:49:06 +03:00
|
|
|
|
|
|
|
def tags(self):
|
2021-09-17 11:44:18 +03:00
|
|
|
return self.props["tags"]
|
2021-09-17 08:49:06 +03:00
|
|
|
|
|
|
|
def parameters(self):
|
2021-09-17 11:44:18 +03:00
|
|
|
return self.props["parameters"]
|
2021-09-17 08:06:20 +03:00
|
|
|
|
2021-09-17 08:49:06 +03:00
|
|
|
def parametersForTypes(self, types):
|
|
|
|
return list(filter(lambda p: (p["in"] in types), self.parameters()))
|
|
|
|
|
|
|
|
def bodyRef(self):
|
2021-09-17 11:44:18 +03:00
|
|
|
if "requestBody" in self.props:
|
|
|
|
return self.props["requestBody"]["content"]["application/json"]["schema"]["$ref"]
|
2021-09-17 08:49:06 +03:00
|
|
|
return None
|
2021-09-17 08:06:20 +03:00
|
|
|
|
2021-09-17 08:49:06 +03:00
|
|
|
# header injection is not currently supported
|
|
|
|
def injectable(self, body):
|
2021-11-02 08:54:38 +03:00
|
|
|
return len(self.parametersForTypes(["query", "path", "header"])) > 0 or body
|
2021-09-17 08:49:06 +03:00
|
|
|
|
|
|
|
def queryString(self):
|
|
|
|
queryParameters = self.parametersForTypes(["query"])
|
|
|
|
if len(queryParameters) < 1:
|
|
|
|
return None
|
|
|
|
queryString = ""
|
|
|
|
for qp in queryParameters:
|
2021-09-17 11:44:18 +03:00
|
|
|
if "example" not in qp:
|
|
|
|
raise SqlmapSkipTargetException("missing example for parameter '%s'" %qp["name"])
|
2021-09-17 08:49:06 +03:00
|
|
|
queryString += "&%s=%s" %(qp["name"], qp["example"])
|
|
|
|
|
|
|
|
return queryString.replace('&', '', 1)
|
|
|
|
|
|
|
|
def path(self, path):
|
|
|
|
pathParameters = self.parametersForTypes(["path"])
|
|
|
|
if len(pathParameters) < 1:
|
|
|
|
return path
|
|
|
|
parameterPath = path
|
|
|
|
for p in pathParameters:
|
2021-09-17 11:44:18 +03:00
|
|
|
if "example" not in p:
|
|
|
|
raise SqlmapSkipTargetException("missing example for parameter '%s'" %p["name"])
|
2021-09-17 08:49:06 +03:00
|
|
|
parameterPath = parameterPath.replace("{%s}" %p["name"], "%s*" %p["example"])
|
|
|
|
return parameterPath
|
2021-09-17 08:06:20 +03:00
|
|
|
|
2021-11-02 08:54:38 +03:00
|
|
|
def headers(self):
|
|
|
|
hdrs = []
|
|
|
|
headerParameters = self.parametersForTypes(["header"])
|
|
|
|
if len(headerParameters) < 1:
|
|
|
|
return hdrs
|
|
|
|
for hp in headerParameters:
|
|
|
|
if "example" not in hp:
|
|
|
|
raise SqlmapSkipTargetException("missing example for header '%s'" %hp["name"])
|
|
|
|
hdrs.append((hp["name"], "%s*" %hp["example"]))
|
|
|
|
return hdrs
|
|
|
|
|
2021-09-17 08:06:20 +03:00
|
|
|
def _ref(swagger, refPath):
|
|
|
|
paths = refPath.replace("#/", "", 1).split('/')
|
|
|
|
r = swagger
|
|
|
|
for p in paths:
|
|
|
|
r = r[p]
|
|
|
|
return r
|
|
|
|
|
2021-09-17 08:49:06 +03:00
|
|
|
def _example(swagger, refPath):
|
|
|
|
example = {}
|
2021-09-17 08:06:20 +03:00
|
|
|
ref = _ref(swagger, refPath)
|
|
|
|
if "type" in ref and ref["type"] == "object" and "properties" in ref:
|
|
|
|
properties = ref["properties"]
|
|
|
|
for prop in properties:
|
|
|
|
if "example" in properties[prop]:
|
|
|
|
value = properties[prop]["example"]
|
2021-09-17 08:49:06 +03:00
|
|
|
example[prop] = value
|
2021-09-17 08:06:20 +03:00
|
|
|
elif "$ref" in properties[prop]:
|
2021-09-17 08:49:06 +03:00
|
|
|
example[prop] = _example(swagger, properties[prop]["$ref"])
|
2021-09-17 08:06:20 +03:00
|
|
|
elif properties[prop]["type"] == "array" and "$ref" in properties[prop]["items"]:
|
2021-09-17 08:49:06 +03:00
|
|
|
example[prop] = [ _example(swagger, properties[prop]["items"]["$ref"]) ]
|
2021-09-17 11:44:18 +03:00
|
|
|
else:
|
|
|
|
raise SqlmapSkipTargetException("missing example for parameter '%s'" %prop)
|
|
|
|
|
2021-09-17 08:06:20 +03:00
|
|
|
|
2021-09-17 08:49:06 +03:00
|
|
|
return example
|
2021-09-17 08:06:20 +03:00
|
|
|
|
|
|
|
def parse(content, tags):
|
|
|
|
"""
|
|
|
|
Parses Swagger OpenAPI 3.x.x JSON documents
|
2021-09-17 11:48:56 +03:00
|
|
|
|
|
|
|
Target injectable parameter values are generated from the "example" properties.
|
|
|
|
Only property-level "example" is supported. The "examples" property is not supported.
|
2021-09-17 08:06:20 +03:00
|
|
|
"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
swagger = json.loads(content)
|
|
|
|
|
|
|
|
# extra validations
|
|
|
|
if "openapi" not in swagger or not swagger["openapi"].startswith("3."):
|
|
|
|
errMsg = "swagger must be OpenAPI 3.x.x!"
|
|
|
|
raise SqlmapSyntaxException(errMsg)
|
|
|
|
|
|
|
|
if ("servers" not in swagger or
|
|
|
|
not isinstance(swagger["servers"], list) or
|
|
|
|
len(swagger["servers"]) < 1 or
|
|
|
|
"url" not in swagger["servers"][0]):
|
|
|
|
errMsg = "swagger server is missing!"
|
|
|
|
raise SqlmapSyntaxException(errMsg)
|
|
|
|
|
|
|
|
server = swagger["servers"][0]["url"]
|
|
|
|
|
|
|
|
logger.info("swagger OpenAPI version '%s', server '%s'" %(swagger["openapi"], server))
|
|
|
|
|
|
|
|
for path in swagger["paths"]:
|
2021-09-17 11:44:18 +03:00
|
|
|
for method in swagger["paths"][path]:
|
|
|
|
op = Operation(path, method, swagger["paths"][path][method])
|
|
|
|
method = method.upper()
|
2021-09-17 08:06:20 +03:00
|
|
|
|
|
|
|
# skip any operations without one of our tags
|
2021-09-17 08:49:06 +03:00
|
|
|
if tags is not None and not any(tag in op.tags() for tag in tags):
|
2021-09-17 08:06:20 +03:00
|
|
|
continue
|
|
|
|
|
2021-09-17 11:44:18 +03:00
|
|
|
try:
|
|
|
|
body = {}
|
|
|
|
bodyRef = op.bodyRef()
|
|
|
|
if bodyRef:
|
|
|
|
body = _example(swagger, bodyRef)
|
|
|
|
|
|
|
|
if op.injectable(body):
|
|
|
|
url = None
|
|
|
|
data = None
|
|
|
|
cookie = None
|
|
|
|
|
|
|
|
parameterPath = op.path(path)
|
2021-11-02 08:54:38 +03:00
|
|
|
headers = op.headers()
|
2021-09-17 11:44:18 +03:00
|
|
|
qs = op.queryString()
|
|
|
|
url = "%s%s" % (server, parameterPath)
|
|
|
|
if body:
|
|
|
|
data = json.dumps(body)
|
|
|
|
|
|
|
|
if qs is not None:
|
|
|
|
url += "?" + qs
|
|
|
|
|
|
|
|
logger.debug("including url '%s', method '%s', data '%s', cookie '%s'" %(url, method, data, cookie))
|
2021-11-02 08:54:38 +03:00
|
|
|
yield (url, method, data, cookie, tuple(headers))
|
2021-09-17 11:44:18 +03:00
|
|
|
else:
|
|
|
|
logger.info("excluding path '%s', method '%s' as there are no parameters to inject" %(path, method))
|
|
|
|
|
|
|
|
except SqlmapSkipTargetException as e:
|
|
|
|
logger.warn("excluding path '%s', method '%s': %s" %(path, method, e))
|
2021-09-17 08:06:20 +03:00
|
|
|
|
|
|
|
except json.decoder.JSONDecodeError:
|
|
|
|
errMsg = "swagger file is not valid JSON"
|
|
|
|
raise SqlmapSyntaxException(errMsg)
|