Klartext-Speicherung in der Registry

Beschreibung

Klartext-Speicherung in der Registry ist eine Windows-spezifische Schwachstelle, bei der ein Produkt sensible Informationen im Klartext in der Windows-Registry speichert. Die Windows-Registry ist eine hierarchische Datenbank, die Konfigurationseinstellungen und Optionen für das Betriebssystem und Anwendungen speichert. Wenn sensible Daten wie Passwörter, API-Schlüssel oder Verschlüsselungsschlüssel ohne Verschlüsselung in Registry-Schlüsseln gespeichert werden, werden sie für jeden zugänglich, der die entsprechenden Registry-Zugriffsberechtigungen hat. Selbst kodierte Daten (wie Base64) bieten keine echte Sicherheit, da Angreifer leicht die Kodierungsmethode bestimmen und die Informationen dekodieren können.

Risiko

Klartext-Registry-Speicherung exponiert sensible Daten gegenüber mehreren Bedrohungsvektoren auf Windows-Systemen. Lokale Benutzer mit Registry-Lesezugriff können gespeicherte Anmeldedaten einsehen. Malware scannt häufig Registry-Speicherorte nach gespeicherten Passwörtern und Anmeldedaten. Administrative Tools und Backup-Utilities können Registry-Inhalte exponieren. Remote-Registry-Zugriff, falls aktiviert, erweitert die Angriffsfläche auf netzwerkbasierte Angreifer. Registry-Exportdateien (.reg), die sensible Daten enthalten, können unbeabsichtigt geteilt oder unsicher gespeichert werden. Die Persistenz und zentrale Natur der Registry macht sie zu einem attraktiven Ziel für Credential-Harvesting-Angriffe. Im Gegensatz zu Dateispeicherung kann Registry-Zugriff weniger überwacht werden, was Angreifern ermöglicht, Daten zu extrahieren, ohne Alarme auszulösen.

Lösung

Vermeiden Sie die Speicherung sensibler Informationen in der Registry, wann immer möglich. Wenn Registry-Speicherung notwendig ist, verschlüsseln Sie sensible Werte vor der Speicherung mit der Windows Data Protection API (DPAPI) oder anderen starken Verschlüsselungsmechanismen. Verwenden Sie DPAPI mit Benutzerbereich, um die Verschlüsselung an spezifische Benutzerkontexte zu binden, oder Maschinenbereich mit zusätzlichen Zugriffskontrollen. Implementieren Sie den Windows Credential Manager oder Credential Locker für sichere Anmeldedatenspeicherung. Setzen Sie angemessene Registry-Schlüsselberechtigungen (ACLs), um den Zugriff auf notwendige Konten zu beschränken. Speichern Sie niemals Klartext-Passwörter oder Verschlüsselungsschlüssel in der Registry. Erwägen Sie zertifikatsbasierte Authentifizierung oder Windows-Credential-Provider für Anwendungsauthentifizierung anstelle von in der Registry gespeicherten Anmeldedaten.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Angreifer mit Registry-Zugriff können sensible Informationen direkt lesen. Selbst kodierte Daten können leicht dekodiert werden, sobald die Kodierungsmethode identifiziert ist, was keinen bedeutungsvollen Schutz bietet.

Beispielcode

