Post

Decoding CA Security Descriptors from the Registry

Converting the Security registry key from hex to an understandable format

Decoding CA Security Descriptors from the Registry

Converting AD CS “Security” Registry Values into Readable ACLs

When digging into the registry of a Certification Authority (CA), you will eventually find a key that defines who can manage, issue, read, or request certificates:

1
HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CAName>\Security

That single Security value controls the entire CA’s access permissions, but it’s stored as a blob of hex, meaning that it is not immediately readable or digestable.

This post explains what that value actually is, how to decode it, and how to make sense of it using some PowerShell snippets.


Understanding What’s in the “Security” Value

The Security value is a Windows Security Descriptor stored in self-relative binary form. It’s the same structure Windows uses for file and service permissions, it just lives in the registry instead of on disk.

A security descriptor includes:

  • Owner – The account that “owns” the object
  • Group – The primary group
  • DACL – Discretionary Access Control List: who can do what
  • SACL – System Access Control List: who gets audited (if auditing is enabled)

When you view it in the CA MMC under Properties → Security, you’re actually looking at this same structure, just parsed and rendered by Windows.

The Problem

If you export the CA configuration, you’ll see something like this:

1
"Security"=hex:01,00,04,80,ac,00,00,00,14,00,00,00,30,00,00,00,02,00,1c,00,01,00,00,00,...

That’s the security descriptor in binary form. You can’t directly map that to text, because it’s encoded using internal offsets and length fields.

Fortunately, the .NET framework already knows how to read and interpret these descriptors, and PowerShell gives you direct access to those classes.


Step 1: Read the Raw Bytes from the Registry

PowerShell can return binary registry values as byte arrays. For example:

1
2
$path = "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\My-CA-01"
$bytes = (Get-ItemProperty -Path $path -Name Security).Security

Now you have a raw byte[] containing the CA’s access control information.


Step 2: Parse the Binary into a Security Descriptor

Use .NET’s System.Security.AccessControl.RawSecurityDescriptor class to interpret the binary:

1
2
$sd = New-Object System.Security.AccessControl.RawSecurityDescriptor($bytes, 0)
$sddl = $sd.GetSddlForm("All")

That one-liner converts the hex into SDDL (Security Descriptor Definition Language) — the compact, string-based format Windows uses internally:

1
O:BAG:SYD:(A;;LCRP;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;DA)

Much better than hex, but still hard to read.


Step 3: Decode the DACL (Who Can Do What)

You can take it a step further and enumerate the Access Control Entries (ACEs) inside the DACL. Each ACE contains:

  • The security identifier (SID)
  • The access mask (bitwise permissions)
  • Whether it’s an allow or deny rule

A simple pattern to use:

1
2
3
4
5
6
7
8
9
foreach ($ace in $sd.DiscretionaryAcl) {
    $account = try { $ace.SecurityIdentifier.Translate([System.Security.Principal.NTAccount]) } catch { $ace.SecurityIdentifier }
    [pscustomobject]@{
        Type   = $ace.AceType
        Rights = ('0x{0:X}' -f $ace.AccessMask)
        Account = $account
    }
}

This outputs a table showing each account and its access mask.

To make it more meaningful, map those bit values to AD CS-specific rights — for example:

BitmaskRightDescription
0x00000001ManageCAFull control of CA configuration
0x00000002ManageCertificatesIssue or revoke certificates
0x00000004EnrollRequest new certificates
0x00000008ReadView CA configuration

Once you translate those masks, you get an easy view of who has what kind of access.


Step 4: Converting Hex from a .REG File

Sometimes you only have a .reg export. You can still decode it, just turn the comma-separated hex values into bytes:

1
2
3
4
5
$hex = "01,00,04,80,ac,00,00,00,14,00,00,00,30,00,00,00"
$bytes = $hex.Split(',') | ForEach-Object { [byte]("0x$_") }

$sd = New-Object System.Security.AccessControl.RawSecurityDescriptor($bytes, 0)
$sd.GetSddlForm("All")

That’s all it takes to decode a CA’s permissions offline.


Step 5: (Optional) Writing It Back

You can also reverse the process, for example, to apply a known-good security descriptor to another CA.

If you need to do it, always:

  1. Export the current binary value for backup
  2. Convert your updated SDDL or CommonSecurityDescriptor back into binary
  3. Write it back with Set-ItemProperty
  4. Restart the CA service

Example:

1
2
3
4
$bytesOut = New-Object byte[] ($sd.BinaryLength)
$sd.GetBinaryForm($bytesOut, 0)
Set-ItemProperty -Path $path -Name Security -Value $bytesOut
Restart-Service certsvc

Use with caution, and only after verifying the SDDL is valid.


This post is licensed under CC BY 4.0 by the author.