Intelligence
highVulnerabilityActive

Scriban TemplateContext Cache Poisoning: MemberFilter Bypass via Accessor Reuse

Scriban's TemplateContext caches type accessors by Type alone, ignoring MemberFilter changes. Reusing a context with a tightened filter after a permissive render allows attackers to access members that should be hidden, bypassing sandbox restrictions.

S
Sebastion

CVE References

Affected

Scriban

Vulnerability Description

Scriban's TemplateContext implements a cache for TypedObjectAccessor objects keyed solely by Type, without accounting for the MemberFilter or MemberRenamer configurations. The TypedObjectAccessor constructor precomputes the set of exposed members based on the current filter state. However, TemplateContext.Reset() does not invalidate this cache, meaning that if the same context is reused with a stricter MemberFilter after an initial render with a permissive filter, the old accessor—with its broader member exposure—remains in use. This violates the principle of least privilege that sandboxing policies depend on.

PoC Significance

This vulnerability proves that security boundaries enforced via dynamic policy changes are ineffective when the same TemplateContext instance is reused across multiple render operations. The attack does not require template injection or code execution; it only requires:

  1. A permissive MemberFilter allowing access to sensitive members during an initial render
  2. Subsequent tightening of the MemberFilter on the same context instance
  3. Another template render using the same context

The precomputed member set in the cached accessor makes this 100% reliable—there is no runtime re-evaluation. This is particularly critical in multi-tenant or dynamic policy scenarios where rendering contexts are pooled for performance.

Detection Guidance

Log Indicators:

  • Template renders where TemplateContext.MemberFilter is modified between operations on the same instance
  • Unexpected property access in template output that contradicts current MemberFilter rules
  • Repeated calls to TemplateContext.GetMemberAccessor() that return identical accessor objects despite filter changes

Code Review:

  • Search for patterns reusing TemplateContext instances across filter changes
  • Audit calls to TemplateContext.Reset() to verify cache invalidation expectations
  • Identify scenarios where MemberFilter is tightened post-initialization

Runtime Detection (if instrumentation possible):

  • Monitor _memberAccessors cache hits after MemberFilter modifications
  • Track accessor creation timestamps vs. filter change timestamps

Mitigation Steps

  1. Patch: Update Scriban to a version where TemplateContext.Reset() clears _memberAccessors or where the cache key includes MemberFilter identity
  2. Workaround: Create a fresh TemplateContext for each filter policy change rather than reusing instances
  3. Configuration: If filter changes are dynamic, consider immutable context instances per security policy tier
  4. Code Practice: Avoid reusing TemplateContext across security boundaries; treat context pooling with extreme caution

Risk Assessment

Likelihood: Moderate to high in real-world scenarios because:

  • Scriban docs recommend reusing TemplateContext for performance
  • Multi-tenant SaaS platforms commonly adjust permissions dynamically
  • The vulnerability is silent—no error or warning signals the bypass

Threat Actors: Privileged insiders, multi-tenant attackers, supply-chain adversaries with template control can exploit this to exfiltrate sensitive data (credentials, PII, business logic) that should be filtered from templates. Enterprise usage of Scriban in template engines makes this attractive.