This is the long overdue follow-up to the “An ACE in the Hole: Stealthy Host Persistence via Security Descriptors” presentation (slides and video) that @tifkin_, @enigma0x3, and I gave at DerbyCon last year. This past weekend we gave a talk at @Sp4rkCon titled “The Unintended Risks of Trusting Active Directory” that explored combining our host-based security descriptor research with the work that @_wald0 and I detailed at Black Hat and DEF CON last year on Active Directory security descriptor backdooring. One of the more interesting case studies at both DerbyCon and Sp4rkCon involved a host-based security descriptor modification primitive that allows indefinite remote retrieval of a machine’s account hash. This post will dive deeply into this approach, the newly released weaponized code that implements it, and the extension allowing for the extraction of local account hashes and domain cached credentials.

The majority of the weaponization work was accomplished at the 11th hour before the DerbyCon 2017 presentation by @tifkin_ and @enigma0x3, who also helped develop this post.

Tl;dr if you gain “administrative” access to a remote machine, you can modify a few host security descriptors and have a security principal/trustee of your choice generate Silver Tickets indefinitely, as well as remotely retrieve local hashes and domain cached credentials. No malware on the host, no changes to the Administrators local group, and the code is now live!

Security Descriptor Crash Course

I’m not going to go into a complete background on security descriptors in this post. If you are interested, check out our “An ACE Up the Sleeve” white paper for a complete breakdown or our Sp4rkCon slides for a more gentle introduction. I recommend you at least skim our slides if you don’t have any background in this area. As a quick refresher, here’s a list of terms we’ll be using throughout this post:

Securable Object: defined by Microsoft as an object that can have a security descriptor. Includes things like files, threads, the remote registry, Active Directory objects, and many many more things.

defined by Microsoft as an object that can have a security descriptor. Includes things like files, threads, the remote registry, Active Directory objects, and many many more things. Security Descriptor: a binary structure containing many fields, including the object’s owner, and pointers to an object’s SACL and DACL (as well as header control bits and other fields we won’t worry about here.)

a binary structure containing many fields, including the object’s owner, and pointers to an object’s SACL and DACL (as well as header control bits and other fields we won’t worry about here.) ACL: an Access Control List, a common term for the superset of the SACL and a DACL.

an Access Control List, a common term for the superset of the SACL and a DACL. SACL: System Access Control Lists, a collection of ACEs that controls auditing the access or modifications to an object.

System Access Control Lists, a collection of ACEs that controls auditing the access or modifications to an object. DACL : Discretionary Access Control List, a collection of ACEs that define what principals (or trustees) have what specific rights over the given object.

: Discretionary Access Control List, a collection of ACEs that define what principals (or trustees) have what specific rights over the given object. ACE : Access Control Entry, an individual rule which controls (if contained in a DACL) or monitors (if contained in a SACL) access to an object by a specified trustee.

: Access Control Entry, an individual rule which controls (if contained in a DACL) or monitors (if contained in a SACL) access to an object by a specified trustee. Principal/Trustee: the group or user who has the particular right over the given object, can be a local group/user on the host, a group/user in Active Directory, or a well-known built in identifier like ‘Everyone’ or ‘Authenticated Users.’

There are a lot of caveats and subtleties with security descriptors. If you’re actually interested, check out the whitepaper. Srsly.

One of the main things to remember with host-based security descriptors, which are the focus of the post, is that local machine “administrative access” is more complicated than we, as the pentest industry, have viewed it. The vast majority of security professionals use this term as a synonym for membership in a machine’s local Administrators group, but we want to argue that this isn’t the “correct” interpretation. What actually matters is what local/domain groups have access to specific remote resources (RPC, remote registry, WMI, SQL, etc.) based on the host service’s security descriptors:

Nearly every service is backed by a separate security descriptor that defines how specific security principals (“trustees”) can interact with the remote service. The local Administrators group is just a security principal that is added by default to the security descriptors for most remotely accessible services on a system. This principal can be removed from these services and other (non-”local admin”) principals can be added. So while in most situations, membership in local Administrators can be used interchangeably with “administrative access”, this viewpoint starts to break down in some situations.

