runas - create new SYSTEM token on become (#71714) (#71751)

(cherry picked from commit fc08c1f3c5)
This commit is contained in:
Jordan Borean 2020-09-28 15:29:45 +10:00 committed by GitHub
parent 07a9de1247
commit 2327ef9da8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 6 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- runas - create a new token when running as ``SYSTEM`` to ensure it has the full privileges assigned to that account

View file

@ -339,7 +339,11 @@ namespace Ansible.Become
// Try and impersonate a SYSTEM token, we need a SYSTEM token to either become a well known service // 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. // account or have administrative rights on the become access token.
systemToken = GetPrimaryTokenForUser(new SecurityIdentifier("S-1-5-18"), new List<string>() { "SeTcbPrivilege" }); // 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) if (systemToken != null)
{ {
try try
@ -425,7 +429,8 @@ namespace Ansible.Become
return userTokens; return userTokens;
} }
private static SafeNativeHandle GetPrimaryTokenForUser(SecurityIdentifier sid, List<string> requiredPrivileges = null) private static SafeNativeHandle GetPrimaryTokenForUser(SecurityIdentifier sid,
List<string> requiredPrivileges = null, bool mostPrivileges = false)
{ {
// According to CreateProcessWithTokenW we require a token with // According to CreateProcessWithTokenW we require a token with
// TOKEN_QUERY, TOKEN_DUPLICATE and TOKEN_ASSIGN_PRIMARY // TOKEN_QUERY, TOKEN_DUPLICATE and TOKEN_ASSIGN_PRIMARY
@ -435,6 +440,9 @@ namespace Ansible.Become
TokenAccessLevels.AssignPrimary | TokenAccessLevels.AssignPrimary |
TokenAccessLevels.Impersonate; TokenAccessLevels.Impersonate;
SafeNativeHandle userToken = null;
int privilegeCount = 0;
foreach (SafeNativeHandle hToken in TokenUtil.EnumerateUserTokens(sid, dwAccess)) foreach (SafeNativeHandle hToken in TokenUtil.EnumerateUserTokens(sid, dwAccess))
{ {
// Filter out any Network logon tokens, using become with that is useless when S4U // Filter out any Network logon tokens, using become with that is useless when S4U
@ -443,10 +451,15 @@ namespace Ansible.Become
if (tokenLogonType == NativeHelpers.SECURITY_LOGON_TYPE.Network) if (tokenLogonType == NativeHelpers.SECURITY_LOGON_TYPE.Network)
continue; 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 // Check that the required privileges are on the token
if (requiredPrivileges != null) if (requiredPrivileges != null)
{ {
List<string> actualPrivileges = TokenUtil.GetTokenPrivileges(hToken).Select(x => x.Name).ToList();
int missing = requiredPrivileges.Where(x => !actualPrivileges.Contains(x)).Count(); int missing = requiredPrivileges.Where(x => !actualPrivileges.Contains(x)).Count();
if (missing > 0) if (missing > 0)
continue; continue;
@ -455,16 +468,22 @@ namespace Ansible.Become
// Duplicate the token to convert it to a primary token with the access level required. // Duplicate the token to convert it to a primary token with the access level required.
try try
{ {
return TokenUtil.DuplicateToken(hToken, TokenAccessLevels.MaximumAllowed, SecurityImpersonationLevel.Anonymous, userToken = TokenUtil.DuplicateToken(hToken, TokenAccessLevels.MaximumAllowed,
TokenType.Primary); SecurityImpersonationLevel.Anonymous, TokenType.Primary);
privilegeCount = actualPrivileges.Count;
} }
catch (Process.Win32Exception) catch (Process.Win32Exception)
{ {
continue; 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 null; return userToken;
} }
private static SafeNativeHandle GetS4UTokenForUser(SecurityIdentifier sid, LogonType logonType) private static SafeNativeHandle GetS4UTokenForUser(SecurityIdentifier sid, LogonType logonType)