Skip to content

[Security] ProtectedKeyProvider.GetMachineEntropy — uses Registry.LocalMachine which silently falls back to MachineName entropy when run as 32-bit (WoW64 redirection) #993

@Christophe-Rogiers

Description

@Christophe-Rogiers

Severity: Info

File: src/Servy.Core/Security/ProtectedKeyProvider.cs:118-137

Code:

```csharp
private static byte[] GetMachineEntropy()
{
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography"))
{
var guid = key?.GetValue("MachineGuid") as string;

    if (!string.IsNullOrWhiteSpace(guid))
    {
        return Encoding.UTF8.GetBytes(guid);
    }

    Logger.Error(\"CRITICAL SECURITY DEGRADATION: 'MachineGuid' registry key is missing or inaccessible. \" +
                 \"Falling back to predictable Environment.MachineName for DPAPI entropy. \" +
                 \"This reduces protection against offline attacks.\");

    return Encoding.UTF8.GetBytes(Environment.MachineName);
}

}
```

Explanation:

The path `SOFTWARE\Microsoft\Cryptography` exists in the 64-bit view of the registry only — `MachineGuid` is not present in the WoW6432Node mirror. `Registry.LocalMachine.OpenSubKey(...)` follows the WoW64 redirection rules of the current process, so:

  • 64-bit Servy process → reads the real `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid` (works, today's case for x64 builds)
  • 32-bit process loading the same assembly (e.g. a future x86 build, or a third-party tool importing `Servy.Core`) → reads `HKLM\SOFTWARE\WoW6432Node\Microsoft\Cryptography\MachineGuid`, which is empty → silently falls back to `Environment.MachineName`.

The fallback is logged loudly, but the encryption key is still derived from a low-entropy, easily guessable source (`MachineName` is often `DESKTOP-XXXXXXX` or a fleet-wide naming scheme). Anyone who can see the source code and the file knows the entropy.

The code is correct in practice today because the published artifacts target `win-x64` (per AppConfig.cs:84), but the registry call is bitness-dependent and there is no guard preventing a 32-bit caller from silently downgrading the entropy. Worse, a 32-bit caller is the exact case where the loud error message would fire — so anyone reading their logs for the first time after a bitness migration gets a vague "key inaccessible" message rather than "the lookup is bitness-redirected."

Suggested fix:

Force the 64-bit registry view explicitly so the call is bitness-independent:

```csharp
private static byte[] GetMachineEntropy()
{
using (var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
using (var key = hklm.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography"))
{
var guid = key?.GetValue("MachineGuid") as string;

    if (!string.IsNullOrWhiteSpace(guid))
        return Encoding.UTF8.GetBytes(guid);

    Logger.Error(\"CRITICAL SECURITY DEGRADATION: 'MachineGuid' registry key is missing or inaccessible. \" +
                 \"Falling back to predictable Environment.MachineName for DPAPI entropy.\");
    return Encoding.UTF8.GetBytes(Environment.MachineName);
}

}
```

`RegistryView.Registry64` works from both 32-bit and 64-bit processes on 64-bit Windows, and degrades gracefully on 32-bit-only OSes (which Servy doesn't target anyway).

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions