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

This commit is contained in:
Jordan Borean 2020-09-15 05:39:23 +10:00 committed by GitHub
parent e813b0151c
commit fc08c1f3c5
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
// 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)
{
try
@ -425,7 +429,8 @@ namespace Ansible.Become
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
// TOKEN_QUERY, TOKEN_DUPLICATE and TOKEN_ASSIGN_PRIMARY
@ -435,6 +440,9 @@ namespace Ansible.Become
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
@ -443,10 +451,15 @@ namespace Ansible.Become
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)
{
List<string> actualPrivileges = TokenUtil.GetTokenPrivileges(hToken).Select(x => x.Name).ToList();
int missing = requiredPrivileges.Where(x => !actualPrivileges.Contains(x)).Count();
if (missing > 0)
continue;
@ -455,16 +468,22 @@ namespace Ansible.Become
// Duplicate the token to convert it to a primary token with the access level required.
try
{
return TokenUtil.DuplicateToken(hToken, TokenAccessLevels.MaximumAllowed, SecurityImpersonationLevel.Anonymous,
TokenType.Primary);
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 null;
return userToken;
}
private static SafeNativeHandle GetS4UTokenForUser(SecurityIdentifier sid, LogonType logonType)