HIGH symlink attackbasic auth

Symlink Attack with Basic Auth

How Symlink Attack Manifests in Basic Auth

Symlink attacks in Basic Auth contexts typically exploit path traversal vulnerabilities that allow attackers to access files outside intended directories. In Basic Auth implementations, this often occurs when authentication logic constructs file paths dynamically based on user input without proper validation.

A common scenario involves user-supplied usernames or paths being concatenated directly into file system operations. For example, an API endpoint might use the username from Basic Auth credentials to load user-specific configuration files:

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    res.statusCode = 401;
    res.setHeader('WWW-Authenticate', 'Basic');
    res.end('Unauthorized');
    return;
  }

  const [username, password] = Buffer.from(authHeader.split(' ')[1], 'base64')
    .toString('utf8')
    .split(':');

  // Vulnerable: no path validation
  const filePath = `./users/${username}/config.json`;
  
  fs.readFile(filePath, 'utf8', (err, data) => {
    if (err) {
      res.statusCode = 500;
      res.end('Error loading config');
      return;
    }
    res.end(data);
  });
});

The vulnerability becomes exploitable when an attacker uses a username containing path traversal sequences like ../. With Basic Auth, the attacker can craft a username such as ../../../../etc to traverse outside the intended directory structure and access sensitive system files.

Another manifestation occurs in file upload scenarios where Basic Auth is used for authentication but file paths are constructed insecurely:

const http = require('http');
const formidable = require('formidable');

const server = http.createServer((req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    res.statusCode = 401;
    res.setHeader('WWW-Authenticate', 'Basic');
    res.end('Unauthorized');
    return;
  }

  const form = formidable({
    uploadDir: './uploads/'
  });

  form.parse(req, (err, fields, files) => {
    const username = fields.username;
    const filePath = `./uploads/${username}/${files.file.name}`;
    
    fs.rename(files.file.path, filePath, (err) => {
      if (err) {
        res.statusCode = 500;
        res.end('Upload failed');
        return;
      }
      res.end('Upload successful');
    });
  });
});

In this case, an attacker could upload a file with a name like ../../../../etc/passwd and traverse to sensitive system files. The Basic Auth mechanism provides authentication but doesn't prevent the path traversal attack that occurs after authentication succeeds.

Symlink attacks can also target configuration files that Basic Auth implementations might read. If an application uses user-controlled data to construct paths to configuration files, an attacker could potentially replace legitimate files with symlinks pointing to sensitive locations:

// Vulnerable configuration loading
const configPath = `./configs/${username}.json`;
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));

If the application doesn't verify that configs/${username}.json is a regular file and not a symlink, an attacker could create a symlink pointing to /etc/shadow or other sensitive files, bypassing Basic Auth's access controls.

Basic Auth-Specific Detection

Detecting symlink attacks in Basic Auth implementations requires both static analysis of the code and dynamic testing of the authentication endpoints. The key is identifying where user input influences file system operations without proper validation.

Static analysis should focus on authentication middleware and file handling code. Look for patterns where Basic Auth credentials are extracted and used directly in file operations:

// What to look for in code review
const [username] = extractBasicAuth(req);
const filePath = `./data/${username}/file.txt`;
fs.readFile(filePath, callback);

Specific indicators include: concatenation of username or other Basic Auth-derived values with file paths, use of path.join() without normalization, and lack of path.normalize() or path.resolve() with a fixed base directory.

Dynamic testing involves attempting path traversal with crafted Basic Auth credentials. A comprehensive test suite would include:

// Test cases for path traversal
const testCases = [
  '../etc/passwd',           // Unix path traversal
  '..\..\..\..\windows\system32\drivers\etc\hosts', // Windows traversal
  '../../../../../',        // Multiple traversal attempts
  './././././etc/passwd',   // Dot tricks
  '%2e%2e%2fetc%2fpasswd',   // URL encoded traversal
  '..%2Fetc%2Fpasswd',      // Mixed encoding
];

middleBrick's black-box scanning approach automatically tests these patterns against Basic Auth endpoints. The scanner attempts authentication with malicious usernames containing path traversal sequences and observes whether the server responds with sensitive file contents or error messages that reveal system information.

For Basic Auth specifically, middleBrick tests the unauthenticated attack surface by attempting to access endpoints with crafted credentials, looking for:

  • Path traversal responses that reveal system files
  • Directory listing functionality
  • Information disclosure through error messages
  • Symlink resolution that allows access to unauthorized files

The scanner also examines the authentication mechanism itself for weaknesses. For Basic Auth, this includes checking whether credentials are properly validated before file operations begin, and whether timing differences in error responses could enable brute-force attacks.

