starts_with() 前缀检查绕过Notepad++ — 通过 session.xml 的 backupFilePath 遍历实现任意文件读取
PowerEditor/src/Notepad_plus.cpp(会话恢复 — backupFilePath 验证)session.xml 具有写入权限)Notepad++ 在会话恢复期间通过 std::wstring::starts_with() 对 backupFilePath 属性进行原始字符串前缀比较来验证其合法性,且未先对路径进行规范化处理。以合法备份目录前缀开头但包含 ..\ 遍历序列的构造路径能够通过检查,但操作系统打开文件时却会解析到任意位置——从而将任意可读文件的内容加载到编辑器标签页中。
启用快照(备份)模式启动时,Notepad++ 会从 session.xml 恢复之前的会话。每个条目的 backupFilePath 预期位于应用程序的备份目录下。完整性检查仅验证存储的路径是否以预期的备份目录字符串开头。由于比较发生在原始、未规范化的字符串上,攻击者可以预先附加合法前缀,然后通过 ..\ 序列走出该目录。
检查针对文本前缀;而使用方打开的是操作系统解析后的路径。一旦存在 ..\,两者便不一致。
| 操作 | 来源 | 行为 |
|---|---|---|
| 检查 | 原始 backupFilePath 字符串 | starts_with(backupDir) — 前缀匹配即通过 |
| 使用 | 操作系统解析后的路径 | ..\ 序列被折叠至任意位置 |
前提条件: 对 session.xml 具有写入权限,已启用快照/备份模式。
步骤:
session.xml,注入构造的 backupFilePath:
<backupDir>\..\..\..\..\Users\victim\.ssh\id_rsa<backupDir>\ 满足 starts_with(),因此验证通过。..\ 序列;目标文件被打开。结果: 只读外泄 — 目标文件内容在编辑器中可见,包括 SSH 密钥、环境配置文件、凭据存储等敏感文件。
.env / 配置文件、凭据存储 — 用户可读取的任何文件要求:
session.xml 具有写入权限backupFilePath 载荷:备份目录前缀 + ..\ 遍历序列 + 目标文件路径注入构造的路径并启动应用程序后,目标文件会被加载到编辑器标签页中。
该修复(提交 7e66f36)在验证之前执行了正确的路径规范化,因此 ..\ 序列会在前缀比较之前被折叠——现在检查的路径与使用的路径保持一致。