You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何启用SeRestorePrivilege权限可获得文件的READ_CONTROL访问权限?

Why does SeRestorePrivilege allow READ_CONTROL access to files?

First, let's recap the official Windows API documentation descriptions for these privileges:

  • SeBackupPrivilege: Grants full read access to any file regardless of its ACL, including permissions like READ_CONTROL, ACCESS_SYSTEM_SECURITY, FILE_GENERIC_READ, and FILE_TRAVERSE. Non-read access requests are still evaluated against the ACL.
  • SeRestorePrivilege: Grants full write access to any file regardless of its ACL, including permissions like WRITE_DAC, WRITE_OWNER, ACCESS_SYSTEM_SECURITY, FILE_GENERIC_WRITE, FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, and DELETE. Non-write access requests are still evaluated against the ACL.

But as your test demonstrates, when a process holds an enabled SeRestorePrivilege, opening a file with READ_CONTROL succeeds even when the file's DACL explicitly denies all access. Let's break down why this happens.

Test Validation

Here's the test code that reproduces the behavior:

import os
import sys
import ntsecuritycon
import win32con
import win32file
import win32security
import win32api

def print_token_info():
    tok = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32security.TOKEN_QUERY)
    sid, attr = win32security.GetTokenInformation(tok, win32security.TokenUser)
    name, domain, sid_name_use = win32security.LookupAccountSid(None, sid)
    print(f"User: {name}@{domain}")
    sid = win32security.GetTokenInformation(tok, win32security.TokenOwner)
    name, domain, sid_name_use = win32security.LookupAccountSid(None, sid)
    print(f"Owner of new files: {name}@{domain}")
    privileges = win32security.GetTokenInformation(tok, win32security.TokenPrivileges)
    print("Privileges")
    priv_attr_flags = {
        win32security.SE_PRIVILEGE_ENABLED: 'SE_PRIVILEGE_ENABLED',
        win32security.SE_PRIVILEGE_ENABLED_BY_DEFAULT: 'SE_PRIVILEGE_ENABLED_BY_DEFAULT',
        win32security.SE_PRIVILEGE_REMOVED: 'SE_PRIVILEGE_REMOVED',
        win32security.SE_PRIVILEGE_USED_FOR_ACCESS: 'SE_PRIVILEGE_USED_FOR_ACCESS',
    }
    for luid, attr in privileges:
        priv_name = win32security.LookupPrivilegeName(None, luid)
        flag_str = []
        for flag in priv_attr_flags:
            if (attr & flag) == flag:
                flag_str.append(priv_attr_flags[flag])
        print(f"- {priv_name} -> {attr} ({', '.join(flag_str)})")
    win32api.CloseHandle(tok)

def disable_privilege(privilege_name):
    print(f"Disabling privilege {privilege_name}")
    tok = win32security.OpenProcessToken(
        win32api.GetCurrentProcess(),
        win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY
    )
    luid = win32security.LookupPrivilegeValue(None, privilege_name)
    se_privilege_disabled = 0
    new_state = [(luid, se_privilege_disabled)]
    win32security.AdjustTokenPrivileges(tok, 0, new_state)
    win32api.CloseHandle(tok)

def enable_privilege(privilege_name):
    print(f"Enabling privilege {privilege_name}")
    tok = win32security.OpenProcessToken(
        win32api.GetCurrentProcess(),
        win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY
    )
    luid = win32security.LookupPrivilegeValue(None, privilege_name)
    new_state = [(luid, win32security.SE_PRIVILEGE_ENABLED)]
    win32security.AdjustTokenPrivileges(tok, 0, new_state)
    win32api.CloseHandle(tok)

def open_handle(path):
    try:
        handle = win32file.CreateFileW(
            path,
            ntsecuritycon.READ_CONTROL,
            0,
            None,
            win32con.OPEN_EXISTING,
            win32file.FILE_FLAG_OPEN_REPARSE_POINT | win32file.FILE_FLAG_BACKUP_SEMANTICS,
            None,
        )
        handle.Close()
        print(f"Successfully opened {path}")
    except Exception as e:
        print(f"Failed to open {path}: {e}")

