Skip to content

The Windows registry is full of information, and with the proper tools, can be a gold mine for attackers and defenders alike. Attackers look to find specific configurations, credentials, or any information that can help them further attack systems, while defenders can use the registry to ensure that settings are configured as they are expected to. This is something that is not always easy to do with standard tools in Windows, or with the right level of performance. Fortunately, osquery solves that for us.

 

What Is Windows Registry?

A central, hierarchical database used in Windows 98, Windows CE, Windows NT, and Windows 2000 used to store information that is necessary to configure the system for one or more users, applications, and hardware devices, the Windows Registry can be used by the kernel, device drivers, services, Security Accounts Manager, and user interfaces.

 

What Is Windows Registry Used For?

As the Windows Registry is a database that stores low-level settings for the Microsoft Windows operating system and for applications that opt to use the registry, it functions as a repository resource of information that Windows continually references during operation. 

 

GPOs

Let’s consider GPOs, which most organizations with a Windows environment and Domain use. GPOs are usually just a way to get a set of specific values configured in the registry.

 

Osquery allows us to query the registry for those values very easily.

 

For example, this query returns the settings related to Microsoft LAPS. 

SELECT data, path FROM registry

WHERE key = 'HKEY_LOCAL_MACHINE\Software\Policies\Microsoft Services\AdmPwd';

 

That was easy, but many settings in the registry are « per user ».

The registry hive « HKEY_CURRENT_USER » (HKCU) represents the path to the current user’s registry settings.

 

Learn how to use osquery for threat hunting in our free on-demand webinar.

 

The problem when monitoring systems for security is that every user has its own set of registry settings, located under « HKEY_USERS », so querying for a specific path will not work, as different profiles get created on systems.

 

In order to monitor configurations that are done « per user » in Windows, we can leverage the power of wildcards in osquery:

SELECT data, path FROM registry

WHERE key LIKE 'HKEY_USERS\%\Control Panel\Accessibility\StickyKeys';

 

 Unless a user needs sticky keys, that value should actually be set to 506, to prevent abuse to elevate privileges, as it is the value that gets written when sticky keys are disabled completely.

This query returns the Sticky Keys configuration values found for every user. The results are there, but, as someone trying to understand what user is impacted by what setting, they are not very readable.

 

Fortunately, using SQL, we can easily join tables together, and the users table contains the data we are looking for.

 

To join tables, we need a row with common data.

The  registry table contains: key, path, name, type, data, mtime

The users table contains none of these, but contains uuid, which, on Windows, returns the SID(Security Identifier).

 

If you are not familiar with SIDs, they are unique identifiers for users, groups and logon sessions. Generic accounts and groups on Windows have the same SID on every installation, but each account created has a random SID. The SID is exactly what is used to separate users in the registry. So while the registry table doesn’t have a column with the SID, the path column does contain the SID.

 

osquery and Split

Osquery supports SQL additions, including split. Split allows us to specify that a column be separated, and to create a new column with only that part of the value.

 

The registry, like many things in Windows, is separated by backslashes. In this case, we want the first value, returned after a backslash, to be its own column. Therefore, we will use split(path, ‘\’, 1), to obtain the first value located between backslashes in path.

Then, we need to map this to the user table, on the uuid field. We’ll call the column we are creating sid.

 

SELECT username, data, split(path, '\', 1) AS sid
FROM
(SELECT data, path FROM registry
WHERE key LIKE 'HKEY_USERS\%\Control Panel\Accessibility\StickyKeys')
JOIN users ON users.uuid = sid;

 

We now know that g and bob need to have their settings updated. Local Service and Network Service are not interactive accounts.

 

Wait! Those are only usernames. It would be much more useful to know which ones are used interactively, and what their Active Directory User-Principal-Names are, so appropriate GPOs can be deployed.

 

Luckily for us, the logon_sesions table contains the SID and the UPN, as well as a logon type table.

 

To achieve this, we simply need to add the required columns, and join on the logon_sessions.logon_sid column.

 

SELECT user, logon_sid, logon_type, upn, data, split(path, '\', 1) AS sid
FROM
(SELECT data, path FROM registry
WHERE key LIKE 'HKEY_USERS\%\Control Panel\Accessibility\StickyKeys')
JOIN logon_sessions ON logon_sessions.logon_sid = sid;

 

We now have a query that returns local and domain accounts, with the UPN for domain accounts, for users that are actually logged in.

 

You could refine the results further, to hide any line for logon_type that is not equal to Interactive and to hide data where it is properly configured to 506, or you could configure scheduled queries to send the entire data to your centralized repository, so you could then crunch it there.

 

By constantly monitoring this data, you will now be able to know exactly what user population to target with a configuration change.

 

This is just a simple example of the combined power of osquery and the Windows registry. By joining the registry and users or logon_sessions tables, you should now be able to monitor any user setting.

 

PS: Never disable accessibility features for security reasons without having a clear, managed system to keep them enabled for people that need them!

 

Learn More About osquery

 

cloud fundamentals ebook cta image