Jul 6, 2026·6 min read·2 visits
Scriban dynamically resolves and modifies restricted C# object properties (private, internal, init) within templates, bypassing language-level access controls and enabling mass assignment attacks.
An improper access control and mass assignment vulnerability in Scriban allows templates to write to arbitrary, restricted CLR properties (including private, internal, and init-only properties) of context-bound objects, causing unexpected state mutations on the host application heap.
Scriban is a high-performance text template engine for .NET. To facilitate dynamic content rendering, developers routinely expose host-level Common Language Runtime (CLR) objects to Scriban scripts using the standard context registration flow. This mapping is designed to allow templates to query object properties and insert runtime data directly into text layouts.\n\nHowever, the mechanism implemented to execute these integrations exposes an unexpected attack surface. During template evaluation, the library translates property-resolution and state-modification directives into reflection calls on the underlying host object. Because of this integration design, any code executing a template can interact with the memory space of registered .NET classes.\n\nThe vulnerability, tracked under GHSA-7jvp-hj45-2f2m, exists due to a critical asymmetry between read-indexing and write-dispatch paths. This design failure allows an attacker with template execution privileges to modify object properties that should remain immutable or inaccessible. The flaw encompasses both Improper Access Control (CWE-284) and Mass Assignment (CWE-915).
The root cause of the flaw resides in the member caching architecture of the TypedObjectAccessor class. During host-object initialization, Scriban maps the object's available public interfaces using the PrepareMembers method. This method identifies valid properties by searching exclusively for the presence of a public getter accessor. Properties containing public getters are stored in an internal cache array named _members.\n\nAn engineering oversight occurs during the write execution path. When a Scriban template processes an assignment operation (such as updating a field value), the execution engine invokes the TrySetValue method. Instead of consulting a separate write-validated registry, TrySetValue queries the getter-populated _members cache. If the target property is present in the cache, Scriban attempts to write the new value to the host object.\n\nTo perform the update, Scriban invokes standard .NET reflection via the PropertyInfo.SetValue interface. Because standard reflection bypasses compile-time safety and access-control checks, the underlying runtime executes writes to properties even if their setter accessors are marked with restrictive modifiers. The framework fails to verify if a valid, accessible setter exists before executing the reflection update.
The structural asymmetry is visible when evaluating the vulnerable implementation of the member accessor. The original code path retrieves properties based solely on getter availability, then utilizes reflection to execute modifications without evaluating setter visibility.\n\nBelow is a comparison highlighting the vulnerable structure against the remediation logic:\n\ncsharp\n// VULNERABLE: TypedObjectAccessor.cs (<= 7.2.1)\npublic bool TrySetValue(TemplateContext context, SourceSpan span, object target, string member, object value) {\n // Resolves based on '_members' cache, which was populated using only getter checks\n if (_members.TryGetValue(member, out var propertyAccessor)) {\n // BUG: Directly invokes reflection, ignoring private/internal/init constraints\n propertyAccessor.SetValue(target, context.ToObject(span, value, propertyAccessor.PropertyType));\n return true;\n }\n return false;\n}\n\n\nThe patched framework addresses this deficiency by validating both the existence and the accessibility level of the target setter. The correction includes explicit evaluation of the C# 9 init modifier, which is represented in intermediate language as a public setter decorated with a mandatory custom compiler modifier.\n\ncsharp\n// PATCHED: TypedObjectAccessor.cs (>= 7.2.2)\npublic bool TrySetValue(TemplateContext context, SourceSpan span, object target, string member, object value) {\n if (_members.TryGetValue(member, out var propertyAccessor)) {\n // Retrieve only the public set method\n var setM = propertyAccessor.GetSetMethod(nonPublic: false);\n if (setM is null) {\n return false; // Block if setter is private, protected, or internal\n }\n\n // Check for C# 9 \"init-only\" modifier (IsExternalInit)\n if (setM.ReturnParameter.GetRequiredCustomModifiers()\n .Any(m => m.FullName == \"System.Runtime.CompilerServices.IsExternalInit\")) {\n return false; // Block modification of init-only properties\n }\n\n propertyAccessor.SetValue(target, context.ToObject(span, value, propertyAccessor.PropertyType));\n return true;\n }\n return false;\n}\n
To exploit the vulnerability, an attacker must first target a host application that executes user-supplied or user-controlled Scriban templates. The host must also expose a CLR object containing sensitive internal state variables. The attack does not require prior authentication if the template execution flow is exposed to unauthenticated web inputs.\n\nThe attacker formats a liquid or scriban assignment statement referencing the protected field of the exposed model. Because the template engine parses the syntax and matches the member name to the getter cache, the security barrier is bypassed. The template processor hands execution to the reflection sink, which rewrites the host object's memory space directly on the C# heap.\n\nThe following diagram details the transaction flow and execution path from template input to state corruption on the heap:\n\nmermaid\ngraph LR\n A[\"Scriban Template Input\"] --> B[\"TemplateContext evaluation\"]\n B --> C[\"TypedObjectAccessor.TrySetValue()\"]\n C --> D[\"_members cache lookup\"]\n D --> E[\"PropertyInfo.SetValue() call\"]\n E --> F[\"Direct Heap Object Modification\"]\n\n\nA representative scenario involves an order processing model containing an immutable price field. When the script executes, the value is updated directly within the application's runtime memory, bypassing standard business logic constraints.
The impact of this vulnerability is significant, exposing host applications to unauthorized state modification and data tampering. By overwriting private and internal state fields, templates can alter execution parameters, skip security checkpoints, or modify system thresholds. This capability invalidates the threat boundaries assumed when hosting untrusted scripts.\n\nA typical vulnerability impact involves the bypass of state invariants. Under standard .NET execution, private or init-only setters guarantee that an object's state cannot be altered post-instantiation. Scriban's behavior neutralizes these platform guarantees, converting secure, read-only data structures into mutable buffers.\n\nThe CVSS v4 score is calculated at 7.7, reflecting a high impact on integrity. Because modifications occur directly in-memory, downstream components processing the compromised object will act on tampered data. This can lead to logical privilege escalation or remote control over application behavior.
The primary and recommended action is upgrading the Scriban NuGet dependency to version 7.2.2 or higher. The updated version implements strict verification of setter access modifiers and actively rejects attempts to write to properties lacking public, mutable setters.\n\nIn environments where updating the package is not immediately viable, developers must implement structural mitigations. This includes mapping data transfer objects (DTOs) specifically configured for template consumption, rather than binding raw business or domain models directly. Ensuring that context-bound objects contain no write-sensitive state will limit the exposure surface.\n\nAdditionally, custom MemberFilters can be registered to explicitly intercept write operations and block unauthorized properties. This provides a containment layer while the system is prepared for library patch deployment.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P| Product | Affected Versions | Fixed Version |
|---|---|---|
Scriban Scriban | <= 7.2.1 | 7.2.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-284, CWE-915 |
| Attack Vector | Network / Context Template Injection |
| CVSS v4 Score | 7.7 |
| Exploit Status | poc |
| CISA KEV Status | Not Listed |
| Impact | Improper Access Control / State Mutation |
The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
A path validation bypass vulnerability exists in the chmod utility of uutils/coreutils before version 0.6.0. The '--preserve-root' safety mechanism relies on a literal string comparison, allowing local users to bypass root directory protection via unnormalized paths (such as '/../' or symbolic links) and recursively alter system-wide permissions.
A critical heap-based out-of-bounds write vulnerability exists in Open Babel 3.1.1 and master commit 530dbfa3 within the parsing of Translation Vectors in the MOPAC output file format parser. By supplying a crafted MOPAC file containing more than three translation vector entries under the Unit Cell Translation block, an attacker can corrupt heap memory. This vulnerability can lead to arbitrary code execution or denial of service.
Adobe ColdFusion versions 2025.9 and 2023.20 and earlier are affected by an Unrestricted Upload of File with Dangerous Type vulnerability (CWE-434). An unauthenticated remote attacker can exploit this flaw to upload malicious ColdFusion Markup Language (CFML) files directly into web-accessible directories. Accessing the uploaded script triggers arbitrary code execution in the security context of the running service account.
CVE-2026-46599 (also identified by Go vulnerability alias GO-2026-5032) is a high-severity denial-of-service vulnerability in the Go image repository, specifically within the TIFF decoder's PackBits decompression engine. A lack of resource limits during the parsing of Run-Length Encoded PackBits streams allows an attacker to construct a crafted TIFF image that achieves significant decompression amplification. This flaw enables an unauthenticated remote attacker to exhaust system resources, leading to an Out-of-Memory crash or a prolonged application hang.
A property shadowing vulnerability exists in protobufjs where schema-derived names can collide with and overwrite runtime-critical internal helper properties. This issue leads to uncaught runtime exceptions and crash-based Denial of Service.
An integer truncation vulnerability (CWE-197) exists in SQLite before version 3.50.2 during the processing of aggregate queries with more than 32,767 distinct column references. This causes an internal 32-bit counter to truncate to a signed 16-bit integer, producing negative values that cause out-of-bounds heap operations in release builds.