GHSA-RFX3-98H7-V3XP
Vulnerability from github – Published: 2026-09-17 14:51 – Updated: 2026-09-17 14:51Several 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 throughSelect(x => new { L = runtimeString })Internal/Operations/DeleteAllForTenant.cs— tenant id reaching per-tenant projection teardown viaIEventStore.DeleteProjectionProgressAsync(an in-code comment wrongly claimed the value was validated)Schema/DatabaseScopedTenantPartitions.cs— tenant id inlined intoFOR VALUES IN ('...')partition DDLEvents/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.mt_doc_doc 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": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 9.12.0"
},
"package": {
"ecosystem": "NuGet",
"name": "Marten"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "9.13.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-75513"
],
"database_specific": {
"cwe_ids": [
"CWE-89"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-17T14:51:41Z",
"nvd_published_at": "2026-09-16T21:17:13Z",
"severity": "CRITICAL"
},
"details": "Several code paths in Marten\u0027s 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 (`\u0027`) could break out of the literal and inject arbitrary SQL.\n\nThe primary vector is a `Dictionary\u003c,\u003e` indexer **key** used in a `Where` filter \u2014 `Where(x =\u003e x.Attributes[key] == v)` \u2014 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\u0027 rows) and blind data exfiltration. Where the application permits `;`-batched statements (Npgsql default), data modification is also possible.\n\n## Affected sinks\n\n- `Linq/Members/Dictionaries/DictionaryItemMember.cs` \u2014 dictionary indexer key (primary, confirmed PoC)\n- `Linq/Members/Dictionaries/DictionaryContainsKeyFilter.cs` \u2014 `Dictionary.ContainsKey(key)` (Newtonsoft serializer only; System.Text.Json escapes the quote)\n- `Linq/Parsing/SelectParser.cs` \u2014 a constant string projected through `Select(x =\u003e new { L = runtimeString })`\n- `Internal/Operations/DeleteAllForTenant.cs` \u2014 tenant id reaching per-tenant projection teardown via `IEventStore.DeleteProjectionProgressAsync` (an in-code comment wrongly claimed the value was validated)\n- `Schema/DatabaseScopedTenantPartitions.cs` \u2014 tenant id inlined into `FOR VALUES IN (\u0027...\u0027)` partition DDL\n- `Events/Daemon/Internals/EventLoader.cs` (defense-in-depth) \u2014 per-tenant partition-pruning literal\n\n## Proof of concept (primary vector)\n\nBenign: `x.Attributes[\"nonexistent-key\"] == \"v\"` returns 0 rows. Attack: `x.Attributes[\"nonexistent\u0027 = \u0027\u0027 or 1=1 --\"] == \"v\"` returns all rows. Generated SQL:\n\n```\nselect d.data from public.mt_doc_doc as d where d.data -\u003e \u0027Attributes\u0027 -\u003e\u003e \u0027x\u0027 or 1=1 --\u0027 = :p0;\n```\n\n## Fix\n\nEach sink now escapes embedded single quotes (`.Replace(\"\u0027\", \"\u0027\u0027\")`, 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.\n\n## Workarounds\n\nUntil 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.\n\n## Credit\n\nReported privately and responsibly by an external security researcher with an executed PoC. Additional sinks found during the follow-up LINQ-wide audit.",
"id": "GHSA-rfx3-98h7-v3xp",
"modified": "2026-09-17T14:51:41Z",
"published": "2026-09-17T14:51:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/security/advisories/GHSA-rfx3-98h7-v3xp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-75513"
},
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/pull/4911"
},
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/commit/61882d0424854cb48703f08bdb246894ac576bed"
},
{
"type": "PACKAGE",
"url": "https://github.com/JasperFx/marten"
},
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/releases/tag/9.13.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "Marten\u0027s LINQ provider has SQL injection via unescaped string literals"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.