Media

Scriban has Multiple Denial-of-Service Vectors via Unbounded Resource Consumption During Expression Evaluation

  • Publicada: 24.03.2026
  • Actualizado: 11.10.2026 11:23

Summary

Scriban'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.

Details

Vector 1: Unbounded string multiplication

In ScriptBinaryExpression.cs, the CalculateToString method handles the string * int operator by looping without any upper bound:

csharp
// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:319-334
var leftText = context.ObjectToString(left);
var builder = new StringBuilder();
for (int i = 0; i < value; i++)
{
    builder.Append(leftText);
}
return builder.ToString();

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 left

The CalculateLongWithInt and CalculateBigIntegerNoFit methods handle ShiftLeft without any bound on the shift amount:

csharp
// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:710-711
case ScriptBinaryOperator.ShiftLeft:
    return (BigInteger)left << (int)right;
csharp
// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:783-784
case ScriptBinaryOperator.ShiftLeft:
    return left << (int)right;

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 functions

The range operators .. and ..< produce lazy IEnumerable<object> iterators:

csharp
// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:401-417
private static IEnumerable<object> RangeInclude(BigInteger left, BigInteger right)
{
    if (left < right)
    {
        for (var i = left; i <= right; i++)
        {
            yield return FitToBestInteger(i);
        }
    }
    // ...
}

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 limit

PoC

Vector 1 — String multiplication OOM:

csharp
var template = Template.Parse("{{ 'AAAA' * 500000000 }}");
var context = new TemplateContext();
// context.LimitToString is 1048576 by default — does NOT protect this path
template.Render(context); // OutOfMemoryException: attempts ~2GB allocation

Vector 2 — BigInteger shift OOM:

csharp
var template = Template.Parse("{{ 1 << 100000000 }}");
var context = new TemplateContext();
template.Render(context); // Allocates BigInteger with 100M bits (~12.5MB)
// {{ 1 << 2000000000 }} attempts ~250MB

Vector 3 — LoopLimit bypass via range + builtin:

csharp
var template = Template.Parse("{{ (0..1000000000) | array.size }}");
var context = new TemplateContext();
// context.LoopLimit is 1000 — does NOT protect builtin function iteration
template.Render(context); // CPU exhaustion: enumerates 1 billion items
csharp
var template = Template.Parse("{{ (0..10000000) | array.join ',' }}");
var context = new TemplateContext();
template.Render(context); // Memory exhaustion: builds ~80MB+ joined string

Impact

An 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:

  1. 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.
  2. A single malicious template expression is sufficient — no complex template logic is required.
  3. The OutOfMemoryException in vectors 1 and 2 typically terminates the entire process, not just the template evaluation.

Recommended Fix

Vector 1 — String multiplication: Check LimitToString before the loop

csharp
// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs, before line 330
var leftText = context.ObjectToString(left);
if (context.LimitToString > 0 && (long)value * leftText.Length > context.LimitToString)
{
    throw new ScriptRuntimeException(span,
        $"String multiplication would exceed LimitToString ({context.LimitToString} characters)");
}
var builder = new StringBuilder();
for (int i = 0; i < value; i++)

Vector 2 — BigInteger shift: Cap the shift amount

csharp
// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs, lines 710-711 and 783-784
case ScriptBinaryOperator.ShiftLeft:
    if (right > 1048576) // Same as MaxBigInteger bit count
        throw new ScriptRuntimeException(span,
            $"Shift amount {right} exceeds maximum allowed (1048576)");
    return (BigInteger)left << (int)right;

Vector 3 — Range + builtins: Add iteration counting to range iterators

Pass TemplateContext to RangeInclude/RangeExclude and enforce a limit:

csharp
private static IEnumerable<object> RangeInclude(TemplateContext context, BigInteger left, BigInteger right)
{
    var maxRange = context.LoopLimit > 0 ? context.LoopLimit : int.MaxValue;
    int count = 0;
    if (left < right)
    {
        for (var i = left; i <= right; i++)
        {
            if (++count > maxRange)
                throw new ScriptRuntimeException(context.CurrentNode.Span,
                    $"Range enumeration exceeds LoopLimit ({maxRange})");
            yield return FitToBestInteger(i);
        }
    }
    // ... same for descending branch
}

Alternatively, validate range size eagerly at creation time: if (BigInteger.Abs(right - left) > maxRange) throw ...

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
.NET (NuGet) Scriban < 7.0.0 7.0.0
.NET (NuGet) Scriban.Signed < 7.0.0 7.0.0

Referencias

Puntuación CVSS 6.5/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Debilidad (CWE)
CWE-400
Plataforma
.NET (NuGet)
Paquete
Scriban, Scriban.Signed
Publicada
24.03.2026
Actualizado
11.10.2026 11:23

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-xw6w-9jjh-p9cr - CC BY 4.0

Historial de vulnerabilidades: Scriban.Signed

Todas las vulnerabilidades publicadas de este paquete (.NET (NuGet)), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (14) en el archivo
Entradas en total
14
Primera → última
03.2026 06.2026
Sin corrección
0
Explotada en ataques
0

Distribución por gravedad

  • Crítica 1
  • Alta 8
  • Media 5

Divulgaciones por año

  1. 2026: 14
Gravedad:
  1. Scriban has Multiple Denial-of-Service Vectors via Unbounded Resource Consumption During Expression Evaluation esta entrada
    Media · 6.5 CVE-2026-74785 < 7.0.0 7.0.0

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.