T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- scripts/annotation-api.py:24
- Finding
- Unrestricted Filesystem Enumeration and JSONL File Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/annotation-api.py`, lines 24-40 and 121-153 **Vulnerability Type**: Improper filesystem access control **Risk Level**: High ### Vulnerable Code ```python def do_GET(self): parsed = urlparse(self.path) params = parse_qs(parsed.query) if parsed.path == '/': # List data files and annotation results data_dir = params.get('dir', [DATA_DIR])[0] results_file = params.get('results', [''])[0] files = self._list_files(data_dir) annotations = {} if results_file and os.path.exists(results_file): annotations = self._load_annotations(results_file) self._send_json({ 'files': files, 'annotations': annotations, 'dataDir': data_dir, 'resultsFile': results_file }) ``` ```python def _list_files(self, data_dir): """Recursively list files in a data directory.""" files = [] if not os.path.exists(data_dir): return files # ... for root, dirs, filenames in os.walk(data_dir): dirs.sort() for fname in sorted(filenames): fpath = os.path.join(root, fname) # ... files.append({ 'path': fpath, 'name': fname, 'type': ftype, 'size': os.path.getsize(fpath) }) ``` ### Technical Analysis The `dir` and `results` query parameters are used directly as filesystem paths. Neither path is constrained to the configured `DATA_DIR`. The `dir` parameter is passed to `os.walk()`, permitting recursive enumeration of any directory readable by the API process. The response exposes absolute paths, filenames, types, and sizes. The `results` parameter is passed to `_load_annotations()`, which reads attacker-selected files as JSONL. Although only parseable JSON objects with a `source_file` field are returned meaningfully, this still ...[truncated 984 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not accept arbitrary filesystem paths from API clients. - Represent datasets and result files with opaque server-side identifiers. - Resolve candidate paths and enforce containment under a fixed root: ```python from pathlib import Path root = Path(DATA_DIR).resolve() candidate = (root / requested_relative_path).resolve() if not candidate.is_relative_to(root): raise PermissionError("Path is outside the configured data root") ``` - Reject absolute paths and path traversal components such as `..`. - Apply the same validation independently to both dataset and result paths. - Return relative paths instead of absolute server filesystem paths. - Run the service as a dedicated, unprivileged account with access only to the required annotation directory. - Require authentication and per-dataset authorization before returning listings or annotations. ]]>