An adversary, after compromising a system, can modify the security descriptor information for a single remotely accessible service to allow a user they specify to access that “administrative” functionality. The user the attacker specifies doesn’t have to be a member of the local Administrators group, but can execute the “administrative” actions specified in the DACL change. Additionally, we have found existing host-based service ACL configurations in the field that allow non-”administrative” users the ability to execute some useful actions on the host (more on that this summer ;)

Obvious caveat and tautology warning: in order to modify the access control information for the services we’re talking about, you need to have the rights to modify the access control information for the services we’re talking about. This isn’t a privilege escalation vector, so you need a specific ACE that grants you this ability, which in the majority of cases will be membership in the local Administrators group.

Backdooring Remote Registry

In our exploration of remotely accessible services while doing research for the DerbyCon presentation, @tifkin_ and @enigma0x3 took a look at remote registry. The “RemoteRegistry” service, which is started by default on Windows Server but not Windows workstations, allows for remote interaction with the system’s registry hives. This includes key reads, key writes, permissions modifications, etc. Of note, even if remote registry is accessible to a user, individual hives/keys have their own associated permissions as well.

So how is access to remote registry controlled? Microsoft nicely tells us:

So, the permissions set on this key (HKLM:\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg) grant a remote trustee/principal the ability to interact with the remote registry on the system.

First, we need to ensure the remote registry service is running. We can do this via WMI (assuming we have appropriate rights):

$RemoteServiceObject = Get-WMIObject -Class Win32_Service -Filter "name='RemoteRegistry'" -ComputerName <computer>

if ($RemoteServiceObject.State -ne 'Running') {

$Null = $RemoteServiceObject.StartService()

}

Next, we need to change the permissions on the specific winreg key. Of note, since we’re just adding a new explicit allow ACE, we can set the principal/trustee to whatever we want. This means that we can allow a specific domain user, domain group, or built-in SID (like S-1–1–0/‘Everyone’) access to remote registry.

The reason we use WMI’s StdRegProv here instead of the [Microsoft.Win32.RegistryKey] remote registry interface is because we ran into several issues using the .NET interface to modify the DACL on a remote registry key. It might be possible, and we are likely missing something, but as a workaround we decided to go with the WMI approach.

First, we get our StdRegProv provider through WMI:

$Reg = Get-WmiObject -Namespace root/default -Class Meta_Class -Filter "__CLASS = 'StdRegProv'" -ComputerName <computer>

Then, we grab the current security descriptor for the winreg key. The weird 2147483650 value here refers to the HKEY_LOCAL_MACHINE hive:

$SD = $Reg.GetSecurityDescriptor(2147483650, "SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg").Descriptor

Next, we create a new win32_Ace WMI object, set the access mask to 983103 (ALL_ACCESS), set the AceFlags to 0x2 (CONTAINER_INHERIT_ACE) to ensure inheritance, and set the AceType to 0x0 (Allow):

$RegAce = (New-Object System.Management.ManagementClass("win32_Ace")).CreateInstance()

$RegAce.AccessMask = 983103

$RegAce.AceFlags = 2

$RegAce.AceType = 0x0

Then, we create a win32_Trustee WMI object, set the .Name and .Domain properties to the values for our principal/trustee, and then set this object as the .Trustee property of the win32_Ace object:

$RegTrustee = (New-Object System.Management.ManagementClass("win32_Trustee")).CreateInstance()

$RegTrustee.Name = ‘matt’

$RegTrustee.Domain = ‘external.local’

$RegAce.Trustee = $RegTrustee

Finally, we set the .DACL property of the retrieved security descriptor object, and use the SetSecurityDescriptor() method on the remote registry provider to set this newly constructed security descriptor for the winreg key:

$SD.DACL += $RegAce.PSObject.ImmediateBaseObject

$Null = $Reg.SetSecurityDescriptor(2147483650, "SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg", $SD.PSObject.ImmediateBaseObject)

Success!

Backdooring The Necessary Registry Keys

OK, so now we have remote registry access handled, but what interesting stuff can we do with this? Well, the hash for the machine’s computer account, as well as the hashes for its local accounts and domain cached credentials, are stored in various registry keys. What if we enable the remote retrieval of these hashes from the account context of a principal we specify? Think it’s not possible?

We’ll go over the retrieval of these hashes in more details in the next section, but will touch on a few points here and break out the specific registry keys whose permissions we need to modify.

