Scriban Template Engine Vulnerability: Arbitrary CLR Property Writes (Mass Assignment & Setter Bypass)
The Scriban templating engine, specifically its `TypedObjectAccessor`, allows template code to write to arbitrary CLR object properties, including those with `private set`, `internal set`, and `init` modifiers, effectively bypassing intended C# access restrictions. This mass assignment (CWE-915) and access-modifier bypass (CWE-284) vulnerability can lead to unauthorized modification of sensitive host object properties, such as changing `user.is_admin = true`, with changes persisting after template rendering, affecting Scriban versions up to and including 7.2.1, with the `init` bypass specifically impacting .NET 5+.
A critical vulnerability exists in the Scriban templating engine (versions <= 7.2.1) where its TypedObjectAccessor component permits template code to arbitrarily write to properties of Common Language Runtime (CLR) objects pushed into a TemplateContext. This flaw encompasses two primary weaknesses: mass assignment of public setters (CWE-915) and an access-modifier bypass (CWE-284), which allows templates to modify properties declared with private set, internal set, or init modifiers. The TypedObjectAccessor fails to perform CanWrite or setter-visibility checks, enabling reflective invocation of property setters. This means that a malicious template can alter sensitive data or application state, such as elevating privileges (e.g., setting user.is_admin to true) directly on the host application's live objects. The changes persist even after the template rendering completes. The init keyword bypass specifically affects applications running on .NET 5.0 and later versions, while private set and internal set bypasses affect all supported runtimes.
Attack Chain
- Application Embeds Host Object: A host .NET application initializes a Scriban
TemplateContextand pushes a sensitive CLR object (e.g., aUserobject) into it using aScriptObject(e.g.,so["user"] = currentUser; context.PushGlobal(so);). - Malicious Template Content: An attacker introduces or manipulates Scriban template code executed by the host application to include an assignment operation targeting a property of the embedded CLR object (e.g.,
{{ user.IsAdmin = true }},{{ order.TotalPrice = 0 }}, or{{ config.SensitiveSetting = 'malicious_value' }}). - Template Execution Initiated: The host application calls a method like
Render()on the Scriban template, triggering its execution. - Vulnerable Accessor Invocation: During template execution, when the malicious assignment is encountered, Scriban's internal dispatch mechanism calls
TypedObjectAccessor.TrySetValuefor the targeted property. - Setter Visibility Bypass: The
TypedObjectAccessor.TrySetValuemethod, specifically in lines 108–123 ofTypedObjectAccessor.cs, proceeds without verifying theCanWritestatus of the property or checking the visibility (e.g.,private set,internal set) orinitstatus of its setter. - Reflection Property Modification: The vulnerability allows the
propertyAccessor.SetValuemethod to be directly invoked via reflection, bypassing the standard C# access modifiers and developer intent for restricted properties. - Persistent Property Alteration: The live CLR object instance within the host application has its property value permanently altered. These changes persist after the template rendering operation has completed.
- Unauthorized Data Modification/Privilege Escalation: The host application continues execution with the maliciously altered object property, leading to unintended consequences such as unauthorized privilege escalation, data corruption, or configuration changes.
Impact
The vulnerability in Scriban can lead to severe data integrity issues and potential privilege escalation within host applications. By exploiting this flaw, attackers can bypass critical C# access modifiers (private set, internal set, init) and modify any CLR object property exposed to the template engine. This could result in unauthorized administrative access if a user.is_admin property is toggled, financial manipulation if order.total_price is set to zero, or critical system configuration changes. The altered state persists in the live application object, meaning the impact is immediate and enduring until the application state is reset or the object is reloaded, compromising the application's security and reliability.
Recommendation
- Review all Scriban templates and the host application's interaction with
TemplateContextto identify and restrict the types of CLR objects exposed. - As no patch currently exists, implement custom controls as described in the remediation "Fix 2" in the reference advisory: add a
MemberWriteFilteronTemplateContextor use a[ScriptMemberReadOnly]attribute to explicitly control write access to properties. - Regularly monitor the Scriban project's GitHub repository or NuGet feed for a patched version (
nuget/Scriban) and apply it immediately once available. - Review application code for
TypedObjectAccessorusage and its implications, paying close attention to sensitive properties that are pushed into theTemplateContext.