Anfälliger Code (C#/C++)

Die folgenden Beispiele demonstrieren Klartext-Registry-Speicherungs-Schwachstellen:

// Anfällig: Anmeldedaten im Klartext in Registry speichern
using Microsoft.Win32;

public class VulnerableRegistryStorage
{
    private const string RegKeyPath = @"SOFTWARE\MyApplication";

    public void SaveCredentials(string username, string password)
    {
        // Anfällig: Klartext-Anmeldedaten in Registry
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath))
        {
            key.SetValue("Username", username);
            key.SetValue("Password", password);  // Klartext!
        }
    }

    public void SaveDatabaseConfig(string connectionString)
    {
        // Anfällig: Connection-String mit eingebettetem Passwort
        // "Server=db.example.com;Database=App;User=admin;Password=SecretPass123;"
        using (RegistryKey key = Registry.LocalMachine.CreateSubKey(RegKeyPath))
        {
            key.SetValue("ConnectionString", connectionString);  // Enthält Passwort!
        }
    }

    public void SaveApiKeys(Dictionary<string, string> keys)
    {
        // Anfällig: API-Schlüssel im Klartext
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath + @"\ApiKeys"))
        {
            foreach (var apiKey in keys)
            {
                key.SetValue(apiKey.Key, apiKey.Value);  // Klartext-API-Schlüssel!
            }
        }
    }
}
// Anfällig: C++ Klartext-Registry-Speicherung
#include <windows.h>
#include <string>

class VulnerableConfig {
public:
    bool SavePassword(const std::wstring& password) {
        HKEY hKey;

        // Anfällig: Registry-Schlüssel für Passwortspeicherung erstellen
        LONG result = RegCreateKeyExW(
            HKEY_CURRENT_USER,
            L"SOFTWARE\\MyApp\\Credentials",
            0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL
        );

        if (result != ERROR_SUCCESS) {
            return false;
        }

        // Anfällig: Passwort als Klartext-String speichern
        result = RegSetValueExW(
            hKey,
            L"Password",
            0,
            REG_SZ,
            (LPBYTE)password.c_str(),
            (DWORD)((password.length() + 1) * sizeof(wchar_t))
        );

        RegCloseKey(hKey);
        return result == ERROR_SUCCESS;
    }

    bool SaveEncryptionKey(const BYTE* key, DWORD keySize) {
        HKEY hKey;
        LONG result = RegCreateKeyExW(
            HKEY_LOCAL_MACHINE,
            L"SOFTWARE\\MyApp\\Crypto",
            0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL
        );

        if (result != ERROR_SUCCESS) {
            return false;
        }

        // Anfällig: Verschlüsselungsschlüssel im Klartext-Binärformat speichern
        result = RegSetValueExW(
            hKey,
            L"MasterKey",
            0,
            REG_BINARY,
            key,  // Klartext-Verschlüsselungsschlüssel!
            keySize
        );

        RegCloseKey(hKey);
        return result == ERROR_SUCCESS;
    }
};
' Anfällig: VBScript/VBA Registry-Speicherung
Sub SaveCredentials(username, password)
    Dim WshShell
    Set WshShell = CreateObject("WScript.Shell")

    ' Anfällig: Klartext-Anmeldedaten in Registry
    WshShell.RegWrite "HKCU\SOFTWARE\MyApp\Username", username, "REG_SZ"
    WshShell.RegWrite "HKCU\SOFTWARE\MyApp\Password", password, "REG_SZ"  ' Klartext!

    Set WshShell = Nothing
End Sub

