GHSA-xw6w-9jjh-p9cr Scriban has Multiple Denial-of-Service Vectors via Unbounded Resource Consumption During Expression Evaluation
SummaryScriban's expression evaluation contains three distinct code paths that allow an attacker who can supply a template to cause denial of service through unbounded memory allocation or CPU exhaustion. The existing safety controls (`LimitToString`, `LoopLimit`) do not protect these paths, giving applications a false sense of safety when evaluating untrusted templates.DetailsVector 1: Unbounded string multiplicationIn `ScriptBinaryExpression.cs`, the `CalculateToString` method handles the `string * int` operator by looping without any upper bound:The `LimitToString` safety control (default 1MB) does **not** protect this code path. It only applies to `ObjectToString` output conversions in `TemplateContext.Helpers.cs` (lines 101-121), not to intermediate string values constructed inside `CalculateToString`. The `LoopLimit` also does not apply because this is a C# `for` loop, not a template-level loop — `StepLoop()` is never called here.Vector 2: Unbounded BigInteger shift leftThe `CalculateLongWithInt` and `CalculateBigIntegerNoFit` methods handle `ShiftLeft` without any bound on the shift amount:In contrast, the `Power` operator at lines 722 and 795 uses `BigInteger.ModPow(left, right, MaxBigInteger)` to cap results. The `MaxBigInteger` constant (`BigInteger.One << 1024 * 1024`, defined at line 690) already exists but is never applied to shift operations.Vector 3: LoopLimit bypass via range enumeration in builtin functionsThe range operators `..` and `..<` produce lazy `IEnumerable<object>` iterators:When these ranges are consumed by builtin functions, `LoopLimit` is completely bypassed because `StepLoop()` is only called in `ScriptForStatement` and `ScriptWhileStatement` — it is never called in any function under `src/Scriban/Functions/`. For example:`ArrayFunctions.Size` (line 609) calls `.Cast<object>().Count()`, fully enumerating the range`ArrayFunctions.Join` (line 388) iterates with `foreach` and appends to a `StringBuilder` with no size limitPoCVector 1 — String multiplication OOM:Vector 2 — BigInteger shift OOM:Vector 3 — LoopLimit bypass via range + builtin:ImpactAn attacker who can supply a Scriban template (common in CMS platforms, email templating systems, reporting tools, and other applications embedding Scriban) can cause denial of service by crashing the host process via `OutOfMemoryException` or exhausting CPU resources. This is particularly impactful because:Applications relying on the default safety controls (`LoopLimit=1000`, `LimitToString=1MB`) believe they are protected against resource exhaustion from untrusted templates, but these controls have gaps.A single malicious template expression is sufficient — no complex template logic is required.The `OutOfMemoryException` in vectors 1 and 2 typically terminates the entire process, not just the template evaluation.Recommended FixVector 1 — String multiplication: Check `LimitToString` before the loopVector 2 — BigInteger shift: Cap the shift amountVector 3 — Range + builtins: Add iteration counting to range iteratorsPass `TemplateContext` to `RangeInclude`/`RangeExclude` and enforce a limit:Alternatively, validate range size eagerly at creation time: `if (BigInteger.Abs(right - left) > maxRange) throw ...`
CVSS v3.1 score: 6.5