为什么启用了SeRestorePrivilege允许对文件进行READ_CONTROL访问

问题描述 投票:0回答:1

根据Windows API documentation

SeBackupPrivilege

使系统授予对任何文件的所有读取访问控制,而不管为该文件指定的访问控制列表(ACL)。 ACL仍会评估除读取以外的任何访问请求。如果拥有此特权,则授予以下访问权限:READ_CONTROL,ACCESS_SYSTEM_SECURITY,FILE_GENERIC_READ,FILE_TRAVERSE

SeRestorePrivilege:

使系统授予对任何文件的所有写访问控制,而不管为该文件指定的ACL。除写入外,其他访问请求仍将通过ACL进行评估。如果拥有此特权,则授予以下访问权限:WRITE_DAC,WRITE_OWNER,ACCESS_SYSTEM_SECURITY,FILE_GENERIC_WRITE,FILE_ADD_FILE,FILE_ADD_SUBDIRECTORY,DELETE

但是,我注意到,如果进程令牌具有SeRestorePrivilege,则成功打开READ_CONTROL文件。为什么会这样?

代码:

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()

在高级控制台上的输出:

> 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', 'Access is denied.')

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

Disabling privilege SeBackupPrivilege
Failed to open C:\tmp\test\file-10: (5, 'CreateFileW', 'Access is denied.')

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

Disabling privilege SeRestorePrivilege
Failed to open C:\tmp\test\file-10: (5, 'CreateFileW', 'Access is denied.')
windows acl privileges elevated-privileges
1个回答
0
投票

如问题中所述,具有备份语义的SeRestorePrivilege授予以下权限:ACCESS_SYSTEM_SECURITYWRITE_DACWRITE_OWNERDELETEFILE_ADD_FILEFILE_ADD_SUBDIRECTORYFILE_GENERIC_WRITE

FILE_GENERIC_WRITE是访问掩码,已映射到文件对象的GENERIC_WRITE访问。它包括READ_CONTROLSYNCHRONIZEFILE_WRITE_ATTRIBUTES(0x0100),FILE_WRITE_EA(0x0010),FILE_APPEND_DATA(0x0004)和FILE_WRITE_DATA(0x0002)。请注意,后两个值与FILE_ADD_SUBDIRECTORY(0x0004)和FILE_ADD_FILE(0x0002)相同,因此SeRestorePrivilege的文档是多余的。

蒙版中的READ_CONTROL来自STANDARD_RIGHTS_WRITE。大多数通用访问掩码包括以下标准权限掩码之一:

STANDARD_RIGHTS_READ     = READ_CONTROL
STANDARD_RIGHTS_WRITE    = READ_CONTROL
STANDARD_RIGHTS_EXECUTE  = READ_CONTROL
STANDARD_RIGHTS_REQUIRED = READ_CONTROL | WRITE_DAC | WRITE_OWNER | DELETE

以上全部包括READ_CONTROL权限,该权限确保请求通用访问包括查询对象的自行决定性和强制性安全性的权利。


请注意,您的实验还需要控制隐式所有者权利。如果文件的所有者是发出请求的用户或用户的已启用组之一,则将隐式授予该用户READ_CONTROLWRITE_DAC访问权限。您可以通过确保文件由不同的安全性原则(例如“ SYSTEM”)拥有,或通过为“ OWNER_RIGHTS”安全性原则添加一个不授予访问权限的ACE(覆盖隐式所有者权限)来进行控制。

也请注意,即使CreateFile为0,SYNCHRONIZE也会隐式请求FILE_READ_ATTRIBUTESdwDesiredAccess访问。SeRestorePrivilege不授予FILE_READ_ATTRIBUTES访问权限。在您的实验中,此权限必须已由文件系统策略授予。不管文件的安全描述符如何,如果用户具有对父目录的FILE_READ_DATA(即FILE_LIST_DIRECTORY)访问权限,则该用户将被授予读取属性的权限。这样可以使文件API保持一致,因为大多数文件元数据(例如文件属性,时间戳和大小)都可以通过列出父目录来获得。要控制实验,您需要使用不授予用户读取数据访问权限的子目录。然后,您将观察到SeRestorePrivilege本身并未启用CreateFile成功。

© www.soinside.com 2019 - 2024. All rights reserved.