K7 Ultimate Security 17.0.2045 的配置界面会限制普通用户,但对应的 SYSTEM 服务曾接受低权限客户端提交的注册表内容。更深的问题出现在补丁之后:服务认定可信的客户端,与驱动实际保护的进程,并不是同一个集合。
这个案例适合分三层看:管道是否可达、请求是否有权修改对象、客户端身份是否代表运行时完整性。以下将历史版本的行为记录与静态分析分开,并用离线模型核对协议字段;不将它们视为当前版本的复现结果。
从配置按钮找到服务边界
| 对象 | 历史记录中的角色 |
|---|---|
| K7 Ultimate Security 17.0.2045 | 初始测试产品版本 |
K7TSMain.exe |
普通用户上下文中的界面进程 |
K7TSMngr.exe |
SYSTEM 服务进程 |
K7TSMngrService1 |
与配置操作对应的命名管道 |
K7MailProxyV1 |
枚举发现的空 DACL 线索,并非已确认的配置通路 |
普通用户打开配置页会看到限制提示;管理员可以开放非管理员修改设置的选项。关键动作是把按钮点击与管道读写关联,而非看到一个宽松 ACL 就假定它承载所有功能。Windows 的管道访问检查控制连接权限,业务操作仍需单独授权。

界面示意图。用于说明两个入口的权限语义,不表示某次实验的截图或状态。
捕获中的配置请求长 213 字节,其中 28 字节为头部,185 字节为正文;对应回复长 36 字节。正文包含 HKLM\Software\K7 Computing\K7TotalSecurity\CommonInfo 和三个值名:
AdminNonAdminIsValid:开放非管理员配置的状态。AdminChangesNeedPassword:修改配置是否需要密码。AdminChangesPasswordHash:密码相关状态字段。
这更像通用注册表文本的传输,而不是只接收布尔值的专用配置接口。但“含有路径”尚不足以推出任意注册表写入,需要观察服务端最终修改了什么。
长度差分揭示协议约束
下面是正文缩短一字节后的请求头。字节顺序是小端序,七个字段各占一个 DWORD。
magic0x00–0x03version0x04–0x07header_size0x08–0x0Breserved0x0C–0x0Fcommand0x10–0x13body_length0x14–0x17auxiliary_length0x18–0x1B
| 偏移 | 数值 | 解释 |
|---|---|---|
0x00 |
0x4b375453 |
原始字节为 ASCII ST7K |
0x04 |
0x1010 |
协议标识 |
0x08 |
0x1c |
头部长度 28 |
0x0c |
0 |
样本中的保留字段 |
0x10 |
0x10044 |
记录中的注册表请求命令 |
0x14 |
0xb8 |
正文长度 184 |
0x18 |
0 |
样本中的辅助长度字段 |
差分测试用于区分两种解释:值名白名单,以及正文长度检查。这里的“成功”指观察到新值,不只是客户端收到回复。
| 修改 | 值名字节数 | 头部正文长度 | 历史结果 |
|---|---|---|---|
AdminNonAdminIsValid |
20 | 185 | 原始配置请求 |
改为 aaaaa,保留旧长度 |
5 | 185 | 未成功 |
等长改为 AdminNonAdminIsValie |
20 | 185 | 新值被写入 |
缩短为 AdminNonAdminIsVali,同步长度 |
19 | 184 | 新值被写入 |
等长的新值名成功,说明服务并未只认原有值名;缩短请求在更新长度后成功,说明先前失败与帧结构有关。它支持“值名可控”的结论,路径范围仍需另行验证。
通用编码应写满偏移 0x14 处的四字节字段。仅改一个字节,只对小于 256 字节的特定长度成立;长度必须按实际编码后的字节计算,包含真实传输的换行和终止字节。
import struct
header = bytes.fromhex(
"53 54 37 4b 10 10 00 00 1c 00 00 00 00 00 00 00 "
"44 00 01 00 b8 00 00 00 00 00 00 00"
)
fields = struct.unpack("<7I", header)
assert fields == (0x4b375453, 0x1010, 28, 0, 0x10044, 184, 0)
body = b"x" * 300 # Illustrative bytes, not registry syntax.
updated = bytearray(header)
struct.pack_into("<I", updated, 20, len(body))
assert updated[20:24] == b"\x2c\x01\x00\x00"这个离线检查已运行:原头解析为 28 字节头部与 184 字节正文;示例长度 300 编码为 2c 01 00 00。示例正文只是占位字节,不连接管道,也不修改注册表。
用后端状态验证重放效果
配置重放的动画先显示普通用户访问受限,随后出现 36 字节回复,再打开设置页时,非管理员配置选项已启用。回复本身只证明有数据返回;真正支持权限变化的是配置页状态与后端写入记录。
Process Monitor 中的写入主体是 SYSTEM,实际路径包含 HKLM\SOFTWARE\WOW6432Node\K7 Computing\K7TotalSecurity\CommonInfo。它与请求文本没有完全相同的路径拼写:WOW64 注册表重定向让 32 位与 64 位视图有所区别。核对证据时应记录进程位数和注册表视图,而非把两个路径当成互相矛盾的结果。
| 证据 | 支持的结论 | 不直接支持的结论 |
|---|---|---|
| 低权限发起进程 | 请求不依赖管理员界面操作 | 每个产品版本均可接受 |
| 回复 36 字节 | 服务处理了连接并返回数据 | 注册表写入必然成功 |
| SYSTEM 的注册表写入 | 高权限服务应用了请求 | 任意路径均可写 |
| 新值名与配置变化 | 对象内容可受客户端影响 | 任意后续操作都会提权 |
注册表写入与进程启动相接
早期版本的后续记录把可控路径延伸到 Image File Execution Options(IFEO),目标是辅助程序 K7TSHlpr.exe 的 Debugger 配置。IFEO 的调试器设置影响目标程序的新实例;当 SYSTEM 服务启动这个辅助程序时,注册表数据就可能改变高权限执行入口。
flowchart TD A["低权限客户端"] --> B["服务接受注册表内容"] B --> C["SYSTEM 写入 IFEO 配置"] C --> D["服务启动辅助程序"] D --> E["调试器配置改变启动入口"] E --> F["高权限子进程与账户状态变化"]
动画中的对照顺序很重要:测试账户起初不存在,直接创建账户得到访问拒绝;管道操作后,进程树显示 K7TSMngr.exe 下出现 SYSTEM 的命令解释器,最后账户查询显示新增账户属于本地管理员组。证据链因此不只依赖脚本打印的成功文字。
这条路径依赖三个独立条件:相关注册表位置受写入原语影响、目标映像实际被启动、启动上下文具有高权限。配置开关变化与 SYSTEM 执行是两个不同层次的结论。
补丁暴露了信任集合差异
| 历史阶段 | 观察 | 边界 |
|---|---|---|
| 初始 17.0.2045 | 低权限脚本可重放配置请求 | 缺少完整的服务端授权 |
| 加入客户端识别后 | 普通 PowerShell 请求失效;产品进程中的请求仍有成功记录 | 文件身份不等于运行时完整性 |
K7Sentry.sys 22.0.0.70 |
先前载体路径受阻,K7QuervarCleaningTool.exe 路径仍使配置生效 |
客户端允许范围与进程保护范围不一致 |
中间阶段还记录了常规模块加载与手动映射结果不同。这说明客户端所在进程值得检查,但不意味着每个签名文件或每种映射方法都有效。表中的 22.0.0.70 是测试中的中间驱动版本,不是厂商最终公告的修复版本。
服务端静态分析涉及 ProcessPipeConnection:读取 28 字节头,检查魔数、协议标识和头长,并把两个长度限制在 1 MiB 内。客户端验证前的语义条件为:
command == 0x1004C || old_windows_condition || ValidatePipeClient(this)这里是 0x1004C,而配置请求是 0x10044。二者不同;没有命令分发与运行轨迹,就不应把配置操作直接归入这个验证例外。该表达式是静态分析的语义摘录,不是完整可编译函数。
接受身份与保护身份必须对齐
ValidatePipeClient 通过 GetNamedPipeClientProcessId 获取客户端 PID,再用 OpenProcess 和 QueryFullProcessImageNameA 查询映像路径。记录中的接受条件包括安装路径匹配、文件摘要缓存命中,以及厂商签名验证。
驱动的 ShouldProtectProcess 采用另一套规则:命中 VDefProtectedProcs 中的名称,或满足 K7 文件名前缀与规范化安装路径条件。路径子串匹配不是严格目录归属;磁盘文件签名也不是进程内存的完整性证明。
令 A 表示服务接受该客户端,P 表示驱动给予预期保护,可以把不一致直接写成真值表:
| A | P | 含义 |
|---|---|---|
| false | false | 不接受,也未按该规则保护 |
| false | true | 被保护,但不被此服务接受 |
| true | false | 服务信任了未被同等保护的载体 |
| true | true | 两条规则在该进程上对齐 |
离线模型枚举四种组合,只有 A and not P 命中缺口。这是设计一致性的检查,不是新版本漏洞证明:候选进程还要满足可控制请求逻辑、服务接受操作等运行时条件。
后续公告补充与修复重点
2025-12-22 的厂商公告将问题列为 CVE-2025-67826,确认 17.0.2045 的本地命名管道问题,并要求至少更新到 K7 Ultimate Security 17.0.2057,且 K7Sentry.sys 为 22.0.0.74 或更高。这是原发表日之后的公告补充,不应倒推为早期实验已验证的修复状态。
服务端应把“可连接”“来自产品文件”“有权修改某个配置”拆成独立检查。配置接口要限制键、值、类型和允许的状态迁移;若依赖进程保护维持客户端可信,就要共同维护接受集合与保护集合,并在身份查询或完整性判断失败时停止处理敏感操作。
这个案例最重要的结论不是某个特定管道名,而是验证顺序:先证明帧结构成立,再证明对象被修改,最后证明高权限行为发生。任何一层的成功,都不替代下一层证据。