首页
社区
课程
招聘
[原创]Windows 文件所有权陷阱:管理员夺取所有权后真的改不了 DACL 吗?
发表于: 13小时前 185

[原创]Windows 文件所有权陷阱:管理员夺取所有权后真的改不了 DACL 吗?

13小时前
185

环境: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 的报错)。


[招生]科锐逆向工程师培训(2026年11月13日实地,远程教学同时开班, 第57期)

上传的附件:
收藏
点赞 ・1
打赏
分享
最新回复 (3)
雪    币: 9258
活跃值: (13674)
能力值: (RANK:385 )
在线值:
发帖
回帖
粉丝
2 楼
感谢楼主分享 OWNER RIGHTS 与夺取所有权后 DACL 变化的实验分析

帖子要点:
1. 现象:SYSTEM 所有且 DACL 拒绝 OW 写 DACL 的文件,提升管理员 takeown 后 icacls 授权成功
2. 说明所有者默认隐式拥有 READ_CONTROL 与 WRITE_DAC,DACL 中存在 S-1-3-4 的 ACE 时该隐式权利消失
3. 对比普通 DENY 与 OWNER RIGHTS DENY:前者覆盖不了隐式权利,后者先取消隐式权利再按 ACE 判定
4. 引用 MS-DTYP 伪代码说明隐式所有者权利在遍历 ACE 前处理,ACE 遍历跳过 INHERIT_ONLY_ACE
5. 核心变化:写所有者后 OW ACE 的 flags 变为 INHERIT_ONLY,不再参与当前对象访问检查,原生 API 层即发生
6. 边界验证:文件、目录、注册表键均发生转换,已有子对象副本不变,新建子对象不再继承 DENY
7. S04:SetNamedSecurityInfo 因打开时附带 READ_CONTROL 而失败,SetFileSecurity 只请求 WRITE_DAC 故成功
8. takeown 与 icacls 实测:单独 icacls /grant 返回 5,takeown 后再授权成功
9. 结论:OWNER RIGHTS 可限制普通用户改自身对象 DACL,但不能作为抵御具备 SeTakeOwnershipPrivilege 管理员的边界
10. 附件含 aclprobe 源码、预编译程序与复现脚本,覆盖 S01–S15 场景

谢谢楼主的实验记录,读完后我也有些想法:
· 优点:把访问检查、原生 API 与自带工具三层结果放在一起对照,S04 的打开阶段失败定位得很清楚
· 局限:结论集中在文件、目录、注册表键三类对象,服务、共享等其他对象类型未覆盖,icacls 内部调用路径也未追踪
· 对比:相比只讲 takeown 能夺权的常规资料,这里补充了 OWNER RIGHTS ACE 变 inherit-only 这一层,解释了为何夺权后立刻可改 DACL
· 对比:SetNamedSecurityInfo、SetSecurityInfo、SetFileSecurity 在同一 DACL 下表现不同,说明按 API 实现判断权限需求比按操作语义更贴近实际
大家看完帖子,对 OWNER RIGHTS 或几个 API 的差异有什么补充,欢迎一起聊聊

小结由 AI 辅助整理,方便大家先看个大概,完整内容还是看楼主原帖更准确。
13小时前
0
雪    币: 610
活跃值: (2481)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3 楼
TkBinary 感谢楼主分享 OWNER RIGHTS 与夺取所有权后 DACL 变化的实验分析 帖子要点: 1. 现象:SYSTEM 所有且 DACL 拒绝 OW 写 DACL 的文件,提升管理员 takeown ...
感谢版主认真阅读和点评!icacls的内部调用路径目前确实还没追,后面有空我会尝试跟一下,有新发现再补充到帖子里。
3小时前
0
雪    币: 9258
活跃值: (13674)
能力值: (RANK:385 )
在线值:
发帖
回帖
粉丝
4 楼
幸运三叶草 感谢版主认真阅读和点评!icacls的内部调用路径目前确实还没追,后面有空我会尝试跟一下,有新发现再补充到帖子里。
辛苦了.也是AI 辅助
2小时前
0
游客
登录 | 注册 方可回帖
返回