Verwendung von Standardpasswörtern
Beschreibung
Verwendung von Standardpasswörtern tritt auf, wenn ein Produkt Standardpasswörter für potenziell kritische Funktionalität verwendet, was es Angreifern erleichtert, die Authentifizierung über mehrere Organisationen hinweg zu umgehen, da Listen mit Standardpasswörtern leicht verfügbar sind. Produkte verwenden häufig Standardpasswörter, um Herstellung und Systeminstallation zu vereinfachen. Wenn Administratoren diese Anmeldedaten jedoch nicht ändern, können Angreifer schnell unautorisierten Zugang erlangen, indem sie öffentlich verfügbare Scanner und Datenbanken für Standardpasswörter nutzen.
Risiko
Standardpasswörter haben schwerwiegende Auswirkungen. Massenausnutzung mit automatisierten Scannern. Botnet-Rekrutierung (Mirai-artige Angriffe). Vollständige Authentifizierungsumgehung. Administrativer Zugang zu kritischer Infrastruktur. Netzwerkkompromittierung. Datenlecks. Regulatorische Nicht-Compliance. Supply-Chain-Angriffe über kompromittierte Geräte. Hohe Wahrscheinlichkeit, da Standardanmeldedaten öffentlich dokumentiert sind.
Lösung
Verwendung von Standard-, fest kodierten oder anderen Werten, die nicht pro Installation variieren, in der Anforderungsphase verbieten (hohe Wirksamkeit). Standardpasswörter in der Produktdokumentation hervorheben und Änderungsverfahren bereitstellen (begrenzte Wirksamkeit). Administratoren zur Änderung der Anmeldedaten bei der Installation zwingen in der Architektur- und Designphase (hohe Wirksamkeit). Passwortänderung während der Einrichtung oder des Betriebs ermöglichen (mittlere Wirksamkeit).
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Authentifizierung | Scope: Authentifizierung Angreifer können unveränderte Standardanmeldedaten nutzen, um unautorisierten Zugang zu erlangen und administrative Rollen zu übernehmen. |
| Zugriffskontrolle | Scope: Zugriffskontrolle Vollständige Umgehung von Authentifizierungskontrollen, die zur Systemkompromittierung führt. |
Beispielcode und Lösung
Verwundbarer Code
# VERWUNDBAR: Produkte mit Standardpasswörtern
# VERWUNDBAR: Router/IoT-Gerät-Standards
DEVICE_DEFAULTS = {
"admin_user": "admin",
"admin_password": "admin", # VERWUNDBAR: Häufiger Standard
"enable_password": "cisco", # VERWUNDBAR: Bekannter Standard
"wifi_password": "password123", # VERWUNDBAR: Schwaches Standard-WLAN
"snmp_community": "public" # VERWUNDBAR: Standard-SNMP
}
class VulnerableDevice:
def __init__(self):
# VERWUNDBAR: Initialisiert mit Standards
self.admin_user = DEVICE_DEFAULTS["admin_user"]
self.admin_password = DEVICE_DEFAULTS["admin_password"]
self.password_changed = False
def authenticate(self, username, password):
# VERWUNDBAR: Akzeptiert Standardanmeldedaten
if username == self.admin_user and password == self.admin_password:
if not self.password_changed:
# VERWUNDBAR: Warnt nur, erzwingt keine Änderung
print("Warnung: Bitte ändern Sie das Standardpasswort")
return True
return False
def change_password(self, new_password):
# VERWUNDBAR: Optional, nicht erzwungen
self.admin_password = new_password
self.password_changed = True
# VERWUNDBAR: Datenbank mit Standard-Root-Passwort
class VulnerableDatabase:
DEFAULT_ROOT_PASSWORD = "" # VERWUNDBAR: Leerer Standard!
def connect(self):
return connect(
user="root",
password=self.DEFAULT_ROOT_PASSWORD # VERWUNDBAR
)
# VERWUNDBAR: Webanwendung-Standards
class VulnerableWebApp:
def __init__(self):
self.users = {
"admin": "admin123", # VERWUNDBAR: Standard-Admin
"user": "user123", # VERWUNDBAR: Standard-Benutzer
"guest": "guest" # VERWUNDBAR: Standard-Gast
}
def first_login(self, username, password):
if username in self.users and self.users[username] == password:
# VERWUNDBAR: Erzwingt keine Passwortänderung
return {"status": "success", "token": generate_token()}
return {"status": "failed"}
# VERWUNDBAR: Drucker/Scanner-Standards
PRINTER_DEFAULTS = {
"admin": "admin",
"tech": "tech",
"service": "service" # VERWUNDBAR: Dienstkonten mit Standards
}
# VERWUNDBAR: Industriesteuerungssystem
PLC_CONFIG = {
"username": "Administrator",
"password": "password", # VERWUNDBAR: Industriesystem-Standard
"level2_password": "level2", # VERWUNDBAR: Engineering-Zugang
"factory_reset_code": "1234" # VERWUNDBAR: Zurücksetzungscode
}
// VERWUNDBAR: Java-Anwendungen mit Standardpasswörtern
public class VulnerableProduct {
// VERWUNDBAR: Fest kodierte Standardpasswörter
private static final String DEFAULT_ADMIN_USER = "admin";
private static final String DEFAULT_ADMIN_PASS = "admin";
// VERWUNDBAR: Dienstkonto-Standards
private static final Map<String, String> SERVICE_ACCOUNTS = Map.of(
"backup", "backup123",
"monitor", "monitor123",
"update", "update123"
);
private String adminPassword = DEFAULT_ADMIN_PASS;
public boolean login(String username, String password) {
// VERWUNDBAR: Akzeptiert Standardanmeldedaten
if (DEFAULT_ADMIN_USER.equals(username) &&
adminPassword.equals(password)) {
return true;
}
// VERWUNDBAR: Dienstkonten mit Standards
if (SERVICE_ACCOUNTS.containsKey(username) &&
SERVICE_ACCOUNTS.get(username).equals(password)) {
return true;
}
return false;
}
// VERWUNDBAR: Erstlauf erzwingt keine Passwortänderung
public void firstRunSetup() {
System.out.println("Willkommen! Standardanmeldedaten:");
System.out.println("Benutzername: admin");
System.out.println("Passwort: admin");
System.out.println("Wir empfehlen, das Passwort zu ändern.");
// VERWUNDBAR: Nur Empfehlung, nicht erzwungen
}
}
// VERWUNDBAR: Eingebettete Geräte-Firmware
public class VulnerableFirmware {
// VERWUNDBAR: Telnet mit Standardpasswort
public static final String TELNET_PASSWORD = "alpine"; // iOS-Standard
// VERWUNDBAR: Debug-Hintertür
public static final String DEBUG_PASSWORD = "debug123";
// VERWUNDBAR: Werksreset-Passwort
public static final String FACTORY_RESET = "000000";
public boolean authenticateTelnet(String password) {
// VERWUNDBAR: Akzeptiert bekannten Standard
return TELNET_PASSWORD.equals(password);
}
}
// VERWUNDBAR: Node.js mit Standardpasswörtern
// VERWUNDBAR: Anwendungs-Standards
const defaultCredentials = {
admin: {
username: 'admin',
password: 'admin' // VERWUNDBAR
},
database: {
username: 'postgres',
password: 'postgres' // VERWUNDBAR
},
redis: {
password: '' // VERWUNDBAR: Kein Passwort
}
};
// VERWUNDBAR: Ersteinrichtung
async function setupApp() {
const adminExists = await checkAdminExists();
if (!adminExists) {
// VERWUNDBAR: Erstellt Admin mit Standardpasswort
await createUser({
username: defaultCredentials.admin.username,
password: hashPassword(defaultCredentials.admin.password),
role: 'admin',
mustChangePassword: false // VERWUNDBAR: Nicht erzwungen
});
console.log('Admin-Konto mit Standardanmeldedaten erstellt');
console.log('Benutzername: admin, Passwort: admin');
// VERWUNDBAR: Angreifer liest Logs, kennt Anmeldedaten
}
}
// VERWUNDBAR: Docker/Container-Standards
const containerDefaults = {
mysql_root_password: 'root',
postgres_password: 'postgres',
mongodb_admin: 'admin',
redis_password: ''
};
// VERWUNDBAR: IoT-Geräteregistrierung
function registerDevice(serialNumber) {
return {
deviceId: serialNumber,
username: 'device',
password: serialNumber.slice(-6), // VERWUNDBAR: Abgeleitet von öffentlicher Info
apiKey: `key_${serialNumber}`
};
}
Sichere Lösung
# SICHER: Ordnungsgemäße Passwortverwaltung
import secrets
import string
import os
import sys
class SecureDevice:
def __init__(self):
# SICHER: Keine Standardpasswörter
self.admin_user = None
self.admin_password_hash = None
self.is_setup_complete = False
def initialize(self):
"""SICHER: Einrichtung vor Verwendung erzwingen."""
if not self._check_configuration():
self._run_initial_setup()
def _check_configuration(self):
"""SICHER: Verifizieren, dass Anmeldedaten konfiguriert und keine Standards sind."""
password_hash = os.environ.get('ADMIN_PASSWORD_HASH')
if not password_hash:
return False
# SICHER: Gespeicherter Hash des Passworts, nicht Klartext
self.admin_password_hash = password_hash
self.admin_user = os.environ.get('ADMIN_USER', 'admin')
self.is_setup_complete = True
return True
def _run_initial_setup(self):
"""SICHER: Interaktive Einrichtung mit stärker Passwortanforderung."""
print("=== Ersteinrichtung des Geräts ===")
print("Ein starkes Passwort ist erforderlich.")
# SICHER: Starkes Passwort generieren und vorschlagen
suggested = self._generate_strong_password()
print(f"\nVorgeschlagenes Passwort: {suggested}")
password = input("\nAdmin-Passwort eingeben (min. 12 Zeichen): ").strip()
# SICHER: Passwortstärke validieren
if not self._is_strong_password(password):
print("FEHLER: Passwort erfüllt nicht die Anforderungen:")
print(" - Mindestens 12 Zeichen")
print(" - Mindestens ein Großbuchstabe")
print(" - Mindestens ein Kleinbuchstabe")
print(" - Mindestens eine Zahl")
print(" - Mindestens ein Sonderzeichen")
sys.exit(1)
# SICHER: Sicher speichern (Hash, nicht Klartext)
password_hash = self._hash_password(password)
print(f"\nUmgebungsvariable setzen:")
print(f"export ADMIN_PASSWORD_HASH='{password_hash}'")
sys.exit(0)
@staticmethod
def _generate_strong_password(length=16):
"""SICHER: Kryptographisch sicheres Passwort generieren."""
chars = string.ascii_letters + string.digits + "!@#$%^&*"
return ''.join(secrets.choice(chars) for _ in range(length))
@staticmethod
def _is_strong_password(password):
"""SICHER: Stärke Passwortanforderungen durchsetzen."""
if len(password) < 12:
return False
if not any(c.isupper() for c in password):
return False
if not any(c.islower() for c in password):
return False
if not any(c.isdigit() for c in password):
return False
if not any(c in "!@#$%^&*" for c in password):
return False
return True
def authenticate(self, username, password):
"""SICHER: Gegen konfigurierte Anmeldedaten authentifizieren."""
if not self.is_setup_complete:
raise SecurityError("Gerät nicht konfiguriert")
if username != self.admin_user:
return False
return self._verify_password(password, self.admin_password_hash)
# SICHER: Werksbereitstellung mit einzigartigen Anmeldedaten
class SecureFactoryProvisioning:
def provision_device(self, serial_number):
"""SICHER: Einzigartige Anmeldedaten pro Gerät generieren."""
# SICHER: Zufällige Anmeldedaten, nicht von Seriennummer abgeleitet
device_password = secrets.token_urlsafe(16)
api_key = secrets.token_urlsafe(32)
# SICHER: In sicherer Bereitstellungsdatenbank speichern
self._store_credentials(serial_number, device_password, api_key)
# SICHER: Auf Geräte-Etikett während der Herstellung drucken
return {
"serial": serial_number,
"initial_password": device_password, # Auf Etikett gedruckt
"api_key": api_key
}
def first_boot(self, serial_number):
"""SICHER: Passwortänderung beim ersten Start erzwingen."""
creds = self._get_provisioned_credentials(serial_number)
if not creds.get('password_changed'):
raise SetupRequired(
"Erstmalige Passwortänderung erforderlich. "
"Siehe Geräte-Etikett für Erstanmeldedaten."
)
// SICHER: Java-Anwendung ohne Standardpasswörter
public class SecureProduct {
private boolean setupComplete = false;
private String adminPasswordHash;
// SICHER: Keine Standardpasswörter im Code
public void initialize() throws SecurityException {
// SICHER: Auf ordnungsgemäße Konfiguration prüfen
String passwordHash = System.getenv("ADMIN_PASSWORD_HASH");
if (passwordHash == null || passwordHash.isEmpty()) {
throw new SecurityException(
"Admin-Passwort nicht konfiguriert. Erst Einrichtung ausführen."
);
}
// SICHER: Hash-Format validieren (kein schwacher/Standard-Wert)
if (passwordHash.length() < 60) { // bcrypt-Hash ist 60 Zeichen
throw new SecurityException(
"Ungültiges Passwort-Hash-Format. Bitte neu konfigurieren."
);
}
this.adminPasswordHash = passwordHash;
this.setupComplete = true;
}
// SICHER: Erstlauf erzwingt Passworteinrichtung
public static void firstRunSetup() {
Console console = System.console();
if (console == null) {
System.err.println("Konsole für Einrichtung nicht verfügbar");
System.exit(1);
}
System.out.println("=== Ersteinrichtung ===\n");
// SICHER: Starkes vorgeschlagenes Passwort generieren
String suggested = generateStrongPassword();
System.out.println("Vorgeschlagenes Passwort: " + suggested);
char[] passwordChars = console.readPassword(
"Admin-Passwort eingeben (min. 12 Zeichen): "
);
String password = new String(passwordChars);
// SICHER: Stärke validieren
if (!isStrongPassword(password)) {
System.err.println("Passwort zu schwach. Anforderungen:");
System.err.println("- 12+ Zeichen");
System.err.println("- Gemischte Groß-/Kleinschreibung, Zahlen, Symbole");
System.exit(1);
}
// SICHER: Hash für Speicherung generieren
String hash = BCrypt.hashpw(password, BCrypt.gensalt(12));
System.out.println("\nUmgebungsvariable setzen:");
System.out.println("export ADMIN_PASSWORD_HASH='" + hash + "'");
// Passwort aus dem Speicher löschen
Arrays.fill(passwordChars, ' ');
}
private static String generateStrongPassword() {
SecureRandom random = new SecureRandom();
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16; i++) {
sb.append(chars.charAt(random.nextInt(chars.length())));
}
return sb.toString();
}
private static boolean isStrongPassword(String password) {
if (password.length() < 12) return false;
if (!password.chars().anyMatch(Character::isUpperCase)) return false;
if (!password.chars().anyMatch(Character::isLowerCase)) return false;
if (!password.chars().anyMatch(Character::isDigit)) return false;
return password.chars().anyMatch(c -> "!@#$%^&*".indexOf(c) >= 0);
}
}
// SICHER: Node.js ohne Standardpasswörter
const crypto = require('crypto');
const bcrypt = require('bcrypt');
const readline = require('readline');
// SICHER: Keine Standardpasswörter
class SecureApplication {
constructor() {
this.setupComplete = false;
this.adminPasswordHash = null;
}
async initialize() {
// SICHER: Auf Konfiguration prüfen
const passwordHash = process.env.ADMIN_PASSWORD_HASH;
if (!passwordHash) {
console.error('FEHLER: ADMIN_PASSWORD_HASH nicht konfiguriert');
console.error('Mit --setup Flag für Erstkonfiguration ausführen');
process.exit(1);
}
// SICHER: Hash-Format validieren
if (!passwordHash.startsWith('$2')) { // bcrypt-Präfix
console.error('FEHLER: Ungültiges Passwort-Hash-Format');
process.exit(1);
}
this.adminPasswordHash = passwordHash;
this.setupComplete = true;
}
static async runSetup() {
console.log('=== Ersteinrichtung ===\n');
// SICHER: Starkes vorgeschlagenes Passwort generieren
const suggested = crypto.randomBytes(12).toString('base64');
console.log(`Vorgeschlagenes Passwort: ${suggested}`);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const password = await new Promise(resolve => {
rl.question('Admin-Passwort eingeben (min. 12 Zeichen): ', resolve);
});
rl.close();
// SICHER: Stärke validieren
if (!SecureApplication.isStrongPassword(password)) {
console.error('\nPasswort zu schwach. Anforderungen:');
console.error('- 12+ Zeichen');
console.error('- Gemischte Groß-/Kleinschreibung, Zahlen, Symbole');
process.exit(1);
}
// SICHER: Hash generieren
const hash = await bcrypt.hash(password, 12);
console.log('\nUmgebungsvariable setzen:');
console.log(`export ADMIN_PASSWORD_HASH='${hash}'`);
process.exit(0);
}
static isStrongPassword(password) {
if (password.length < 12) return false;
if (!/[A-Z]/.test(password)) return false;
if (!/[a-z]/.test(password)) return false;
if (!/[0-9]/.test(password)) return false;
if (!/[!@#$%^&*]/.test(password)) return false;
return true;
}
}
// SICHER: CLI-Behandlung
if (process.argv.includes('--setup')) {
SecureApplication.runSetup();
} else {
const app = new SecureApplication();
app.initialize();
}
CVE-Beispiele
- CVE-2016-1560: DVR-Geräte mit Standardanmeldedaten vom Mirai-Botnet ausgenutzt.
- CVE-2020-9054: ZyXEL-Geräte mit Standard-admin:1234-Anmeldedaten.
- CVE-2021-36260: Hikvision-Kameras mit Standardpasswörtern.
Verwandte CWEs
- CWE-1392: Use of Default Credentials (übergeordnet)
- CWE-798: Use of Hard-coded Credentials (verwandt)
- CWE-259: Use of Hard-coded Password (verwandt)
Referenzen
- MITRE Corporation. "CWE-1393: Use of Default Password." https://cwe.mitre.org/data/definitions/1393.html
- CISA. "Default Passwords in Critical Infrastructure"
- OWASP. "Default Passwords"