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

In this article, we’ll cover session enumeration through Remote Registry and summarize all our findings. Further articles in this series:

  • 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:

  • NetWkstaUserEnum
  • NetSessionEnum
  • Remote Registry

We covered the two first in the previous post and the last one will be covered here.

Remote Registry

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 versionStartup Type
Windows 8.1Disabled
Windows 10 1809Disabled
Windows 10 21H1Disabled
Windows Server 2012 R2Automatic (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

Service Triggers

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>

Wireshark trace

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:

  1. Establish an SMB connection to the remote host (Kerberos authentication)
  2. Connect to the IPC$ share
  3. Open the winreg named pipe (this is similar to opening a file with that name)
  4. Bind to the winreg interface with UUID 338cd001-2244-31f1-aaaa-900038001003
  5. Interact using the Windows Remote Registry Protocol
  6. Close and logoff

Authorization is performed at three different places in this trace:

  1. When we attempt to open the IPC$ share
  2. When we attempt to open the winreg pipe
  3. 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 regedit.exe:

  1. Click on “File” → “Connect Network Registry…”
  2. Enter target machine
  3. Once it is connected, we are presented with the two registry hives HKEY_USERS and HKEY_LOCAL_MACHINE

The other hives you are used to on your local machine (HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_CURRENT_CONFIG) are not there since they are only shortcuts to subkeys of HKU or HKLM.

We are interested in the HKEY_USERS hive, since all logged in users on that machine have their NTUSER.DAT a.k.a. 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\SYSTEM, 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.

Filtering results

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.

Limitations

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 HKEY_USERS can.

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.

Further reading

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.

Summary

Now lets sum up all the learnings in one table:

MethodOS
version
Admin
needed
Collection
method
BloodHound
edge
Local
users
Complete
-ness
–stealth
SAMRWindows 10 1607+
Windows Server 2016 1607+
YesDirect:
LocalAdmin
DCOM
RDP
PSRemote

Indirect:
LocalGroup
ComputerOnly
Default
All
AdminTo
CanRDP
ExecuteDCOM
CanPSRemote
NoYesDirect and indirect:
replaced with GPOLocalGroup.
Group
Policy
NoDirect:
GPOLocalGroup

Indirect:
All
DCOnly
AdminTo
CanRDP
ExecuteDCOM
CanPSRemote
NoNo
NetWksta
UserEnum
AllYesDirect:
LoggedOn

Indirect:
All
HasSessionNoYesDirect:
removed

Indirect:
replaced with GPOLocalGroup
NetSession
Enum
Windows 10 1709+ Windows Server 2019 1809+YesDirect:
Session

Indirect:
Default
ComputerOnly
All
HasSessionNoNoDirect and indirect:
Only queries Domain Controllers and fileservers (based on LDAP user profiles path)
Remote RegistryWindows ServerNoDirect: LoggedOn

Indirect:
All
HasSessionYesYesDirect:
removed

Indirect:
replaced with GPOLocalGroup

Acknowledgments

Thanks to @_wald0, @CptJesus@harmj0y and the others for the awesome tool.

@SadProcessor has a great cheatsheet on SharpHound, check it out.