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 version | Startup Type |
---|---|
Windows 8.1 | Disabled |
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
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:
- Establish an SMB connection to the remote host (Kerberos authentication)
- Connect to the
IPC$
share - Open the
winreg
named pipe (this is similar to opening a file with that name) - Bind to the
winreg
interface with UUID338cd001-2244-31f1-aaaa-900038001003
- 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
IPC$
share - When we attempt to open the
winreg
pipe - 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
:
- Click on “File” → “Connect Network Registry…”
- Enter target machine
- Once it is connected, we are presented with the two registry hives
HKEY_USERS
andHKEY_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:
Method | OS version | Admin needed | Collection method | BloodHound edge | Local users | Complete -ness | –stealth |
---|---|---|---|---|---|---|---|
SAMR | Windows 10 1607+ Windows Server 2016 1607+ | Yes | Direct: LocalAdmin DCOM RDP PSRemote Indirect: LocalGroup ComputerOnly Default All | AdminTo CanRDP ExecuteDCOM CanPSRemote | No | Yes | Direct and indirect: replaced with GPOLocalGroup. |
Group Policy | – | No | Direct: GPOLocalGroup Indirect: All DCOnly | AdminTo CanRDP ExecuteDCOM CanPSRemote | No | No | – |
NetWksta UserEnum | All | Yes | Direct: LoggedOn Indirect: All | HasSession | No | Yes | Direct: removed Indirect: replaced with GPOLocalGroup |
NetSession Enum | Windows 10 1709+ Windows Server 2019 1809+ | Yes | Direct: Session Indirect: Default ComputerOnly All | 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 Indirect: All | HasSession | Yes | Yes | Direct: 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.
Leave a Reply