middleBrick's OpenAPI analysis can identify risky path construction patterns in the API specification. When it finds endpoints that use Basic Auth and accept path parameters or construct file paths dynamically, it flags these for additional testing and provides specific findings about the symlink vulnerability risk.

Basic Auth-Specific Remediation

Remediating symlink attacks in Basic Auth implementations requires a defense-in-depth approach that validates all user input and properly handles file system operations. The most critical fix is implementing strict path validation before any file access occurs.

Start by validating the username extracted from Basic Auth credentials. Use a whitelist approach to ensure only expected characters are allowed:

function validateUsername(username) {
  // Allow only alphanumeric, underscore, and hyphen
  const validUsername = /^[a-zA-Z0-9_-]+$/;
  return validUsername.test(username);
}

// Usage in authentication handler
const [username, password] = decodeBasicAuth(authHeader);
if (!validateUsername(username)) {
  res.statusCode = 400;
  res.end('Invalid username');
  return;
}

Next, implement safe path construction using Node.js's path module with a fixed base directory:

const path = require('path');
const fs = require('fs');

function getSafeFilePath(baseDir, relativePath) {
  const resolvedPath = path.resolve(baseDir, relativePath);
  
  // Ensure the resolved path starts with the base directory
  if (!resolvedPath.startsWith(baseDir)) {
    throw new Error('Path traversal attempt detected');
  }
  
  return resolvedPath;
}

// Usage
const baseDir = path.resolve(__dirname, 'users');
const relativePath = `${username}/config.json`;
try {
  const safePath = getSafeFilePath(baseDir, relativePath);
  const data = fs.readFileSync(safePath, 'utf8');
  res.end(data);
} catch (err) {
  res.statusCode = 403;
  res.end('Access denied');
}

For additional protection against symlink attacks, verify that files are not symlinks before accessing them:

function isRegularFile(filePath) {
  try {
    const stats = fs.statSync(filePath);
    return stats.isFile() && !stats.isSymbolicLink();
  } catch (err) {
    return false;
  }
}

// Usage
const safePath = getSafeFilePath(baseDir, relativePath);
if (!isRegularFile(safePath)) {
  res.statusCode = 403;
  res.end('Access denied');
  return;
}

Consider implementing a chroot-like environment for file operations by changing the working directory to a safe base directory before any file operations:

const safeBaseDir = path.resolve(__dirname, 'safe-files');

function withSafeDirectory(fn) {
  const originalCwd = process.cwd();
  try {
    process.chdir(safeBaseDir);
    return fn();
  } finally {
    process.chdir(originalCwd);
  }
}

// Usage
withSafeDirectory(() => {
  const data = fs.readFileSync(`${username}/config.json`, 'utf8');
  res.end(data);
});

For applications that must support user-controlled paths, implement a strict allowlist of permitted directories and files:

const allowedFiles = {
  'alice': ['config.json', 'profile.json'],
  'bob': ['config.json']
};

function getPermittedFilePath(username, filename) {
  if (!allowedFiles[username] || !allowedFiles[username].includes(filename)) {
    throw new Error('File not permitted');
  }
  
  const safePath = path.resolve(__dirname, 'users', username, filename);
  if (!safePath.startsWith(path.resolve(__dirname, 'users'))) {
    throw new Error('Path traversal detected');
  }
  
  return safePath;
}

Finally, implement comprehensive logging and monitoring for file access patterns. Log all file operations, especially those that fail due to path validation, to detect attempted symlink attacks:

const logger = require('./logger');

function safeReadFile(username, filename) {
  try {
    const safePath = getSafeFilePath(baseDir, `${username}/${filename}`);
    if (!isRegularFile(safePath)) {
      logger.warn(`Symlink attempt detected: ${safePath}`);
      throw new Error('Access denied');
    }
    return fs.readFileSync(safePath, 'utf8');
  } catch (err) {
    logger.error(`File access failed for ${username}/${filename}: ${err.message}`);
    throw err;
  }
}

Frequently Asked Questions

How does Basic Auth make symlink attacks more dangerous?
Basic Auth itself doesn't make symlink attacks more dangerous, but the combination creates a scenario where authentication is bypassed after successful login. Once authenticated via Basic Auth, the application may trust the username parameter and use it to construct file paths without validation. This trust boundary violation allows attackers who successfully authenticate to then exploit path traversal vulnerabilities that would otherwise be caught at the authentication stage.
Can middleBrick detect symlink attacks in Basic Auth implementations?
Yes, middleBrick's black-box scanning approach specifically tests for symlink attacks in Basic Auth endpoints. The scanner attempts authentication with crafted usernames containing path traversal sequences and observes whether the server responds with sensitive file contents. It also analyzes OpenAPI specifications to identify risky path construction patterns and provides specific findings about symlink vulnerability risks with severity levels and remediation guidance.