Skip to content

Commit 395bffc

Browse files
authored
Merge pull request #47 from LuemmelSec/main
Added option to do Session Enumeration as local Admin User
2 parents 4ddf1b2 + 26a5f56 commit 395bffc

2 files changed

Lines changed: 258 additions & 8 deletions

File tree

src/CommonLib/Impersonate.cs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
//credit to Phillip Allan-Harding (Twitter @phillipharding) for this library.
2+
using System;
3+
using System.ComponentModel;
4+
using System.Runtime.InteropServices;
5+
using System.Security.Principal;
6+
using System.Xml.Linq;
7+
8+
namespace Impersonate
9+
{
10+
public enum LogonType
11+
{
12+
LOGON32_LOGON_INTERACTIVE = 2,
13+
LOGON32_LOGON_NETWORK = 3,
14+
LOGON32_LOGON_BATCH = 4,
15+
LOGON32_LOGON_SERVICE = 5,
16+
LOGON32_LOGON_UNLOCK = 7,
17+
LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Win2K or higher
18+
LOGON32_LOGON_NEW_CREDENTIALS = 9 // Win2K or higher
19+
};
20+
21+
public enum LogonProvider
22+
{
23+
LOGON32_PROVIDER_DEFAULT = 0,
24+
LOGON32_PROVIDER_WINNT35 = 1,
25+
LOGON32_PROVIDER_WINNT40 = 2,
26+
LOGON32_PROVIDER_WINNT50 = 3
27+
};
28+
29+
public enum ImpersonationLevel
30+
{
31+
SecurityAnonymous = 0,
32+
SecurityIdentification = 1,
33+
SecurityImpersonation = 2,
34+
SecurityDelegation = 3
35+
}
36+
37+
class Win32NativeMethods
38+
{
39+
[DllImport("advapi32.dll", SetLastError = true)]
40+
public static extern int LogonUser(string lpszUserName,
41+
string lpszDomain,
42+
string lpszPassword,
43+
int dwLogonType,
44+
int dwLogonProvider,
45+
ref IntPtr phToken);
46+
47+
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
48+
public static extern int DuplicateToken(IntPtr hToken,
49+
int impersonationLevel,
50+
ref IntPtr hNewToken);
51+
52+
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
53+
public static extern bool RevertToSelf();
54+
55+
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
56+
public static extern bool CloseHandle(IntPtr handle);
57+
}
58+
59+
/// <summary>
60+
/// Allows code to be executed under the security context of a specified user account.
61+
/// </summary>
62+
/// <remarks>
63+
///
64+
/// Implements IDispose, so can be used via a using-directive or method calls;
65+
/// ...
66+
///
67+
/// var imp = new Impersonator( "myUsername", "myDomainname", "myPassword" );
68+
/// imp.UndoImpersonation();
69+
///
70+
/// ...
71+
///
72+
/// var imp = new Impersonator();
73+
/// imp.Impersonate("myUsername", "myDomainname", "myPassword");
74+
/// imp.UndoImpersonation();
75+
///
76+
/// ...
77+
///
78+
/// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
79+
/// {
80+
/// ...
81+
/// 1
82+
/// ...
83+
/// }
84+
///
85+
/// ...
86+
/// </remarks>
87+
public class Impersonator : IDisposable
88+
{
89+
private WindowsImpersonationContext _wic;
90+
91+
/// <summary>
92+
/// Begins impersonation with the given credentials, Logon type and Logon provider.
93+
/// </summary>
94+
///<param name = "userName" > Name of the user.</param>
95+
///<param name = "domainName" > Name of the domain.</param>
96+
///<param name = "password" > The password. <see cref = "System.String" /></ param >
97+
///< param name="logonType">Type of the logon.</param>
98+
///<param name = "logonProvider" > The logon provider. <see cref = "Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider" /></ param >
99+
public Impersonator(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
100+
{
101+
Impersonate(userName, domainName, password, logonType, logonProvider);
102+
}
103+
104+
/// <summary>
105+
/// Begins impersonation with the given credentials.
106+
/// </summary>
107+
///<param name = "userName" > Name of the user.</param>
108+
///<param name = "domainName" > Name of the domain.</param>
109+
///<param name = "password" > The password. <see cref = "System.String" /></ param >
110+
public Impersonator(string userName, string domainName, string password)
111+
{
112+
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
113+
}
114+
115+
/// <summary>
116+
/// Initializes a new instance of the <see cref="Impersonator"/> class.
117+
/// </summary>
118+
public Impersonator()
119+
{ }
120+
121+
/// <summary>
122+
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
123+
/// </summary>
124+
public void Dispose()
125+
{
126+
UndoImpersonation();
127+
}
128+
129+
/// <summary>
130+
/// Impersonates the specified user account.
131+
/// </summary>
132+
///<param name = "userName" > Name of the user.</param>
133+
///<param name = "domainName" > Name of the domain.</param>
134+
///<param name = "password" > The password. <see cref = "System.String" /></ param >
135+
public void Impersonate(string userName, string domainName, string password)
136+
{
137+
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
138+
}
139+
140+
/// <summary>
141+
/// Impersonates the specified user account.
142+
/// </summary>
143+
///<param name = "userName" > Name of the user.</param>
144+
///<param name = "domainName" > Name of the domain.</param>
145+
///<param name = "password" > The password. <see cref = "System.String" /></ param >
146+
///< param name="logonType">Type of the logon.</param>
147+
///<param name = "logonProvider" > The logon provider. <see cref = "Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider" /></ param >
148+
public void Impersonate(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
149+
{
150+
UndoImpersonation();
151+
152+
IntPtr logonToken = IntPtr.Zero;
153+
IntPtr logonTokenDuplicate = IntPtr.Zero;
154+
try
155+
{
156+
// revert to the application pool identity, saving the identity of the current requestor
157+
_wic = WindowsIdentity.Impersonate(IntPtr.Zero);
158+
159+
// do logon & impersonate
160+
if (Win32NativeMethods.LogonUser(userName,
161+
domainName,
162+
password,
163+
(int)logonType,
164+
(int)logonProvider,
165+
ref logonToken) != 0)
166+
{
167+
if (Win32NativeMethods.DuplicateToken(logonToken, (int)ImpersonationLevel.SecurityImpersonation, ref logonTokenDuplicate) != 0)
168+
{
169+
var wi = new WindowsIdentity(logonTokenDuplicate);
170+
wi.Impersonate(); // discard the returned identity context (which is the context of the application pool)
171+
}
172+
else
173+
throw new Win32Exception(Marshal.GetLastWin32Error());
174+
}
175+
else
176+
throw new Win32Exception(Marshal.GetLastWin32Error());
177+
}
178+
finally
179+
{
180+
if (logonToken != IntPtr.Zero)
181+
Win32NativeMethods.CloseHandle(logonToken);
182+
183+
if (logonTokenDuplicate != IntPtr.Zero)
184+
Win32NativeMethods.CloseHandle(logonTokenDuplicate);
185+
}
186+
}
187+
188+
/// <summary>
189+
/// Stops impersonation.
190+
/// </summary>
191+
private void UndoImpersonation()
192+
{
193+
// restore saved requestor identity
194+
if (_wic != null)
195+
_wic.Undo();
196+
_wic = null;
197+
}
198+
}
199+
}

src/CommonLib/Processors/ComputerSessionProcessor.cs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,42 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.DirectoryServices.ActiveDirectory;
4+
using System.Drawing.Text;
35
using System.Linq;
6+
using System.Runtime.InteropServices;
7+
using System.Runtime.Remoting.Contexts;
48
using System.Security.Principal;
59
using System.Text.RegularExpressions;
610
using System.Threading.Tasks;
11+
using Impersonate;
712
using Microsoft.Extensions.Logging;
813
using Microsoft.Win32;
914
using SharpHoundCommonLib.OutputTypes;
1015

16+
1117
namespace SharpHoundCommonLib.Processors
1218
{
19+
1320
public class ComputerSessionProcessor
1421
{
1522
private static readonly Regex SidRegex = new(@"S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$", RegexOptions.Compiled);
1623
private readonly string _currentUserName;
1724
private readonly ILogger _log;
1825
private readonly NativeMethods _nativeMethods;
1926
private readonly ILDAPUtils _utils;
27+
private readonly bool _doLocalAdminSessionEnum;
28+
private readonly string _localAdminUsername;
29+
private readonly string _localAdminPassword;
2030

21-
public ComputerSessionProcessor(ILDAPUtils utils, string currentUserName = null,
22-
NativeMethods nativeMethods = null, ILogger log = null)
31+
public ComputerSessionProcessor(ILDAPUtils utils, string currentUserName = null, NativeMethods nativeMethods = null, ILogger log = null, bool doLocalAdminSessionEnum = false, string localAdminUsername = null, string localAdminPassword = null)
2332
{
2433
_utils = utils;
2534
_nativeMethods = nativeMethods ?? new NativeMethods();
2635
_currentUserName = currentUserName ?? WindowsIdentity.GetCurrent().Name.Split('\\')[1];
2736
_log = log ?? Logging.LogProvider.CreateLogger("CompSessions");
37+
_doLocalAdminSessionEnum = doLocalAdminSessionEnum;
38+
_localAdminUsername = localAdminUsername;
39+
_localAdminPassword = localAdminPassword;
2840
}
2941

3042
/// <summary>
@@ -43,7 +55,27 @@ public async Task<SessionAPIResult> ReadUserSessions(string computerName, string
4355

4456
try
4557
{
46-
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
58+
// If we are authenticating using a local admin, we need to impersonate for this
59+
if (_doLocalAdminSessionEnum)
60+
{
61+
try
62+
{
63+
Impersonator Impersonate;
64+
using (Impersonate = new Impersonator(_localAdminUsername, ".", _localAdminPassword, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
65+
{
66+
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
67+
}
68+
}
69+
catch
70+
{
71+
// Fall back to default User
72+
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
73+
}
74+
}
75+
else
76+
{
77+
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
78+
}
4779
}
4880
catch (APIException e)
4981
{
@@ -139,7 +171,27 @@ public SessionAPIResult ReadUserSessionsPrivileged(string computerName,
139171

140172
try
141173
{
142-
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
174+
// If we are authenticating using a local admin, we need to impersonate for this
175+
if (_doLocalAdminSessionEnum)
176+
{
177+
try
178+
{
179+
Impersonator Impersonate;
180+
using (Impersonate = new Impersonator(_localAdminUsername, ".", _localAdminPassword, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
181+
{
182+
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
183+
}
184+
}
185+
catch
186+
{
187+
// Fall back to default User
188+
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
189+
}
190+
}
191+
else
192+
{
193+
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
194+
}
143195
}
144196
catch (APIException e)
145197
{
@@ -209,7 +261,6 @@ public SessionAPIResult ReadUserSessionsRegistry(string computerName, string com
209261
string computerSid)
210262
{
211263
var ret = new SessionAPIResult();
212-
213264
RegistryKey key = null;
214265

215266
try
@@ -235,6 +286,6 @@ public SessionAPIResult ReadUserSessionsRegistry(string computerName, string com
235286
{
236287
key?.Dispose();
237288
}
238-
}
239-
}
240-
}
289+
}
290+
}
291+
}

0 commit comments

Comments
 (0)