GHSA-rfx3-98h7-v3xp: SQL Injection

Published Sep 17, 2026
·
Updated

Several code paths in Marten's LINQ provider and tenant-management internals interpolated a runtime, potentially attacker-influenced value into generated SQL as a single-quoted string literal without escaping or parameterization. A value containing a single quote (') could break out of the literal and inject arbitrary SQL.

The primary vector is a Dictionary<,> indexer key used in a Where filter — Where(x => x.Attributes[key] == v) — a common "filter by attribute name" / EAV pattern. It is confirmed with an executed proof-of-concept and yields filter / multi-tenant authorization bypass (returning other tenants' rows) and blind data exfiltration. Where the application permits ;-batched statements (Npgsql default), data modification is also possible.

Affected sinks

- Linq/Members/Dictionaries/DictionaryItemMember.cs — dictionary indexer key (primary, confirmed PoC) - Linq/Members/Dictionaries/DictionaryContainsKeyFilter.cs — Dictionary.ContainsKey(key) (Newtonsoft serializer only; System.Text.Json escapes the quote) - Linq/Parsing/SelectParser.cs — a constant string projected through Select(x => new { L = runtimeString }) - Internal/Operations/DeleteAllForTenant.cs — tenant id reaching per-tenant projection teardown via IEventStore.DeleteProjectionProgressAsync (an in-code comment wrongly claimed the value was validated) - Schema/DatabaseScopedTenantPartitions.cs — tenant id inlined into FOR VALUES IN ('...') partition DDL - Events/Daemon/Internals/EventLoader.cs (defense-in-depth) — per-tenant partition-pruning literal

Proof of concept (primary vector)

Benign: x.Attributes["nonexistent-key"] == "v" returns 0 rows. Attack: x.Attributes["nonexistent' = '' or 1=1 --"] == "v" returns all rows. Generated SQL:

select d.data from public.mtdocdoc as d where d.data -> 'Attributes' ->> 'x' or 1=1 --' = :p0;

Fix

Each sink now escapes embedded single quotes (.Replace("'", "''"), mirroring the existing Ordering.BuildNgramRankExpression) or binds the value as a parameter (DeleteAllForTenant). Where a literal is retained for partition pruning, escaping preserves plan-time pruning while closing the injection. Regression tests lock down each vector.

Workarounds

Until upgrading: do not pass untrusted input as a dictionary indexer key / ContainsKey argument / Select constant, nor as a tenant id into projection teardown or provisioning; and disable multi-statement command batching to limit blast radius.

Credit

Reported privately and responsibly by an external security researcher with an executed PoC. Additional sinks found during the follow-up LINQ-wide audit.

Affected Software

1 affected componentFixes available
nuget/Marten>=7.0.0<=9.12.0
9.13.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade nuget/Marten to a version that resolves this vulnerability.

    Fixed in 9.13.0
  2. Configuration

    Until upgrading, disable multi-statement command batching (the text notes Npgsql default allows ';'-batched statements, enabling data modification).

    Npgsql (multi-statement command batching) multi-statement command batching (allow/disable ';' batched statements) = disable
  3. Compensating control

    Until upgrading, do not pass untrusted input as a dictionary indexer key (e.g., in Where(x => x.Attributes[key] == v)), Dictionary.ContainsKey(key) argument, or Select constant (Select(x => new { L = runtimeString })); and do not pass untrusted tenant id values into tenant-management projection teardown/provisioning.

Event History

Sep 17, 2026
Advisory Published
via GitHub·02:51 PM
Data Sourced
via GitHub·02:51 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which application inputs are most important to review first?

Prioritize runtime values used as Dictionary indexer keys in LINQ Where filters, such as Where(x => x.Attributes[key] == v). Also review Dictionary.ContainsKey(key) when using the Newtonsoft serializer, runtime strings projected through Select, and the referenced tenant-management delete operation.

2

Can this expose data across tenant boundaries?

Yes. The confirmed proof of concept can bypass filters and multi-tenant authorization, returning rows belonging to other tenants. Blind data exfiltration is also confirmed.

3

What does an attacker need to control to exploit the issue?

An attacker needs to influence a runtime value that is interpolated into one of the affected SQL string-literal sinks. A single quote in that value can terminate the generated literal and inject SQL; the primary confirmed vector is an attacker-influenced dictionary key used in a Where filter.

4

Is Dictionary.ContainsKey affected with all JSON serializers?

The Dictionary.ContainsKey(key) sink is identified as affected only with the Newtonsoft serializer. System.Text.Json escapes the quote for that path, but that does not address the separately confirmed dictionary-indexer Where-filter vector.

5

Can exploitation modify data as well as read it?

Data modification is possible where the application permits semicolon-batched statements. The advisory notes that this is the Npgsql default.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203