Instituto Nacional de ciberseguridad. Sección Incibe
Instituto Nacional de Ciberseguridad. Sección INCIBE-CERT

Vulnerabilidad en `arrayLimit` en qs (CVE-2026-2391)

Gravedad CVSS v4.0:
MEDIA
Tipo:
CWE-20 Validación incorrecta de entrada
Fecha de publicación:
12/02/2026
Última modificación:
12/02/2026

Descripción

### Resumen<br /> La opción `arrayLimit` en qs no aplica límites para valores separados por comas cuando `comma: true` está habilitado, permitiendo a los atacantes causar denegación de servicio a través del agotamiento de memoria. Esto es un bypass de la aplicación del límite de array, similar al bypass de notación de corchetes abordado en GHSA-6rw7-vpxm-498p (CVE-2025-15284).<br /> <br /> ### Detalles<br /> Cuando la opción `comma` se establece en `true` (no es el valor predeterminado, pero es configurable en las aplicaciones), qs permite analizar cadenas separadas por comas como arrays (por ejemplo, `?param=a,b,c` se convierte en `[&amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;, &amp;#39;c&amp;#39;]`). Sin embargo, la verificación de límite para `arrayLimit` (predeterminado: 20) y la opción throwOnLimitExceeded ocurren después de la lógica de manejo de comas en `parseArrayValue`, lo que permite un bypass. Esto permite la creación de arrays arbitrariamente grandes a partir de un solo parámetro, lo que lleva a una asignación excesiva de memoria.<br /> <br /> Código vulnerable (lib/parse.js: líneas ~40-50):<br /> ```js<br /> if (val &amp;amp;&amp;amp; typeof val === &amp;#39;string&amp;#39; &amp;amp;&amp;amp; options.comma &amp;amp;&amp;amp; val.indexOf(&amp;#39;,&amp;#39;) &amp;gt; -1) {<br /> return val.split(&amp;#39;,&amp;#39;);<br /> }<br /> <br /> if (options.throwOnLimitExceeded &amp;amp;&amp;amp; currentArrayLength &amp;gt;= options.arrayLimit) {<br /> throw new RangeError(&amp;#39;Array limit exceeded. Only &amp;#39; + options.arrayLimit + &amp;#39; element&amp;#39; + (options.arrayLimit === 1 ? &amp;#39;&amp;#39; : &amp;#39;s&amp;#39;) + &amp;#39; allowed in an array.&amp;#39;);<br /> }<br /> <br /> return val;<br /> ```<br /> El `split(&amp;#39;,&amp;#39;)` devuelve el array inmediatamente, omitiendo la verificación de límite subsiguiente. La fusión posterior a través de `utils.combine` no evita la asignación, incluso si marca desbordamientos para arrays dispersos. Esta discrepancia permite a los atacantes enviar un solo parámetro con millones de comas (por ejemplo, `?param=,,,,,,,,...`), asignando arrays masivos en memoria sin activar los límites. Bypassea la intención de `arrayLimit`, que se aplica correctamente para las notaciones indexadas (`a[0]=`) y de corchetes (`a[]=`) (esta última corregida en v6.14.1 según GHSA-6rw7-vpxm-498p).<br /> <br /> ### PoC<br /> Prueba 1 - Bypass básico:<br /> ```<br /> npm install qs<br /> ```<br /> <br /> ```js<br /> const qs = require(&amp;#39;qs&amp;#39;);<br /> <br /> const payload = &amp;#39;a=&amp;#39; + &amp;#39;,&amp;#39;.repeat(25); // 26 elements after split (bypasses arrayLimit: 5)<br /> const options = { comma: true, arrayLimit: 5, throwOnLimitExceeded: true };<br /> <br /> try {<br /> const result = qs.parse(payload, options);<br /> console.log(result.a.length); // Outputs: 26 (bypass successful)<br /> } catch (e) {<br /> console.log(&amp;#39;Limit enforced:&amp;#39;, e.message); // Not thrown<br /> }<br /> ```<br /> Configuración:<br /> - `comma: true`<br /> - `arrayLimit: 5`<br /> - `throwOnLimitExceeded: true`<br /> <br /> Esperado: Lanza el error &amp;#39;Array limit exceeded&amp;#39;.<br /> Real: Analiza con éxito, creando un array de longitud 26.<br /> <br /> ### Impacto<br /> Denegación de Servicio (DoS) a través del agotamiento de memoria.