Jul 6, 2026·6 min read·15 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 security vulnerability in Copier versions 9.5.0 through 9.15.1 allows unauthenticated remote code execution via crafted HTTP requests or local paths containing traversal sequences. The trust-evaluation mechanism compares target repository paths or URLs against trusted prefixes using unnormalized string comparison, while the subsequent fetching mechanism normalizes the path before cloning. Attackers can exploit this asymmetry to bypass security warning prompts and execute arbitrary commands under the local user context.
GeoLens before version 1.2.4 contains multiple critical-tier security vulnerabilities including improper authorization in metadata access, tile cache scope leakage, dataset title enumeration, weak default credentials, and denial of service via STAC POST search.
CVE-2026-55694 is a chained Information Disclosure and Insecure Direct Object Reference (IDOR) vulnerability in Snipe-IT prior to version 8.6.3. The vulnerability allows authenticated, restricted users to completely bypass randomized file-naming security controls, leak the obfuscated filenames of signed End User License Agreements (EULAs), and subsequently download these confidential documents across tenant boundaries.
Snipe-IT is an IT asset/license management system. Prior to 8.6.3, any activated account can request /maintenances/{id} and read maintenance records for assets in the same company without asset or maintenance permission. app/Http/Controllers/MaintenancesController.php show() renders the record without authorize(), while company-scoped route-model binding only prevents access to other companies. Disclosed fields include asset tags, suppliers, purchase costs, notes, and dates. This issue is fixed in version 8.6.3.
A Stored DOM-based Cross-Site Scripting (DOM XSS) vulnerability exists in Snipe-IT versions prior to 8.6.2. The vulnerability occurs when a stored manufacturer or supplier name is converted to CamelCase and rendered within the 'data-selected-count-id' attribute of a table. Client-side JavaScript retrieves this decoded attribute and performs unsafe string concatenation, passing it directly into jQuery's '.after()' method, enabling authenticated attackers to execute arbitrary JavaScript in the victim's session.
CVE-2026-62673 (also known as CVE-2026-62230 and GHSA-vwg3-w8w3-pc79) is a high-severity security bypass vulnerability in the Grav CMS. It permits unauthenticated remote attackers to circumvent directory and file access policies defined in Apache .htaccess. This flaw allows direct retrieval of sensitive configuration files, system-level credentials, and database equivalents from case-insensitive host filesystems.