runas - create new SYSTEM token on become (#71714)
This commit is contained in:
parent
e813b0151c
commit
fc08c1f3c5
2 changed files with 27 additions and 6 deletions
2
changelogs/fragments/runas-become-system-privileges.yml
Normal file
2
changelogs/fragments/runas-become-system-privileges.yml
Normal 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
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue