Skip to content
RESEARCH INDEX BREACHROAD / INTELLIGENCE NOTE

Scriban CVE-2026-73060–74795: 15 ways around a template sandbox

A wave of Scriban CVEs shows why LoopLimit and object filters are insufficient. We analyse DoS, CLR property writes and safer template execution.

PUBLIC RESEARCH
AUTHOR
/ Breachroad CEO · OSCP · PNPT
PUBLISHED
16 August 2026
READING TIME
16 min read
TOPIC
Penetration Testing and AppSec
Scriban CVE-2026-73060–74795: 15 ways around a template sandbox

Fifteen CVE records covering Scriban, a template engine for .NET, were published on 16 August 2026. The set consists of CVE-2026-73060, CVE-2026-73061, CVE-2026-73062, CVE-2026-74783–74792 and CVE-2026-74794–74795. This is not one issue copied across many identifiers. The records expose several failure classes: unbounded allocation, loop-limit bypass, process-ending recursion, data crossing reused contexts and writes to CLR object properties that a host believed were protected.

The shared condition matters. Risk appears when an application lets an untrusted party control a template or meaningful data that triggers expensive template operations. Using Scriban only for static, trusted files does not automatically create a remote vulnerability. Reporting products, document generators, low-code interfaces, message personalisation and AI features that produce templates on demand often move that trust boundary further than their designers realise.

The advisory for CVE-2026-73060 lists releases 3.0.0 through 7.2.5 and a fix in 7.2.6. That makes 7.2.6 a useful safe baseline for the modern 7.x line because it also includes earlier corrections shipped in 7.2.0, 7.2.1 and 7.2.2. Organisations must still verify the assembly inside the deployed artefact rather than relying on a project-file declaration.

Why a loop limit was not a security boundary

Scriban provides LoopLimit to bound iterations performed by a template. Different interpreter paths, however, accounted for work differently. A conventional loop could increment the budget while a built-in function, array multiplication, lazy-sequence materialisation or text operation performed expensive work outside that budget.

CVE-2026-73060 concerns multiplication of a lazy sequence. A correction for a normal array checked the predicted result size before doing work, but the related ScriptRange.Multiply path could still execute a huge number of uncharged iterations. The lesson is broader than one method: resource controls must meter the unit of cost rather than a particular syntax construct.

CVE-2026-73062 and several records between CVE-2026-74784 and 74789 describe related asymmetries. An array multiplier, distant insertion index, large integer shift, repeated string padding or iteration hidden inside a built-in function could demand major allocation or CPU time before a result limit was checked. A limit applied after object creation is too late because memory has already been allocated and the process may already be facing OutOfMemoryException.

The architectural conclusion extends beyond Scriban. A sandbox execution budget should cover interpreter steps, element count, size of a single allocation, cumulative output, stack depth and wall-clock time. Any one-dimensional counter lets hostile work move into another dimension.

Recursion that .NET cannot safely recover from

Several records cover deeply nested syntax or cyclic objects. A recursive parser can descend through thousands of parentheses, initialisers and operators. A JSON renderer or generic object renderer may follow a reference back to the same object. Without an effective depth limit, the result is stack exhaustion.

StackOverflowException is particularly dangerous in .NET because a normal application cannot generally turn it into an ordinary failed request. The process terminates, so one template affects every concurrent request handled by that worker. Automated restart reduces the outage duration but does not remediate the vulnerability; an attacker can repeat the trigger after each restart.

CVE-2026-74783 describes a non-enforcing ExpressionDepthLimit, while CVE-2026-74787, 74792, 74794 and 74795 cover different uncontrolled recursion paths. Adding a check to one parser entry point is therefore insufficient. The depth budget must propagate through every recursive function and must also govern serialisation and object-graph rendering.

When a template writes to a live CLR object

