Mar 25, 2026·7 min read·56 visits
Scriban versions before 7.0.0 are vulnerable to Denial of Service via unbounded resource consumption. Unauthenticated or low-privileged users can crash the host application by providing crafted template expressions that bypass configured string length and loop limits.
The Scriban .NET templating engine versions prior to 7.0.0 contain three distinct denial-of-service vulnerabilities. These flaws arise from improper enforcement of resource limits during expression evaluation, specifically concerning string multiplication, bitwise shifts, and range enumerations. An attacker with template authoring privileges can exploit these vectors to trigger OutOfMemoryException or CPU exhaustion, resulting in abrupt application termination or degraded performance.
Scriban is a fast, powerful, and safe templating language and engine for .NET, frequently utilized in content management systems, static site generators, and email marketing platforms. The engine exposes a configuration object, TemplateContext, which administrators use to enforce execution limits. These boundaries include LimitToString for string allocations and LoopLimit for maximum iteration counts.
Versions of Scriban prior to 7.0.0 fail to enforce these safety controls across three specific expression evaluation paths. The application trusts the constraints established in TemplateContext but contains implementation flaws that allow certain operations to bypass these checks entirely. This results in unrestricted resource allocation and processing.
An attacker who can supply or modify template content can exploit these paths to cause a Denial of Service (DoS). The vulnerabilities are classified under CWE-400 (Uncontrolled Resource Consumption), CWE-770 (Allocation of Resources Without Limits or Throttling), and CWE-834 (Excessive Iteration). The impact varies depending on the targeted vector but culminates in either process termination or complete thread pool exhaustion.
The attack vector requires no elevated privileges beyond the ability to submit template data. Applications that parse user-provided strings via Template.Parse() without pre-processing validation are inherently vulnerable. The severity is constrained to a 6.5 Medium rating due to the requirement of application-specific template input mechanisms and the lack of confidentiality or integrity impacts.
The first vulnerability vector exists within ScriptBinaryExpression.cs handling the string multiplication operator (string * int). The CalculateToString method attempts to replicate the input string a specified number of times using a standard for loop and a StringBuilder. The implementation lacks a pre-allocation check against context.LimitToString, allowing the StringBuilder to grow until the .NET runtime throws an OutOfMemoryException.
The second vector involves the ShiftLeft operator (<<), implemented in CalculateLongWithInt and CalculateBigIntegerNoFit. Unlike the exponentiation operator (Power), which uses BigInteger.ModPow and enforces a MaxBigInteger boundary, the left-shift operation applies no such limits. Shifting a BigInteger by an extremely large integer value forces the .NET runtime to allocate a backing array large enough to hold the shifted bits, again leading to an immediate OutOfMemoryException.
The third vector exploits range operators (.. and ..<) used in conjunction with built-in array functions. Range operators in Scriban return lazy IEnumerable<object> iterators. When these ranges are passed directly to functions like array.size or array.join, the underlying implementation fully enumerates the iterator. Because this enumeration happens inside the native function rather than the template engine's main execution loop, it never invokes context.StepLoop(). Consequently, the enumeration entirely bypasses the context.LoopLimit protection, causing CPU exhaustion.
The flaw in the string multiplication implementation is located between lines 319 and 334 in src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs. The vulnerable code executes a loop based strictly on user-supplied values without boundary verification.
var leftText = context.ObjectToString(left);
var builder = new StringBuilder();
for (int i = 0; i < value; i++)
{
builder.Append(leftText);
}
return builder.ToString();The fundamental issue here is that context.LimitToString (which defaults to 1MB) is ignored. The patch resolves this by introducing a mathematical boundary check prior to the loop execution. By calculating the expected final size (long)value * leftText.Length, the engine can throw a controlled ScriptRuntimeException before any substantial memory allocation occurs.
// Patched logic
if (context.LimitToString > 0 && (long)value * leftText.Length > context.LimitToString)
{
throw new ScriptRuntimeException(span, "String multiplication would exceed LimitToString");
}The fix for the loop limit bypass requires the engine to track iteration counts within the built-in functions that consume IEnumerable<object>. The patched RangeInclude and RangeExclude structures are updated to increment and verify against context.LoopLimit during every iteration step. This ensures that a range calculation like (0..1000000000) halts after reaching the threshold.
Exploitation relies on passing a single crafted expression into the Template.Parse() method and subsequently calling template.Render(context). The attacker does not need to understand the underlying architecture, only that Scriban is processing the input.
To trigger the string multiplication Out of Memory (OOM) condition, the attacker supplies a template that multiplies a small string by an exceptionally large integer. The code below demonstrates this attack. The default LimitToString configuration of 1,048,576 bytes fails to prevent the approximately 2GB allocation attempt.
var template = Template.Parse("{{ 'AAAA' * 500000000 }}");
var context = new TemplateContext();
template.Render(context); // Throws OutOfMemoryExceptionFor the bitwise shift OOM, the attacker leverages the << operator. The resulting BigInteger allocation bypasses normal numerical limits. The following snippet forces the application to allocate a BigInteger containing 100 million bits (approximately 12.5MB per evaluation, easily scaled by the attacker to exhaust available memory).
var template = Template.Parse("{{ 1 << 100000000 }}");
var context = new TemplateContext();
template.Render(context); // Throws OutOfMemoryExceptionThe CPU exhaustion vector targets the loop limit bypass. By piping an enormous range into the array.size built-in function, the application attempts to enumerate 1 billion items synchronously. This blocks the executing thread indefinitely, starving the application's thread pool and resulting in a denial of service.
var template = Template.Parse("{{ (0..1000000000) | array.size }}");
var context = new TemplateContext();
template.Render(context); // High CPU utilization, hangs executionThe primary consequence of this vulnerability is a complete Denial of Service. In the context of .NET runtime environments, an OutOfMemoryException is typically a fatal error. Modern .NET runtimes generally terminate the process entirely when an OOM exception is thrown by the execution engine, rather than allowing the application to catch and recover from the error.
Process termination affects all users currently interacting with the host application. If the application handles web requests, all active connections are dropped. In environments orchestrated by container managers (e.g., Kubernetes), this leads to continuous pod restarts (CrashLoopBackOff) if the attacker automates the malicious request.
The CPU exhaustion vector introduces a secondary impact model. While it may not crash the process immediately, it ties up application threads. If an attacker submits multiple requests utilizing the loop bypass vector, the .NET thread pool becomes starved. Subsequent legitimate requests will queue and eventually time out, effectively rendering the application unresponsive.
The definitive mitigation for this vulnerability is upgrading the Scriban NuGet package to version 7.0.0 or later. This release introduces comprehensive boundary checks in the abstract syntax tree evaluation logic, explicitly capping string operations, bitwise shifts, and range iterators to the limits defined in TemplateContext.
If immediate patching is not technically feasible, development teams must implement input validation workarounds. The most robust temporary measure involves restricting template authoring capabilities strictly to highly trusted administrators. In multi-tenant environments where user-supplied templates are a core feature, restricting access may not be an option.
In cases where untrusted input must be processed by vulnerable versions, implement a strict pre-compilation regex filter. The filter should scan the template string and reject inputs containing multiplication operators (*), left shift operators (<<), and range operators (.. and ..<). This degrades the functionality of the templating engine but neutralizes the known attack vectors.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Scriban Alexandre Mutel | >= 0.1.0, < 7.0.0 | 7.0.0 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Denial of Service (DoS) |
| CWE ID | CWE-400, CWE-770, CWE-834 |
| CVSS v3.1 Score | 6.5 Medium |
| Attack Vector | Network (Remote) |
| Privileges Required | Low |
| User Interaction | None |
| Exploit Status | Proof of Concept available |
The software does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.
AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.
A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.
CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.
CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.
CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.