环境:Windows 11 24H2(build 26100.4652),NTFS;标准令牌(UAC 过滤)+ 提升令牌。
1. 现象
测试文件所有者是 NT AUTHORITY\SYSTEM,DACL 只给 SYSTEM 读,并拒绝"所有者"的 WRITE_DAC:
t01-explicit.txt
owner = S-1-5-18 (SYSTEM)
DACL = (D;;WD;;;S-1-3-4)(A;;FR;;;S-1-5-18)
用提升的管理员操作:
icacls t01 /grant user:F -> Access is denied. (5)
takeown /f t01 -> SUCCESS, owner = 用户 (0)
icacls t01 /grant user:F -> Successfully processed 1 file (0)
夺权前后的安全描述符:
夺权前: O:SYD:PAI(D;;WD;;;OW)(A;;FR;;;SY)
夺权后: O:<user>D:PAI(D;IO;WD;;;OW)(A;;FR;;;SY)
takeown 执行后,OWNER RIGHTS ACE 被添加了 INHERIT_ONLY_ACE 标志,不再参与当前对象的访问检查。
因此,管理员重新取得所有者的隐式 WRITE_DAC,随后可以通过 icacls 修改 DACL。以下通过访问检查、
所有者写入和 API 行为实验分析这一过程。
2. 机制:隐式所有者权利与 OWNER RIGHTS
2.1 所有者默认拥有 READ_CONTROL 和 WRITE_DAC
DACL 为空时,所有者的 READ_CONTROL、WRITE_DAC 仍被授予(S02):
NTACCESS(rc) granted=0x00020000 NTACCESS(wd) granted=0x00040000
RAWOPEN(READ_CONTROL) OK RAWOPEN(WRITE_DAC) OK
官方表述见 Security identifiers:
只要对象上存在 S-1-3-4 的 ACE,系统就忽略所有者的隐式 READ_CONTROL/WRITE_DAC。
实验中发现,同样的空 DACL 下 CreateFile(READ_CONTROL) 会失败(Win32 5,它附带请求 SYNCHRONIZE),
所以本文一律用 NtOpenFile 精确掩码和 NtAccessCheck 判定(S02)。
2.2 OWNER RIGHTS ACE:取消隐式权利,并按普通 ACE 参与
| 场景 |
DACL |
NtAccessCheck 结果 |
| S03 |
(A;;RC;;;OW) |
rc 授予、wd 拒绝 |
| S04 |
(A;;WD;;;OW) |
rc 拒绝、wd 授予 |
| S05 |
(D;;WD;;;OW)(A;;FR;;;user) |
rc 授予、wd 拒绝;SETDACL 返回 5 |
S03/S04 说明:只要 DENY/ALLOW ACE 的 SID 是 S-1-3-4,隐式 RC/WD 就整体消失,之后只剩 ACE 里
明确写出的权限。S05 是本文的"陷阱":文件所有者(提升或未提升)都改不了 DACL。
2.3 普通 DENY 与 OWNER RIGHTS 的访问检查差异
| 场景 |
DACL |
结果 |
| S06 |
(D;;WD;;;user)(A;;FR;;;user) |
wd 仍授予、SETDACL 成功 |
| S07 |
(D;;WD;;;WD)(A;;FR;;;WD) |
wd 仍授予 |
| S09 |
(D;;WD;;;OW)(A;;WD;;;user) |
wd 拒绝(DENY 在前,短路) |
| S09b |
(A;;WD;;;user)(D;;WD;;;OW) |
wd 授予(ALLOW 先满足请求) |
| S08 |
(D;;WD;;;OW)(A;;WD;;;BA) |
提升令牌 wd 仍拒绝 |
| S08b |
ALLOW 在 DENY 之前 |
提升令牌 wd 授予(标准令牌因 BA deny-only 仍拒绝) |
普通 DENY 覆盖不了隐式权利(S06/S07);OWNER RIGHTS DENY 之所以有效,是因为它先让隐式权利消失。
它并不是绝对禁止:ACE 顺序、其它允许 ACE、令牌特权都会改变结果(S08b/S09b/S14)。
2.4 算法位置
[MS-DTYP] 2.5.3.2 伪代码里,隐式所有者权利在遍历 ACE 之前处理:
IF SidInToken(Token, SecurityDescriptor.Owner, ...) THEN
IF DACL does not contain ACEs from object owner THEN
Remove READ_CONTROL and WRITE_DAC from RemainingAccess
ACE 遍历会跳过 INHERIT_ONLY_ACE,DENY 命中即整体拒绝(return access_denied)。
伪代码的守卫写法字面有歧义,S06 证明"给所有者 SID 的普通 DENY"并不会取消隐式权利;
实际生效条件是 DACL 中存在 S-1-3-4 的 ACE(S03/S04)。
2.5 文档口径
面向管理员的 Access control overview
仍写"所有者总能改权限";普通 DACL 确实无法剥夺所有者隐式拥有的 WRITE_DAC(S06),
但存在有效的 OWNER RIGHTS ACE 时,该隐式授权可以被取消(S05)。
3. 夺取所有权:OWNER RIGHTS ACE 自动失效
3.1 核心变化:flags 变成 inherit-only
对已有 OW ACE 的对象写所有者(任意 SID,甚至同一个 SID),ACE flags 都会变成 INHERIT_ONLY:
| 操作 |
之前 |
之后 |
| 显式 OW DENY,BA → 用户 |
(D;;WD;;;OW) |
(D;IO;WD;;;OW) |
| 显式 OW DENY,SYSTEM → 用户 |
(D;;WD;;;OW) |
(D;IO;WD;;;OW) |
| 所有者本来就是用户,再写同一个 SID |
(D;;WD;;;OW) |
(D;IO;WD;;;OW) |
| OW ALLOW |
(A;;RC;;;OW) |
(A;IO;RC;;;OW) |
| 只重设 DACL,不写所有者 |
(D;;WD;;;OW) |
不变 |
| 普通 SID 的 DENY(非 OW) |
(D;;WD;;;user) |
不变 |
直接 NtSetSecurityObject(OWNER) |
(D;;WD;;;OW) |
(D;IO;WD;;;OW) |
转换后 flags = INHERIT_ONLY(原本继承的再保留 INHERITED),OI/CI/NP 全部清除。
最后一行说明这发生在原生 API 层,不是 Win32 封装的副作用。
于是同一个文件在夺权后立刻可改 DACL(S12):夺权前 NtAccessCheck(wd) 拒绝、SETDACL 返回 5;
夺权后 SDDL 里 ACE 变 (D;IO;WD;;;OW),wd 授予、SETDACL 成功。
3.2 边界:文件、目录、继承、注册表
最小对照:
| 对象 / ACE 形态 |
写所有者前 |
写所有者后 |
| 文件,显式 OW DENY |
(D;;WD;;;OW) |
(D;IO;WD;;;OW) |
| 文件,设置时带 OI/CI |
(D;;WD;;;OW)(设置时 OI/CI 被丢弃) |
(D;IO;WD;;;OW) |
| 目录,显式 OICI / OI / CI / OICIIO |
对应 flags |
均变为 (D;IO;WD;;;OW) |
| 子目录,继承来的 ACE |
(D;OICIID;WD;;;OW) |
(D;IOID;WD;;;OW) |
| 注册表键,显式 OW DENY |
(D;;WD;;;OW) |
(D;IO;WD;;;OW) |
注册表键同样发生转换,说明这不是 NTFS 私有行为;本文只验证了文件、目录、注册表键三类对象。
3.3 继承影响:当前目录 / 已有子对象 / 新建子对象
S15:
- 当前目录:
(D;OICI;WD;;;OW) → (D;IO;WD;;;OW),ACE 不再参与本目录的访问检查;
- 已有子对象:保持
(D;ID;WD;;;OW) 不变——子对象保存的是继承时的副本;
- 新建子对象:只有
(A;ID;FA;;;user),没有 DENY——父 ACE 已没有 OI/CI 可继承。
3.4 所有者变更时的 ACE 处理规则
Jesper Johansson 在 2008 年的 TechNet Magazine 文章中明确介绍了这一行为:管理员修改所有者后,
系统会将 OWNER RIGHTS ACE 标记为 inherit-only,并清除其继承作用,以避免管理员被彻底锁定在
对象之外。实验结果与该描述一致。
4. S04:失败不在访问检查,而在 API 的打开动作
S04 的现象:DACL 只给 OW 发 WRITE_DAC,NtAccessCheck(WD) 成功,但 SetNamedSecurityInfo(DACL) 返回 5。
同一 DACL 下拆开四条路径:
| OW 的权限 |
NtOpenFile(WD) |
NtOpenFile(WD+RC) |
WD 句柄 + NtSetSecurityObject |
WD 句柄 + SetSecurityInfo |
RC|WD 句柄 + SetSecurityInfo |
SetNamedSecurityInfo |
SetFileSecurity |
| 仅 WD |
成功 |
拒绝 |
成功 |
5 |
打开失败 |
5 |
成功 |
| WD+RC |
成功 |
成功 |
成功 |
5 |
成功 |
成功 |
成功 |
| 仅 RC |
拒绝 |
拒绝 |
— |
— |
打开失败 |
5 |
5 |
| WD(OW)+RC(用户 SID) |
成功 |
成功 |
成功 |
5 |
成功 |
成功 |
成功 |
cdb 在 ntdll!NtOpenFile 抓到的实际请求:
CDB-CALL-BEGIN
OPEN 00060080 ; READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES
OPEN 00060000 ; READ_CONTROL | WRITE_DAC
CDB-CALL-END win32=5
原因分析:
- 对象本身允许写 DACL:
NtOpenFile(WRITE_DAC) + NtSetSecurityObject(DACL) 返回 STATUS_SUCCESS;
SetNamedSecurityInfo 的失败发生在打开阶段:请求里包含 READ_CONTROL,DACL 没有给,
打开即 ACCESS_DENIED,没走到写 DACL;
SetSecurityInfo(句柄版)要求句柄本身带 READ_CONTROL(WD 句柄 5,RC|WD 句柄成功);
SetFileSecurity 是例外:仅 WD 的 DACL 上直接成功,说明它只请求 WRITE_DAC。
5. 自带工具的实际表现
t01 是显式 OW DENY(owner=SYSTEM);t02 是父目录可继承 DENY、子文件 owner=用户。
| 命令 |
标准令牌 |
提升令牌 |
takeown /f t01 |
EXIT=1,Access is denied |
EXIT=0 |
icacls t01 /grant user:F |
EXIT=5 |
EXIT=5 |
icacls t01 /setowner user |
EXIT=5 |
EXIT=5 |
icacls t01 /reset / /inheritance:e |
EXIT=5 |
— |
takeown /f t01 && icacls /grant |
— |
均 EXIT=0 |
takeown /a /f t01 && icacls /grant |
— |
均 EXIT=0(owner→BA) |
icacls t02 /grant(takeown 前) |
EXIT=5 |
EXIT=5 |
takeown /f t02(owner 已是 user,DACL 给 FA) |
EXIT=0 |
EXIT=0 |
takeown 通过写入所有者使 OWNER RIGHTS ACE 失效,随后 icacls /grant 可以正常修改 DACL。
单独执行 icacls /grant 则返回错误 5。icacls 的内部调用路径未追踪,Explorer 属性页未测试。
6. 结论与安全意义
OWNER RIGHTS 改变了 Windows 对对象所有者的默认授权行为。普通 DACL 无法剥夺所有者隐式拥有的
READ_CONTROL 和 WRITE_DAC(S06/S07),但有效的 OWNER RIGHTS ACE 可以取消这两项授权(S05)。
不过,写入对象所有者时,系统会自动将相关 ACE 标记为 inherit-only,使其不再限制当前对象(S12)。
实验还发现,这一操作会改变目录后续子对象的权限继承结果(S15)。
因此,OWNER RIGHTS 适用于限制普通用户或服务账户修改自己创建对象的 DACL,但不能作为抵御具备
SeTakeOwnershipPrivilege 的管理员的安全边界(S11/S14)。此外,SetNamedSecurityInfo、
SetSecurityInfo 和 SetFileSecurity 在相同 DACL 下表现不同(S04),说明实际权限需求不仅取决于
目标操作,还取决于 API 的具体实现。
7. 实验复现
环境:Windows 11 24H2(build 26100.4652),NTFS;PowerShell 7;管理员账户。
附件内已含预编译的 x64 程序 experiments/bin/aclprobe.exe,解压后直接运行,不需要编译。
脚本先以标准令牌运行,再通过 UAC 提升跑一轮;若 UAC 需要交互确认,手动确认即可。
pwsh -NoProfile -File experiments/run-matrix.ps1 # S01–S15,标准/提升两轮
pwsh -NoProfile -File experiments/tools-matrix.ps1 # takeown / icacls
pwsh -NoProfile -File experiments/run-supplement.ps1 # 边界、注册表、S04、cdb(可选)
所有测试对象都在附件目录下的 lab/ 中创建,注册表测试只用 HKCU\Software\_AclProbeOwnerRights;
run-matrix.ps1 与 run-supplement.ps1 结束时自动 cleanup,恢复 DACL 并删除 lab/。
场景与预期:
| 场景 |
条件 |
预期 |
| S01 |
DACL 只给所有者 FullControl |
全部访问位授予 |
| S02 |
空 DACL |
RC/WD 仍授予;Win32 CreateFile(READ_CONTROL) 失败 5 |
| S03/S04 |
OWNER RIGHTS 只允许 RC / 只允许 WD |
一授一拒;S04 的 SetNamedSecurityInfo 返回 5 |
| S05 |
OWNER RIGHTS DENY WD + 用户可读 |
wd 拒绝、SETDACL 5 |
| S06/S07 |
普通 DENY(自己 / Everyone) |
wd 仍授予 |
| S08/S08b |
OW DENY + Administrators 允许(DENY 前 / ALLOW 前) |
拒绝 / 授予(提升令牌) |
| S09/S09b |
OW DENY + 所有者允许(DENY 前 / ALLOW 前) |
拒绝 / 授予 |
| S10 |
父目录下发继承 DENY |
子文件 wd 拒绝 |
| S11 |
自我夺权序列 |
未启用特权 SETOWNER 5;启用后 SETDACL(未写所有者)5;写所有者后成功;令牌外 SID 报 1307 |
| S12 |
owner=SYSTEM 的文件,夺权 |
夺权后 SDDL 为 (D;IO;WD;;;OW),wd 授予 |
| S13 |
继承 DENY + 夺权 |
ACE 由 (D;ID;WD;;;OW) 变为 (D;IOID;WD;;;OW) |
| S14 |
只切换特权、不写所有者 |
takeown 只给 WRITE_OWNER;restore 下 SETDACL 成功 |
| S15 |
目录写所有者后新建子文件 |
新子文件无 DENY(父 ACE 丢失 OI/CI) |
附件内容:experiments/bin/aclprobe.exe、experiments/src/aclprobe.c、experiments/src/supplement.c、
run-matrix.ps1、tools-matrix.ps1、run-supplement.ps1 与 README(含 EXE 的 SHA-256)。
如需自行编译,在 VS 2022 x64 开发者命令行中进入 experiments 目录:
New-Item -ItemType Directory -Force bin
cl /nologo /W4 /O2 /utf-8 /EHsc /DUNICODE /D_UNICODE src\aclprobe.c /Fe:bin\aclprobe.exe /Fobin\
附:访问位与错误码
READ_CONTROL=0x20000,WRITE_DAC=0x40000,WRITE_OWNER=0x80000,DELETE=0x10000,
FILE_GENERIC_READ=0x120089,FILE_ALL_ACCESS=0x1F01FF。
5=ERROR_ACCESS_DENIED,1307=ERROR_INVALID_OWNER,0xC0000022=STATUS_ACCESS_DENIED,
0xC0000079=STATUS_INVALID_SECURITY_DESCR(NtAccessCheck 对不完整 SD 的报错)。