T09 · Insecure Skill Coding Practices
- Location
process_base64_zip.py:48- Finding
Unvalidated ZIP Extraction May Allow Writes Outside the Intended Directory
- Content
View full analysis
Vulnerability Details
File Location:
process_base64_zip.py:48-49
Vulnerability Type: Unvalidated archive extraction
Risk Level: HighVulnerable Code
python # 提取ZIP内容 print(f"提取ZIP内容到: {output_dir}") with zipfile.ZipFile(zip_buffer, 'r') as zip_ref: zip_ref.extractall(output_dir) print(f"成功提取 {len(zip_ref.namelist())} 个文件") for file in zip_ref.namelist(): print(f" - {file}")Technical Analysis
The function decodes externally supplied Base64 data and passes the resulting ZIP archive directly to
ZipFile.extractall()without explicitly validating archive member paths.Crafted archive entries may contain absolute paths, parent-directory components such as
../, or other unsafe path forms. If the runtime or extraction behavior does not adequately normalize these names, extraction can place files outsideoutput_dir. The implementation also lacks checks for symbolic links, special files, duplicate entries, decompressed size, member count, and compression ratio.Exploitation requires an attacker to control the Base64 archive and cause
process_base64_zip()to process it. The utility is not exposed directly through the reviewed FastAPI endpoints, so the immediate attack surface is limited to local use or another integration that accepts attacker-controlled archives.Attack Path
- The attacker creates a ZIP archive containing a malicious member name such as
../../application/config.pyor another path targeting a writable file. - The attacker Base64-encodes the archive.
- A user or integrating application passes the encoded file to
process_base64_zip(). - The function decodes the archive and invokes
zip_ref.extractall(output_dir)without performing its own containment validation. - The malicious member is written outside the intended extraction directory if the active runtime permits the unsafe path.
- If a startup file, application module, conf ...[truncated 813 chars]
- The attacker creates a ZIP archive containing a malicious member name such as
- Remediation
View remediation
Remediation Suggestions
- Resolve every archive member against the canonical extraction directory before writing it.
- Reject absolute paths, drive-qualified paths, parent-directory traversal components, symbolic links, hard links, and special-device entries.
- Extract validated members individually rather than calling
extractall()on an untrusted archive. - Verify that each resolved destination remains beneath the canonical output directory using
Path.resolve()andPath.relative_to(). - Apply limits to the number of entries, total uncompressed size, per-file size, path length, and compression ratio to mitigate ZIP bombs.
- Extract into a newly created directory with restrictive permissions.
- Avoid running archive processing under an administrative account.
Example hardening pattern:
python from pathlib import Path destination = Path(output_dir).resolve() destination.mkdir(parents=True, exist_ok=True) with zipfile.ZipFile(zip_buffer, "r") as zip_ref: for member in zip_ref.infolist(): member_path = destination.joinpath(member.filename).resolve() try: member_path.relative_to(destination) except ValueError: raise ValueError(f"Unsafe archive path: {member.filename}") if member.is_dir(): member_path.mkdir(parents=True, exist_ok=True) continue member_path.parent.mkdir(parents=True, exist_ok=True) with zip_ref.open(member, "r") as source: with member_path.open("wb") as target: target.write(source.read())This example should additionally be extended with symbolic-link detection and extraction-size limits before it is used with fully untrusted archives.
