CVE-2025-64746 Overview
CVE-2025-64746 is a broken access control vulnerability in Directus, an open-source real-time API and application dashboard for managing SQL database content. Versions prior to 11.13.0 fail to clean up field-level permissions when a field is deleted from a collection. The reference in the directus_permissions table remains intact after deletion. When an administrator later creates a new field using the same name, that field inherits the outdated permission entry. The flaw is tracked under CWE-284 (Improper Access Control) and CWE-863 (Incorrect Authorization).
Critical Impact
Recreated fields silently inherit stale role permissions, potentially exposing sensitive data to unauthorized roles in multi-tenant or production Directus deployments.
Affected Products
- Monospace Directus versions prior to 11.13.0
- Directus deployments using role-based field-level permissions
- Multi-tenant Directus instances where field names may be reused
Discovery Timeline
- 2025-11-13 - CVE-2025-64746 published to NVD
- 2025-11-13 - GitHub Security Advisory GHSA-9x5g-62gj-wqf2 published alongside patch commit 84d7636
- 2026-06-17 - Last updated in NVD database
Technical Details for CVE-2025-64746
Vulnerability Analysis
Directus stores role-based field permissions in the directus_permissions table. Each row references a collection and a comma-separated list of field names. When a field is deleted through the schema API, Directus removes the field metadata but does not prune matching entries from the permissions table. The stale row continues to reference the deleted field name.
When an administrator recreates a field with the same name in the same collection, Directus resolves permissions by matching field names against existing permission rows. The newly created field is treated as governed by the pre-existing (stale) permission entry. Roles that previously had read or write access to the old field silently regain access to the new field, even if the administrator intended the new field to be private or restricted.
Root Cause
The root cause is missing cleanup logic in the fields service. The delete-field transaction updates schema state without issuing a corresponding update against directus_permissions. Combined with permission resolution by field name rather than by immutable identifier, this creates a state divergence between schema truth and permission truth.
Attack Vector
Exploitation requires user interaction, specifically an administrator recreating a field with a previously used name. An attacker with a role that formerly held permissions on the deleted field would automatically obtain equivalent access to the new field once it is created. The impact scales in multi-tenant environments where field names are commonly reused across tenants and administrators assume that deleting a field revokes its permissions.
// Patch from api/src/services/fields.ts
// Adds explicit cleanup of directus_permissions on field deletion
// cleanup permissions for deleted field
const permissionRows: { id: number; collection: string; fields: string }[] = await trx
.select('id', 'collection', 'fields')
.from('directus_permissions')
.whereRaw('?? = ? AND ?? LIKE ?', ['collection', collection, 'fields', '%' + field + '%']);
if (permissionRows.length > 0) {
for (const permissionRow of permissionRows) {
const newFields = permissionRow['fields']
.split(',')
.filter((v) => v !== field)
.join(',');
await trx('directus_permissions')
.update('fields', newFields.length > 0 ? newFields : null)
.where('id', '=', permissionRow['id']);
}
}
Source: Directus commit 84d7636. The fix loads all permission rows referencing the deleted field, removes the field name from the comma-separated list, and writes back either the pruned list or null when empty.
Detection Methods for CVE-2025-64746
Indicators of Compromise
- Rows in directus_permissions referencing field names that no longer exist in the corresponding collection schema.
- Non-admin roles reading or writing to newly created fields shortly after the field was created, without an explicit permission grant action in audit logs.
- Discrepancies between the current schema state and permissions state when comparing the directus_fields and directus_permissions tables.
Detection Strategies
- Query directus_permissions and cross-reference each entry in the fields column against the current schema; flag any field name absent from directus_fields.
- Review the Directus activity log for fields.delete events followed by fields.create events targeting the same collection and field name.
- Audit role permissions before and after schema changes in staging environments to identify unexpected access inheritance.
Monitoring Recommendations
- Enable and centralize Directus activity and revision logs, ingesting them into a SIEM for correlation of schema and permission events.
- Alert on any field creation that occurs on a field name previously deleted within the same collection.
- Periodically export directus_permissions and diff against known-good baselines to detect stale entries.
How to Mitigate CVE-2025-64746
Immediate Actions Required
- Upgrade Directus to version 11.13.0 or later, which includes the cleanup logic in api/src/services/fields.ts.
- Audit the directus_permissions table for stale references to deleted fields and manually remove them.
- Review all roles that previously held field-level permissions and confirm they do not have unintended access to recently recreated fields.
Patch Information
The fix is delivered in Directus 11.13.0 via commit 84d7636. The patch modifies the field deletion transaction to query directus_permissions for rows matching the deleted field and either updates the fields column with the pruned list or sets it to null. Full details are in the GHSA-9x5g-62gj-wqf2 advisory.
Workarounds
- Before deleting a field, manually remove its name from every relevant row in directus_permissions and verify no orphaned references remain.
- Avoid reusing field names in the same collection; use distinct names when recreating fields with different intended access controls.
- Enforce a change-management review that inspects the permissions table after each schema modification.
# Identify stale field references in directus_permissions
# Run against your Directus database (PostgreSQL example)
SELECT p.id, p.collection, p.fields
FROM directus_permissions p
WHERE p.fields IS NOT NULL
AND p.fields <> '*';
# Then manually verify each listed field still exists in directus_fields
# for the same collection before upgrading to 11.13.0.
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

