ApostropheCMS: Eliminación de miembros del prototipo por exclusión de proyección en @apostrophecms/db-connect causa denegación de servicio
Resumen
@apostrophecms/db-connect incluye una emulación de MongoDB en memoria utilizada por sus adaptadores de SQLite, PostgreSQL y multipostgres. Su función auxiliar unsetNestedField recorre una ruta de campos con puntos controlada por el atacante sin validar los segmentos y ejecuta delete sobre la propiedad resuelta. Un usuario autenticado con pocos privilegios (cualquier rol con view-draft, que poseen todos los editores y colaboradores; reducido a cualquier usuario autenticado si guestApiAccess está habilitado) puede enviar una solicitud GET REST de tipo piece cuyo generador de consultas project contiene una clave de exclusión como constructor.prototype.hasOwnProperty. En un adaptador SQLite o PostgreSQL, esta clave llega a unsetNestedField, que resuelve Object.prototype y elimina un método en tiempo de ejecución compartido. Dado que este método se comparte en todo el proceso de Node, la eliminación interrumpe el procesamiento de solicitudes para todos los inquilinos y usuarios hasta que se reinicie el proceso, provocando una denegación de servicio persistente con una sola solicitud.
Causa raíz
El generador de consultas de proyección solo sanea los valores de proyección y nunca las claves, por lo que una clave arbitraria con puntos llega a la capa de base de datos. En los adaptadores SQLite y PostgreSQL, applyProjection aplica una proyección de exclusión en JavaScript llamando a unsetNestedField(result, field) para cada campo excluido. unsetNestedField divide el campo por . y recorre los segmentos resultantes sin protección: a diferencia de su equivalente setNestedField, ni siquiera bloquea un valor literal __proto__, y ninguna de las funciones auxiliares bloquea constructor o prototype. Dado que el objeto de trabajo es un objeto simple generado por JSON.parse(JSON.stringify(doc)), su constructor resuelve en el Object global, por lo que una ruta como constructor.prototype.<member> alcanza el Object.prototype compartido y elimina un método principal configurable.
Affected functions (repository-relative paths):
- unsetNestedField packages/db-connect/lib/shared.js:119-129 (no segment guard; delete on resolved property)
- applyProjection packages/db-connect/lib/shared.js:164-216 (exclusion branch calls unsetNestedField per excluded key)
Reachability driver (consumer on a SQL adapter):
- project.launder / project.finalize packages/apostrophe/modules/@apostrophecms/doc-type/index.js:2003-2103
- getRestQuery packages/apostrophe/modules/@apostrophecms/piece-type/index.js:1139-1157
- SQLite adapter applyProjection call packages/db-connect/adapters/sqlite.js:906,980
Impacto
Requisitos previos: una cuenta autenticada con view-draft (cualquier rol de edición; cualquier usuario que haya iniciado sesión cuando guestApiAccess: true), y una configuración que utilice un adaptador SQLite, PostgreSQL o multipostgres. El adaptador predeterminado mongodb delega la proyección al controlador real de MongoDB y no se ve afectado. Con estas condiciones, una única solicitud GET manipulada elimina un método de prototipo compartido elegido por el atacante (por ejemplo Object.prototype.hasOwnProperty, toString o un miembro de Array.prototype). Tras la eliminación, las llamadas comunes como obj.hasOwnProperty(...) generan excepciones en todo el proceso de Node, impidiendo atender solicitudes de cualquier usuario hasta que se reinicie.
Prueba de concepto
The finding was reproduced by executing the shipped helper directly against the published db-connect source; a live consumer requires a SQLite or PostgreSQL adapter, an authenticated view-draft session, and at least one matching document.
- Autenticarse como cualquier usuario con
view-draften un sitio cuyo adaptador de base de datos seasqlite,postgresomultipostgres. - Emitir una lectura REST de tipo piece con una proyección de exclusión cuya clave apunte a un método de prototipo compartido.
- Observar que las solicitudes posteriores fallan a nivel de todo el proceso debido a la eliminación del método compartido.
GET /api/v1/@apostrophecms/article?project[constructor.prototype.hasOwnProperty]=0
Cookie: <authenticated view-draft session>
safe benign exclusion {title:0} leaves Object.prototype.hasOwnProperty intact
VULNERABLE exclusion projection deleted Object.prototype.hasOwnProperty (callableBefore=true, brokeEveryObject=true)
VULNERABLE exclusion projection deleted an arbitrary Object.prototype method (__pocMarker)
Cadena de ataque
- Exposición: Un usuario autenticado
view-draftllama al extremo GET RESTGET /api/v1/@apostrophecms/<piece-type>. - Control: La cadena de consulta
project[constructor.prototype.hasOwnProperty]=0es analizada porqs(conallowDotsdesactivado) en una clave propia con puntos literales:{ 'constructor.prototype.hasOwnProperty': '0' }. - Ruta:
getRestQueryaplica primero los generadores de consultas;project.laundercopia la clave de forma literal y fuerza solo el valor afalse;project.finalizeomite el valor falsy conif (!val) continuey conserva la clave; el cursor la almacena como la proyección. - Protección: La anulación
publicApiProjectionsolo se ejecuta sicanAccessApi(req)es falso, por lo que se omite para emisores autenticados; no existe lista de permitidos, validación de segmentos ni protección de prototipos. - Primitiva: En adaptadores SQLite o PostgreSQL,
applyProjectiontoma la rama de exclusión y llama aunsetNestedField(result, 'constructor.prototype.hasOwnProperty'), que resuelveObject.prototypey ejecutadelete Object.prototype.hasOwnProperty. - Resultado: Se elimina un método de prototipo compartido para todo el proceso de Node; las solicitudes fallan para todos los usuarios.
Evidencia de evasión
Se examinaron individualmente las protecciones pertinentes y ninguna bloquea esta ruta. El constructor launder solo sanea valores y retiene claves arbitrarias. La comprobación publicApiProjection depende de !canAccessApi(req), por lo que no se aplica a usuarios autenticados view-draft. El bucle finalize se interrumpe de forma anticipada ante valores falsy. unsetNestedField carece por completo de validación de segmentos; ni constructor ni prototype se bloquean, y constructor.prototype.<member> elude incluso la comprobación parcial __proto__. Dado que el objeto procede de JSON.parse(JSON.stringify(doc)), su constructor es el global Object, y como Object.prototype.hasOwnProperty es configurable, la eliminación se ejecuta con éxito. La corrección previa de prototype pollution solo introdujo un control {__proto__, constructor, prototype} en apos.util.set/get dentro del paquete apostrophe, sin proteger db-connect.
Versiones afectadas
Ecosystem: npmPackage: @apostrophecms/db-connectConfirmed affected range: >= 1.0.0, <= 1.0.2Latest release checked: 1.0.2 (npm registry)Fix status: not fixed
La función no protegida unsetNestedField y la llamada applyProjection son idénticas a nivel de bytes en las versiones estables 1.0.0, 1.0.1 y 1.0.2. Las versiones preliminares (1.0.0-alpha.1, 1.0.0-alpha.2, 1.0.2-alpha.0) contienen el mismo código. La vulnerabilidad requiere un adaptador SQLite, PostgreSQL o multipostgres; el adaptador mongodb por defecto no está afectado. El paquete apostrophe declara @apostrophecms/db-connect@^1.0.1, que resuelve en el paquete afectado 1.0.2.
Corrección sugerida
Garantizar que las rutas de campo no confiables no puedan acceder a prototipos compartidos. Tanto unsetNestedField como setNestedField en packages/db-connect/lib/shared.js deben rechazar rutas que contengan __proto__, constructor o prototype, similar al control unsafePathSegments usado en apostrophe (apos.util.set/get). De forma equivalente, pueden limitarse a propiedades propias (por ejemplo mediante Object.prototype.hasOwnProperty o accesos con Map). Como mitigación temporal, se puede utilizar el adaptador predeterminado mongodb o restringir el acceso a la API REST a roles de total confianza.
Notificado por zx (GitHub: @manus-pi).
Qué hacer
Actualice apostrophe a la versión 4.33.0 o superior y @apostrophecms/db-connect a la versión 1.1.0 o superior. Como mitigación temporal, utilice el adaptador predeterminado mongodb o limite el acceso a los endpoints REST a roles de plena confianza.
Software afectado
Haz clic en una tecnología para ver todas sus vulnerabilidades.
| Plataforma | Paquete | Versiones vulnerables | Corregida en |
|---|---|---|---|
| Paquete de npm | apostrophe | < 4.33.0 | 4.33.0 |
| Paquete de npm | @apostrophecms/db-connect | < 1.1.0 | 1.1.0 |
Referencias
- https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-j5rq-xfvr-p969
- https://github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce
- https://github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350
- https://github.com/apostrophecms/apostrophe
- https://github.com/apostrophecms/apostrophe/releases/tag/@apostrophecms/db-connect@1.1.0
- https://github.com/apostrophecms/apostrophe/releases/tag/apostrophe@4.33.0
- Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H- Debilidad (CWE)
CWE-1321, CWE-400- Plataforma
- npm
- Paquete
apostrophe, @apostrophecms/db-connect- Publicada
- 20.10.2026
- Actualizado
- 20.10.2026 21:23
Fuentes y licencias · Editado o traducido por DockRay; se mantienen los derechos y las licencias de las fuentes.
OSV / GitHub Advisory Database - GHSA-j5rq-xfvr-p969 - CC BY 4.0
Historial de vulnerabilidades: apostrophe
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.
- Entradas en total
- 27
- Primera → última
- 09.2020 10.2026
- Sin corrección
- 4
- Explotada en ataques
- 0
Distribución por gravedad
- Crítica 2
- Alta 8
- Media 13
- Baja 4
Divulgaciones por año
- 2020: 2
- 2021: 2
- 2022: 0
- 2023: 0
- 2024: 0
- 2025: 0
- 2026: 23
-
ApostropheCMS: Eliminación de miembros del prototipo por exclusión de proyección en @apostrophecms/db-connect causa denegación de servicio esta entrada
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.