public static extern bool LogonUserW( string lpszUsername, string lpszDomain, string lpszPassword, LogonType dwLogonType, NativeHelpers.LogonProvider dwLogonProvider, out SafeNativeHandle phToken);
public static extern bool DuplicateTokenEx( SafeNativeHandle hExistingToken, TokenAccessLevels dwDesiredAccess, IntPtr lpTokenAttributes, NativeHelpers.SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, NativeHelpers.TOKEN_TYPE TokenType, out SafeNativeHandle phNewToken);
private static SecurityIdentifier GetTokenUserSID(SafeNativeHandle hToken) { using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, NativeHelpers.TokenInformationClass.TokenUser)) { NativeHelpers.TOKEN_USER tokenUser = (NativeHelpers.TOKEN_USER)Marshal.PtrToStructure(tokenInfo.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_USER)); return(new SecurityIdentifier(tokenUser.User.Sid)); } }
public static extern bool CreateProcessWithTokenW( SafeNativeHandle hToken, LogonFlags dwLogonFlags, [MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName, StringBuilder lpCommandLine, Process.NativeHelpers.ProcessCreationFlags dwCreationFlags, Process.SafeMemoryBuffer lpEnvironment, [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, Process.NativeHelpers.STARTUPINFOEX lpStartupInfo, out Process.NativeHelpers.PROCESS_INFORMATION lpProcessInformation);
private static List <string> GetTokenPrivileges(SafeNativeHandle hToken) { using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, NativeHelpers.TokenInformationClass.TokenPrivileges)) { NativeHelpers.TOKEN_PRIVILEGES tokenPrivileges = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure( tokenInfo.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_PRIVILEGES)); NativeHelpers.LUID_AND_ATTRIBUTES[] luidAndAttributes = new NativeHelpers.LUID_AND_ATTRIBUTES[tokenPrivileges.PrivilegeCount]; PtrToStructureArray(luidAndAttributes, IntPtr.Add(tokenInfo.DangerousGetHandle(), Marshal.SizeOf(tokenPrivileges.PrivilegeCount))); return(luidAndAttributes.Select(x => GetPrivilegeName(x.Luid)).ToList()); } }
private static NativeHelpers.SECURITY_LOGON_TYPE GetTokenLogonType(SafeNativeHandle hToken) { UInt64 tokenLuidId; using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, NativeHelpers.TokenInformationClass.TokenStatistics)) { NativeHelpers.TOKEN_STATISTICS stats = (NativeHelpers.TOKEN_STATISTICS)Marshal.PtrToStructure( tokenInfo.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_STATISTICS)); tokenLuidId = (UInt64)stats.AuthenticationId; } // Default to Network, if we weren't able to get the actual type treat it as an error and assume // we don't want to run a process with the token NativeHelpers.SECURITY_LOGON_TYPE logonType = NativeHelpers.SECURITY_LOGON_TYPE.Network; UInt32 sessionCount; SafeLsaMemoryBuffer sessionPtr; UInt32 res = NativeMethods.LsaEnumerateLogonSessions(out sessionCount, out sessionPtr); if (res != 0) { throw new Win32Exception((int)NativeMethods.LsaNtStatusToWinError(res), "LsaEnumerateLogonSession() failed"); } using (sessionPtr) { for (IntPtr p = sessionPtr.DangerousGetHandle(); p != IntPtr.Add(sessionPtr.DangerousGetHandle(), (int)(IntPtr.Size * sessionCount)); p = IntPtr.Add(p, Marshal.SizeOf(typeof(NativeHelpers.LUID)))) { SafeLsaMemoryBuffer sessionDataPtr; res = NativeMethods.LsaGetLogonSessionData(p, out sessionDataPtr); if (res != 0) { continue; } using (sessionDataPtr) { NativeHelpers.SECURITY_LOGON_SESSION_DATA sessionData = (NativeHelpers.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure( sessionDataPtr.DangerousGetHandle(), typeof(NativeHelpers.SECURITY_LOGON_SESSION_DATA)); UInt64 sessionId = (UInt64)sessionData.LogonId; if (sessionId == tokenLuidId) { logonType = sessionData.LogonType; break; } } } } return(logonType); }
public static extern UInt32 LsaLogonUser( SafeLsaHandle LsaHandle, NativeHelpers.LSA_STRING OriginName, LogonType LogonType, UInt32 AuthenticationPackage, IntPtr AuthenticationInformation, UInt32 AuthenticationInformationLength, IntPtr LocalGroups, NativeHelpers.TOKEN_SOURCE SourceContext, out SafeLsaMemoryBuffer ProfileBuffer, out UInt32 ProfileBufferLength, out Luid LogonId, out SafeNativeHandle Token, out IntPtr Quotas, out UInt32 SubStatus);
private static SafeNativeHandle GetElevatedToken(SafeNativeHandle hToken) { // First determine if the current token is a limited token using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, NativeHelpers.TokenInformationClass.TokenElevationType)) { NativeHelpers.TokenElevationType tet = (NativeHelpers.TokenElevationType)Marshal.ReadInt32(tokenInfo.DangerousGetHandle()); // We already have the best token we can get, just use it if (tet != NativeHelpers.TokenElevationType.TokenElevationTypeLimited) { return(hToken); } } // We have a limited token, get the linked elevated token using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, NativeHelpers.TokenInformationClass.TokenLinkedToken)) return(new SafeNativeHandle(Marshal.ReadIntPtr(tokenInfo.DangerousGetHandle()))); }
private static SafeMemoryBuffer GetTokenInformation(SafeNativeHandle hToken, NativeHelpers.TokenInformationClass tokenClass) { UInt32 tokenLength; bool res = NativeMethods.GetTokenInformation(hToken, tokenClass, new SafeMemoryBuffer(IntPtr.Zero), 0, out tokenLength); if (!res && tokenLength == 0) // res will be false due to insufficient buffer size, we ignore if we got the buffer length { throw new Win32Exception(String.Format("GetTokenInformation({0}) failed to get buffer length", tokenClass.ToString())); } SafeMemoryBuffer tokenInfo = new SafeMemoryBuffer((int)tokenLength); if (!NativeMethods.GetTokenInformation(hToken, tokenClass, tokenInfo, tokenLength, out tokenLength)) { throw new Win32Exception(String.Format("GetTokenInformation({0}) failed", tokenClass.ToString())); } return(tokenInfo); }
private static NativeHelpers.SECURITY_LOGON_TYPE GetTokenLogonType(SafeNativeHandle hToken) { TokenStatistics stats = TokenUtil.GetTokenStatistics(hToken); SafeLsaMemoryBuffer sessionDataPtr; UInt32 res = NativeMethods.LsaGetLogonSessionData(ref stats.AuthenticationId, out sessionDataPtr); if (res != 0) { // Default to Network, if we weren't able to get the actual type treat it as an error and assume // we don't want to run a process with the token return(NativeHelpers.SECURITY_LOGON_TYPE.Network); } using (sessionDataPtr) { NativeHelpers.SECURITY_LOGON_SESSION_DATA sessionData = (NativeHelpers.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure( sessionDataPtr.DangerousGetHandle(), typeof(NativeHelpers.SECURITY_LOGON_SESSION_DATA)); return(sessionData.LogonType); } }
/// <summary> /// Creates a process as another user account. This method will attempt to run as another user with the /// highest possible permissions available. The main privilege required is the SeDebugPrivilege, without /// this privilege you can only run as a local or domain user if the username and password is specified. /// </summary> /// <param name="username">The username of the runas user</param> /// <param name="password">The password of the runas user</param> /// <param name="logonFlags">LogonFlags to control how to logon a user when the password is specified</param> /// <param name="logonType">Controls what type of logon is used, this only applies when the password is specified</param> /// <param name="lpApplicationName">The name of the executable or batch file to executable</param> /// <param name="lpCommandLine">The command line to execute, typically this includes lpApplication as the first argument</param> /// <param name="lpCurrentDirectory">The full path to the current directory for the process, null will have the same cwd as the calling process</param> /// <param name="environment">A dictionary of key/value pairs to define the new process environment</param> /// <param name="stdin">Bytes sent to the stdin pipe</param> /// <returns>Ansible.Process.Result object that contains the command output and return code</returns> public static Result CreateProcessAsUser(string username, string password, LogonFlags logonFlags, LogonType logonType, string lpApplicationName, string lpCommandLine, string lpCurrentDirectory, IDictionary environment, byte[] stdin) { // While we use STARTUPINFOEX having EXTENDED_STARTUPINFO_PRESENT causes a parameter validation error Process.NativeHelpers.ProcessCreationFlags creationFlags = Process.NativeHelpers.ProcessCreationFlags.CREATE_UNICODE_ENVIRONMENT; Process.NativeHelpers.PROCESS_INFORMATION pi = new Process.NativeHelpers.PROCESS_INFORMATION(); Process.NativeHelpers.STARTUPINFOEX si = new Process.NativeHelpers.STARTUPINFOEX(); si.startupInfo.dwFlags = Process.NativeHelpers.StartupInfoFlags.USESTDHANDLES; SafeFileHandle stdoutRead, stdoutWrite, stderrRead, stderrWrite, stdinRead, stdinWrite; ProcessUtil.CreateStdioPipes(si, out stdoutRead, out stdoutWrite, out stderrRead, out stderrWrite, out stdinRead, out stdinWrite); FileStream stdinStream = new FileStream(stdinWrite, FileAccess.Write); // $null from PowerShell ends up as an empty string, we need to convert back as an empty string doesn't // make sense for these parameters if (lpApplicationName == "") { lpApplicationName = null; } if (lpCurrentDirectory == "") { lpCurrentDirectory = null; } using (Process.SafeMemoryBuffer lpEnvironment = ProcessUtil.CreateEnvironmentPointer(environment)) using (SafeNativeHandle hToken = GetUserToken(username, password, logonType)) { StringBuilder commandLine = new StringBuilder(lpCommandLine); if (!NativeMethods.CreateProcessWithTokenW(hToken, logonFlags, lpApplicationName, commandLine, creationFlags, lpEnvironment, lpCurrentDirectory, si, out pi)) { throw new Process.Win32Exception("CreateProcessWithTokenW() failed"); } } return(ProcessUtil.WaitProcess(stdoutRead, stdoutWrite, stderrRead, stderrWrite, stdinStream, stdin, pi.hProcess)); }
private static SafeNativeHandle GetElevatedToken(SafeNativeHandle hToken) { TokenElevationType tet = TokenUtil.GetTokenElevationType(hToken); // We already have the best token we can get, no linked token is really available. if (tet != TokenElevationType.Limited) { return(null); } SafeNativeHandle linkedToken = TokenUtil.GetTokenLinkedToken(hToken); TokenStatistics tokenStats = TokenUtil.GetTokenStatistics(linkedToken); // We can only use a token if it's a primary one (we had the SeTcbPrivilege set) if (tokenStats.TokenType == TokenType.Primary) { return(linkedToken); } else { return(null); } }
private static List <SafeNativeHandle> GetUserTokens(string username, string password, LogonType logonType) { List <SafeNativeHandle> userTokens = new List <SafeNativeHandle>(); SafeNativeHandle systemToken = null; bool impersonated = false; string becomeSid = username; if (logonType != LogonType.NewCredentials) { // If prefixed with .\, we are becoming a local account, strip the prefix if (username.StartsWith(".\\")) { username = username.Substring(2); } NTAccount account = new NTAccount(username); becomeSid = ((SecurityIdentifier)account.Translate(typeof(SecurityIdentifier))).Value; // Grant access to the current Windows Station and Desktop to the become user GrantAccessToWindowStationAndDesktop(account); // Try and impersonate a SYSTEM token, we need a SYSTEM token to either become a well known service // account or have administrative rights on the become access token. // If we ultimately are becoming the SYSTEM account we want the token with the most privileges available. // https://github.com/ansible/ansible/issues/71453 bool mostPrivileges = becomeSid == "S-1-5-18"; systemToken = GetPrimaryTokenForUser(new SecurityIdentifier("S-1-5-18"), new List <string>() { "SeTcbPrivilege" }, mostPrivileges); if (systemToken != null) { try { TokenUtil.ImpersonateToken(systemToken); impersonated = true; } catch (Process.Win32Exception) { } // We tried, just rely on current user's permissions. } } // We require impersonation if becoming a service sid or becoming a user without a password if (!impersonated && (SERVICE_SIDS.Contains(becomeSid) || String.IsNullOrEmpty(password))) { throw new Exception("Failed to get token for NT AUTHORITY\\SYSTEM required for become as a service account or an account without a password"); } try { if (becomeSid == "S-1-5-18") { userTokens.Add(systemToken); } // Cannot use String.IsEmptyOrNull() as an empty string is an account that doesn't have a pass. // We only use S4U if no password was defined or it was null else if (!SERVICE_SIDS.Contains(becomeSid) && password == null && logonType != LogonType.NewCredentials) { // If no password was specified, try and duplicate an existing token for that user or use S4U to // generate one without network credentials SecurityIdentifier sid = new SecurityIdentifier(becomeSid); SafeNativeHandle becomeToken = GetPrimaryTokenForUser(sid); if (becomeToken != null) { userTokens.Add(GetElevatedToken(becomeToken)); userTokens.Add(becomeToken); } else { becomeToken = GetS4UTokenForUser(sid, logonType); userTokens.Add(null); userTokens.Add(becomeToken); } } else { string domain = null; switch (becomeSid) { case "S-1-5-19": logonType = LogonType.Service; domain = "NT AUTHORITY"; username = "******"; break; case "S-1-5-20": logonType = LogonType.Service; domain = "NT AUTHORITY"; username = "******"; break; default: // Trying to become a local or domain account if (username.Contains(@"\")) { string[] userSplit = username.Split(new char[1] { '\\' }, 2); domain = userSplit[0]; username = userSplit[1]; } else if (!username.Contains("@")) { domain = "."; } break; } SafeNativeHandle hToken = TokenUtil.LogonUser(username, domain, password, logonType, LogonProvider.Default); // Get the elevated token for a local/domain accounts only if (!SERVICE_SIDS.Contains(becomeSid)) { userTokens.Add(GetElevatedToken(hToken)); } userTokens.Add(hToken); } } finally { if (impersonated) { TokenUtil.RevertToSelf(); } } return(userTokens); }
private static SafeNativeHandle GetPrimaryTokenForUser(SecurityIdentifier sid, List <string> requiredPrivileges = null, bool mostPrivileges = false) { // According to CreateProcessWithTokenW we require a token with // TOKEN_QUERY, TOKEN_DUPLICATE and TOKEN_ASSIGN_PRIMARY // Also add in TOKEN_IMPERSONATE so we can get an impersonated token TokenAccessLevels dwAccess = TokenAccessLevels.Query | TokenAccessLevels.Duplicate | TokenAccessLevels.AssignPrimary | TokenAccessLevels.Impersonate; SafeNativeHandle userToken = null; int privilegeCount = 0; foreach (SafeNativeHandle hToken in TokenUtil.EnumerateUserTokens(sid, dwAccess)) { // Filter out any Network logon tokens, using become with that is useless when S4U // can give us a Batch logon NativeHelpers.SECURITY_LOGON_TYPE tokenLogonType = GetTokenLogonType(hToken); if (tokenLogonType == NativeHelpers.SECURITY_LOGON_TYPE.Network) { continue; } List <string> actualPrivileges = TokenUtil.GetTokenPrivileges(hToken).Select(x => x.Name).ToList(); // If the token has less or the same number of privileges than the current token, skip it. if (mostPrivileges && privilegeCount >= actualPrivileges.Count) { continue; } // Check that the required privileges are on the token if (requiredPrivileges != null) { int missing = requiredPrivileges.Where(x => !actualPrivileges.Contains(x)).Count(); if (missing > 0) { continue; } } // Duplicate the token to convert it to a primary token with the access level required. try { userToken = TokenUtil.DuplicateToken(hToken, TokenAccessLevels.MaximumAllowed, SecurityImpersonationLevel.Anonymous, TokenType.Primary); privilegeCount = actualPrivileges.Count; } catch (Process.Win32Exception) { continue; } // If we don't care about getting the token with the most privileges, escape the loop as we already // have a token. if (!mostPrivileges) { break; } } return(userToken); }
public static extern bool GetTokenInformation( SafeNativeHandle TokenHandle, NativeHelpers.TokenInformationClass TokenInformationClass, SafeMemoryBuffer TokenInformation, UInt32 TokenInformationLength, out UInt32 ReturnLength);
public static extern bool ImpersonateLoggedOnUser( SafeNativeHandle hToken);
public static extern bool OpenProcessToken( SafeNativeHandle ProcessHandle, TokenAccessLevels DesiredAccess, out SafeNativeHandle TokenHandle);
private static List <SafeNativeHandle> GetUserTokens(string username, string password, LogonType logonType) { List <SafeNativeHandle> userTokens = new List <SafeNativeHandle>(); SafeNativeHandle systemToken = null; bool impersonated = false; string becomeSid = username; if (logonType != LogonType.LOGON32_LOGON_NEW_CREDENTIALS) { // If prefixed with .\, we are becoming a local account, strip the prefix if (username.StartsWith(".\\")) { username = username.Substring(2); } NTAccount account = new NTAccount(username); becomeSid = ((SecurityIdentifier)account.Translate(typeof(SecurityIdentifier))).Value; // Grant access to the current Windows Station and Desktop to the become user GrantAccessToWindowStationAndDesktop(account); // Try and impersonate a SYSTEM token, we need a SYSTEM token to either become a well known service // account or have administrative rights on the become access token. systemToken = GetPrimaryTokenForUser(new SecurityIdentifier("S-1-5-18"), new List <string>() { "SeTcbPrivilege" }); if (systemToken != null) { impersonated = NativeMethods.ImpersonateLoggedOnUser(systemToken); } } // We require impersonation if becoming a service sid or becoming a user without a password if (!impersonated && (SERVICE_SIDS.Contains(becomeSid) || String.IsNullOrEmpty(password))) { throw new Exception("Failed to get token for NT AUTHORITY\\SYSTEM required for become as a service account or an account without a password"); } try { if (becomeSid == "S-1-5-18") { userTokens.Add(systemToken); } // Cannot use String.IsEmptyOrNull() as an empty string is an account that doesn't have a pass. // We only use S4U if no password was defined or it was null else if (!SERVICE_SIDS.Contains(becomeSid) && password == null && logonType != LogonType.LOGON32_LOGON_NEW_CREDENTIALS) { // If no password was specified, try and duplicate an existing token for that user or use S4U to // generate one without network credentials SecurityIdentifier sid = new SecurityIdentifier(becomeSid); SafeNativeHandle becomeToken = GetPrimaryTokenForUser(sid); if (becomeToken != null) { userTokens.Add(GetElevatedToken(becomeToken)); userTokens.Add(becomeToken); } else { becomeToken = GetS4UTokenForUser(sid, logonType); userTokens.Add(becomeToken); } } else { string domain = null; switch (becomeSid) { case "S-1-5-19": logonType = LogonType.LOGON32_LOGON_SERVICE; domain = "NT AUTHORITY"; username = "******"; break; case "S-1-5-20": logonType = LogonType.LOGON32_LOGON_SERVICE; domain = "NT AUTHORITY"; username = "******"; break; default: // Trying to become a local or domain account if (username.Contains(@"\")) { string[] userSplit = username.Split(new char[1] { '\\' }, 2); domain = userSplit[0]; username = userSplit[1]; } else if (!username.Contains("@")) { domain = "."; } break; } SafeNativeHandle hToken; if (!NativeMethods.LogonUserW(username, domain, password, logonType, NativeHelpers.LogonProvider.LOGON32_PROVIDER_DEFAULT, out hToken)) { throw new Win32Exception("LogonUserW() failed"); } // Get the elevated token for a local/domain accounts only if (!SERVICE_SIDS.Contains(becomeSid)) { userTokens.Add(GetElevatedToken(hToken)); } userTokens.Add(hToken); } } finally { if (impersonated) { NativeMethods.RevertToSelf(); } } return(userTokens); }
private static SafeNativeHandle GetPrimaryTokenForUser(SecurityIdentifier sid, List <string> requiredPrivileges = null) { NativeHelpers.ProcessAccessFlags accessFlags = NativeHelpers.ProcessAccessFlags.PROCESS_QUERY_INFORMATION; // According to CreateProcessWithTokenW we require a token with // TOKEN_QUERY, TOKEN_DUPLICATE and TOKEN_ASSIGN_PRIMARY // Also add in TOKEN_IMPERSONATE so we can get an impersonated token TokenAccessLevels dwAccess = TokenAccessLevels.Query | TokenAccessLevels.Duplicate | TokenAccessLevels.AssignPrimary | TokenAccessLevels.Impersonate; foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses()) { using (process) { using (SafeNativeHandle hProcess = NativeMethods.OpenProcess(accessFlags, false, (UInt32)process.Id)) { if (hProcess.IsInvalid) { continue; } SafeNativeHandle hToken; NativeMethods.OpenProcessToken(hProcess, dwAccess, out hToken); if (hToken.IsInvalid) { continue; } using (hToken) { if (!sid.Equals(GetTokenUserSID(hToken))) { continue; } // Filter out any Network logon tokens, using become with that is useless when S4U // can give us a Batch logon NativeHelpers.SECURITY_LOGON_TYPE tokenLogonType = GetTokenLogonType(hToken); if (tokenLogonType == NativeHelpers.SECURITY_LOGON_TYPE.Network) { continue; } // Check that the required privileges are on the token if (requiredPrivileges != null) { List <string> actualPrivileges = GetTokenPrivileges(hToken); int missing = requiredPrivileges.Where(x => !actualPrivileges.Contains(x)).Count(); if (missing > 0) { continue; } } SafeNativeHandle dupToken; if (!NativeMethods.DuplicateTokenEx(hToken, TokenAccessLevels.MaximumAllowed, IntPtr.Zero, NativeHelpers.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, NativeHelpers.TOKEN_TYPE.TokenPrimary, out dupToken)) { continue; } return(dupToken); } } } } return(null); }