更新器删除旧组件、性能服务恢复本地状态、清理器移除临时目录:三种日常功能,都把普通用户可影响的数据交给了 SYSTEM。Avira 的这组漏洞分别暴露了路径解析、对象反序列化,以及扫描结果与实际对象脱节的问题。
关键不是把所有结果统称为“提权”,而是分清每条链真正获得的能力、前提和证据。文件删除、目录删除与代码执行之间仍有边界。
三个组件,三种信任错误
| 编号 | 组件 | 低信任输入 | 已记录的能力 |
|---|---|---|---|
| CVE-2026-27748 | Software Updater | 可重定向的组件路径 | SYSTEM 文件删除 |
| CVE-2026-27749 | System Speedup | 用户可影响的状态文件 | SYSTEM 反序列化执行 |
| CVE-2026-27750 | Optimizer | 扫描后被替换的目录路径 | SYSTEM 目录删除;后续组合依赖额外条件 |
CVE 登记产品为 Windows 上的 Avira Internet Security,列出的受影响版本为 1.1.109.1990 及以下,修复版本为 1.1.114.3113。案例界面标题出现 Avira Free Security,但界面品牌并不自动证明所有 SKU 都具有相同组件、权限与漏洞范围。

界面示意图。三个入口触发不同后台组件;按钮状态不是漏洞成立的证据。
更新器删除了哪个文件
Software Updater 处理的组件位于 C:\ProgramData\OPSWAT\MDES SDK\wa_3rd_party_host_32.exe。案例将组件目录的解析关系接入 \RPC Control,再由同名对象链接指向测试目标。服务仍使用组件路径,最终文件对象却已改变。
flowchart TD A["计划清理组件文件"] --> B["可变目录的重解析"] B --> C["对象管理器链接"] C --> D["非预期测试文件"] D --> E["SYSTEM 设置删除状态"]
这里有一个经常被忽略的前提:低权限用户必须实际控制有关目录或路径构造过程。ProgramData 的名称本身不证明任意子目录可写;ACL、对象归属、重解析点及服务的打开方式需要一起检查。
| 阶段 | 观察 | 支持的结论 |
|---|---|---|
| 基线 | 普通用户可读测试文件,直接删除得到 ACCESS DENIED | 目标原本受删除权限保护 |
| 解析 | Avira.Spotlight.Service.Worker.exe 遇到 REPARSE,随后访问 System32 中的测试文件 | 实际目标偏离组件路径 |
| 动作 | SYSTEM 的 SetDispositionInformationEx 返回 SUCCESS | 高权限主体成功设置删除状态 |
| 事后 | 目标查询返回 NAME NOT FOUND | 测试文件随后不再由该路径找到 |
这些事件比“查找更新完成 100%”更有证明力。它们支持文件删除结论,却没有单独证明任意目录删除或代码执行。
Windows 11 24H2 的一个具体变化是:打开 $INDEX_ALLOCATION 属性时,NtCreateFile 会遵守 FILE_NON_DIRECTORY_FILE。因此,过去借这一属性将文件删除扩展成目录删除的推理,必须重新核对标志与系统版本;这也不意味着所有路径重定向问题都被系统统一修复。
状态文件不是可信对象图
System Speedup 的危险入口包括 LoadDBFromFile、LoadCrashRestoreKnowledge 和 LoadProcessExceptionKnowledge。其中崩溃恢复文件由 CommonApplicationData 与 Avira\SystemSpeedup\temp_rto.dat 拼接而成。
下面是关键调用顺序的语义摘录,省略外围类与异常处理:
if (File.Exists(CrashRestoreKnowledgeBasePath)) {
var stream = new FileStream(
CrashRestoreKnowledgeBasePath, FileMode.OpenOrCreate);
var formatter = new BinaryFormatter();
_crashRestoreKnowledge =
(Dictionary<int, ProcInfo>)formatter.Deserialize(stream);
}Deserialize 先恢复对象图,强制类型转换随后才发生。即使转换失败,先前发生的对象行为也不会被撤销;外层捕获异常同样不是事务回滚。
案例初始目录没有 temp_rto.dat,普通用户可以创建它。若文件已存在且受保护,删除旧文件与重新创建文件是两个独立条件,不应因拥有删除原语就默认拥有重建权限。
| 证据 | 结论 | 尚不证明什么 |
|---|---|---|
| 前端为普通用户,后台服务为 SYSTEM | 功能入口与执行身份不同 | 单凭身份差异尚未证明解析器可被控制 |
| RealTimeOptimizer 的 CreateFile 返回 NAME NOT FOUND | 服务尝试打开该状态路径 | 尚未发生成功解析 |
| 后续成功读取状态文件,并出现 SYSTEM 命令写入身份结果 | 支持案例中高权限执行已发生 | 不代表任意文件内容或任意版本都会执行 |
Microsoft 建议移除 BinaryFormatter,而不是依赖类型转换、异常处理或 SerializationBinder 来建立可信边界。采用固定字段的数据格式并校验大小、类型和范围,同时保护状态文件的创建与替换权限,才直接针对输入来源和解释方式。
.NET 9 内置实现会抛出异常,但这一运行时变化不等于已经修复使用旧运行时的产品。
清理器使用了过期的对象判断
Optimizer 的检查与使用时间差(TOCTOU)跨越两个阶段:扫描目录、等待用户确认,然后再按路径执行清理。界面里的人工确认反而让这段间隔变得清晰可见。
- 1
扫描命中
案例目录为
C:\temp\foobar,筛选条件包含至少十分钟的年龄要求。这是样本规则,不是 Windows 对所有临时目录的规定。SYSTEM 的目录枚举事件确认扫描访问了该对象。 - 2
确认候选项
在 Review results → System junk → Temporary system files 中确认同一目录,减少其他清理项对结果的干扰。
- 3
路径含义改变
扫描完成后,目录路径被替换为新的链接解析链。保留下来的字符串相同,下一次打开得到的对象已不同。
- 4
清理访问新目标
事件从原测试路径的 REPARSE 转向
C:\Config.msi,最后由 SYSTEM 成功执行 SetDispositionInformationEx。
第三条链真正提供的是目录删除。案例随后利用安装与回滚状态继续组合,工具报告写入受保护目录,最终身份检查显示 SYSTEM;最终窗口的系统版本为 10.0.26100.4652。这些是该次运行的结果,而非对所有 Windows 构建的保证。
Windows Installer 回滚用于恢复安装前状态。将删除能力继续组合成受保护位置写入,还依赖目录重建权限、回滚内容控制、安装器时序和后续加载条件。看到某个 DLL 路径,既不等于文件已经加载,也不等于每次目录删除都会通向执行。
用对象身份解释竞态
下面的离线模型只改变内存中的路径映射,不访问或删除文件。它区分“字符串仍在允许的前缀下”与“操作对象仍是扫描过的对象”:
objects = {"TEMP": {"kind": "directory"}, "OTHER": {"kind": "directory"}}
namespace = {"cleanup/candidate": "TEMP"}
scanned_path = "cleanup/candidate"
scanned_id = namespace[scanned_path]
namespace[scanned_path] = "OTHER"
path_only_accepts = scanned_path.startswith("cleanup/")
same_object_accepts = namespace[scanned_path] == scanned_id
assert path_only_accepts and not same_object_accepts
assert scanned_id == "TEMP"
print("path_only=True; same_object=False; held_object=TEMP")path_only=True; same_object=False; held_object=TEMP这是设计约束模型,不是完整 Windows 文件系统实现。真正的修复还要处理父目录替换、多级重解析、并发修改,以及对象打开后是否仍处于允许的删除范围;只检查最终一层链接或者在使用前再比较一次字符串,仍可能留下窗口。
修复要对应具体边界
| 边界 | 应验证的行为 | 仅做这些仍不足 |
|---|---|---|
| 组件路径 → 文件对象 | 打开与删除都限定在允许对象范围,控制可变祖先路径 | 文件名白名单、UI 二次确认 |
| 状态字节 → 对象图 | 移除危险对象恢复,采用固定结构并保护文件来源 | try/catch、强制转换、仅增加 Binder |
| 扫描对象 → 清理对象 | 在检查和使用之间维持对象身份及范围约束 | 复用旧扫描结果、仅重新比较路径文本 |
部署侧应按 CVE 记录升级到 1.1.114.3113 或后续受支持版本,并记录产品与相关服务的实际版本。复测应同时保存调用者身份、服务身份、ACL、原始路径、最终对象、文件操作结果及事后状态。只有把这些条件逐项对应,才能判断修复覆盖了哪条链。