Informationsoffenlegung durch Shell-Fehlermeldung
Beschreibung
Informationsoffenlegung durch Shell-Fehlermeldung ist eine Schwachstelle, bei der eine Kommando-Shell-Fehlermeldung anzeigt, dass eine unbehandelte Ausnahme im Webanwendungscode existiert und potenziell sensible Systeminformationen offenlegt. Wenn Anwendungen Shell-Befehle aufrufen und Fehler nicht ordnungsgemäß behandeln, können die rohen Shell-Fehlermeldungen an Benutzer zurückgegeben werden. Diese Meldungen können Dateipfade, Systemkonfiguration, Benutzernamen, Befehlssyntax und andere interne Details offenlegen, die Angreifern helfen, die Systemarchitektur zu verstehen und potenzielle Angriffsvektoren zu identifizieren.
Risiko
Shell-Fehlermeldungen können kritische Systeminformationen an Angreifer offenlegen. Dateipfade enthüllen Verzeichnisstrukturen und Installationsorte. Befehlssyntaxfehler zeigen, welche Programme aufgerufen werden und wie. Berechtigungsfehler zeigen Sicherheitskonfigurationen an. Fehlende-Datei-Fehler enthüllen den erwarteten Systemzustand. Angreifer können diese Informationen nutzen, um gezielte Angriffe zu entwickeln, bestimmte Softwareversionen auszunutzen oder die Ausführungsumgebung zu verstehen. In vielen Fällen können Fehlerbedingungen, die diese Meldungen auslösen, absichtlich durch fehlerhafte Eingaben herbeigeführt werden, was aktive Aufklärung ermöglicht.
Lösung
Implementieren Sie umfassende Fehlerbehandlung für alle Shell-Befehlsausführungen. Fangen Sie alle Ausnahmen ab und geben Sie generische, benutzerfreundliche Fehlermeldungen zurück, die keine Systeminterna offenlegen. Loggen Sie detaillierte Fehlerinformationen serverseitig für Debugging, während Sie bereinigte Meldungen an Benutzer präsentieren. Verwenden Sie Whitelisting für Shell-Befehlseingaben und validieren Sie alle Parameter. Erwägen Sie die Verwendung nativer Bibliotheken anstelle von Shell-Befehlen wenn möglich. Implementieren Sie ordnungsgemäße Ausnahmebehandlungs-Wrapper um Befehlsausführungscode. Testen Sie Fehlerbehandlung mit verschiedenen Fehlerszenarien, um sicherzustellen, dass keine Informationen durchsickern.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Anwendungsdaten lesen - Shell-Fehlermeldungen können sensible Anwendungs- und Systeminformationen offenlegen, einschließlich Dateipfade, Benutzernamen, Softwareversionen und Konfigurationsdetails. |
Beispielcode
Verwundbarer Code
<?php
// Verwundbar: Shell-Befehl mit offengelegten Fehlern
function vulnerableBackup($filename) {
// Verwundbar: Direkte Shell-Ausführung
$output = shell_exec("tar czf backup.tar.gz " . $filename);
// Verwundbar: Shell-Fehler direkt an Benutzer zurückgegeben
if ($output === null) {
// Fehlermeldung von Shell wird angezeigt
echo "Fehler: Befehl fehlgeschlagen";
echo shell_exec("tar czf backup.tar.gz " . $filename . " 2>&1");
}
return $output;
}
// Bei Aufruf mit ungültiger Eingabe:
// tar: nonexistent_file: Cannot stat: No such file or directory
// tar: Error is not recoverable: exiting now
// Enthüllt: Dateisystempfade, tar-Befehlsverwendung
// Verwundbar: exec() mit Fehlerausgabe
function vulnerableSearch($query) {
$cmd = "grep -r '" . $query . "' /var/www/data/";
exec($cmd, $output, $return_var);
if ($return_var !== 0) {
// Verwundbar: Zeigt Shell-Fehler an Benutzer
echo "Suche fehlgeschlagen: ";
system($cmd . " 2>&1");
// Enthüllt: Verzeichnisstruktur, grep-Befehlssyntax
}
return $output;
}
?>
# Verwundbar: Python-Subprocess mit offengelegten Fehlern
import subprocess
def vulnerable_convert(input_file, output_file):
try:
# Verwundbar: shell=True erlaubt Command-Injection
cmd = f"convert {input_file} {output_file}"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
# Verwundbar: Rohes stderr an Benutzer zurückgegeben
return f"Konvertierung fehlgeschlagen: {result.stderr}"
# Könnte enthüllen:
# convert: unable to open image '/var/www/uploads/test.jpg': No such file or directory
# convert: no images defined 'output.png'
except Exception as e:
# Verwundbar: Vollständige Ausnahmedetails offengelegt
return f"Fehler: {str(e)}"
def vulnerable_ping(host):
import os
# Verwundbar: os.popen mit Fehleroffenlegung
result = os.popen(f"ping -c 1 {host} 2>&1").read()
return result
# Könnte enthüllen:
# ping: unknown host malicious-input; cat /etc/passwd
# sh: syntax error near unexpected token
// Verwundbar: Java Runtime.exec mit Fehleroffenlegung
public class VulnerableCommandExecutor {
public String executeCommand(String filename) {
try {
// Verwundbar: Benutzereingabe im Befehl
String command = "ls -la " + filename;
Process process = Runtime.getRuntime().exec(command);
BufferedReader errorReader = new BufferedReader(
new InputStreamReader(process.getErrorStream())
);
StringBuilder errors = new StringBuilder();
String line;
while ((line = errorReader.readLine()) != null) {
errors.append(line).append("\n");
}
if (process.waitFor() != 0) {
// Verwundbar: Shell-Fehler an Aufrufer zurückgegeben
return "Fehler bei Befehlsausführung: " + errors.toString();
// Könnte enthüllen:
// ls: cannot access '/etc/shadow': Permission denied
// ls: cannot access '/nonexistent': No such file or directory
}
return "Erfolg";
} catch (Exception e) {
// Verwundbar: Ausnahmedetails offengelegt
return "Befehlsausführung fehlgeschlagen: " + e.getMessage();
}
}
}
// Verwundbar: Node.js child_process mit Fehleroffenlegung
const { exec, execSync } = require('child_process');
function vulnerableCommand(userInput) {
return new Promise((resolve, reject) => {
// Verwundbar: Benutzereingabe im Shell-Befehl
exec(`cat ${userInput}`, (error, stdout, stderr) => {
if (error) {
// Verwundbar: Shell-Fehler an Benutzer zurückgegeben
resolve({
success: false,
error: stderr,
// Könnte enthüllen:
// cat: /etc/shadow: Permission denied
// cat: /nonexistent: No such file or directory
code: error.code
});
return;
}
resolve({ success: true, data: stdout });
});
});
}
function vulnerableSyncCommand(filename) {
try {
// Verwundbar: Sync exec mit Fehler in Ausnahme
const result = execSync(`file ${filename}`, { encoding: 'utf8' });
return result;
} catch (error) {
// Verwundbar: Vollständige Fehlerdetails offengelegt
return `Befehl fehlgeschlagen: ${error.stderr}`;
}
}
Lösungscode
<?php
// Behoben: Sichere Befehlsausführung mit Fehlerbehandlung
function secureBackup($filename) {
// Behoben: Dateinamen validieren
if (!preg_match('/^[a-zA-Z0-9_.-]+$/', $filename)) {
return ['success' => false, 'error' => 'Ungültiger Dateiname'];
}
// Behoben: escapeshellarg verwenden
$safeFilename = escapeshellarg($filename);
// Behoben: Fehler intern erfassen
$descriptors = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'] // stderr
];
$process = proc_open(
"tar czf backup.tar.gz {$safeFilename}",
$descriptors,
$pipes
);
if (is_resource($process)) {
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
$returnCode = proc_close($process);
if ($returnCode !== 0) {
// Behoben: Fehler intern loggen
error_log("Backup-Befehl fehlgeschlagen: " . $stderr);
// Behoben: Generische Meldung an Benutzer zurückgeben
return [
'success' => false,
'error' => 'Backup-Operation fehlgeschlagen. Bitte kontaktieren Sie den Support.'
];
}
return ['success' => true, 'message' => 'Backup erfolgreich erstellt'];
}
return ['success' => false, 'error' => 'Backup-Prozess könnte nicht gestartet werden'];
}
// Behoben: Sichere Suchfunktion
function secureSearch($query) {
// Behoben: Whitelist-Validierung
if (!preg_match('/^[a-zA-Z0-9\s]+$/', $query)) {
return ['error' => 'Ungültige Suchanfrage'];
}
$safeQuery = escapeshellarg($query);
$safeDir = '/var/www/data/';
$cmd = "grep -r {$safeQuery} " . escapeshellarg($safeDir);
exec($cmd . " 2>/dev/null", $output, $returnCode);
if ($returnCode !== 0) {
// Behoben: Generischer Fehler, detailliertes Log
error_log("Suche fehlgeschlagen mit Code $returnCode");
return ['results' => [], 'message' => 'Keine Ergebnisse gefunden'];
}
return ['results' => $output];
}
?>
# Behoben: Python-Subprocess mit sicherer Fehlerbehandlung
import subprocess
import logging
import re
logger = logging.getLogger(__name__)
def secure_convert(input_file, output_file):
# Behoben: Dateinamen validieren
if not re.match(r'^[\w\-./]+
```java
// Behoben: Java sichere Befehlsausführung
import java.util.logging.Logger;
import java.util.regex.Pattern;
public class SecureCommandExecutor {
private static final Logger logger = Logger.getLogger(
SecureCommandExecutor.class.getName()
);
private static final Pattern SAFE_FILENAME = Pattern.compile("^[\\w\\-./]+$");
public CommandResult executeCommand(String filename) {
// Behoben: Eingabe validieren
if (!SAFE_FILENAME.matcher(filename).matches()) {
return new CommandResult(false, "Ungültiges Dateinamenformat");
}
try {
// Behoben: ProcessBuilder mit Listen-Argumenten verwenden
ProcessBuilder pb = new ProcessBuilder("ls", "-la", filename);
pb.redirectErrorStream(false);
Process process = pb.start();
// Behoben: stderr lesen aber nicht offenlegen
String stderr = readStream(process.getErrorStream());
String stdout = readStream(process.getInputStream());
int exitCode = process.waitFor();
if (exitCode != 0) {
// Behoben: Intern loggen, generische Meldung zurückgeben
logger.warning("Befehl fehlgeschlagen: " + stderr);
return new CommandResult(false, "Datei-Operation fehlgeschlagen");
}
return new CommandResult(true, "Operation erfolgreich abgeschlossen");
} catch (IOException e) {
logger.log(Level.SEVERE, "IO-Fehler bei Befehlsausführung", e);
return new CommandResult(false, "Systemfehler aufgetreten");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warning("Befehl unterbrochen");
return new CommandResult(false, "Operation abgebrochen");
}
}
private String readStream(InputStream stream) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
}
}
// Behoben: Node.js sichere Befehlsausführung
const { execFile } = require('child_process');
const path = require('path');
function secureCommand(userInput) {
return new Promise((resolve) => {
// Behoben: Eingabe validieren
if (!/^[\w\-./]+$/.test(userInput)) {
resolve({
success: false,
error: 'Ungültiges Eingabeformat'
});
return;
}
// Behoben: execFile mit Argument-Array verwenden
execFile('cat', [userInput], { timeout: 5000 }, (error, stdout, stderr) => {
if (error) {
// Behoben: Fehler loggen, generische Meldung zurückgeben
console.error('Befehlsfehler:', stderr);
resolve({
success: false,
error: 'Datei könnte nicht gelesen werden'
});
return;
}
resolve({ success: true, data: stdout });
});
});
}
// Behoben: Fehlerbehandlungs-Wrapper
function safeExec(command, args, options = {}) {
return new Promise((resolve, reject) => {
execFile(command, args, {
timeout: options.timeout || 10000,
maxBuffer: options.maxBuffer || 1024 * 1024
}, (error, stdout, stderr) => {
if (error) {
// Behoben: Strukturierter Fehler ohne Shell-Details
console.error(`${command} fehlgeschlagen:`, stderr);
resolve({
success: false,
code: 'COMMAND_FAILED',
message: 'Operation könnte nicht abgeschlossen werden'
});
return;
}
resolve({ success: true, output: stdout });
});
});
}
CVE-Beispiele
Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE gelistet. Das Schwachstellenmuster ist jedoch dokumentiert in:
- Allgemeine Fehlerbehandlungs-Best-Practices
- Web-Anwendungs-Sicherheitstestrichtlinien
Referenzen
-
MITRE. "CWE-535: Exposure of Information Through Shell Error Message." https://cwe.mitre.org/data/definitions/535.html
-
OWASP. "Error Handling Cheat Sheet."
-
OWASP. "OS Command Injection Prevention."