T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- chem_ui.py:9
- Finding
- Unauthenticated Public Gradio Interface Uses a Shared Output File## Vulnerability Details **File Location**: `chem_ui.py`, lines 9–34 **Vulnerability Type**: Unauthenticated public service exposure and unsafe shared-file handling **Risk Level**: High **Vulnerable Code:** ```python def analyze(smiles): # Props proc = subprocess.run(["python3", "rdkit_mol.py", "--smiles", smiles, "--action", "props"], cwd=WORK_DIR, capture_output=True, text=True) props = json.loads(proc.stdout) # Draw PNG subprocess.run(["python3", "rdkit_mol.py", "--smiles", smiles, "--action", "draw", "--output", "mol.png"], cwd=WORK_DIR) img = Image.open(os.path.join(WORK_DIR, "mol.png")) # ADMET proc = subprocess.run(["python3", "admet_predict.py", "--smiles", smiles], cwd=WORK_DIR, capture_output=True, text=True) admet = json.loads(proc.stdout) return props, img, admet iface = gr.Interface( fn=analyze, inputs=gr.Textbox(label="SMILES", value="CCO"), outputs=[gr.JSON(label="Props"), gr.Image(label="2D Viz"), gr.JSON(label="ADMET")], title="Chemistry Query Agent 🧪", description="PubChem/RDKit analysis" ) if __name__ == "__main__": iface.launch(share=True) ``` ### Technical Analysis Calling `iface.launch(share=True)` instructs Gradio to create an externally reachable share tunnel. No authentication, authorization, request-size restriction, or rate limiting is configured. Starting the UI can therefore expose its chemistry-processing functions beyond the local host without an explicit access-control boundary. Each request launches multiple local Python subprocesses and performs RDKit processing on attacker-controlled SMILES input. Consequently, a remote caller can repeatedly consume local CPU, memory, and process resources. The drawing workflow also uses the fixed path `mol.png` for every request. Generation and reading are separate operations, with no locking or request-specific filename. Concurrent requests c ...[truncated 1688 chars]
- Remediation
- ## Remediation Suggestions 1. Disable public tunneling by default: ```python iface.launch(share=False, server_name="127.0.0.1") ``` 2. Require explicit, documented operator opt-in before enabling any public share URL. 3. If remote access is required, add authentication and place the application behind an access-controlled reverse proxy. 4. Add per-client rate limits, request timeouts, concurrency limits, and maximum input-length or molecular-complexity limits. 5. Replace the fixed output path with a unique temporary file for each request: ```python import tempfile from pathlib import Path with tempfile.TemporaryDirectory() as temp_dir: output = Path(temp_dir) / "mol.png" # Generate and read the request-specific image here. ``` 6. Prefer returning an in-memory image directly, avoiding shared filesystem state. 7. Check subprocess return codes before parsing output or opening generated files. 8. Add operational documentation that clearly explains whether the UI is local-only or externally reachable.
