CVE-2007-4559 / CWE-22

Python tarfile.extractall() Path Traversal (CVE-2007-4559): The 15-Year-Old Flaw in 350k Repos

How tarfile.extractall() blindly concatenates archive member paths with the destination folder, allowing attackers to overwrite arbitrary system files via relative traversal sequences.

💡 Plain English Explainer (ELI5)

When you download a `.tar.gz` archive, you expect files to unzip into your project folder. But a tar file can contain a file named `../../../../etc/cron.d/malicious`. Python's `tarfile.extractall()` trusts that name completely and writes the file straight into your operating system's root folders, overwriting critical server files.

Core Concepts & Key Terms

TarInfo.name
The metadata header inside each tar archive member specifying its relative or absolute target filepath.
Canonical Path Resolution
Resolving symbolic links, relative references (`../`), and directory separators to determine the true, absolute destination on disk.
Backwards Compatibility Freeze
The historical reason Python maintainers left `extractall()` vulnerable for 15 years: fixing it would break legacy build scripts expecting unchecked path extraction.
PEP 706 (Python 3.12 Filter)
The formal Python enhancement introducing the `filter='data'` parameter to enforce safe extraction boundaries by default.

Step-by-Step Attack Flow

Step 1

1. Malicious Archive Generation

An attacker creates a tar archive with an embedded member named: ../../../../home/user/.ssh/authorized_keys.

Step 2

2. Automated Archive Ingestion

An AI model hub, CI/CD runner, or backup script downloads the tarball and runs tar.extractall('/tmp/workspace').

Step 3

3. Path Concatenation Without Bounds Check

Python's standard library joins /tmp/workspace + ../../../../home/user/.ssh/authorized_keys, resolving directly to the victim's SSH directory.

Step 4

4. Arbitrary File Overwrite & RCE

The victim's SSH keys or cron schedules are overwritten, giving the attacker root shell access on the host machine.

Source Code: Flaw vs. Secure Implementation

VULNERABLE PATTERN
# VULNERABLE: Standard Library tarfile.extractall()
import tarfile

def unpack_model_weights(tar_path: str, destination_dir: str):
    # CRITICAL VULNERABILITY (CVE-2007-4559):
    # extractall() blindly extracts members containing relative path sequences!
    # A tarball with '../../etc/shadow' or '../../.bashrc' escapes destination_dir!
    with tarfile.open(tar_path, "r:*") as tar:
        tar.extractall(path=destination_dir)
HARDENED DEFENSE
# SECURE: Strict Canonical Directory Containment Check & PEP 706 Filter
import os
import tarfile

def is_within_directory(directory: str, target: str) -> bool:
    """Ensure resolved realpath stays strictly inside destination boundary."""
    abs_directory = os.path.abspath(directory)
    abs_target = os.path.abspath(target)
    prefix = os.path.commonpath([abs_directory, abs_target])
    return prefix == abs_directory

def safe_extract(tar: tarfile.TarFile, target_path: str):
    """Iterate members and disallow any path traversal, device nodes, or symlink escapes."""
    for member in tar.getmembers():
        # Prevent absolute paths or relative traversal
        member_path = os.path.join(target_path, member.name)
        if not is_within_directory(target_path, member_path):
            raise SecurityError(f"Directory traversal attempt detected: {member.name}")
        
        # Disallow extraction of dangerous special files (block/char devices, FIFOs)
        if member.isdev() or member.isfifo():
            raise SecurityError(f"Special device extraction rejected: {member.name}")
            
    # If using Python 3.12+, utilize native PEP 706 filter
    if hasattr(tarfile, 'data_filter'):
        tar.extractall(path=target_path, filter='data')
    else:
        tar.extractall(path=target_path)

Engineering Hardening Checklist

← Browse Full Security Directory Explore Vulnerability Playbooks →