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

AuswirkungDetails
AuthentifizierungScope: Authentifizierung

Angreifer können unveränderte Standardanmeldedaten nutzen, um unautorisierten Zugang zu erlangen und administrative Rollen zu übernehmen.
ZugriffskontrolleScope: 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

  1. MITRE Corporation. "CWE-1393: Use of Default Password." https://cwe.mitre.org/data/definitions/1393.html
  2. CISA. "Default Passwords in Critical Infrastructure"
  3. OWASP. "Default Passwords"