No Shells Required - a Walkthrough on Using Impacket and Kerberos to Delegate Your Way to DA
There are a ton of great resources that have been released in the
past few years on a multitude of Kerberos delegation abuse avenues. However, most of the guidance out there is
pretty in-depth and/or focuses on the usage of @Harmj0y’s Rubeus. While Rubeus is a super well-written tool
that can do quite a few things extremely well, in engagements where I’m already
running off of a primarily Linux environment, having tools that function on
that platform can be beneficial. To that
end, all the functionality we need to perform unconstrained, constrained, and
resource-based constrained delegation attacks is already available to us in the
impacket suite of tools.
This post will cover how to identify potential delegation attack
paths, when you would want to use them, and give detailed walkthroughs of how
to perform them on a Linux platform.
What we won’t be covering in this guide is a detailed background of
Kerberos authentication, or how various types of delegation work in-depth, as there are some
really great articles already out that go into a ton of detail on the
inner-workings of the protocol. If you
are interested in a deeper dive, the most comprehensive & enlightening post
I’ve read is @Elad_Shamir’s write-up: https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html
Unconstrained Delegation
What Is It?
Back in the early days of Windows Active Directory (pre-Server
2003) this was really the only way to delegate access, which at a high level
effectively means configuring a service with privileges to impersonate users
elsewhere on the network. Unconstrained
Delegation would be used for something like a front-end web server that needed
to take in requests from users, and then impersonate those users to access
their data on a second database server.
Unfortunately, as the name implies, these impersonation rights were not limited to a single system or service, but rather allowed a configured account to impersonate anyone that authenticated against it anywhere on the network. This is due to the fact that when an object authenticates to a service tied to an account configured with unconstrained delegation, they send the remote service a copy of their TGT (Ticket Granting Ticket), which allows the remote system to generate new TGS (Ticket Granting Service / service ticket) requests at-will. These TGS’ are used for authenticating to Kerberos-enabled services across the network, meaning that if you possess an object’s TGT you can impersonate them anywhere on the network where you can authenticate with Kerberos.
Unfortunately, as the name implies, these impersonation rights were not limited to a single system or service, but rather allowed a configured account to impersonate anyone that authenticated against it anywhere on the network. This is due to the fact that when an object authenticates to a service tied to an account configured with unconstrained delegation, they send the remote service a copy of their TGT (Ticket Granting Ticket), which allows the remote system to generate new TGS (Ticket Granting Service / service ticket) requests at-will. These TGS’ are used for authenticating to Kerberos-enabled services across the network, meaning that if you possess an object’s TGT you can impersonate them anywhere on the network where you can authenticate with Kerberos.
When To Use:
If you can gain access to an account (user or computer) that is
configured with unconstrained delegation.
To identify users & computers configured with unconstrained
delegation I use pywerview, a python port of a good chunk of powerview’s
functionality (https://github.com/the-useless-one/pywerview)
but feel free to use whatever tools works best for you. This tool has handy
flags to pull both accounts configured with both constrained + unconstrained
delegation. In this case what we’re
really looking for is any user or computer with a UserAccountControl attribute
that includes ‘TRUSTED_FOR_DELEGATION’.
All we’ll need at this point is a set of creds for AD to allow us to do
the enumeration. Taking a look at the
output of the check we ran below, we can see that the user ‘unconstrained’ is
configured with unconstrained delegation:
If you have found you have access to a computer object that is
configured with unconstrained delegation, it may be easier simply to perform
the print spooler attack and extract the ticket from memory using Rubeus, as detailed
here: https://posts.specterops.io/hunting-in-active-directory-unconstrained-delegation-forests-trusts-71f2b33688e1. However, if you have access to a user account configured with
delegation or would prefer to avoid running code on remote systems as much as
possible, the following should be helpful.
Process Walkthrough:
Note: This section is pretty much a direct walkthrough of the
awesome work @_dirkjan wrote up in his blog here: https://dirkjanm.io/krbrelayx-unconstrained-delegation-abuse-toolkit/
If you’re familiar with this style of attack it’s nothing new, just a (hopefully)
fairly straightforward walkthrough of the path that I’ve had the most success
with on engagements after identifying unconstrained delegation.
If we do end up identifying any user accounts configured with
unconstrained delegation, we’ll want to obtain Kerberos tickets we can attempt
to crack. For an account to be
configured with delegation, they also need to be configured with an SPN (Service Principal Name). This means that we should be able to retrieve
a crackable Kerberos ticket for the account using GetUserSPNs.py
GetUserSPNs.py DOMAIN/USER:PASSWORD -request-user UNCONSTRAINED_USER
Assuming we’re able
to recover the password for an account / used another method to get admin
access on a computer configured with unconstrained delegation, we
can now move on to attempting to leverage this access to get DA on the
network. We’ll start by attempting to
add an SPN to the account we have access to. This is the only part of the
attack that will require non-default settings to be configured (for a user
account), but per all the sql devs on stack exchange asking how to enable it,
it seems to be something that should be commonly turned on already. If we have access to a computer account
configured with unconstrained delegation, we can use the ‘Validated write to
DNS host name’ security attribute (configured by default) to add an additional
hostname to the object, which will automatically configure new SPN’s that will
also be configured with unconstrained delegation. We then just have to create a
new DNS record to point that new hostname to us.
We’ll be using
dirk-jan’s krbrelayX toolkit for the rest of this process (https://github.com/dirkjanm/krbrelayx), first using addspn.py to attempt to add a
‘host’ spn for a nonexistent system on the network. Note – it is important to ensure when you’re
adding an SPN you use the fqdn of the network, not just the hostname. You’ll see one of two messages, based on if your
account has privileges to modify its own SPN’s (above = an account with
appropriate attributes set, below = attribute not set).
addspn.py -u DOMAIN\\USER -p PASSWORD -s host/FAKESYSTEM.FQDN ldap://DC.FQDN
If you don’t have privileges, this is pretty much the end of this
potential vector, although I would still recommend targeting the systems(s) on
which the account has SPN’s configured for, as they likely have TGT’s
in-memory.
However, if we are able to successfully add an SPN for a
non-existent system we can keep going.
Next, we’ll want to add a DNS record for this same non-existent system
that links back to our system’s IP, effectively turning our system into this
non-existent system. Due to the actions
we took in the last step (creating an SPN for the ‘host’ service with our user
configured with unconstrained delegation on this non-existent hostname that now
points to our system), we are basically creating a new ‘computer’ on the
network that has unconstrained delegation configured on the ‘host’ service on
it.
We’ll be using another part of the krbrelayx toolkit, dnstool.py,
to complete this step to create a new DNS record and then point it at the IP of
our attack box (Note: dns records take ~3 minutes to update, so don’t worry if
you complete this step and cant immediately ping / nslookup your new host):
dnstool.py -u DOMAIN\\USERNAME -p PASSWORD -r FAKESYSTEM.FQDN -a add -d YOUR_IP DC_HOSTNAME
Everything should be ready to go now, we’ll execute the print
spooler bug to force the DC$ account to attempt to authenticate to the host
service of our new ‘computer’ that is configured with unconstrained
delegation. This will in turn cause the
DC to provide a copy of its TGT when authenticating, which we can then use to
impersonate it on any other Kerberos-enabled service. In one window we’ll set up krbrelayx.py as
follows: **This is very important** the
krbsalt is the FQDN of the domain in ALL CAPS, followed immediately by the
username (case-sensitive). The Krbpass
is the user’s password, nothing crazy there.
krbrelayx.py --krbsalt DOMAIN.FQDNUsernameCaseSensitive --krbpass PASSWORD
Once you have that running in one window, we’ll use the final tool
within the krbrelayx toolkit to kick off the attack (Note: The user used to
kick off the attack doesn’t matter, it can be any domain user). The below shows what the successful attack
looks like:
printerbug.py DOMAIN/USERNAME:PASSWORD@DC_HOSTNAME FAKE_SYSTEM.FQDN
On our krbrelayx window, we should see that we have gotten an
inbound connection, and have obtained a tgt (formatted as .ccache) file for the DC$ account:
At this point, we just need to export the ticket we received into
memory, after which we should be able to run secretsdump against the DC:
export KRB5CCNAME=CCACHE_FILE.CCACHE
secretsdump.py -k DC_Hostname -just-dc
Constrained Delegation
What Is It?
Microsoft’s next iteration of delegation included the ability to
limit where objects had delegation (impersonation) rights to. Now a front-end web server that needed to
impersonate users to access their data on a database could be restricted;
allowing it to only impersonate users on a specific service & system. However, as we will find out, the portion of
the ticket that limits access to a certain service is not encrypted. This gives us some room to gain additional
access to systems if we gain access to an object configured with these rights.
When To Use:If you can gain access to an account (user or computer) that is configured with constrained delegation. You can find this by searching for the ‘TRUSTED_TO_AUTH_FOR_DELEGATION’ value in the UserAccountControl attribute of AD objects. This can be also be found through the use of Pywerview, as outlined in the above section.
Process Walkthrough:This time, we’ll
start by targeting another account, httpDelegUser. As we can see from our initial enumeration
with Pywerview, this account has the ‘TRUSTED_TO_AUTH_FOR_DELEGATION’ flag
set. We can also check the contents of
the account’s msDS-AllowedToDelegateTo attribute to determine that it has
delegation privileges to the www service on Server02. Not the worst thing in the world, but
probably not going to get us a remote shell.
Also a quick recap of the account’s group memberships:
To start this attack, we’ll use another impacket tool – getST.py –
to retrieve a ticket for an impersonated user to the service we have delegation
rights to (the www service on server02 in this case). In this example we’ll impersonate ‘bob’, a
domain admin in this environment. Note:
If a user is marked as ‘Account is sensitive and cannot be delegated’ in AD,
you will not be able to impersonate them.
k
getST.py -spn SERVICE/HOSTNAME_YOU_HAVE_DELEGATION_RIGHTS_TO.FQDN -impersonate TARGET_USER DOMAIN/USERNAME:PASSWORD
From here, the initial assumption would be that we could only
authenticate against the www service on server02 with this ticket. However, Alberto Solino discovered that the
service name portion of the ticket (sname) is not actually a protected part of
the ticket. This allows us to change the
sname to any value we want, as long as its another service running under the
same account as the original one we have delegation rights to. For example, if our account (httpDelegUser)
has delegation rights to a service that the server02 computer object is running
(example SPN: www/server02), we can change our sname to any other SPN
associated with server02 (ex. cifs/server02).
His blog on the mechanism by which this occurs is super insightful, and
worth a read: https://www.secureauth.com/blog/kerberos-delegation-spns-and-more
Even better for us, as Alberto Solino is one of the primary
writers of impacket, he built this logic in so that these sname conversions
happen automatically for us on the back-end:
From an operational standpoint, what this means is that the ticket
for the www service we obtained in the step above can be loaded into memory and
used to run just about any of the impacket suite of tools to run commands, dump
SAM, etc.
Resource-Based Constrained Delegation
Note: Microsoft is releasing an update in March 2020 that will
enable LDAP channel binding & LDAP signing by default on Windows systems,
remediating this potential attack vector on fully patched systems.
What Is It?
Starting with Windows Server 2012, objects in AD could set their
own msDS-AllowedToActOnBehalfOfOtherIdentity attribute, effectively allowing
objects to set what remote objects had rights to delegate to them. This allows those remote objects with delegation
rights to impersonate any account in AD to any service on the local
system. Therefore, if we can convince a
remote system to add an object that we control to their
msDS-AllowedToActOnBehalfOfOtherIdentity attribute, we can use it to
impersonate any other user not marked as ‘Account is sensitive and cannot be
delegated' on it.
When To Use:
Basically, when you’re on a network and want to get a shell on a
different system on that same network segment.
This attack can be ran without needing any prior credentials, as
described by @_dirkjan in his blog here: https://dirkjanm.io/worst-of-both-worlds-ntlm-relaying-and-kerberos-delegation/
. However, the method described does require
that a domain controller in the environment is configured with LDAPS, which
seems to be somewhat uncommon based on the environments I’ve tested against
over the past 6 months.
I’ll focus on a secondary scenario for this attack – one where you
have compromised a standard low-privilege user account (no admin rights) or a
computer account, and are on a network segment with other systems you want to
compromise.
Process Walkthrough:
To begin with, what this attack really needs is *some* sort of
account that is configured with an SPN. This can be a computer account, a user
account that is already configured with an SPN, or can be a computer account we
create using a non-privileged user account by taking advantage of a default
MachineAccountQuota configuration (https://blog.netspi.com/machineaccountquota-is-useful-sometimes/). We need an account that is configured with an
SPN as this is a requirement if we want the TGS produced by S4U2Self to be
forward-able (Read more why this is necessary here: https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html#a-misunderstood-feature-1). Computer accounts work as by
default they are configured with a variety of SPN’s for all their various Kerberos-enabled
services.
So, in our example let’s say we only have a low privilege account
(we’ll use the ‘tim’ account).
The first step in the process would be to try and create a
computer account, so that we could gain control of an account configured with
SPN’s. To do this, we’ll use a
relatively new impacket example script – addcomputer.py. This script has a SAMR option to add a new
computer, which functions over SMB and uses the same mechanism as when a new
computer is added to a domain using the Windows GUI.
addcomputer.py -method SAMR -computer-pass MADE_UP_PASSWORD -computer-name MADE_UP_NAME DOMAIN/USER:PASSWORD
After running this command, your new computer object will be added
to AD (Note: this example script was not fully working for me in python2.7 –
the computer object was added but its password was not being appropriately set. It does work using Python3.6 though.)
This script was released fairly recently, prior to it I used
PowerMad.ps1 from a Windows VM to perform the same actions. This tool uses a standard LDAP connection vs.
SAMR, but the end result is the same.
For further info on PowerMad I recommend the following: https://github.com/Kevin-Robertson/Powermad
If this part of the attack didn’t work, the default
MachineAccountQuota has likely been changed for users in the environment. In that case you’ll need to use alternative
methods to obtain a computer account / user account configured with an
SPN. However, once you have that, you
can continue to proceed as described below.
For the next part of the attack we’ll be using mitm6 +
ntlmrelayx. Unlike a traditional NTLM
relay attack, really what we’re interested in is intercepting machine account
hashes, as we can forward them to LDAP on a domain controller. This allows us to impersonate the relayed
computer account and set its msDS-AllowedToActOnBehalfOfOtherIdentity attribute
to include the computer object that we control.
Note: We unfortunately can’t relay SMB to LDAP due to the
NTLMSSP_NEGOTIATE_SIGN flag set on SMB traffic, so will be focusing on
intercepting HTTP traffic, such as windows update requests.
We’ll first set up
ntlmrelayx to delegate access to the computer account we just made & have
control of (rbcdTest):
We next start a relay attack using mitm6.py or other relay tool, and wait for requests to start coming in. Eventually you should see something that looks like the following:
ntlmrelayx.py -wh WPAD_Host --delegate-access --escalate-user YOUR_COMPUTER_ACCOUNT\$ -t ldap://DOMAIN_CONTROLLER
We next start a relay attack using mitm6.py or other relay tool, and wait for requests to start coming in. Eventually you should see something that looks like the following:
In the above screenshot we can see that we successfully relayed
the incoming auth request made by the server02$ account to LDAP on the domain
controller and modified the object’s privileges to give rbcdTest$ impersonation
rights on the system.
Once we have delegation rights, the rest of the attack is fairly
straightforward. We’ll use another
impacket tool – getST.py – to create the TGS necessary to connect to Server02
using an impersonated identity.
This tool will get us a Kerberos service ticket (TGS) that is
valid for a selected service on the remote system we relayed to LDAP
(Server02). As the rbcdTest$ account has
delegation rights on this system, we are able to impersonate any user that we
want, in this case choosing to impersonate ‘administrator’, a domain admin on
the testlab.local network.
getST.py -spn cifs/Server_You_Relayed_To_Get_RBCD_Rights_On -impersonate TARGET_ACCOUNT DOMAIN/YOUR_CREATED_COMPUTER_ACCOUNT\$:PASSWORD
With the valid ticket saved to disk, all we need to do is export
it to memory, which will then allow us to remotely connect to the remote system
with administrative privileges:
Comments
Post a Comment