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
| Impact | Details |
|---|---|
| Confidentiality | Scope: 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
-
CVE-2005-2227 — Cleartext passwords stored in registry key.
-
CVE-2007-5778 — Login credentials in unencrypted registry key.
References
-
MITRE Corporation. "CWE-314: Cleartext Storage in the Registry." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/314.html
-
Microsoft. "Data Protection API (DPAPI)." https://docs.microsoft.com/en-us/windows/win32/api/dpapi/
-
Microsoft. "Credential Locker." https://docs.microsoft.com/en-us/windows/uwp/security/credential-locker