CVE-2026-73061 is the most important integrity issue in the set. A host may place a CLR object in TemplateContext so that a template can read a username, order price or report setting. The affected TypedObjectAccessor path allowed the template to modify properties as well as read them. More surprisingly, it did not fully respect setter visibility, so template code could reach properties using private set, internal set or init.

The upstream advisory separates two problems: mass assignment to public setters and the violation of developer intent for restricted setters. A change remains on the live object after rendering finishes. If that object later controls authorisation, pricing, a file path or another business action, impact extends beyond generated text.

This illustrates the dangerous gap between “the template can see an object” and “the template receives a reference to that object”. A safer pattern copies required values into a simple, immutable view model. A template engine should not see ORM entities, session objects, configuration objects or domain models containing methods and writable properties.

Cache and policy crossing requests

CVE-2026-74790 and CVE-2026-74791 reveal hazards in context reuse. An accessor cache was keyed by type without reflecting changes to MemberFilter. After a permissive policy had been used, the same type could retain access to fields that a later, stricter context intended to hide. A related issue left templates cached after TemplateContext.Reset().

In a multi-tenant service, that becomes a request or tenant boundary failure. Resetting an object does not provide clean state when internal caches survive the reset. Context pooling can improve performance, but it needs a formal contract covering what is erased, what is shared and which policy inputs participate in cache keys.

Breachroad recommends treating an untrusted template’s execution context as disposable. If performance measurements truly justify pooling, a security test should alternate privileged and restricted renders and demonstrate that members, objects and loaders from the previous request are unreachable.

What teams should do

First determine whether Scriban appears in the production SBOM and whether any path accepts templates from users, integrations, imports or an AI model. Do not search only for an “edit template” form. A template may arrive through a configuration file, report definition, campaign body, import bundle or generative tool response.

Upgrade the 7.x line to at least 7.2.6 and review transitive dependencies. If a product maintains an older branch, map every CVE to its official affected range and select a supported release containing all relevant fixes. After rebuilding, read the assembly version from the final image and remove old artefacts from deployment caches.

Then separate rendering from the main application process. A dedicated worker needs limits for memory, CPU, execution time, input and output size, with no unnecessary network or filesystem access. A worker crash must not terminate the API or a queue responsible for unrelated jobs. Per-tenant queue quotas prevent one customer from occupying the entire renderer pool.

Pass only an explicit view model containing primitive values into the context. Do not expose secrets because “the filter will hide them”. A filter is a defence-in-depth control, not secret isolation. The strongest control for a sensitive field is its physical absence from the graph reachable by the template.

Detection and regression testing

Monitor render duration, worker memory growth, restart count, rejected budgets and output size. Connect telemetry to a template identifier and tenant, but avoid logging the full template where it may contain personal data or secrets. Abrupt GC growth and process exits without a handled application exception deserve particular attention.

Negative tests should cover each resource family: very deep structures, cyclic models, operations producing large results, lazy sequences and built-in functions. An integrity test must prove that the host object retains its original properties after rendering. An isolation test should use two contexts with different member filters and demonstrate that caches do not cross the boundary.

Do not run these tests in production. Use an isolated environment with hard process limits and assert that rendering ends in a controlled error before exhausting its budget. The objective is to verify the remediation, not discover when a machine fails.

Facts and Breachroad conclusions

It is a fact that fifteen Scriban-related CVE records were published on 16 August and that their documented consequences include denial of service, object-integrity violations and context-isolation bypasses. Fixes arrived across several releases; the latest lazy-sequence issue in this set is corrected in 7.2.6. The records and advisories do not prove widespread exploitation on the public Internet.

Breachroad’s conclusion is that a template library does not become a sandbox merely because it offers a loop limit and member filter. Security needs layers: a current library, a minimal data model, process isolation, multi-dimensional budgets and tests that cross request boundaries.

If your team builds document generators, low-code platforms or AI features that process templates, explore our cybersecurity training for organisations. Real execution boundaries can also be assessed through web application and API penetration testing.

SHARE / COPY