if __name__ == '__main__':
    path = sys.argv[1]
    with open(path, "w") as fo:
        pass
    dacl = win32security.ACL()
    dacl.SetEntriesInAcl([])
    win32security.SetNamedSecurityInfo(
        path,
        win32security.SE_FILE_OBJECT,
        win32security.DACL_SECURITY_INFORMATION | win32security.PROTECTED_DACL_SECURITY_INFORMATION,
        None,
        None,
        dacl,
        None,
    )
    print_token_info()
    open_handle(path)
    print()
    enable_privilege(win32security.SE_BACKUP_NAME)
    # print_token_info()
    open_handle(path)
    print()
    disable_privilege(win32security.SE_BACKUP_NAME)
    # print_token_info()
    open_handle(path)
    print()
    enable_privilege(win32security.SE_RESTORE_NAME)
    # print_token_info()
    open_handle(path)
    print()
    disable_privilege(win32security.SE_RESTORE_NAME)
    # print_token_info()
    open_handle(path)
    print()

And the corresponding console output:

> python .\test_access.py C:\tmp\test\file-10
User: dev@CBIT-02
Owner of new files: Administrators@BUILTIN
Privileges
- SeIncreaseQuotaPrivilege -> 0 ()
- SeSecurityPrivilege -> 0 ()
- SeTakeOwnershipPrivilege -> 0 ()
- SeLoadDriverPrivilege -> 0 ()
- SeSystemProfilePrivilege -> 0 ()
- SeSystemtimePrivilege -> 0 ()
- SeProfileSingleProcessPrivilege -> 0 ()
- SeIncreaseBasePriorityPrivilege -> 0 ()
- SeCreatePagefilePrivilege -> 0 ()
- SeBackupPrivilege -> 0 ()
- SeRestorePrivilege -> 0 ()
- SeShutdownPrivilege -> 0 ()
- SeDebugPrivilege -> 2 (SE_PRIVILEGE_ENABLED)
- SeSystemEnvironmentPrivilege -> 0 ()
- SeChangeNotifyPrivilege -> 3 (SE_PRIVILEGE_ENABLED, SE_PRIVILEGE_ENABLED_BY_DEFAULT)
- SeRemoteShutdownPrivilege -> 0 ()
- SeUndockPrivilege -> 0 ()
- SeManageVolumePrivilege -> 0 ()
- SeImpersonatePrivilege -> 3 (SE_PRIVILEGE_ENABLED, SE_PRIVILEGE_ENABLED_BY_DEFAULT)
- SeCreateGlobalPrivilege -> 3 (SE_PRIVILEGE_ENABLED, SE_PRIVILEGE_ENABLED_BY_DEFAULT)
- SeIncreaseWorkingSetPrivilege -> 0 ()
- SeTimeZonePrivilege -> 0 ()
- SeCreateSymbolicLinkPrivilege -> 2 (SE_PRIVILEGE_ENABLED)
- SeDelegateSessionUserImpersonatePrivilege -> 0 ()
Failed to open C:\tmp\test\file-10: (5, 'CreateFileW', '拒绝访问。')

Enabling privilege SeBackupPrivilege
Successfully opened C:\tmp\test\file-10

Disabling privilege SeBackupPrivilege
Failed to open C:\tmp\test\file-10: (5, 'CreateFileW', '拒绝访问。')

Enabling privilege SeRestorePrivilege
Successfully opened C:\tmp\test\file-10

Disabling privilege SeRestorePrivilege
Failed to open C:\tmp\test\file-10: (5, 'CreateFileW', '拒绝访问。')

Root Cause

The official documentation is somewhat incomplete here. While SeRestorePrivilege is described as granting write-only ACL bypasses, the actual Windows privilege enforcement logic allows more when combined with the FILE_FLAG_BACKUP_SEMANTICS flag (which you're using in your CreateFileW call).

Here's the key reasoning:

  1. Restore operations depend on read access to security data: To restore a file properly (including its original security attributes like owner, DACL, and SACL), a process needs to first read the existing or source security information. READ_CONTROL is necessary to retrieve the file's security descriptor, which is a prerequisite for restoring those attributes. The Windows kernel implicitly allows this access because it's fundamental to the privilege's intended purpose.
  2. Privilege + backup semantics synergy: When you enable SeRestorePrivilege and use FILE_FLAG_BACKUP_SEMANTICS, the system's object manager relaxes access checks beyond just write-related permissions. It treats the process as performing a legitimate backup/restore operation, which requires full access to both read and modify file metadata (including security settings) regardless of the file's ACL.

In short, the documentation focuses on the primary use case (write access), but the implementation includes the necessary read permissions to make the privilege functional for its core purpose: restoring files with complete security context.

Summary

While the official documentation frames SeRestorePrivilege as a write-focused privilege, in practice, enabling it (along with FILE_FLAG_BACKUP_SEMANTICS) grants processes READ_CONTROL access to files. This is an implicit design choice, as restoring files requires the ability to read their security descriptors first.

内容的提问来源于stack exchange,提问作者Marcin Barczyński

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:45:40