T09 · Insecure Skill Coding Practices
Error
- Location
- kb.py:204
- Finding
- Arbitrary Code Execution Through Python Source Injection in PDF Extraction<![CDATA[ ## Vulnerability Details **File Location**: `kb.py:204-217` **Vulnerability Type**: Python source injection through an unescaped file path **Risk Level**: High ### Vulnerable Code ```python result = subprocess.run( ["python3", "-c", f""" import sys try: import fitz doc = fitz.open("{path}") for page in doc: print(page.get_text()) except ImportError: import subprocess r = subprocess.run(["pdftotext", "{path}", "-"], capture_output=True, text=True) print(r.stdout) """], capture_output=True, text=True, timeout=60 ) ``` ### Technical Analysis The `path` value ultimately originates from the positional command-line argument accepted by `ingest.py`. When the argument is classified as a PDF, `extract_pdf()` interpolates it directly into source code passed to `python3 -c`. Although `subprocess.run()` uses an argument array and does not invoke a shell, this does not prevent injection into the dynamically generated Python program. A local filename containing quotation marks, line breaks, or Python syntax can terminate the quoted argument to `fitz.open()` and introduce attacker-controlled statements. The same unescaped value appears a second time in the fallback `pdftotext` call. Escaping only shell metacharacters would therefore be insufficient; the fundamental issue is generation of executable source code from untrusted input. ### Attack Path 1. An attacker creates or supplies a local filename containing embedded Python syntax while ensuring the final name ends in `.pdf`. 2. The victim or an agent invokes: ```bash python ingest.py "/path/to/crafted-name.pdf" ``` 3. `classify_url()` classifies the supplied value as a PDF. 4. `extract_pdf()` inserts the path into the formatted `python3 -c` program. 5. The crafted path terminates the intended Python string and adds arbitrary Python statements. 6. The subprocess executes those statements with the privileges and environment of the user running the Skill. ### Impact As ...[truncated 460 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not construct Python source code using the PDF path. - Prefer importing and invoking PyMuPDF directly in the current process: ```python import fitz with fitz.open(path) as doc: text = "\n".join(page.get_text() for page in doc) ``` - If isolation in a child process is required, use a fixed program and pass the path through `sys.argv`: ```python subprocess.run( ["python3", "-c", FIXED_SCRIPT, str(path)], capture_output=True, text=True, timeout=60, check=True, ) ``` - Invoke `pdftotext` directly with an argument array rather than embedding its invocation in generated Python. - Resolve and validate local paths before use, reject unexpected file types, and verify that the target is a regular file rather than a symbolic link. - Add regression tests using filenames containing quotation marks, backslashes, newlines, and Python metacharacters. ]]>