In order to extract hashes from a remote system, we first need to somehow retrieve the SysKey (often referred to as the bootkey) for the system, which is “a Windows feature that adds an additional encryption layer to the password hashes stored in the SAM [and SYSTEM] database.” To recover this key, we need access to the following registry keys:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\JD

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Skew1

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Data

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\GBG

To recover the LSA key needed to decrypt the machine account hash and the NL$KM key that protects domain cached credentials, we need access to the following key:

HKEY_LOCAL_MACHINE\SECURITY\Policy\PolEKList

The encrypted machine account hash is stored at:

HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets\$MACHINE.ACC\CurrVal

The encrypted local account hashes are stored in:

HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\<RID>

And finally, for domain cached credentials, we need both the NL$KM key as well as the subkeys that contain the cached credentials:

HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets\NL$KM\CurrVal

HKEY_LOCAL_MACHINE\SECURITY\Cache\NL$<1–10>

From our limited testing, it appears that on some systems the LSA subkeys don’t inherit permissions from their parent container, so we can’t just backdoor the root SYSTEM key and be good in all situations. So here are the keys that we want to add our specific ACEs to:

SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg

SYSTEM\CurrentControlSet\Control\Lsa\JD

SYSTEM\CurrentControlSet\Control\Lsa\Skew1

SYSTEM\CurrentControlSet\Control\Lsa\Data

SYSTEM\CurrentControlSet\Control\Lsa\GBG

SECURITY

SAM\SAM\Domains\Account

We can use the same WMI StdRegProv provider approach that we used for the winreg key to add explicit allow ACEs to these keys.

This approach is weaponized in the Add-RemoteRegBackdoor function in newly released DAMP (the Discretionary ACL Modification Project) on GitHub. The -Trustee parameter takes a domain user/group (‘DOMAIN\user’ format), a well known username (like ‘Everyone’), or a security descriptor string representation (‘S-1–1–0’). One or more systems can be supplied to the -ComputerName parameter.

GPOs: “Wait, Hold My Beer”

We then began investigating how else the security descriptors on these account keys could be modified, and the first thing that came to mind was the abuse of Group Policy. Our workmate Andy Robbins recently posted “A Red Teamer’s Guide to GPOs and OUs”, which got us thinking about whether this backdoor could be deployed en masse to machines through GPO modification. As it turns out, this isn’t that hard!

The “Computer Configuration -> Policies -> Windows Settings -> Security Settings -> Registry” section in the Group Policy Management Editor handles the permissions for one or more registry keys on any machine the Group Policy is applied to. Microsoft appears to have intended for these registry security settings to be used to restrict read/modification access to various registry keys for local users on a particular system, but luckily for us, this is just an interface into the access control model for these registry keys! Since all we need to do to ensure backdoored access is to grant a principal the appropriate rights to the winreg and System/Security/Sam keys, let’s create these entries:

If we crack open the <GPOPATH>\Machine\Microsoft\Windows NT\SecEdit\GptTmpl.inf that defines these settings, we can see the text representation of these settings. Now that we know the values and formats for the settings, we could just replace then in a modifiable GptTmpl.inf file instead of going through the GUI:

Any machine this GPO is applied to will allow our trustee (‘Everyone’) to remotely retrieve the machine account and local account hashes! \m/

Remote Hash Retrieval

Note: the functions described here are contained in DAMP\RemoteHashRetrieval.ps1. Also, we are not in any way claiming we “discovered” these hash extraction approaches. Hash dumping tools have been around for over a decade, the groundbreaking PowerDump.ps1 script (written by Kathy Peters, Josh Kelley, and Dave Kennedy) implemented SysKey/local hash extraction in PowerShell and the amazing Impacket project has had various remote hash dumping scripts for years. Our contribution is the “backdooring” approach and weaponized script, an extension to the PowerDump work for the retrieval to work remotely and a PowerShell LSA secrets/domain cache cred retrieval implementation.

The first piece of information we need to decrypt the machine account hash, local hashes, or domain cached credentials is the SysKey, also often referred to as the bootkey. The best account of this key, what it’s used for, and how it’s extracted is the Syskey section of @moyix’s “SysKey and the SAM” post. He also documents LSA secret extraction as well as domain cached credential extraction.