Korrigierter Code (C#/C++)

// Korrigiert: Sichere Anmeldedatenspeicherung mit DPAPI und Credential Manager
using Microsoft.Win32;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using CredentialManagement;  // Oder P/Invoke für CredWrite/CredRead verwenden

public class SecureRegistryStorage
{
    private const string RegKeyPath = @"SOFTWARE\MyApplication";

    // Korrigiert: Windows Credential Manager für Anmeldedaten verwenden
    public void SaveCredentials(string target, string username, string password)
    {
        using (var cred = new Credential())
        {
            cred.Target = target;
            cred.Username = username;
            cred.Password = password;
            cred.Type = CredentialType.Generic;
            cred.PersistanceType = PersistanceType.LocalComputer;
            cred.Save();
        }
        // Anmeldedaten sicher von Windows gespeichert
    }

    public NetworkCredential GetCredentials(string target)
    {
        using (var cred = new Credential())
        {
            cred.Target = target;
            if (cred.Load())
            {
                return new NetworkCredential(cred.Username, cred.Password);
            }
        }
        return null;
    }

    // Korrigiert: Vor Registry-Speicherung mit DPAPI verschlüsseln
    public void SaveEncryptedConfig(string configData)
    {
        // Korrigiert: Mit DPAPI im Benutzerbereich verschlüsseln
        byte[] plainBytes = Encoding.UTF8.GetBytes(configData);
        byte[] encryptedBytes = ProtectedData.Protect(
            plainBytes,
            null,  // Optionale Entropie
            DataProtectionScope.CurrentUser
        );

        // Verschlüsselte Daten speichern
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath))
        {
            key.SetValue("EncryptedConfig", encryptedBytes);
        }

        // Korrigiert: Klartext aus dem Speicher löschen
        Array.Clear(plainBytes, 0, plainBytes.Length);
    }

    public string GetEncryptedConfig()
    {
        using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegKeyPath))
        {
            if (key == null) return null;

            byte[] encryptedBytes = (byte[])key.GetValue("EncryptedConfig");
            if (encryptedBytes == null) return null;

            // Korrigiert: Mit DPAPI entschlüsseln
            byte[] plainBytes = ProtectedData.Unprotect(
                encryptedBytes,
                null,
                DataProtectionScope.CurrentUser
            );

            string result = Encoding.UTF8.GetString(plainBytes);

            // Korrigiert: Entschlüsselte Daten aus dem Speicher löschen
            Array.Clear(plainBytes, 0, plainBytes.Length);

            return result;
        }
    }

    // Korrigiert: Connection-String-Builder ohne Passwortspeicherung verwenden
    public void SaveDatabaseConfig(string server, string database)
    {
        // Korrigiert: Nur nicht-sensible Verbindungsinfos speichern
        // Passwort zur Laufzeit aus Credential Manager geholt
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath))
        {
            key.SetValue("DbServer", server);
            key.SetValue("DbName", database);
            // Passwort NICHT in Registry gespeichert
        }
    }
}
// Korrigiert: C++ sichere Registry-Speicherung mit DPAPI
#include <windows.h>
#include <wincred.h>
#include <dpapi.h>
#include <string>
#include <vector>

#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "credui.lib")

class SecureConfig {
public:
    // Korrigiert: Windows Credential Manager verwenden
    bool SavePassword(const std::wstring& target, const std::wstring& username,
                      const std::wstring& password) {

        CREDENTIALW cred = {0};
        cred.Type = CRED_TYPE_GENERIC;
        cred.TargetName = const_cast<LPWSTR>(target.c_str());
        cred.UserName = const_cast<LPWSTR>(username.c_str());
        cred.CredentialBlob = (LPBYTE)password.c_str();
        cred.CredentialBlobSize = (DWORD)((password.length() + 1) * sizeof(wchar_t));
        cred.Persist = CRED_PERSIST_LOCAL_MACHINE;

        return CredWriteW(&cred, 0) != FALSE;
    }

    bool GetPassword(const std::wstring& target, std::wstring& password) {
        PCREDENTIALW pCred = nullptr;

        if (!CredReadW(target.c_str(), CRED_TYPE_GENERIC, 0, &pCred)) {
            return false;
        }

        password = std::wstring(
            (wchar_t*)pCred->CredentialBlob,
            pCred->CredentialBlobSize / sizeof(wchar_t)
        );

        // Korrigiert: Credential sicher freigeben
        CredFree(pCred);
        return true;
    }

