2

Code-wise, this is pretty straightforward:

var fs = IO.File.GetAccessControl(path);
fs.SetOwner(new NTAccount("NT AUTHORITY\\SYSTEM"));
IO.File.SetAccessControl(path, fs);

This throws an exception that says "The security identifier is not allowed to be the owner of this object."

Supposedly this means that I do not have the right to assign this user as the owner: source 1, source 2. However, I can very easily use Explorer to set the owner of this file to SYSTEM. Since Explorer can do it somehow, I must have the necessary rights - so how can I do what Explorer does and set a file's owner to SYSTEM?

Community
  • 1
  • 1
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • How to you set the _owner_ with Windows Explorer? Isn't adding permissions for a new user/group (as in .NET's [AddAccessRule](https://msdn.microsoft.com/de-de/library/system.security.accesscontrol.filesystemsecurity.addaccessrule(v=vs.100).aspx)) all you can do in explorer? According to [this](http://serverfault.com/a/126046/66762) you cannot change the owner of a file to something else than yourself or an admin group. – Christian.K Jun 23 '16 at 13:20
  • 1
    @Christian.K Properties / Security / Advanced / Owner / Edit / Other users and groups / type "System" / press "OK". Close everything, open again to make sure it worked. It did. – Roman Starkov Jun 23 '16 at 17:06
  • 2
    Oh yes, thanks. After some googling and reference code studying. [It looks](https://social.msdn.microsoft.com/Forums/vstudio/en-US/fe099467-414d-4b46-a248-177fb8fb7a5f/some-issues-regarding-owner-of-a-file-or-directory?forum=vclanguage) as if you need to have the `SE_TAKE_OWNERSHIP_NAME` and `SE_RESTORE_NAME` privileges. Otherwise you can only set the owner to yourself or the BUILTIN\Adminstrations group. Experimenting a little with your code seems to proof the later. Sorry, I have no time right now to lookup/writeup the P/Invoke code to do the `AdjustTokenPrivileges` necessary to test this... – Christian.K Jun 24 '16 at 05:18
  • @Christian.K Thanks, that turned out to be enough information to get this working. `SE_RESTORE_NAME` alone is sufficient. – Roman Starkov Jun 24 '16 at 13:00

1 Answers1

3

With the help of Christian.K who pointed me towards AdjustTokenPrivileges and SE_RESTORE_NAME, all that needs to be done is to enable this privilege on the process token:

// Allow this process to circumvent ACL restrictions
WinAPI.ModifyPrivilege(PrivilegeName.SeRestorePrivilege, true);

// Sometimes this is required and other times it works without it. Not sure when.
WinAPI.ModifyPrivilege(PrivilegeName.SeTakeOwnershipPrivilege, true);

// Set owner to SYSTEM
var fs = IO.File.GetAccessControl(path);
fs.SetOwner(new NTAccount("NT AUTHORITY\\SYSTEM"));
IO.File.SetAccessControl(path, fs);

Here is the code for such a ModifyPrivilege helper method:

static class WinAPI
{
    /// <summary>
    ///     Enables or disables the specified privilege on the primary access token of the current process.</summary>
    /// <param name="privilege">
    ///     Privilege to enable or disable.</param>
    /// <param name="enable">
    ///     True to enable the privilege, false to disable it.</param>
    /// <returns>
    ///     True if the privilege was enabled prior to the change, false if it was disabled.</returns>
    public static bool ModifyPrivilege(PrivilegeName privilege, bool enable)
    {
        LUID luid;
        if (!LookupPrivilegeValue(null, privilege.ToString(), out luid))
            throw new Win32Exception();

        using (var identity = WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query))
        {
            var newPriv = new TOKEN_PRIVILEGES();
            newPriv.Privileges = new LUID_AND_ATTRIBUTES[1];
            newPriv.PrivilegeCount = 1;
            newPriv.Privileges[0].Luid = luid;
            newPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;

            var prevPriv = new TOKEN_PRIVILEGES();
            prevPriv.Privileges = new LUID_AND_ATTRIBUTES[1];
            prevPriv.PrivilegeCount = 1;
            uint returnedBytes;

            if (!AdjustTokenPrivileges(identity.Token, false, ref newPriv, (uint) Marshal.SizeOf(prevPriv), ref prevPriv, out returnedBytes))
                throw new Win32Exception();

            return prevPriv.PrivilegeCount == 0 ? enable /* didn't make a change */ : ((prevPriv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) != 0);
        }
    }

    const uint SE_PRIVILEGE_ENABLED = 2;

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState,
       UInt32 BufferLengthInBytes, ref TOKEN_PRIVILEGES PreviousState, out UInt32 ReturnLengthInBytes);

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

    struct TOKEN_PRIVILEGES
    {
        public UInt32 PrivilegeCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1 /*ANYSIZE_ARRAY*/)]
        public LUID_AND_ATTRIBUTES[] Privileges;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct LUID_AND_ATTRIBUTES
    {
        public LUID Luid;
        public UInt32 Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct LUID
    {
        public uint LowPart;
        public int HighPart;
    }
}

enum PrivilegeName
{
    SeAssignPrimaryTokenPrivilege,
    SeAuditPrivilege,
    SeBackupPrivilege,
    SeChangeNotifyPrivilege,
    SeCreateGlobalPrivilege,
    SeCreatePagefilePrivilege,
    SeCreatePermanentPrivilege,
    SeCreateSymbolicLinkPrivilege,
    SeCreateTokenPrivilege,
    SeDebugPrivilege,
    SeEnableDelegationPrivilege,
    SeImpersonatePrivilege,
    SeIncreaseBasePriorityPrivilege,
    SeIncreaseQuotaPrivilege,
    SeIncreaseWorkingSetPrivilege,
    SeLoadDriverPrivilege,
    SeLockMemoryPrivilege,
    SeMachineAccountPrivilege,
    SeManageVolumePrivilege,
    SeProfileSingleProcessPrivilege,
    SeRelabelPrivilege,
    SeRemoteShutdownPrivilege,
    SeRestorePrivilege,
    SeSecurityPrivilege,
    SeShutdownPrivilege,
    SeSyncAgentPrivilege,
    SeSystemEnvironmentPrivilege,
    SeSystemProfilePrivilege,
    SeSystemtimePrivilege,
    SeTakeOwnershipPrivilege,
    SeTcbPrivilege,
    SeTimeZonePrivilege,
    SeTrustedCredManAccessPrivilege,
    SeUndockPrivilege,
    SeUnsolicitedInputPrivilege,
}
Community
  • 1
  • 1
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • If you're getting error `UnauthorizedAccessException` from `System.Security.AccessControl.Win32.GetSecurityInfo` try this – Rui Caramalho Jan 29 '21 at 19:11