{"id":"GHSA-2g5w-29q9-w6hx","summary":"mindsdb arbitrary file write when extracting a remotely retrieved Tarball","details":"### Summary\n\nAn unsafe extraction is being performed using `tarfile.extractall()` from a remotely retrieved tarball. Which may lead to the writing of the extracted files to an unintended location. Sometimes, the vulnerability is called a TarSlip or a ZipSlip variant.\n\n### Details\n\nI commented the following snippet of code as a vulnerability details. The code is from [file.py#L26..L134](https://github.com/mindsdb/mindsdb/blob/afedd37c16e579b6dc075b0814e42d0505ccdc07/mindsdb/api/http/namespaces/file.py#L26..L134)\n\n```python\n@ns_conf.route('/\u003cname\u003e')\n@ns_conf.param('name', \"MindsDB's name for file\")\nclass File(Resource):\n    @ns_conf.doc('put_file')\n    def put(self, name: str):\n        ''' add new file\n            params in FormData:\n                - file\n                - original_file_name [optional]\n        '''\n\n        data = {}\n\n        ... omitted for brevity\n\n            url = data['source']\n            data['file'] = data['name']\n\n            ... omitted for brevity \n\n            with requests.get(url, stream=True) as r:                   # Source: retrieve the URL which point to a remotely located tarball \n                if r.status_code != 200:\n                    return http_error(\n                        400,\n                        \"Error getting file\",\n                        f\"Got status code: {r.status_code}\"\n                    )\n                file_path = os.path.join(temp_dir_path, data['file'])\n                with open(file_path, 'wb') as f:\n                    for chunk in r.iter_content(chunk_size=8192):   # write with chunks the remote retrieved file into file_path location \n                        f.write(chunk)\n\n        original_file_name = data.get('original_file_name')\n\n        file_path = os.path.join(temp_dir_path, data['file'])      \n        lp = file_path.lower()\n        if lp.endswith(('.zip', '.tar.gz')):\n            if lp.endswith('.zip'):\n                with zipfile.ZipFile(file_path) as f:\n                    f.extractall(temp_dir_path)\n            elif lp.endswith('.tar.gz'):\n                with tarfile.open(file_path) as f:  # Just after \n                    f.extractall(temp_dir_path)  # Sink: the tarball located by file_path is supposed to be extracted to temp_dir_path. \n```\n\nSo, a remotely available tarball is being retrieved and written to the server filesystem in chunks, and then, if the extension ends with `.tar.gz` of a compressed tarball, the mindsdb app applies `tarfile.extractall()` directly with no checks for the destination. \n\nHowever, according to the following [warning](https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall) from the official documentation;\n\n\u003e Warning: Never extract archives from untrusted sources without prior inspection. It is possible that files are created outside of path, e.g. members that have absolute filenames starting with \"/\" or filenames with two dots \"..\". \n\n\n### PoC\n\nThe following PoC is provided for illustration purposes only. It showcases the risk of extracting a non-harmless text file `sim4n6.txt` to one of the parent locations rather than the intended current folder.\n\n```bash\n\u003e tar --list -v -f archive.tar.gz\ntar: Removing leading \"../../../\" from member names\n../../../sim4n6.txt\n\n\u003e python3 \nPython 3.10.6 (main, Nov  2 2022, 18:53:38) [GCC 11.3.0] on linux\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n\u003e\u003e\u003e import tarfile\n\u003e\u003e\u003e with tarfile.open(\"archive.tar.gz\") as tf:\n\u003e\u003e\u003e         tf.extractall()\n\u003e\u003e\u003e exit()\n\n\u003e test -f ../../../sim4n6.txt && echo \"sim4n6.txt exists\"\nsim4n6.txt exists\n```\n\n### Attack Scenario\n\nAn attacker could craft a malicious tarball with a filename path, such as ../../../../../../../../etc/passwd, and then serve the archive remotely, proceed to the PUT request of the tarball through mindsdb and overwrite the system files of the hosting server for instance.\n\n### Mitigation \n\nPotential mitigation could be to:\n - Use a safer module, like `zipfile`.\n - Use an alternative of `tarfile`, such as `tarsafe`. \n  - Validate the location or the absolute path of the extracted files and discard those with malicious paths such as relative path `../../..` or absolute path such as `/etc/password`. A simple wrapper could be written to raise an exception when a path traversal may be identified.\n\nThis is similar to the other report [GHSA-7x45-phmr-9wqp](https://github.com/mindsdb/mindsdb/security/advisories/GHSA-7x45-phmr-9wqp).","aliases":["CVE-2023-30620","PYSEC-2023-27"],"modified":"2026-09-10T03:49:52.273912063Z","published":"2023-03-30T21:42:48Z","database_specific":{"nvd_published_at":"2023-04-21T21:15:08Z","cwe_ids":["CWE-22"],"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2023-03-30T21:42:48Z"},"references":[{"type":"WEB","url":"https://github.com/mindsdb/mindsdb/security/advisories/GHSA-2g5w-29q9-w6hx"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2023-30620"},{"type":"WEB","url":"https://github.com/mindsdb/mindsdb/commit/4419b0f0019c000db390b54d8b9d06e1d3670039"},{"type":"PACKAGE","url":"https://github.com/mindsdb/mindsdb"},{"type":"WEB","url":"https://github.com/mindsdb/mindsdb/blob/afedd37c16e579b6dc075b0814e42d0505ccdc07/mindsdb/api/http/namespaces/file.py#L26..L134"},{"type":"WEB","url":"https://github.com/mindsdb/mindsdb/releases/tag/v23.2.1.0"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/mindsdb/PYSEC-2023-27.yaml"}],"affected":[{"package":{"name":"mindsdb","ecosystem":"PyPI","purl":"pkg:pypi/mindsdb"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"23.2.1.0"}]}],"versions":["0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","0.7.8","0.7.9","0.8.0","0.8.1","0.8.2","0.8.3","0.8.4","0.8.5","0.8.6","0.8.7","0.8.8","0.8.9","0.8.9.1","0.8.9.2","0.8.9.3","0.8.9.4","0.8.9.5","0.8.9.6","0.8.9.7","0.8.9.8","0.9.0.0","0.9.0.1","0.9.1.0","0.9.2.0","1.0","1.0.1","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","1.1.0","1.1.2","1.1.3","1.1.7","1.1.9","1.10.0","1.10.2","1.10.3","1.11.0","1.11.2","1.11.3","1.11.4","1.11.5","1.11.8","1.12.0","1.12.1","1.12.2","1.12.3","1.12.4","1.12.5","1.12.7","1.12.8","1.12.9","1.13.0","1.13.10","1.13.11","1.13.12","1.13.15","1.13.2","1.13.3","1.13.4","1.13.5","1.13.6","1.13.7","1.13.8","1.13.9","1.14.0","1.14.1","1.14.2","1.14.3","1.14.4","1.15.1","1.15.2","1.15.6","1.16.0","1.16.1","1.16.2","1.17.0","1.17.1","1.17.2","1.17.3","1.17.4","1.17.6","1.17.8","1.17.9","1.18.0","1.18.1","1.18.2","1.18.3","1.18.5","1.18.6","1.18.7","1.19.0","1.19.1","1.2.0","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","1.2.6","1.2.8","1.2.9","1.20.0","1.20.1","1.21.0","1.22.0","1.23.0","1.24.0","1.24.1","1.24.2","1.25.0","1.25.1","1.25.2","1.26.0","1.26.1","1.26.2","1.26.3","1.26.4","1.26.5","1.27.0","1.27.1","1.3.1","1.3.2","1.3.3","1.3.4","1.3.5","1.3.6","1.3.7","1.4.0","1.4.1","1.4.10","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.9","1.5.0","1.5.1","1.5.2","1.5.4","1.6.0","1.6.12","1.6.13","1.6.15","1.6.17","1.6.18","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.7.0","1.7.1","1.7.10","1.7.11","1.7.12","1.7.13","1.7.14","1.7.15","1.7.16","1.7.17","1.7.18","1.7.19","1.7.2","1.7.20","1.7.21","1.7.22","1.7.23","1.7.3","1.7.5","1.7.6","1.7.7","1.7.8","1.7.9","1.8.0","1.8.2","1.9.0","1.9.1","1.9.2","1.9.3","1.9.5","1.9.6","1.99.0","1.99.1","1.99.10","1.99.11","1.99.3","1.99.4","1.99.5","1.99.6","1.99.7","1.99.8","1.99.9","2.0.0","2.1.0","2.1.2","2.10.0","2.10.1","2.10.2","2.11.0","2.11.1","2.11.2","2.11.4","2.12.0","2.13.0","2.13.1","2.13.2","2.13.3","2.13.4","2.13.5","2.13.6","2.13.7","2.13.8","2.14.0","2.15.0","2.17.1","2.18.0","2.19.0","2.19.1","2.19.2","2.19.4","2.19.5","2.2.0","2.2.1","2.20.0","2.20.1","2.20.2","2.21.0","2.21.1","2.21.2","2.21.3","2.22.0","2.22.1","2.22.2","2.23.0","2.24.0","2.24.1","2.25.0","2.25.1","2.25.2","2.25.3","2.26.0","2.27.0","2.28.0","2.3.0","2.30.0","2.30.1","2.31.0","2.32.0","2.33.0","2.34.0","2.35.0","2.36.0","2.37.0","2.38.0","2.39.0","2.4.0","2.40.0","2.41.1","2.41.2","2.42.0","2.42.1","2.42.2","2.43.0","2.44.0","2.45.0","2.45.1","2.45.2","2.5.0","2.50.0","2.51.0","2.51.1","2.51.2","2.52.0","2.53.0","2.54.0","2.55.0","2.55.1","2.55.2","2.56.0","2.57.0","2.58.0","2.58.1","2.58.2","2.58.3","2.59.0","2.6.0","2.6.1","2.60.0","2.60.1","2.61.0","2.62.0","2.62.1","2.62.2","2.62.3","2.62.4","2.7.0","2.7.1","2.7.2","2.8.0","2.8.1","2.8.3","2.9.0","2.9.1","22.1.4.0","22.1.4.1","22.10.2.0","22.10.2.1","22.11.3.0","22.11.3.2","22.11.4.0","22.11.4.1","22.11.4.2","22.11.4.3","22.12.4.0","22.12.4.2","22.12.4.3","22.2.1.0","22.2.1.2","22.2.2.0","22.2.2.1","22.2.4.0","22.2.4.1","22.3.1.0","22.3.3.0","22.3.4.0","22.3.4.1","22.3.4.2","22.3.4.3","22.3.5.0","22.4.2.0","22.4.2.1","22.4.2.2","22.4.3.0","22.4.5.0","22.5.1.0","22.5.1.1","22.5.1.2","22.5.2.0","22.5.4.0","22.6.1.0","22.6.1.1","22.6.1.2","22.6.2.0","22.6.2.1","22.6.2.2","22.7.3.0","22.7.3.1","22.7.3.2","22.7.3.3","22.7.3.4","22.7.4.0","22.7.4.1","22.7.5.0","22.7.5.1","22.8.2.0","22.8.2.1","22.8.3.0","22.8.3.1","22.8.4.0","22.8.4.1","22.8.5.0","22.9.3.0","22.9.3.1","22.9.4.0","22.9.5.1","22.9.5.2","22.9.5.3","22.9.5.4","23.1.3.0","23.1.3.1","23.1.3.2","23.1.5.0"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/03/GHSA-2g5w-29q9-w6hx/GHSA-2g5w-29q9-w6hx.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N"},{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N"}]}