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:
- User Rights Enumeration Through SAMR and GPOLocalGroup
- Session Enumeration Through Remote Registry & Summary (to be published May 25th)
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:
- Establish an SMB connection to the remote host (Kerberos authentication)
- Connect to the
IPC$
share - Open the
wkssvc
named pipe (this is similar to opening a file with that name) - Bind to the
wkssvc
interface with UUID6BFFD098-A112-3610-9833-46C3F87E345A
using RPC over SMB - Interact using the Workstation Service Remote Protocol, call
NetWkstaUserEnum
- 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
wkssvc
pipe - 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:
- PsLoggedOn from Sysinternals,
- NetSess from Joeware.net,
- Invoke-NetSessionEnum.ps1 from Fuzzy Security
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:
- Establish an SMB connection to the remote host (Kerberos authentication)
- Connect to the
IPC$
share - Open the
srvsvc
named pipe (this is similar to opening a file with that name) - Bind to the
srvsvc
interface with its UUID4b324fc8-1670-01d3-1278-5a47bf6ee188
- Interact using the Server Service Remote Protocol to query
NetSessionEnum
- 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
srvsvc
named pipe - 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 version | Patch level after installation | Authenticated users allowed? | Impact of promotion to DC? | Patch level after updates | Impact of security updates? |
---|---|---|---|---|---|
Windows 10 1607 | 14393.0 | Yes | – | 14393.2214 | No |
Windows 10 1703 | 15063.0 | Yes | – | 15063.1418 | No |
Windows 10 1709 | 16299.15 | No | – | 16299.2166 | No |
Windows 10 21H1 | 19043.1165 | No | – | 19043.1288 | No |
Windows Server 2016 (1607) | 14393.693 | Yes | No | 14393.4704 | No |
Windows Server 2019 (1809) | 17763.737 | No | No | 17763.2237 | No |
Windows Server 2022 (21H2) | 20348.169 | No | No | 20348.288 | No |
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/Level | 0 | 1 | 2 | 10 | 502 |
---|---|---|---|---|---|
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.
Leave a Reply