Skip to content

Commit

Permalink
Session Enumeration as local Admin User
Browse files Browse the repository at this point in the history
Co-authored-by: CI/CD Bot <svrath@web.de>
  • Loading branch information
2 people authored and JonasBK committed Oct 13, 2023
1 parent 45f5385 commit 5991fd6
Show file tree
Hide file tree
Showing 2 changed files with 258 additions and 6 deletions.
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;
}
}
}
65 changes: 59 additions & 6 deletions src/CommonLib/Processors/ComputerSessionProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
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
{
public delegate Task ComputerStatusDelegate(CSVComputerStatus status);
Expand All @@ -19,14 +26,19 @@ public class ComputerSessionProcessor
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;
}

public event ComputerStatusDelegate ComputerStatusEvent;
Expand All @@ -43,8 +55,29 @@ public async Task<SessionAPIResult> ReadUserSessions(string computerName, string
string computerDomain)
{
var ret = new SessionAPIResult();
SharpHoundRPC.NetAPINative.NetAPIResult<IEnumerable<SharpHoundRPC.NetAPINative.NetSessionEnumResults>> result;

if (_doLocalAdminSessionEnum)
{
// If we are authenticating using a local admin, we need to impersonate for this
Impersonator Impersonate;
using (Impersonate = new Impersonator(_localAdminUsername, ".", _localAdminPassword, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
result = _nativeMethods.NetSessionEnum(computerName);
}

if (result.IsFailed)
{
// Fall back to default User
_log.LogDebug("NetSessionEnum failed on {ComputerName} with local admin credentials: {Status}. Fallback to default user.", computerName, result.Status);
result = _nativeMethods.NetSessionEnum(computerName);
}
}
else
{
result = _nativeMethods.NetSessionEnum(computerName);
}

var result = _nativeMethods.NetSessionEnum(computerName);
if (result.IsFailed)
{
await SendComputerStatus(new CSVComputerStatus
Expand Down Expand Up @@ -148,7 +181,28 @@ public async Task<SessionAPIResult> ReadUserSessionsPrivileged(string computerNa
string computerSamAccountName, string computerSid)
{
var ret = new SessionAPIResult();
var result = _nativeMethods.NetWkstaUserEnum(computerName);
SharpHoundRPC.NetAPINative.NetAPIResult<IEnumerable<SharpHoundRPC.NetAPINative.NetWkstaUserEnumResults>> result;

if (_doLocalAdminSessionEnum)
{
// If we are authenticating using a local admin, we need to impersonate for this
Impersonator Impersonate;
using (Impersonate = new Impersonator(_localAdminUsername, ".", _localAdminPassword, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
result = _nativeMethods.NetWkstaUserEnum(computerName);
}

if (result.IsFailed)
{
// Fall back to default User
_log.LogDebug("NetWkstaUserEnum failed on {ComputerName} with local admin credentials: {Status}. Fallback to default user.", computerName, result.Status);
result = _nativeMethods.NetWkstaUserEnum(computerName);
}
}
else
{
result = _nativeMethods.NetWkstaUserEnum(computerName);
}

if (result.IsFailed)
{
Expand Down Expand Up @@ -232,7 +286,6 @@ public async Task<SessionAPIResult> ReadUserSessionsRegistry(string computerName
string computerSid)
{
var ret = new SessionAPIResult();

RegistryKey key = null;

try
Expand Down Expand Up @@ -306,4 +359,4 @@ private async Task SendComputerStatus(CSVComputerStatus status)
if (ComputerStatusEvent is not null) await ComputerStatusEvent.Invoke(status);
}
}
}
}

0 comments on commit 5991fd6

Please sign in to comment.