Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option to do Session Enumeration as local Admin User #47

Merged
merged 10 commits into from
Oct 30, 2023
199 changes: 199 additions & 0 deletions src/CommonLib/Impersonate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
//credit to Phillip Allan-Harding (Twitter @phillipharding) for this library.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Xml.Linq;

namespace Impersonate
{
public enum LogonType
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Win2K or higher
LOGON32_LOGON_NEW_CREDENTIALS = 9 // Win2K or higher
};

public enum LogonProvider
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35 = 1,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3
};

public enum ImpersonationLevel
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}

class Win32NativeMethods
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int LogonUser(string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
}

/// <summary>
/// Allows code to be executed under the security context of a specified user account.
/// </summary>
/// <remarks>
///
/// Implements IDispose, so can be used via a using-directive or method calls;
/// ...
///
/// var imp = new Impersonator( "myUsername", "myDomainname", "myPassword" );
/// imp.UndoImpersonation();
///
/// ...
///
/// var imp = new Impersonator();
/// imp.Impersonate("myUsername", "myDomainname", "myPassword");
/// imp.UndoImpersonation();
///
/// ...
///
/// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
/// {
/// ...
/// 1
/// ...
/// }
///
/// ...
/// </remarks>
public class Impersonator : IDisposable
{
private WindowsImpersonationContext _wic;

/// <summary>
/// Begins impersonation with the given credentials, Logon type and Logon provider.
/// </summary>
///<param name = "userName" > Name of the user.</param>
///<param name = "domainName" > Name of the domain.</param>
///<param name = "password" > The password. <see cref = "System.String" /></ param >
///< param name="logonType">Type of the logon.</param>
///<param name = "logonProvider" > The logon provider. <see cref = "Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider" /></ param >
public Impersonator(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
{
Impersonate(userName, domainName, password, logonType, logonProvider);
}

/// <summary>
/// Begins impersonation with the given credentials.
/// </summary>
///<param name = "userName" > Name of the user.</param>
///<param name = "domainName" > Name of the domain.</param>
///<param name = "password" > The password. <see cref = "System.String" /></ param >
public Impersonator(string userName, string domainName, string password)
{
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
}

/// <summary>
/// Initializes a new instance of the <see cref="Impersonator"/> class.
/// </summary>
public Impersonator()
{ }

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
UndoImpersonation();
}

/// <summary>
/// Impersonates the specified user account.
/// </summary>
///<param name = "userName" > Name of the user.</param>
///<param name = "domainName" > Name of the domain.</param>
///<param name = "password" > The password. <see cref = "System.String" /></ param >
public void Impersonate(string userName, string domainName, string password)
{
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
}

/// <summary>
/// Impersonates the specified user account.
/// </summary>
///<param name = "userName" > Name of the user.</param>
///<param name = "domainName" > Name of the domain.</param>
///<param name = "password" > The password. <see cref = "System.String" /></ param >
///< param name="logonType">Type of the logon.</param>
///<param name = "logonProvider" > The logon provider. <see cref = "Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider" /></ param >
public void Impersonate(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
{
UndoImpersonation();

IntPtr logonToken = IntPtr.Zero;
IntPtr logonTokenDuplicate = IntPtr.Zero;
try
{
// revert to the application pool identity, saving the identity of the current requestor
_wic = WindowsIdentity.Impersonate(IntPtr.Zero);

// do logon & impersonate
if (Win32NativeMethods.LogonUser(userName,
domainName,
password,
(int)logonType,
(int)logonProvider,
ref logonToken) != 0)
{
if (Win32NativeMethods.DuplicateToken(logonToken, (int)ImpersonationLevel.SecurityImpersonation, ref logonTokenDuplicate) != 0)
{
var wi = new WindowsIdentity(logonTokenDuplicate);
wi.Impersonate(); // discard the returned identity context (which is the context of the application pool)
}
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
if (logonToken != IntPtr.Zero)
Win32NativeMethods.CloseHandle(logonToken);

if (logonTokenDuplicate != IntPtr.Zero)
Win32NativeMethods.CloseHandle(logonTokenDuplicate);
}
}

/// <summary>
/// Stops impersonation.
/// </summary>
private void UndoImpersonation()
{
// restore saved requestor identity
if (_wic != null)
_wic.Undo();
_wic = null;
}
}
}
67 changes: 59 additions & 8 deletions src/CommonLib/Processors/ComputerSessionProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices.ActiveDirectory;
using System.Drawing.Text;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Contexts;
using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Impersonate;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using SharpHoundCommonLib.OutputTypes;


namespace SharpHoundCommonLib.Processors
{

public class ComputerSessionProcessor
{
private static readonly Regex SidRegex = new(@"S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$", RegexOptions.Compiled);
private readonly string _currentUserName;
private readonly ILogger _log;
private readonly NativeMethods _nativeMethods;
private readonly ILDAPUtils _utils;
private readonly bool _doLocalAdminSessionEnum;
private readonly string _localAdminUsername;
private readonly string _localAdminPassword;

public ComputerSessionProcessor(ILDAPUtils utils, string currentUserName = null,
NativeMethods nativeMethods = null, ILogger log = null)
public ComputerSessionProcessor(ILDAPUtils utils, string currentUserName = null, NativeMethods nativeMethods = null, ILogger log = null, bool doLocalAdminSessionEnum = false, string localAdminUsername = null, string localAdminPassword = null)
{
_utils = utils;
_nativeMethods = nativeMethods ?? new NativeMethods();
_currentUserName = currentUserName ?? WindowsIdentity.GetCurrent().Name.Split('\\')[1];
_log = log ?? Logging.LogProvider.CreateLogger("CompSessions");
_doLocalAdminSessionEnum = doLocalAdminSessionEnum;
_localAdminUsername = localAdminUsername;
_localAdminPassword = localAdminPassword;
}

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

try
{
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
// If we are authenticating using a local admin, we need to impersonate for this
if (_doLocalAdminSessionEnum)
{
try
{
Impersonator Impersonate;
using (Impersonate = new Impersonator(_localAdminUsername, ".", _localAdminPassword, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
}
}
catch
{
// Fall back to default User
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
}
}
else
{
apiResult = _nativeMethods.CallNetSessionEnum(computerName).ToArray();
}
}
catch (APIException e)
{
Expand Down Expand Up @@ -139,7 +171,27 @@ public SessionAPIResult ReadUserSessionsPrivileged(string computerName,

try
{
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
// If we are authenticating using a local admin, we need to impersonate for this
if (_doLocalAdminSessionEnum)
{
try
{
Impersonator Impersonate;
using (Impersonate = new Impersonator(_localAdminUsername, ".", _localAdminPassword, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
}
}
catch
{
// Fall back to default User
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
}
}
else
{
apiResult = _nativeMethods.CallNetWkstaUserEnum(computerName).ToArray();
}
}
catch (APIException e)
{
Expand Down Expand Up @@ -209,7 +261,6 @@ public SessionAPIResult ReadUserSessionsRegistry(string computerName, string com
string computerSid)
{
var ret = new SessionAPIResult();

RegistryKey key = null;

try
Expand All @@ -235,6 +286,6 @@ public SessionAPIResult ReadUserSessionsRegistry(string computerName, string com
{
key?.Dispose();
}
}
}
}
}
}
}
Loading