Summary
An unauthenticated path traversal vulnerability in load_grammar() allows reading any file on the server filesystem with no extension restriction.
Gradio does not server-side validate dropdown values, so an attacker can POST directory traversal payloads (e.g., ../../../etc/passwd) via the API and receive the full file contents in the response.
Details
The vulnerable code is in modules/ui_parameters.py at lines 137-142:
def load_grammar(name):
p = shared.user_data_dir / 'grammars' / name
if p.exists():
return open(p, 'r', encoding='utf-8').read()
else:
return ''
The name parameter comes from a Gradio Dropdown component.
Gradio does not validate that the submitted value matches the declared choices, so any string is accepted via the /gradio_api/call/load_grammar endpoint.
There is no call to os.path.basename(), sanitize_filename(), or any other path validation.
Unlike load_preset() or load_prompt(), this function does not append a file extension, meaning any file type can be read.
PoC
- Clone the repository and install dependencies.
- Start the server:
python server.py --listen-port 7861
- Send a crafted API request:
curl -X POST http://127.0.0.1:7861/gradio_api/call/load_grammar \
-H "Content-Type: application/json" \
-d '{"data": ["../../../etc/passwd"]}'
poc.zip
- Fetch the result using the returned
event_id:
curl http://127.0.0.1:7861/gradio_api/call/load_grammar/<event_id>
- The full contents of
/etc/passwd are returned in the response.
I verified this by cloning the repository at current HEAD, extracting the verbatim load_grammar() function, and confirming that /etc/passwd (3,192 bytes, 55 lines) and ~/.ssh/id_rsa (3,381 bytes) are both exfiltrated in full.
Impact
Any file readable by the server process can be exfiltrated by an unauthenticated attacker.
This includes /etc/passwd, SSH private keys, .env files, API tokens, and application configuration.
The default deployment has no authentication (--gradio-auth is optional).
Cloud deployments commonly use --listen or --share, making the server remotely accessible.
The same vulnerability pattern exists in load_preset() (presets.py), load_prompt() (prompts.py), and load_template() (training.py), reported separately.
Remediation: apply os.path.basename(name) before path construction.
We believe this qualifies as a valid security issue.
If you agree, we'd appreciate the following credit on the CVE:
Reported by Woohyun Choi, Sunwoo Lee, and Seunghyun Yoon (Korea Institute of Energy Technology, KENTECH)
Summary
An unauthenticated path traversal vulnerability in
load_grammar()allows reading any file on the server filesystem with no extension restriction.Gradio does not server-side validate dropdown values, so an attacker can POST directory traversal payloads (e.g.,
../../../etc/passwd) via the API and receive the full file contents in the response.Details
The vulnerable code is in
modules/ui_parameters.pyat lines 137-142:The
nameparameter comes from a Gradio Dropdown component.Gradio does not validate that the submitted value matches the declared choices, so any string is accepted via the
/gradio_api/call/load_grammarendpoint.There is no call to
os.path.basename(),sanitize_filename(), or any other path validation.Unlike
load_preset()orload_prompt(), this function does not append a file extension, meaning any file type can be read.PoC
python server.py --listen-port 7861poc.zip
event_id:/etc/passwdare returned in the response.I verified this by cloning the repository at current HEAD, extracting the verbatim
load_grammar()function, and confirming that/etc/passwd(3,192 bytes, 55 lines) and~/.ssh/id_rsa(3,381 bytes) are both exfiltrated in full.Impact
Any file readable by the server process can be exfiltrated by an unauthenticated attacker.
This includes
/etc/passwd, SSH private keys,.envfiles, API tokens, and application configuration.The default deployment has no authentication (
--gradio-authis optional).Cloud deployments commonly use
--listenor--share, making the server remotely accessible.The same vulnerability pattern exists in
load_preset()(presets.py),load_prompt()(prompts.py), andload_template()(training.py), reported separately.Remediation: apply
os.path.basename(name)before path construction.We believe this qualifies as a valid security issue.
If you agree, we'd appreciate the following credit on the CVE:
Reported by Woohyun Choi, Sunwoo Lee, and Seunghyun Yoon (Korea Institute of Energy Technology, KENTECH)