T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- scripts/register.js:84
- Finding
- Wallet path validation can escape the user's home directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/register.js:84-114, 133-137` **Vulnerability Type**: Improper path containment validation and symlink traversal **Risk Level**: Medium ### Vulnerable Code ```javascript function validateWalletPath(walletPath) { const resolved = path.resolve(walletPath); // Must be under home directory (prevent reading system files) const home = process.env.HOME; if (!resolved.startsWith(home)) { console.error(`Security: Wallet path must be under your home directory (${home})`); process.exit(1); } // Check for suspicious path components if (walletPath.includes('..') || walletPath.includes('\0')) { console.error('Security: Invalid path — must not contain ".." or null bytes'); process.exit(1); } // Check file size try { const stat = fs.statSync(resolved); if (stat.size > MAX_WALLET_FILE_SIZE) { console.error(`Security: Wallet file too large (${stat.size} bytes, max ${MAX_WALLET_FILE_SIZE})`); process.exit(1); } if (!stat.isFile()) { console.error('Security: Path must point to a regular file'); process.exit(1); } } catch (e) { // File doesn't exist — let the caller handle it } return resolved; } ``` The validated path is subsequently read as a private key: ```javascript const walletArg = getArg('--wallet'); if (walletArg) { const walletPath = validateWalletPath(walletArg.replace(/^~/, process.env.HOME)); if (fs.existsSync(walletPath)) { console.log(`Using wallet file: ${walletPath}`); const key = fs.readFileSync(walletPath, 'utf8').trim(); ``` ### Technical Analysis The containment check uses: ```javascript resolved.startsWith(home) ``` String-prefix comparison does not establish that one filesystem path is a child of another. For example, if `$HOME` is `/home/alice`, a path such as `/home/alice-backup/key` begins with the same string and passes the check even though it is outside `/home/alice`. The implem ...[truncated 1766 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Canonicalize both the home directory and selected file using `fs.realpathSync`. 2. Use `path.relative()` rather than string-prefix comparison: ```javascript const realHome = fs.realpathSync(process.env.HOME); const realFile = fs.realpathSync(resolved); const relative = path.relative(realHome, realFile); if ( relative === '' || relative.startsWith(`..${path.sep}`) || relative === '..' || path.isAbsolute(relative) ) { throw new Error('Wallet file must be located inside the home directory'); } ``` 3. Use `fs.lstatSync()` before following the path and reject symbolic links if symlink-based wallet files are not explicitly required. 4. Open the file only after validation, preferably using a file descriptor and platform-supported anti-symlink flags to reduce time-of-check/time-of-use races. 5. Verify that the file is owned by the current user and reject files writable by group or other users. 6. Replace `process.exit()` inside validation helpers with exceptions so callers can handle failures consistently and securely. ]]>
