Skip to content

Commit c643eb0

Browse files
committed
added functionality to check available logon type for the user
1 parent 6d1feb3 commit c643eb0

File tree

1 file changed

+45
-6
lines changed

1 file changed

+45
-6
lines changed

RunasCs.cs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class RunasCs
3939
private const int LOGON32_LOGON_SERVICE = 5;
4040
private const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
4141
private const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
42+
private const int ERROR_LOGON_TYPE_NOT_GRANTED = 1385;
4243
private const int BUFFER_SIZE_PIPE = 1048576;
4344
private const uint CREATE_NO_WINDOW = 0x08000000;
4445
private const uint CREATE_SUSPENDED = 0x00000004;
@@ -462,6 +463,36 @@ private bool IsLimitedUserLogon(IntPtr hToken, string username, string domainNam
462463
return isLimitedUserLogon;
463464
}
464465

466+
private void CheckAvailableUserLogonType(string username, string password, string domainName, int logonType, int logonProvider) {
467+
IntPtr hTokenCheck1 = IntPtr.Zero;
468+
if (!LogonUser(username, domainName, password, logonType, logonProvider, ref hTokenCheck1)) {
469+
if (Marshal.GetLastWin32Error() == ERROR_LOGON_TYPE_NOT_GRANTED) {
470+
int availableLogonType = 0;
471+
int[] logonTypeTryOrder = new int[] { LOGON32_LOGON_SERVICE, LOGON32_LOGON_BATCH, LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_LOGON_NETWORK, LOGON32_LOGON_INTERACTIVE};
472+
foreach (int logonTypeTry in logonTypeTryOrder)
473+
{
474+
IntPtr hTokenCheck2 = IntPtr.Zero;
475+
if (LogonUser(username, domainName, password, logonTypeTry, logonProvider, ref hTokenCheck2)) {
476+
availableLogonType = logonTypeTry;
477+
if (AccessToken.GetTokenIntegrityLevel(hTokenCheck2) > AccessToken.IntegrityLevel.Medium)
478+
{
479+
availableLogonType = logonTypeTry;
480+
CloseHandle(hTokenCheck2);
481+
break;
482+
}
483+
}
484+
if (hTokenCheck2 != IntPtr.Zero) CloseHandle(hTokenCheck2);
485+
}
486+
if (availableLogonType != 0)
487+
throw new RunasCsException(String.Format("Selected logon type '{0}' is not granted to the user '{1}'. Use available logon type '{2}'.", logonType, username, availableLogonType.ToString()));
488+
else
489+
throw new RunasCsException("LogonUser", true);
490+
}
491+
throw new RunasCsException("LogonUser", true);
492+
}
493+
if (hTokenCheck1 != IntPtr.Zero) CloseHandle(hTokenCheck1);
494+
}
495+
465496
private void RunasSetupStdHandlesForProcess(uint processTimeout, string[] remote, ref STARTUPINFO startupInfo, out IntPtr hOutputWrite, out IntPtr hErrorWrite, out IntPtr hOutputRead, out IntPtr socket) {
466497
IntPtr hOutputReadTmpLocal = IntPtr.Zero;
467498
IntPtr hOutputWriteLocal = IntPtr.Zero;
@@ -537,8 +568,6 @@ private void RunasRemoteImpersonation(string username, string domainName, string
537568
private void RunasCreateProcessWithLogonW(string username, string domainName, string password, int logonType, uint logonFlags, string commandLine, bool bypassUac, ref STARTUPINFO startupInfo, ref ProcessInformation processInfo, ref int logonTypeNotFiltered) {
538569
if (logonType == LOGON32_LOGON_NEW_CREDENTIALS)
539570
{
540-
if (domainName == "")
541-
domainName = ".";
542571
if (!CreateProcessWithLogonW(username, domainName, password, LOGON_NETCREDENTIALS_ONLY, null, commandLine, CREATE_NO_WINDOW, (UInt32)0, null, ref startupInfo, out processInfo))
543572
throw new RunasCsException("CreateProcessWithLogonW logon type 9", true);
544573
}
@@ -548,7 +577,8 @@ private void RunasCreateProcessWithLogonW(string username, string domainName, st
548577
// the below logon types are not filtered by UAC, we allow login with them. Otherwise stick with NetworkCleartext
549578
if (logonType == LOGON32_LOGON_NETWORK || logonType == LOGON32_LOGON_BATCH || logonType == LOGON32_LOGON_SERVICE || logonType == LOGON32_LOGON_NETWORK_CLEARTEXT)
550579
logonTypeBypassUac = logonType;
551-
else {
580+
else
581+
{
552582
// Console.Out.WriteLine("[*] Warning: UAC Bypass is not compatible with logon type '" + logonType.ToString() + "'. Reverting to the NetworkCleartext logon type '8'. To force a specific logon type, use the flag combination --bypass-uac and --logon-type.");
553583
logonTypeBypassUac = LOGON32_LOGON_NETWORK_CLEARTEXT;
554584
}
@@ -559,7 +589,9 @@ private void RunasCreateProcessWithLogonW(string username, string domainName, st
559589
{
560590
IntPtr hTokenUacCheck = new IntPtr(0);
561591
if (logonType != LOGON32_LOGON_INTERACTIVE)
562-
Console.Out.WriteLine("[*] Warning: The function CreateProcessWithLogonW is not compatible with the requested logon type " + logonType.ToString() + ". Reverting to the Interactive logon type (2). To force a specific logon type, use the flag combination --remote-impersonation and --logon-type.");
592+
Console.Out.WriteLine("[*] Warning: The function CreateProcessWithLogonW is not compatible with the requested logon type '" + logonType.ToString() + "'. Reverting to the Interactive logon type '2'. To force a specific logon type, use the flag combination --remote-impersonation and --logon-type.");
593+
// we check if the user has been granted the logon type requested, if not we show a message suggesting which logon type can be used to succesfully logon
594+
CheckAvailableUserLogonType(username, password, domainName, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT);
563595
// we use the logon type 2 - Interactive because CreateProcessWithLogonW internally use this logon type for the logon
564596
if (!LogonUser(username, domainName, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref hTokenUacCheck))
565597
throw new RunasCsException("LogonUser", true);
@@ -648,6 +680,7 @@ public string RunAs(string username, string password, string cmd, string domainN
648680
{
649681
string commandLine = ParseCommonProcessesInCommandline(cmd);
650682
int logonProvider = LOGON32_PROVIDER_DEFAULT;
683+
int logonTypeNotFiltered = 0;
651684
STARTUPINFO startupInfo = new STARTUPINFO();
652685
startupInfo.cb = Marshal.SizeOf(startupInfo);
653686
startupInfo.lpReserved = null;
@@ -658,8 +691,14 @@ public string RunAs(string username, string password, string cmd, string domainN
658691
this.stationDaclObj = new WindowStationDACL();
659692
string desktopName = this.stationDaclObj.AddAclToActiveWindowStation(domainName, username, logonType);
660693
startupInfo.lpDesktop = desktopName;
661-
if (logonType == LOGON32_LOGON_NEW_CREDENTIALS) logonProvider = LOGON32_PROVIDER_WINNT50;
662-
int logonTypeNotFiltered = 0;
694+
// setup proper logon provider for new credentials (9) logons
695+
if (logonType == LOGON32_LOGON_NEW_CREDENTIALS) {
696+
logonProvider = LOGON32_PROVIDER_WINNT50;
697+
if (domainName == "") // fixing bugs in seclogon when using LOGON32_LOGON_NEW_CREDENTIALS...
698+
domainName = ".";
699+
}
700+
// we check if the user has been granted the logon type requested, if not we show a message suggesting which logon type can be used to succesfully logon
701+
CheckAvailableUserLogonType(username, password, domainName, logonType, logonProvider);
663702
// Use the proper CreateProcess* function
664703
if (remoteImpersonation)
665704
RunasRemoteImpersonation(username, domainName, password, logonType, logonProvider, commandLine, ref startupInfo, ref processInfo, ref logonTypeNotFiltered);

0 commit comments

Comments
 (0)