sqlmapproject / sqlmap

Automatic SQL injection and database takeover tool

Home Page:http://sqlmap.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

sqlmap using OpenAPI schema for testing

nrathaus opened this issue · comments

Is your feature request related to a problem? Please describe.
Feature request, allowing users of sqlmap to read a JSON of OpenAPI 3.0 as source of endpoints and parameters.

I noticed there are other issues in the past that have been closed, not sure if this because of lack of contribution, I can provide the needed code to parse the OpenAPI JSON file and make it into a REQUESTFILE, but I feel this is a bit a long path - requires reading JSON, creating a file, then loading this file via the REQUESTFILE - maybe a "shortcut" can be made with some assistance (from other sqlmap devs).

Describe the solution you'd like
If we take for example: https://brokencrystals.com/swagger-json - this JSON has enough information to build endpoints

If we make them into something like this:

==========
GET /api/testimonials/count?query=* HTTP/1.1
Host: brokencrystals.com:443

==========
==========
GET /api/products/views HTTP/1.1
Host: brokencrystals.com:443
x-product-name: *

==========

We can feed it into sqlmap, and it is able to test them and find sql-related vulnerabilities

Describe alternatives you've considered
Running a third-party python code such as (BaseParser seen here is from OFFAT project - which uses openapi_spec_validator to read the schema) which will generate the above test.rst output:

tmp_spec = tempfile.NamedTemporaryFile(mode="+a", encoding="utf-8")
        tmp_spec.write("""
{
  "openapi": "3.0.0",
  "paths": {
    "/api/testimonials/count": {
      "get": {
        "operationId": "TestimonialsController_getCount",
        "summary": "",
        "description": "Returns count of all testimonials based on provided sql query",
        "parameters": [
          {
            "name": "query",
            "required": true,
            "in": "query",
            "example": "select count(*) as count from testimonial",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": { "schema": { "type": "string" } }
            }
          }
        },
        "tags": ["Testimonials controller"]
      }
    },
    "/api/products/views": {
      "get": {
        "operationId": "ProductsController_viewProduct",
        "summary": "",
        "description": "Updates the product's 'viewsCount' according to product name provided in the header 'x-product-name' and returns the query result.",
        "parameters": [
          {
            "name": "x-product-name",
            "required": true,
            "in": "header",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": { "description": "" },
          "500": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": { "type": "string" },
                    "location": { "type": "string" }
                  }
                }
              }
            }
          }
        },
        "tags": ["Products controller"]
      }
    }
  },
  "info": {
    "title": "Test",
    "description": "info -> description",
    "version": "1.0",
    "contact": {}
  },
  "tags": [],
  "servers": [{ "url": "https://someserver.com" }],
  "components": {
    "schemas": {
    }
  }
}
"""
    )
        tmp_spec.flush()
        obj = BaseParser(tmp_spec.name)

        server_hostname = "brokencrystals.com"
        server_port = ":443"
        server_ssl = "s"

        end_points = obj.specification.get('paths')
        for end_point, end_object in end_points.items():
            for method, method_object in end_object.items():
                parameters = method_object["parameters"]
                req = requests.Request(method=method, url=f"http{server_ssl}://{server_hostname}{server_port}{end_point}")
                req.headers["Host"] = f"{server_hostname}{server_port}"

                for parameter in parameters:
                    if "value" not in parameter:
                      parameter["value"] = "*"
                    if parameter["in"] == "header":
                      req.headers[parameter["name"]] = parameter["value"]
                    if parameter["in"] == "query":
                      req.params[parameter["name"]] =  parameter["value"]
                    if parameter["in"] == "body":
                      req.data.append([parameter["name"], parameter["value"]])
                req = req.prepare()
                print(f"==========\n{self.format_prepped_request(req)}\n==========")

Additional context

  1. I can develop this third-party script that will take a JSON file and make into a REQUESTFILE
  2. I can with some assistance integrate it into sqlmap so that a parser option will do it without requiring the build REQUESTFILE

last time i checked, the main problem i found was how to properly fill the parameter values. putting dumb * will not help. not sure if you are aware, but web applications actually like valid values. another approach would be to lean on "examples", but i can see problems here too - user will provide a dummy schema, without examples, and will immediately open issues here

I agree with your statement - though I think at the moment asking for the user to provide valid values interactively, or by editing a REQUESTFILE file would allow end-users to have a better chance of testing their openapi interface comprehensively than without using the openapi json file

Maybe if you can tell me what you are most worried about, I can think of a solution, if it is lack of valid values for fields - I can think about how to give a solution to this

I guess we can close this down as there is no interest