Coordinated disclosureGHSA-5jx8-p6q2-455gDynamicExpresso · NuGet8.8 High

Proof/CVEs & advisories/GHSA-5jx8-p6q2-455g

GHSA.01 — Advisory credit

A cast to object changed the security answer.

DynamicExpresso blocks reflection-sensitive members unless the host calls EnableReflection(). Casting a System.Type to object moved the same access onto a dynamic call site — where that policy was never applied.

AdvisoryGHSA-5jx8-p6q2-455g
PackageDynamicExpresso.Core
EcosystemNuGet · .NET
Affected2.19.3
Fixed source2.19.4
Fixed package2.19.5
Severity8.8 High
ReporterCharles Vosburgh
Restricted capabilities must remain restricted when a value crosses from static expression typing into dynamic or late-bound dispatch.
Weakness
CWE-470 / CWE-693
Trigger
LateBindObject
Control
direct access blocked
Outcome
process execution
Public-safety boundary.The proof of concept is public in GHSA-5jx8-p6q2-455g and uses a benign command marker. This page documents the policy failure and the remediation boundary; it does not republish an expression payload.

This is an expression-evaluator primitive, not a built-in remote endpoint. Remote reachability depends on a consuming application accepting attacker-controlled expression text.

The published path assumes a consuming application that:

  1. accepts less-trusted DynamicExpresso expression text;
  2. enables InterpreterOptions.LateBindObject;
  3. retains the default primitive/type registrations;
  4. provides a writable object parameter or an equivalent path into late binding;
  5. permits assignment operators; and
  6. has not explicitly enabled reflection.
The expected security property is that LateBindObject changes member-resolution convenience — not the evaluator’s reflection security policy.

DynamicExpresso 2.19.3 installs DisableReflectionVisitor unless reflection is explicitly enabled. That visitor rejects ordinary expression-tree members involving System.Type or System.Reflection.MemberInfo. A direct expression is therefore blocked, and makes a clean negative control:

Blocked — static pathreflection disabled
typeof(string).Assembly     // rejected by DisableReflectionVisitor

The behaviour changes when the same runtime value is first cast to object:

Permitted — dynamic pathLateBindObject enabled
((object)typeof(string)).Assembly   // resolved by the late binder

Parser.cs recognizes the statically typed object receiver and emits Expression.Dynamic nodes using late-binding call-site binders — LateGetMemberCallSiteBinder for member access, LateInvokeMethodCallSiteBinder for invocation. In vulnerable 2.19.3, the implementations in Resolution/LateBinders.cs pass the runtime object to Microsoft’s C# runtime binder without applying the restriction that DisableReflectionVisitor enforces.

The consequence is subtle but fundamental: casting does not change the runtime object’s authority. The value is still a System.Type. Only its expression-tree representation changed.

1

Reflection is disabled by configuration

The host never calls EnableReflection(); the documented policy says reflection-sensitive members are unavailable.

2

A default-registered type is cast to object

Static typing changes; runtime authority does not.

3

Member access becomes a dynamic call site

The static visitor no longer sees a sensitive member expression.

4

The late binder resolves assembly/type facilities

Capabilities that should have been gated behind an explicit opt-in are now reachable.

5

System.Diagnostics.Process is instantiated

A benign marker command starts in the host process context.

The important result is not the specific command string. It is that an evaluator configuration intended to have reflection disabled reached a host capability the documented policy says requires an explicit reflection opt-in.

Negative control

Direct reflection

Blocked when reflection is disabled — the policy is demonstrably active.

Vulnerable path

Late-bound equivalent

The same runtime value reaches the marker through the 2.19.3 dynamic dispatch path.

Fixed control

2.19.5 package

Benign late binding still works; explicit reflection still works when the host opts in; the bypass does not.

CONFIG reflection disabled · LateBindObject enabled CONTROL typeof(string).Assembly → blocked BYPASS ((object)typeof(string)).Assembly → resolved REACHED System.Diagnostics.Process in host context FIXED 2.19.5: bypass blocked, benign late binding intact MARKER=DEXPRESSO_LATEBIND_REFLECTION

DynamicExpresso had two dispatch paths with different security semantics. Statically typed member and method nodes passed through the reflection-denial visitor; dynamic call-site binders created for LateBindObject did not enforce the same restriction.

The reflection policy therefore depended on the representation of the operation rather than the runtime capability being accessed. This is a common policy-bypass pattern in interpreters: the main AST path is guarded, but an alternate dynamic dispatch mechanism reaches the same sink outside the checker.

The issue was never that System.Type became safe after a cast. It was that the security policy stopped following the value when dispatch moved to a different implementation path.
The SecHive research loop

Ask what else can reach the sink.

A reflection-denial visitor is a strong control on the path it inspects. The productive question was which other dispatch mechanism reaches the same runtime capability without passing through it.

SK.01

Source mapping

Read the reflection-denial visitor and enumerate the dispatch paths that bypass expression-tree inspection.

SK.02

Representation hypothesis

Test whether a type cast that preserves runtime authority changes the security decision.

SK.03

Runtime construction

Reach a host capability through the late-bound path with reflection still disabled.

SK.04

Skeptic gate

Prove the direct form is blocked, so the late-bound result is a policy bypass and not a missing control.

HUMAN

Promotion and disclosure

Verify package/tag mapping and prerequisites before publishing severity.

ScopeValueBasis
Public affected version2.19.3advisory scope
Patched version2.19.4advisory scope · verified fixed source
First fixed package executed2.19.52.19.4 NuGet artifact unavailable at verification time
Fixing commit23f7c3ee9e348a46c969190379597092033a8bd5v2.19.4 and v2.19.5 tags resolve to it

The project states that 2.19.5 republishes the same code because of a NuGet issue, and the repository metadata confirms both tags map to one commit.

The LateBindObject feature itself is older — public history traces it to 4aabda0330d7f003c4e893008b560abaf7c305ea in v2.8.0. That is implementation lineage, not an expanded affected-version claim; earlier packages were not independently runtime-assessed.

High
Published advisory severity
8.8
CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

When the prerequisites are present, attacker-controlled expression text can escape the reflection restriction and reach command execution with the privileges of the hosting .NET process — files, credentials, environment data, network access and other application resources available to it. DynamicExpresso itself exposes no network listener; the real remote surface is determined by the integrating application.

Upgrade to DynamicExpresso 2.19.4 or later. For NuGet consumers, use a currently available fixed package such as 2.19.5 or newer.

The essential fix is to enforce reflection policy inside late-binding resolution rather than assuming the static expression visitor sees every sensitive access. Any future alternate dispatch mechanism should apply the same capability checks.

Regression coverage should combine:

  1. reflection disabled + direct System.Type access — must fail;
  2. reflection disabled + object cast + late binding — must also fail;
  3. ordinary non-sensitive late binding — should continue to work;
  4. reflection explicitly enabled — documented behaviour should continue to work;
  5. dynamic method calls as well as dynamic property/member reads.
2026-08-20
GHSA-5jx8-p6q2-455g and the public PoC published.
AUGUST 2026
2.19.4 source fix published; 2.19.5 republished the same code for NuGet availability.
2026-08-26
Public source, package metadata, vulnerable PoC and fixed controls reviewed.
  1. GHSA-5jx8-p6q2-455g — DynamicExpresso advisory and public PoC
  2. DynamicExpresso repository and release history
  3. DynamicExpresso.Core package listing

Credit: Discovered and reported by Charles Vosburgh. Research was AI-assisted through source mapping, hypothesis generation, release comparison and evidence organization; final validation, disclosure coordination, severity calibration and publication review remained human.