    // Korrigiert: Daten vor Registry-Speicherung verschlüsseln
    bool SaveEncryptedKey(const BYTE* key, DWORD keySize) {
        DATA_BLOB dataIn = {keySize, const_cast<BYTE*>(key)};
        DATA_BLOB dataOut = {0};

        // Korrigiert: Mit DPAPI verschlüsseln
        if (!CryptProtectData(&dataIn, L"MyApp Key", NULL, NULL, NULL,
                              CRYPTPROTECT_UI_FORBIDDEN, &dataOut)) {
            return false;
        }

        HKEY hKey;
        LONG result = RegCreateKeyExW(
            HKEY_CURRENT_USER,
            L"SOFTWARE\\MyApp\\Crypto",
            0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL
        );

        if (result == ERROR_SUCCESS) {
            // Korrigiert: DPAPI-verschlüsselte Daten speichern
            result = RegSetValueExW(
                hKey,
                L"ProtectedKey",
                0,
                REG_BINARY,
                dataOut.pbData,
                dataOut.cbData
            );
            RegCloseKey(hKey);
        }

        LocalFree(dataOut.pbData);
        return result == ERROR_SUCCESS;
    }

    bool GetEncryptedKey(std::vector<BYTE>& key) {
        HKEY hKey;
        if (RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\MyApp\\Crypto",
                          0, KEY_READ, &hKey) != ERROR_SUCCESS) {
            return false;
        }

        DWORD dataSize = 0;
        RegQueryValueExW(hKey, L"ProtectedKey", NULL, NULL, NULL, &dataSize);

        std::vector<BYTE> encryptedData(dataSize);
        if (RegQueryValueExW(hKey, L"ProtectedKey", NULL, NULL,
                             encryptedData.data(), &dataSize) != ERROR_SUCCESS) {
            RegCloseKey(hKey);
            return false;
        }
        RegCloseKey(hKey);

        // Korrigiert: Mit DPAPI entschlüsseln
        DATA_BLOB dataIn = {dataSize, encryptedData.data()};
        DATA_BLOB dataOut = {0};

        if (!CryptUnprotectData(&dataIn, NULL, NULL, NULL, NULL,
                                CRYPTPROTECT_UI_FORBIDDEN, &dataOut)) {
            return false;
        }

        key.assign(dataOut.pbData, dataOut.pbData + dataOut.cbData);

        // Korrigiert: Entschlüsselte Daten sicher löschen
        SecureZeroMemory(dataOut.pbData, dataOut.cbData);
        LocalFree(dataOut.pbData);

        return true;
    }
};

Die Korrektur verwendet Windows Credential Manager für Anmeldedaten und DPAPI zum Verschlüsseln anderer sensibler Registry-Daten.


Ausgenutzt in der Praxis

Registry-Passwortexposition (Windows-Anwendungen, 2005)

CVE-2005-2227 dokumentierte eine Windows-Anwendung, die Klartext-Passwörter in Registry-Schlüsseln speicherte, die lokalen Benutzern zugänglich waren.

Malware-Credential-Harvesting (Fortlaufend)

Verschiedene Malware-Familien scannen speziell die Windows-Registry nach gespeicherten Anmeldedaten und zielen auf gängige Anwendungs-Registry-Speicherorte ab.


Tools zum Testen/Ausnutzen

  • Registry Editor (regedit) — Integriertes Windows-Tool zum Durchsuchen von Registry-Inhalten.

  • RegRipper — Registry-Analysetool für Forensik und Sicherheitstests.

  • Mimikatz — Sicherheitstool, das Anmeldedaten aus verschiedenen Windows-Speicherorten extrahieren kann.


CVE-Beispiele

  • CVE-2005-2227 — Klartext-Passwörter in Registry-Schlüssel gespeichert.

  • CVE-2007-5778 — Login-Anmeldedaten in unverschlüsseltem Registry-Schlüssel.


Referenzen

  1. MITRE Corporation. "CWE-314: Cleartext Storage in the Registry." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/314.html

  2. Microsoft. "Data Protection API (DPAPI)." https://docs.microsoft.com/en-us/windows/win32/api/dpapi/

  3. Microsoft. "Credential Locker." https://docs.microsoft.com/en-us/windows/uwp/security/credential-locker