Current Problem

When working in an unknown network, some of the most important pieces of information to have are appraisals of current assets and information contained on them. This is important for any security professional, from tester to defender. Given the prevalence of Active Directory (AD) in most Windows environments, gaining a clear inventory of the network has become a priority in information gathering.”. One of the main problems when relying on AD is the bad hygiene of cleaning out machine accounts for hosts that are no longer part of the network. I have seen environments that have stale 10-year-old records in their AD database where half or more of the records are of hosts that no longer exist. This complicates the matter for a consultant conducting attack emulation or for a threat hunter trying to identify assets. I know I can use the LastLogon date of the machine, but since I use the machine’s extracted credentials for other purposes in several scenarios, I prefer to use the last time the machine changed its password.

Machine Password

One technique that has proven to be of great value to me in all types of engagements is to look at the last time a machine password was changed inside of AD. The machine account password change is initiated by the computer every 30 days by default (it has been this way since Windows 2000 and continues all the way to the latest versions of Windows).

One key factor in leveraging this value is that changing the password in the AD database is initiated by the client machine itself and settings are stored in the registry, so a security professional can check to see if the defaults have been changed. One caveat is that this is only for Windows systems – Mac OS and Linux hosts that are domain-joined do not change their passwords on a regular interval, so when querying AD, one should filter out systems that are running these operating systems or set them aside for further verification via other methods.

Enumerating Settings

Since the action is taken by the Windows host, we can enumerate the setting to get a time frame for use when querying AD, finding valid hosts where the number of old hosts would be low to none. In most large infrastructures, we cannot trust that it will take 30 days for a system to initiate a change of its password. Many organizations have bumped the time past 30 days for mobile users, some into 45 or 60 days, thinking it is necessary because users may not utilize VPN inside of a 30-day window. Most frequently, the reason for the longer timeframe is because VDI and VPN environments rely heavily on virtualization snapshots. In some virtual environments, the time is increased or completely disabled. In this case, it may be that the organization has restored the machine to a time with a previously used and currently invalid password. The password may have changed due to compatibility issues, with the account not authenticating against AD. One more reason when gaining situational awareness to check if a host is a virtual machine and to check the settings, the same thing applies when doing incident response or consulting in threat hunting if this is used in the virtual environments. When consulting in Incident Response or threat hunting, it is worth examining whether a host is a virtual machine or used in virtual environments, and reviewing account settings.

In a GPO (Group Policy Object), the setting to control the password age is managed in Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options > Domain member: Maximum machine account Password age

This GPO setting relates to the registry key:

Key – HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters

– HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters Value – MaximumPasswordAge REG_DWORD

– MaximumPasswordAge REG_DWORD Default – 30

– 30 Range – 1 to 1,000,000 (in days)

The interval that the workstation scavenger thread checks for password age can be controlled via a registry key:

Key – HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters

– HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters Value – ScavengeInterval REG_DWORD

– ScavengeInterval REG_DWORD Default – 900 (15 minutes)

– 900 (15 minutes) Range – 60 to 172800 Seconds (48 hours)

The value with the maximum password age plays a key factor when one extracts the password from HKLM\SECURITY\Policy\Secrets\$machine.ACC, where it will include the current password and an old one under CurrVal & OldVal Keys respectively, including the timestamp of when it was set. This gives us an idea of how long we can leverage these credentials, which is particularly useful when such credentials are for SCCM and Exchange servers, since they are given high privileges inside AD by a misconfiguration in an effort to solve an issue.

Figure 1 Registry key where machine credentials are stored

Figure 2 Mimikatz extracting the machine credential hash from the registry

In cases where there is a need to disable the host changing the password, as discussed before, it can be set via GPO under Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options > Domain member: Disable machine account Password changes

This GPO setting relates to the registry key:

Key – HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters

– HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters Value – DisablePasswordChange REG_DWORD

– DisablePasswordChange REG_DWORD Default – 0

Querying AD

The examples I will use are based on using ADSI LDAP filters as defined in RFC 4510, since they can be used in PowerShell, C, C++, .Net, Python, and any other language that supports LDAP.

We start by looking at the properties of a computer object to look at the field names and their type of value. The two properties we would be interested in are:

pwLastSet

operatingSystem

When we look at the pwLastSet property, we see the value is a 64bit FILETIME structure that represents that number of 100-nanoseconds intervals since January 1, 2020, at 4:01PM UTC.

For those that like building Python tooling, there is a good library that allows for the conversion and is 3-BSD licensed, which can be found here: http://reliablybroken.com/b/2011/09/free-software-ftw-updated-filetimes-py/.

