Jul 6, 2026·6 min read·31 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 critical cross-tenant SQL injection vulnerability exists in the TSQL query compiler of Trigger.dev, allowing authenticated users to bypass tenant isolation boundaries and read arbitrary ClickHouse analytics logs and execution payloads belonging to other organizations.
A logical authorization bypass vulnerability exists in Trigger.dev versions prior to 4.5.6. This flaw allows an authenticated client with a low-trust environment API key, such as development or staging, to cancel active worker deployments in a higher-trust environment like production within the same project. The vulnerability occurs because write operations on deployments were scoped solely by project identifier instead of environment identifier.
An in-depth technical analysis of multiple critical security flaws identified in the Vibe-Trading ecosystem (vibe-trading-ai). These issues range from unauthenticated remote command injection via agent tool executions to arbitrary Python execution through dynamic module loading and unsafe Jinja2 template autoescaping, allowing full system compromise.
An arbitrary file read and path traversal vulnerability in the Vibe-Trading platform allows unauthenticated remote attackers to retrieve sensitive configuration files, API keys, and system secrets. The flaw stems from permissive directory checking in path validation tools and a complete lack of input sanitization in the document reader utility. Remediation was introduced in version 0.1.7 by implementing strict path allowlists, forcing user authentication, and dropping root execution privileges within the container environment.
The vibe-trading-ai package prior to version 0.1.7 contains multiple critical security vulnerabilities including unauthenticated remote code execution (RCE) via session message injection, missing authentication on read endpoints, unrestricted file upload, insecure CORS policies, and sensitive key disclosure. Because the application default settings failed open, ran as root within Docker, and bound to all interfaces, remote unauthenticated attackers could compromise host environments containing sensitive trading data.
CVE-2026-18140 is a denial-of-service vulnerability in the Amazon aws-smithy-json Rust crate. Under-validation of recursion depth within the unknown-key skipping path allows a remote, unauthenticated attacker to cause stack exhaustion and process aborts by sending deeply nested JSON arrays.