Cleartext Storage in the Registry

Description

Cleartext Storage in the Registry is a vulnerability specific to Windows systems where a product stores sensitive information in cleartext in the Windows Registry. The Windows Registry is a hierarchical database that stores configuration settings and options for the operating system and applications. When sensitive data such as passwords, API keys, or encryption keys are stored without encryption in registry keys, they become accessible to anyone with appropriate registry access permissions. Even encoded data (such as Base64) provides no real security, as attackers can easily determine the encoding method and decode the information.

Risk

Cleartext registry storage exposes sensitive data to multiple threat vectors on Windows systems. Local users with registry read access can view stored credentials. Malware frequently scans registry locations for stored passwords and credentials. Administrative tools and backup utilities may expose registry contents. Remote registry access, if enabled, extends the attack surface to network-based attackers. Registry export files (.reg) containing sensitive data may be inadvertently shared or stored insecurely. The persistence and central nature of the registry makes it an attractive target for credential harvesting attacks. Unlike file storage, registry access may be less monitored, allowing attackers to extract data without triggering alerts.

Solution

Avoid storing sensitive information in the registry whenever possible. If registry storage is necessary, encrypt sensitive values before storage using the Windows Data Protection API (DPAPI) or other strong encryption mechanisms. Use DPAPI with the user scope to tie encryption to specific user contexts, or machine scope with additional access controls. Implement the Windows Credential Manager or Credential Locker for storing credentials securely. Set appropriate registry key permissions (ACLs) to restrict access to necessary accounts only. Never store plaintext passwords or encryption keys in the registry. Consider using certificate-based authentication or Windows credential providers for application authentication instead of registry-stored credentials.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Attackers with registry access can read sensitive information directly. Even encoded data can be easily decoded once the encoding method is identified, providing no meaningful protection.

Example Code

Vulnerable Code (C#/C++)

The following examples demonstrate cleartext registry storage vulnerabilities:

// Vulnerable: Storing credentials in cleartext registry
using Microsoft.Win32;

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

    public void SaveCredentials(string username, string password)
    {
        // Vulnerable: Plaintext credentials in registry
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath))
        {
            key.SetValue("Username", username);
            key.SetValue("Password", password);  // Plaintext!
        }
    }

    public void SaveDatabaseConfig(string connectionString)
    {
        // Vulnerable: Connection string with embedded password
        // "Server=db.example.com;Database=App;User=admin;Password=SecretPass123;"
        using (RegistryKey key = Registry.LocalMachine.CreateSubKey(RegKeyPath))
        {
            key.SetValue("ConnectionString", connectionString);  // Contains password!
        }
    }

    public void SaveApiKeys(Dictionary<string, string> keys)
    {
        // Vulnerable: API keys in cleartext
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath + @"\ApiKeys"))
        {
            foreach (var apiKey in keys)
            {
                key.SetValue(apiKey.Key, apiKey.Value);  // Plaintext API keys!
            }
        }
    }
}
// Vulnerable: C++ cleartext registry storage
#include <windows.h>
#include <string>

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

        // Vulnerable: Creating registry key for password storage
        LONG result = RegCreateKeyExW(
            HKEY_CURRENT_USER,
            L"SOFTWARE\\MyApp\\Credentials",
            0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL
        );

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

        // Vulnerable: Storing password as plaintext string
        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;
        }

        // Vulnerable: Storing encryption key in cleartext binary
        result = RegSetValueExW(
            hKey,
            L"MasterKey",
            0,
            REG_BINARY,
            key,  // Plaintext encryption key!
            keySize
        );

        RegCloseKey(hKey);
        return result == ERROR_SUCCESS;
    }
};
' Vulnerable: VBScript/VBA registry storage
Sub SaveCredentials(username, password)
    Dim WshShell
    Set WshShell = CreateObject("WScript.Shell")

    ' Vulnerable: Plaintext credentials in registry
    WshShell.RegWrite "HKCU\SOFTWARE\MyApp\Username", username, "REG_SZ"
    WshShell.RegWrite "HKCU\SOFTWARE\MyApp\Password", password, "REG_SZ"  ' Plaintext!

    Set WshShell = Nothing
End Sub

Fixed Code (C#/C++)

// Fixed: Secure credential storage using DPAPI and Credential Manager
using Microsoft.Win32;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using CredentialManagement;  // Or use P/Invoke for CredWrite/CredRead

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

    // Fixed: Use Windows Credential Manager for credentials
    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();
        }
        // Credentials stored securely by Windows
    }

    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;
    }

    // Fixed: Encrypt before storing in registry using DPAPI
    public void SaveEncryptedConfig(string configData)
    {
        // Fixed: Encrypt using DPAPI with user scope
        byte[] plainBytes = Encoding.UTF8.GetBytes(configData);
        byte[] encryptedBytes = ProtectedData.Protect(
            plainBytes,
            null,  // Optional entropy
            DataProtectionScope.CurrentUser
        );

        // Store encrypted data
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath))
        {
            key.SetValue("EncryptedConfig", encryptedBytes);
        }

        // Fixed: Clear plaintext from memory
        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;

            // Fixed: Decrypt using DPAPI
            byte[] plainBytes = ProtectedData.Unprotect(
                encryptedBytes,
                null,
                DataProtectionScope.CurrentUser
            );

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

            // Fixed: Clear decrypted data from memory
            Array.Clear(plainBytes, 0, plainBytes.Length);

            return result;
        }
    }

    // Fixed: Use connection string builder without storing password
    public void SaveDatabaseConfig(string server, string database)
    {
        // Fixed: Store only non-sensitive connection info
        // Password retrieved from Credential Manager at runtime
        using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegKeyPath))
        {
            key.SetValue("DbServer", server);
            key.SetValue("DbName", database);
            // Password NOT stored in registry
        }
    }
}
// Fixed: C++ secure registry storage with 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:
    // Fixed: Use Windows Credential Manager
    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)
        );

        // Fixed: Securely free credential
        CredFree(pCred);
        return true;
    }

    // Fixed: Encrypt data before registry storage
    bool SaveEncryptedKey(const BYTE* key, DWORD keySize) {
        DATA_BLOB dataIn = {keySize, const_cast<BYTE*>(key)};
        DATA_BLOB dataOut = {0};

        // Fixed: Encrypt using DPAPI
        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) {
            // Fixed: Store DPAPI-encrypted data
            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);

        // Fixed: Decrypt using DPAPI
        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);

        // Fixed: Securely clear decrypted data
        SecureZeroMemory(dataOut.pbData, dataOut.cbData);
        LocalFree(dataOut.pbData);

        return true;
    }
};

The fix uses Windows Credential Manager for credentials and DPAPI for encrypting other sensitive registry data.


Exploited in the Wild

Registry Password Exposure (Windows Applications, 2005)

CVE-2005-2227 documented a Windows application storing cleartext passwords in registry keys accessible to local users.

Malware Credential Harvesting (Ongoing)

Various malware families specifically scan Windows registry for stored credentials, targeting common application registry locations.


Tools to Test/Exploit

  • Registry Editor (regedit) — Built-in Windows tool for browsing registry contents.

  • RegRipper — Registry analysis tool for forensics and security testing.

  • Mimikatz — Security tool that can extract credentials from various Windows storage locations.


CVE Examples


References

  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