In Windows with C++, we can use DOSDateTimeToFileTime() Windows32 API call: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime?redirectedfrom=MSDN. To keep things simple, I will use PowerShell and .Net API calls.

First, we check for the MaximunPasswordAge in the NetLogon service parameters. In this case, the settings are the defaults for 30 days. Since machines can be at both ends of the timeframe, my search will be for 60 days in the past.

We will use the Get-Date cmdlet to get the current DateTime object representing the current time and will add -60 days to get the DateTime for 60 days in the past, which is turned in to a FileTime 64bit value.

$updateTime = ((Get-Date).AddDays(-60)).ToFileTimeUtc()

With this value, we can be a filter that will look for object in AD that matches:

ObjectClass = Computer

operatingSystem = *Windows*

pwLastSet >= FilteTime 60 days in the past

The filter would look something like this:

(&(objectCategory=Computer)(pwdlastset>=132175227430977932)(operatingSystem=*windows*))

We can test the filter using PowerShell:

$filter = ‘(&(objectCategory=Computer)(pwdlastset>=132175227430977932)(operatingSystem=*windows*))’

$searcher = [adsisearcher]$filter

$searcher.FindAll() | Select-Object -Property path

PS C:\> $filter = ‘(&(objectCategory=Computer)(pwdlastset>=132175227430977932)(operatingSystem=*windows*))’

PS C:\> $searcher = [adsisearcher]$filter

PS C:\> $searcher.FindAll() | Select-Object -Property path

I still need to collect all of the Mac OS and Linux hosts. Even for them, the last logon timestamp tends to be off depending on how often the machine is rebooted and if the service uses the machine account in them or not.

For this, we negate all entries in the operatingSystem property that have the word ‘Windows’:

(&(objectCategory=Computer)(!(operatingSystem=*windows*)

These hosts can later be examined, and in their operatingSystem field, we will at least have the version information for macOS and Linux, and distribution in the case of Linux.

Now, what if we hit an environment where changing the password has been disabled? In this case, we would change to looking at the lastLogon field since we don’t need to worry about our operational window for use of machine password credentials. Instead of a value in the registry, we can go with 90 days in the past. Again, just like with the previous setting, this field uses FILETIME, but I have found it to not be as reliable as going with pwdlastset:

PS C:\> $updateTime = ((Get-Date).AddDays(-90)).ToFileTimeUtc()

PS C:\> $filter = “(&(objectCategory=Computer)(lastLogon >= $($updateTime))(operatingSystem=*windows*))”

PS C:\> $searcher.FindAll() | Select-Object -Property path

Conclusion

Enumeration of Active Domain (AD) to find active assets is of great importance no matter the security exercise or task being performed. It is important to know what actions may support other actions and what kind of information to obtain in the process. An initial set of properties to include in the query will also ensure a general view of the environment so as to provide situational awareness:

distinguishedName – Gives an idea of where in the AD structure the computer is. The OU structure often resembles the organization structure.

– Gives an idea of where in the AD structure the computer is. The OU structure often resembles the organization structure. dNSHostName – FQDN name that can be used for Kerberos authentication, not raising suspicious logs or looking odd by going with the IP address.

– FQDN name that can be used for Kerberos authentication, not raising suspicious logs or looking odd by going with the IP address. Name -The name of the machine.

-The name of the machine. operatingSystem – Name of the operating system running, different versions of Windows have different controls available and enabled by default.

– Name of the operating system running, different versions of Windows have different controls available and enabled by default. operatingSystemVersion – With the new continuous release cycle of Windows, this field will contain the build number for the recent versions of new security features; defaults are applied also at the build level.

– With the new continuous release cycle of Windows, this field will contain the build number for the recent versions of new security features; defaults are applied also at the build level. pwdLastSet – Showing when the last password was set, providing an operational time window for when host credentials were leveraged.

– Showing when the last password was set, providing an operational time window for when host credentials were leveraged. userAccountControl – Can be parsed to learn if the host is a possible target for delegation abuse.

– Can be parsed to learn if the host is a possible target for delegation abuse. servicePincipalName – Provides a list of services that are Kerberos enabled. This will also provide an idea of function and purpose inside of an organization.

With the adoption of ETW (Event Tracing for Windows) by many security vendors, and products like Windows Defender ATP now tracking AD queries by process, it is important to plan what queries are performed and ensure that we get the most value with the least number of queries in the case of emulation. For Incident Response it allows for the targeting of those hosts that are live and also helps in the identification of services running on them to further filter out what hosts to look at.