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 NetWkstaUserEnum & NetSessionEnum. Further articles in this series:

Session Enumeration

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 will cover the two first in this post and the last one in the next blog post.

NetWkstaUserEnum

When using the LoggedOn collection method, SharpHound enumerates logged on users by using the NetWkstaUserEnum function.

How does it work?

This works by connecting to a named pipe \PIPE\wkssvc, which is exposed via the IPC$ (inter-process communication) SMB share. Then, it uses the Workstation Service Remote Protocol (RPC over SMB on port 445).

Implementation details

To show the information returned by this function, we can use the NetWkstaUserEnum.ps1 PowerShell script by Will Schroeder:

The function lists all (interactive, service and batch) logons, their logon domains and logon servers.

Results filtering

Some of the entries in the above screenshot are not useful to us. SharpHound is filtering out the following:

  • Local user accounts
  • Empty usernames and computer sessions
  • Entries without a logon domain
  • Any logon domain containing a whitespace (to ignore “NT Authority” and similar)

In our example above, only two entries would be kept by SharpHound:

Wireshark trace

A Wireshark trace of a successful SharpHound call allows us to highlight the steps involved:

  1. Establish an SMB connection to the remote host (Kerberos authentication)
  2. Connect to the IPC$ share
  3. Open the wkssvc named pipe (this is similar to opening a file with that name)
  4. Bind to the wkssvc interface with UUID 6BFFD098-A112-3610-9833-46C3F87E345A using RPC over SMB
  5. Interact using the Workstation Service Remote Protocol, call NetWkstaUserEnum
  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 wkssvc pipe
  3. When we attempt to execute an RPC call via the pipe

This last part fails with a low-privileged user on newer Windows as we’ll see below.

You can try it for yourself using the following Wireshark filter:

((smb2) || (wkssvc)|| (dcerpc) || (smb)) && !(smb2.ioctl.function == 0x001401fc)

What privileges are required?

Only an administrator can successfully use the NetWkstaUserEnum function or as stated by Microsoft:

Members of the Administrators, and the Server, System and Print Operator local groups can also view information

Hence, this method will not be of help in most cases but if you have admin credentials, it is the most reliable way of listing logged-on users.

Further reading

More details on the “NetWkstaUserEnum” function: https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstauserenum

More detailed and technical information about the Workstation Service Remote Protocol: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/5bb08058-bc36-4d3c-abeb-b132228281b7

SharpHound implements “NetWkstaUserEnum” in “ReadUserSessionsPrivileged” method in the CommonLib at: https://github.com/BloodHoundAD/SharpHoundCommon/blob/master/src/CommonLib/Processors/ComputerSessionProcessor.cs and the code relies on native Windows functions to which the P/Invoke signatures are declared in: https://github.com/BloodHoundAD/SharpHoundCommon/blob/master/src/CommonLib/NativeMethods.cs.

NetSessionEnum

When using the Session collection method, SharpHound enumerates logged on users by using the NetSessionEnum function.

How does it work?

This works by connecting to a named pipe \PIPE\srvsvc, which is exposed via the IPC$ (inter-process communication) SMB share. Then, it uses the Server Service Remote Protocol (RPC over SMB on port 445).

Implementation details

We mentioned “logged on users” above. This is actually not accurate and a simplification. In reality NetSessionEnum provides information about who has a session established to the system and from where.

So how does BloodHound come up with the correct data in the Graph? Let’s first see a successful example of a NetSessionEnum query. For this we can use several tools:

In the screenshot above, we see two user accounts which have a session to the target host as well as from which IP the connection is originating. The session from user “Administrator” is actually made by NetSess.exe itself!

By collecting this data from different systems and resolving IP addresses to hostnames, SharpHound is clever enough to correlate sessions to hosts.

Result filtering

Again, some of the entries in the above screenshot are not useful to us. SharpHound filters the following out:

  • Entries with blank or null usernames
  • Entries with blank or null computer/client names
  • Entries where computer accounts are connected
  • Entries with the user used for enumeration (see below why)
  • Entries with anonymous logons

Note: This method will almost never return local accounts since they are usually not allowed to connect via SMB.

From our example above, only one entry would be kept by SharpHound:

Limitations

A major disadvantage compared to the other methods is, that this technique does not provide complete results. Think of users who have no connections to scanned systems, since theses don’t use any network resources, they won’t appear through NetSessionEnum.

By knowing this we can think about what systems make good targets for this method: those that have by design many connections to them. For example file servers, servers hosting user homes or Domain Controllers which hosts scripts that users execute during their logon process. For this reason, it is worth using the Session Loop collection method!

Wireshark trace

A Wireshark trace of a successful SharpHound NetSessionEnumcall allows us to highlight the steps involved:

  1. Establish an SMB connection to the remote host (Kerberos authentication)
  2. Connect to the IPC$ share
  3. Open the srvsvc named pipe (this is similar to opening a file with that name)
  4. Bind to the srvsvc interface with its UUID 4b324fc8-1670-01d3-1278-5a47bf6ee188
  5. Interact using the Server Service Remote Protocol to query NetSessionEnum
  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 srvsvc named pipe
  3. When we attempt to execute an RPC call via the pipe

