T09 · Insecure Skill Coding Practices
- Location
scripts/generate_hf_sdxl.py:86- Finding
Predictable Temporary-File Write Allows Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate_hf_sdxl.py, lines 86–97
Vulnerability Type: Unsafe temporary-file creation and symlink following
Risk Level: MediumVulnerable Code
python output = args.output if output.endswith("/") or os.path.isdir(output) or os.path.splitext(output)[1] == "": os.makedirs(output, exist_ok=True) filename = f"hf-sdxl-{time.strftime('%Y%m%d-%H%M%S')}{infer_extension(content_type)}" output = os.path.join(output, filename) else: os.makedirs(os.path.dirname(os.path.abspath(output)), exist_ok=True) with open(output, "wb") as f: f.write(body) print(output)Technical Analysis
When the output argument identifies a directory, the script constructs a filename using a timestamp with one-second resolution. The documented workflow recommends a path under
/tmp/openclaw/. If that directory is accessible to another local user or process, the generated filename can be predicted.The subsequent
open(output, "wb")operation does not request exclusive creation and follows symbolic links. It therefore truncates an existing destination or follows a pre-created symlink. The code does not use a cryptographically random filename, verify destination ownership, reject symlinks, or atomically ensure that it created a new file.This is a time-of-check/time-of-use and unsafe temporary-file weakness. Exploitation requires local access to the selected output directory and the ability to predict the generation time and likely response extension.
Attack Path
- A local attacker determines that the Skill will generate an image in a shared or attacker-writable directory such as
/tmp/openclaw/. - The attacker predicts the timestamp used in the filename, for example
hf-sdxl-20260915-120000.png. - Before the write occurs, the attacker creates that path as a symbolic link to another file.
- The Hugging Face API returns the generated image. ...[truncated 951 chars]
- A local attacker determines that the Skill will generate an image in a shared or attacker-writable directory such as
- Remediation
View remediation
Remediation Suggestions
- Create generated files atomically with
tempfile.mkstemp()ortempfile.NamedTemporaryFile(delete=False, dir=output_directory, suffix=extension). These APIs provide unpredictable names and exclusive creation. - Use a private temporary directory owned by the Skill process with permissions set to
0700, rather than relying on a shared writable directory. - If manually creating files, use
os.open()withO_CREAT | O_EXCLand, where supported,O_NOFOLLOW, then write through the returned descriptor. - Never reopen the generated pathname after validating it, because doing so can reintroduce a race condition.
- Reject existing destinations unless the caller explicitly requests overwrite behavior.
- If caller-selected output files must be supported, use
lstat()-based validation and platform-specific no-follow semantics, while recognizing that validation alone does not replace atomic creation. - Preserve the documented cleanup behavior by deleting the securely created temporary file after confirmed delivery.
- Create generated files atomically with
