BloodHound is the way to go to for finding attack paths in an Active Directory (AD) environment. However, it is not always clear how the data is gathered without looking at the code of SharpHound, the data ingestor for BloodHound.
Microsoft hardened their systems over time through updates, which makes enumeration of Active Directory (AD) objects and relationships harder than it used to be a couple of years ago.
In this series of articles, we deep dive into the enumeration methods of SharpHound and their limitations.
Table of Contents
- User Rights Enumeration Through SAMR & GPOLocalGroup
- Session Enumeration Through NetWkstaUserEnum & NetSessionEnum
Session Enumeration (continued)
Information that proves to be useful during penetration tests is “Who is logged in on which system?”.
SharpHound will try to enumerate this information and BloodHound displays it with a HasSession Edge.
There are three methods how SharpHound acquires this data:
- Remote Registry
We covered the two first in the previous post and the last one will be covered here.
When using the collection method
LoggedOn, not only
NetWkstaUserEnum is used, the Windows registry of the target is also queried remotely.
This method was present in SharpHound 2 (the PowerShell one) and later in SharpHound 3. But it was disabled at some point, because it was believed to not deliver the right information. It got re-introduced in the newest SharpHound release again and will (hopefully) stay there.
This technique is not entirely new, Sysinternals PsLoggedOn and nmap scripts use the same approach.
How does it work?
This works by connecting to a named pipe
\PIPE\winreg, which is exposed via the
IPC$ (inter-process communication) SMB share. Then, it uses the Windows Remote Registry Protocol (RPC over SMB on port 445).
Remote Registry Service
Let’s start with the basics. The registry can be accessed over the network if the
Remote Registry service is running:
The default behavior depends on the system:
|OS version||Startup Type|
|Windows 10 1809||Disabled|
|Windows 10 21H1||Disabled|
|Windows Server 2012 R2||Automatic (Trigger)|
|Windows Server 2016 (1607)||Automatic (Trigger)|
|Windows Server 2019 (1809)||Automatic (Trigger)|
|Windows Server 2022 (21H2)||Automatic (Trigger)|
The service is disabled by default on clients (starting with Windows 8) and enabled on servers (no difference between a DC and a member server). You might find the service running on clients for other reasons:
- Some remote administration tools require it for remote management
- Vulnerability scanners use it to access the registry remotely
Now, if it’s running automatically, why is the service not started on your server?
As a matter of fact, the service will stop after 10 minutes of idling to not waste resources. This behavior is controlled by the following registry (!) key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\RemoteRegistry REG_DWORD DisableIdleStop 1=It will stop after 10min idle 0=It won't stop when idle
To be able to enumerate sessions we need the service to be started. So let’s take a closer look on the defined trigger of the Remote Registry service:
> sc.exe qtriggerinfo RemoteRegistry [SC] QueryServiceConfig2 SUCCESS SERVICE_NAME: RemoteRegistry START SERVICE NETWORK EVENT : 1f81d131-3fac-4537-9e0c-7e7b0c2f4b55 [NAMED PIPE EVENT] DATA : winreg
The trigger is a
NAMED PIPE EVENT with the name
winreg, it is defined as follows:
> sc.exe triggerinfo [CUT] start/namedpipe/pipename <Start the service when a request arrives for the specified named pipe. Do not include the "\\.\pipe\" portion of the pipe's name>
We can see such a trigger in action live using Wireshark. Here we see the named pipe being called, which will automatically spin up the service once received:
- Establish an SMB connection to the remote host (Kerberos authentication)
- Connect to the
- Open the
winregnamed pipe (this is similar to opening a file with that name)
- Bind to the
winreginterface with UUID
- Interact using the Windows Remote Registry Protocol
- Close and logoff
Authorization is performed at three different places in this trace:
- When we attempt to open the
- When we attempt to open the
- When we attempt to execute an RPC call via the pipe
You can try it for yourself using the following Wireshark filter:
((smb2) || (winreg)|| (dcerpc) || (smb)) && !(smb2.ioctl.function == 0x001401fc)
Definition of logged-in users
For better understanding we establish a Remote Registry session using
- Click on “File” → “Connect Network Registry…”
- Enter target machine
- Once it is connected, we are presented with the two registry hives
The other hives you are used to on your local machine (
HKEY_CURRENT_CONFIG) are not there since they are only shortcuts to subkeys of
We are interested in the
HKEY_USERS hive, since all logged in users on that machine have their
HKEY_CURRENT_USER hive (and classes but we ignore them) loaded:
What defines “logged-in”? This includes interactive user sessions, but also accounts like
NT AUTHORITY\NETWORK SERVICE,
IIS APPPOOL\.NET v4.5 Classic,
NT SERVICE\SQLTELEMETRY$SQLEXPRESS, etc. will be shown if they are running something on the target.
SharpHound matches only the SID of user accounts using a regex (domain and local users always start with “S-1-5-21”):
private static readonly Regex SidRegex = new(@"S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$", RegexOptions.Compiled);
In all other methods, local user accounts are filtered out. This one here will lead to having local user accounts in the BloodHound graph and could be a bug.
The presence of a users SID is not a 100% reliable indicator that he is logged in at this very moment. This has to be taken with a grain of salt, since the hives might be loaded longer than the user itself is logged in.
Our experience is that hives are removed from the registry as soon as an interactive session is ended. However, when a server runs a scheduled task as a user, under unknown conditions, the hive will stay loaded since the “System” process is having an open handle on the
NTUSER.DAT and other files of the said user.
What privileges are required?
Let’s see why reading the registry remotely is possible at all. If we take a closer look on the permission on the
HKEY_USERS hive, we see that
Everyone has “Read” access to it. This is exactly how SharpHound can enumerate who is logged in on the machine:
To be precise, the content of the actual key cannot be read by anybody, but the list of subkeys of
We tested different OS to check if there is a difference in these permissions. All tested versions allow “Everyone” to “Read” the hive:
- Windows 8.1
- Windows 10 1809
- Windows 10 21H1
- Windows Server 2012 R2
- Windows Server 2016 (1607)
- Windows Server 2019 (1809)
- Windows Server 2022 (21H2)
The enumerated SIDs can then be resolved to actual usernames and added to the final output of SharpHound.
More detailed and technical information about the Windows Remote Registry Protocol: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/0fa3191d-bb79-490a-81bd-54c2601b7a78
SharpHound implements the Remote Registry in “ReadUserSessionsRegistry” method in the CommonLib at: https://github.com/BloodHoundAD/SharpHoundCommon/blob/master/src/CommonLib/Processors/ComputerSessionProcessor.cs.
Now lets sum up all the learnings in one table:
|SAMR||Windows 10 1607+|
Windows Server 2016 1607+
|No||Yes||Direct and indirect:|
replaced with GPOLocalGroup.
replaced with GPOLocalGroup
|Windows 10 1709+ Windows Server 2019 1809+||Yes||Direct:|
|HasSession||No||No||Direct and indirect:|
Only queries Domain Controllers and fileservers (based on LDAP user profiles path)
|Remote Registry||Windows Server||No||Direct: LoggedOn |
replaced with GPOLocalGroup