This last part fails with a low-privileged user on newer Windows as we’ll see below.

You can try it for yourself using the following Wireshark filter:

((smb2) || (srvsvc)|| (dcerpc) || (smb)) && !(smb2.ioctl.function == 0x001401fc)

Fun fact

Since NetSessionEnum displays SMB connections it will display a connection for each of the collection methods shown in this serie of articles:

  • SAMR
  • GPOLocalGroup
  • NetWkstaUserEnum
  • Remote Registry
  • even NetSessionEnum itself

What privileges are required?

This method worked for quite some time with any authenticated user. Unfortunately (for pentesters and attackers) this has changed in recent versions but it is not exactly clear when this change happened, since there is simply no clear public documentation about it from Microsoft. It is rumored to have changed in Windows 10 1607 and Windows Server 2019 but we decided to do our own testing, which is presented below.

Before these changes found their way into the Windows default settings, there was a PowerShell script written in 2016 by two Microsoft researchers (Itai Grady and Tal Be’ery) named NetCease, that did exactly the same as the new default values do.

The permissions for who can use NetSessionEnum is defined in the registry value SrvsvcSessionInfo in the hive HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\DefaultSecurity.

The default persmission set changed from:

  • Administrators
  • Server Operators
  • Power Users
  • Authenticated Users

to:

  • Administrators
  • Server Operators
  • Power Users
  • Interactive
  • Service
  • Batch

Hence, the function can only be called locally or by high-privileged users. If you want to check it by yourself you can either decode the binary registry value using PowerShell or use NetCease as follows:

# Download the module
Save-Module -Name NetCease -Repository PSGallery -Path ~/Downloads
# Load the moduleImport-Module ~/Downloads/NetCease/1.0.3/NetCease.psd1 -Force -Verbose # View current NetSessionEnum permissionsGet-NetSessionEnumPermission | Select TranslatedSID,SecurityIdentifier,AccessMask,AceType | ft -AutoSize

This will get you the following output:

The SID S-1-5-32-549 is “BUILTIN\Server Operators”, this matches the list above.

OS comparison

As mentioned above, we looked at the permissions on different Windows versions with different configurations. It was also tested after applying all updates (as of October 2021):

OS versionPatch
level after
installation
Authenticated
users
allowed?
Impact of
promotion
to DC?
Patch level
after updates
Impact of
security
updates?
Windows 10 160714393.0Yes14393.2214No
Windows 10 170315063.0Yes15063.1418No
Windows 10 170916299.15No16299.2166No
Windows 10 21H119043.1165No19043.1288No
Windows Server 2016 (1607)14393.693YesNo14393.4704No
Windows Server 2019 (1809)17763.737NoNo17763.2237No
 Windows Server 2022 (21H2)
20348.169
NoNo20348.288No

As we can see, only the “major” Windows version plays a role. This was expected but it is always nice to verify it by yourself!

Misleading documentation

The Microsoft documentation for NetSessionEnum made understanding the permissions difficult. The function accepts a parameter called level, which decides what information should be returned:

The level is important, because permissions needed by the user invoking the call depend on it:

SharpHound (and all of the above mentioned tools) use level 10:

This can also be seen in Wireshark:

According to Microsoft no special group membership is needed for level 10. In our opinion, this documentation is simply outdated and does not reflect the changes introduced with NetCease.

Levels and permissions

We tested Invoke-NetSessionEnum.ps1 against different versions of Windows as a regular authenticated user and checked the result:

Operating System/Level01210502
Windows 10 1607✔️✔️
Windows 10 1703✔️✔️
Windows 10 1709
Windows 10 21H1
Windows Server 2016 (1607)✔️✔️
Windows Server 2019 (1809)
 Windows Server 2022 (21H2)

What stands out is that level 502 needs higher privileges and this is not mentioned on the Microsoft documentation. The technical implementation guide for the Server Service Remote Protocol gives an explanation about permissions for level 502:

The server SHOULD<51> enforce the security measures to verify that the caller has the required permissions to execute this routine. If the caller does not have the required credentials, the server SHOULD<52> fail the call.

Following the reference <52> leads to the answer:

<52> Section 3.1.4.5: If the caller is not a member of the Administrator or Server Operator local group, Windows-based servers fail the call with the error code ERROR_ACCESS_DENIED.

Further reading

More details on the “NetSessionEnum” function: https://docs.microsoft.com/de-ch/windows/win32/api/lmshare/nf-lmshare-netsessionenum

More detailed and technical information about the Server Service Remote Protocol: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/accf23b0-0f57-441c-9185-43041f1b0ee9

SharpHound implements “NetSessionEnum” in ” ReadUserSessions” method in the CommonLib at: https://github.com/BloodHoundAD/SharpHoundCommon/blob/master/src/CommonLib/Processors/ComputerSessionProcessor.cs and the code relies on native Windows functions to which the P/Invoke signatures are declared in: https://github.com/BloodHoundAD/SharpHoundCommon/blob/master/src/CommonLib/NativeMethods.cs.