The SysKey is calculated from four specific registry keys: HKLM\SYSTEM\CurrentControlSet\Control\Lsa\{JD,Skew1,GBG,Data}, but as @moyix mentions “the actual data needed is stored in a hidden field of the key that cannot be seen using tools like regedit.” Here, StdRegProv and .NET remote registry functions fail us. The only way we’re currently aware of extracting the classnames for these keys is through the RegQueryInfoKey API call, which takes a handle to an open registry key. While many existing tools have accomplished this extraction locally using RegOpenKeyEx, we can open up a connection to a key on a remote system by using the RegConnectRegistry API call. After opening up the HKEY_LOCAL_MACHINE hive, we can extract the classes for the JD, Skew1, Data, and GBG LSA keys keys using RegOpenKeyEx and RegQueryInfoKey. These values are then combined and used to calculate the bootkey. Also luckily for us, the PowerDump script shows how to implement the crypto needed in PowerShell.

Machine Account Hash Decryption

The machine account key is stored in the HKLM:\SECURITY\Policy\Secrets\$MACHINE.ACC registry key. This data is a part of the LSA secrets store, and encrypted using the LSA key which is stored at HKLM:\SECURITY\Policy\PolEKList. The bootkey is used to encrypt an AES128 key that’s stuffed into LSA encrypted structured stored at this registry key. So, we can use the bootkey to decrypt the temporary AES key and then decrypt the LSA key used to protect the machine account. We us RegOpenKeyEx with the bound remote registry connection to open SECURITY\Policy\PolEKList, and RegQueryValueEx to extract this data.

We then use RegOpenKeyEx to open the SECURITY\Policy\Secrets\$MACHINE.ACC\CurrVal key to extract the encrypted data of the machine account hash. We use the LSA key to calculate the SHA-256 hash on the first 32 bytes of the extracted machine account data, and use this temporary key to AES-decrypt the raw machine account hash bytes, and finally MD4 hash the data to get the resulting NTLM machine account hash.

To execute this process on a remote machine that you’ve backdoored, you can use Get-RemoteLocalAccountHash <ComputerName> :

A remotely retrieved machine account hash can then be used to generate Silver Tickets to re-compromise the system, or in the demo we recorded of using this technique against an example Exchange Installation, we can over-pass-the-hash to abuse any ACL edit rights the machine account might have against Active Directory.

Local Account Hash Decryption

In order to extract the local account hashes, the process is a bit different. The bootkey is combined with part of the data from the “F” value at HKLM:\SAM\SAM\Domains\Account and some static string values, the whole mess is MD5 hashed, and the resulting value is then used as an RC4 key to decrypt additional data from the “F” key value to produce the hashed boot key. This key is used to decrypt the local account hash values from HKLM:\SAM\SAM\Domains\Account\Users\<val> where <val> is a hex representation of the RID of the local account. This decryption uses mess of combination of RC4, DES, and binary path- the code to execute this is in the samdump2 source code, and the process is documented here.

To execute this process on a remote machine that you’ve backdoored, you can use Get-RemoteLocalAccountHash <ComputerName> :

Domain cached credentials are extracted in a different way as well. We first pull the LSA key, like we did for the machine account hash, and use this to decrypt the NL$KM key stored at SECURITY\Policy\Secrets\NL$KM\CurrVal. We then extract out the encrypted domain cached credential structures from Security\Cache\NL$<1–10>. Part of this structure is decrypted using the decrypted NL$KM key, and the MsCacheV2 formatted hash and associated username/domain are extracted.

To execute this process on a remote machine that you’ve backdoored, you can use Get-RemoteCachedCredential <ComputerName> :

Final Thoughts

Hash dumping is not new, PowerShell-based hash dumping is not new, and remote hash dumping is not new (secretsdump.py from Impacket has been around for a good while.) Our approach is fundamentally the same as existing work, but offers a few advantages:

An ACL-based backdooring approach that allows specific security principals to extract these hashes, even if they are not in the local Administrators group on the target machine.

A remote hash retrieval method that doesn’t involve volume shadow copy or the duplication of the registry hives.

A pure PowerShell version-2 compliant weaponization package that doesn’t produce any disk artifacts.

A PowerShell implementation of LSA secrets extraction and domain cached credential extraction.

And as we’ve repeatedly expressed in our material on this subject: this is not a vulnerability! Rather it’s a useful post-exploitation approach to ensure continued access on a machine that you’ve already completely compromised. We are just abusing the existing access control model for Windows systems in what we feel is a slightly novel way.