T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/xhs_crawler.py:37
- Finding
- Arbitrary Python Code Execution Through Keyword Configuration Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/xhs_crawler.py`, lines 37–41; execution sink at lines 75–81 **Vulnerability Type**: Python source-code injection **Risk Level**: High ### Vulnerable Code ```python content = re.sub( r'KEYWORDS\s*=\s*"[^"]*"', f'KEYWORDS = "{keywords}"', content ) ``` The generated configuration is subsequently used when MediaCrawler is launched: ```python result = subprocess.run( [str(venv_python), "main.py", "--platform", "xhs", "--lt", "qrcode"], cwd=str(crawler_path), env=env, timeout=600, ) ``` ### Technical Analysis The value supplied through the `--keywords` command-line argument is interpolated directly into the source code of MediaCrawler's `config/base_config.py`. The value is not escaped or serialized as a valid Python string literal. An attacker can include quotation marks, statement delimiters, newlines, or comments in the keyword value. This allows the attacker to terminate the intended `KEYWORDS` string and inject additional Python statements. For example, a keyword value structurally equivalent to the following would produce executable Python code: ```text "; __import__('os').system('id'); # ``` The resulting configuration would contain code equivalent to: ```python KEYWORDS = ""; __import__('os').system('id'); #" ``` When MediaCrawler loads its Python configuration during startup, the injected statement can execute with the privileges of the user running the crawler. Using an argument list in `subprocess.run()` prevents shell injection at that particular call, but it does not mitigate the earlier Python source-code injection. ### Attack Path 1. The attacker causes the Skill to run `scripts/xhs_crawler.py` with a crafted `--keywords` value. 2. `update_config()` embeds the unescaped value into `config/base_config.py`. 3. The malicious value terminates the intended Python string and adds an arbitrary Python statement. 4. `run_crawler()` launches MediaCrawler usin ...[truncated 894 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not modify executable Python source with user-controlled text. 1. Store keywords in a data-only format such as JSON: ```python keywords_file.write_text( json.dumps({"keywords": keywords}, ensure_ascii=False), encoding="utf-8", ) ``` 2. Update MediaCrawler through a supported command-line option, environment variable, or structured configuration mechanism. 3. Validate that keywords are strings and enforce reasonable length and count limits. 4. If source modification is unavoidable, serialize the value with `repr(keywords)` rather than manually surrounding it with quotation marks: ```python replacement = f"KEYWORDS = {keywords!r}" ``` 5. Prefer an AST-aware configuration editor and validate the resulting file with `ast.parse()` before running MediaCrawler. 6. Write changes atomically and restore the original configuration after the crawl. 7. Run the crawler in a restricted environment with minimal filesystem and credential access. 8. Add regression tests using quotation marks, backslashes, newlines, comments, and statement delimiters in keyword input. ]]>
