Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

False negative related to SQL Injection #100

Open
nrathaus opened this issue May 6, 2024 · 2 comments
Open

False negative related to SQL Injection #100

nrathaus opened this issue May 6, 2024 · 2 comments

Comments

@nrathaus
Copy link
Contributor

nrathaus commented May 6, 2024

The endpoint: https://brokencrystals.com/api/testimonials/count?query=%27 is vulnerable to an SQL injection

The endpoint does NOT return 50X error when the SQL injection occurs, thus: STATUS_CODE_FILTER doesn't catch it

I believe it would be a smart idea to look for common SQL errors such as:
' - unterminated quoted string at or near "'"

Other errors are listed here:
https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05-Testing_for_SQL_Injection

I can't easily find one 'list' that has all the SQL errors

@dmdhrumilmistry
Copy link
Collaborator

It would require writing new tests for SQLi to detect it using BODY_REGEX_FILTER. Additionally finding SQLi precisely can be a challenging task. Maybe, OFFAT can integrate sqlmap into the tool.

@nrathaus
Copy link
Contributor Author

Here is a short code we can integrate that will generate text (test.rst) that that an be provided to sqlmap to do SQL injection testing

def test_EndPoint(self):
        """./sqlmap.py --batch -r test.rst --answers="involving it=n"""
        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==========")

I think integrating this into OFFAT is fairly easy - i.e. OFFAT can generate the file and potentially run sqlmap and bundle the results from it

The issue is that sqlmap isn't built to be easily automated - it doesn't output JSON results for example - so some manual labor related to reading the results is needed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants