Search code examples
windowsaclprivilegeselevated-privileges

How to change ownership of a file to another user without obtaining SeRestorePrivilege?


If a process has SeRestorePrivilege, it can change the ownership of a file with win32security.SetNamedSecurityInfo. According to Windows API documentation,

SeRestorePrivilege:

causes the system to grant all write access control to any file, regardless of the ACL specified for the file. Any access request other than write is still evaluated with the ACL. The following access rights are granted if this privilege is held: WRITE_DAC, WRITE_OWNER, ACCESS_SYSTEM_SECURITY, FILE_GENERIC_WRITE, FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, DELETE

I granted all the mentioned access rights to the owner of the file, but changing ownership fails with error 1307: 'This security ID may not be assigned as the owner of this object.'. Why?

Code:

import ntsecuritycon
import win32security
import win32api
import sys
import os

# grant access rights of SeRestorePrivilege
def simulate_se_restore_privilege(path):
    sd = win32security.GetNamedSecurityInfo(path, win32security.SE_FILE_OBJECT, win32security.OWNER_SECURITY_INFORMATION)
    owner_sid = sd.GetSecurityDescriptorOwner()

    dacl = win32security.ACL()
    dacl.SetEntriesInAcl([
        {'AccessPermissions': ntsecuritycon.WRITE_DAC
            | ntsecuritycon.WRITE_OWNER
            | ntsecuritycon.ACCESS_SYSTEM_SECURITY
            | ntsecuritycon.FILE_GENERIC_WRITE
            | ntsecuritycon.FILE_ADD_FILE
            | ntsecuritycon.FILE_ADD_SUBDIRECTORY
            | ntsecuritycon.DELETE,
         'AccessMode': win32security.GRANT_ACCESS,
         'Inheritance': 0,
         'Trustee': {
             'MultipleTrustee': None,
             'MultipleTrusteeOperation': 0,
             'TrusteeForm': win32security.TRUSTEE_IS_SID,
             'TrusteeType': win32security.TRUSTEE_IS_UNKNOWN,
             'Identifier': owner_sid,
         }
        }
    ])

    win32security.SetNamedSecurityInfo(
        path,
        win32security.SE_FILE_OBJECT,
        win32security.DACL_SECURITY_INFORMATION,
        None,
        None,
        dacl,
        None,
    )

def enable_se_restore_privilege():
    tok = win32security.OpenProcessToken(
        win32api.GetCurrentProcess(), win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY
    )
    luid = win32security.LookupPrivilegeValue(None, win32security.SE_RESTORE_NAME)
    new_state = [(luid, win32security.SE_PRIVILEGE_ENABLED)]
    win32security.AdjustTokenPrivileges(tok, 0, new_state)
    win32api.CloseHandle(tok)

def change_ownership(path):
    win32security.SetNamedSecurityInfo(
        path,
        win32security.SE_FILE_OBJECT,
        win32security.OWNER_SECURITY_INFORMATION,
        win32security.ConvertStringSidToSid("S-1-0-0"),
        None,
        None,
        None,
    )


path = sys.argv[1]
try:
    os.unlink(path)
except OSError:
    pass

with open(path, "w"):
    pass

print("Adding access rights from SeRestorePrivilege")
simulate_se_restore_privilege(path)
try:
    change_ownership(path)
except Exception as e:
    print(f"Changing ownership failed: {e}")

print("Enabling SeRestorePrivilege")
enable_se_restore_privilege()
change_ownership(path)
print("Changing ownership succeeded")

Output in elevated console:

> python .\chown.py file
Adding access rights from SeRestorePrivilege
Changing ownership failed: (1307, 'SetNamedSecurityInfo', 'This security ID may not be assigned as the owner of this object.')
Enabling SeRestorePrivilege
Changing ownership succeeded

Solution

  • According to the documentation of SeRestorePrivilege:

    Additionally, this privilege enables you to set any valid user or group SID as the owner of a file.