mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2026-06-15 09:06:08 -05:00
Linux/FreeBSD: Prevent mounting volumes on system directories and PATH (CVE-2025-23021, reported by SivertPL @__tfr)
Added security checks to prevent mounting VeraCrypt volumes on system directories (like /usr/bin) or directories in the user's PATH, which could theoretically allow execution of malicious binaries instead of legitimate system binaries. Key changes: - Block mounting on protected system directories (/usr, /bin, /lib, etc.) This restriction cannot be overridden - Block mounting on directories present in user's PATH environment variable This can be overridden with --allow-insecure-mount flag - Add visual warnings (red border, "[INSECURE MODE]") when mounting on PATH directories is allowed - Handle symlinks properly when checking paths - Add new error messages for blocked mount points To override PATH-based restrictions only (system directories remain protected): veracrypt --allow-insecure-mount [options] volume mountpoint Security Impact: Low to Medium The attack requires either: - User explicitly choosing a system directory as mount point instead of using VeraCrypt's default mount points - Or attacker having both filesystem access to modify favorites configuration AND knowledge of the volume password Default mount points are not affected by this vulnerability. Security: CVE-2025-23021
This commit is contained in:
@@ -596,6 +596,17 @@ namespace VeraCrypt
|
||||
if (IsVolumeMounted (*options.Path))
|
||||
throw VolumeAlreadyMounted (SRC_POS);
|
||||
|
||||
if (options.MountPoint && !options.MountPoint->IsEmpty())
|
||||
{
|
||||
// Reject if the mount point is a system directory
|
||||
if (IsProtectedSystemDirectory(*options.MountPoint))
|
||||
throw MountPointBlocked (SRC_POS);
|
||||
|
||||
// Reject if the mount point is in the user's PATH and the user has not explicitly allowed insecure mount points
|
||||
if (!GetAllowInsecureMount() && IsDirectoryOnUserPath(*options.MountPoint))
|
||||
throw MountPointNotAllowed (SRC_POS);
|
||||
}
|
||||
|
||||
Cipher::EnableHwSupport (!options.NoHardwareCrypto);
|
||||
|
||||
shared_ptr <Volume> volume;
|
||||
@@ -828,4 +839,100 @@ namespace VeraCrypt
|
||||
s << GetDefaultMountPointPrefix() << slotNumber;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
bool CoreUnix::IsProtectedSystemDirectory (const DirectoryPath &directory) const
|
||||
{
|
||||
static const char* systemDirs[] = {
|
||||
"/usr",
|
||||
"/bin",
|
||||
"/sbin",
|
||||
"/lib",
|
||||
#ifdef TC_LINUX
|
||||
"/lib32",
|
||||
"/lib64",
|
||||
"/libx32",
|
||||
#endif
|
||||
"/etc",
|
||||
"/boot",
|
||||
"/root",
|
||||
"/proc",
|
||||
"/sys",
|
||||
"/dev",
|
||||
NULL
|
||||
};
|
||||
|
||||
// Resolve any symlinks in the path
|
||||
string path(directory);
|
||||
char* resolvedPathCStr = realpath(path.c_str(), NULL);
|
||||
if (resolvedPathCStr)
|
||||
{
|
||||
path = resolvedPathCStr;
|
||||
free(resolvedPathCStr); // Free the allocated memory
|
||||
}
|
||||
|
||||
// reject of the path is the root directory "/"
|
||||
if (path == "/")
|
||||
return true;
|
||||
|
||||
// Check if resolved path matches any system directory
|
||||
for (int i = 0; systemDirs[i] != NULL; ++i)
|
||||
{
|
||||
if (path == systemDirs[i] || path.find(string(systemDirs[i]) + "/") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CoreUnix::IsDirectoryOnUserPath(const DirectoryPath &directory) const
|
||||
{
|
||||
// Obtain the PATH environment variable
|
||||
const char* pathEnv = UserEnvPATH.c_str();
|
||||
if (!pathEnv[0])
|
||||
return false;
|
||||
|
||||
// Resolve the given directory
|
||||
string dirPath(directory);
|
||||
char* resolvedDir = realpath(dirPath.c_str(), NULL);
|
||||
if (resolvedDir)
|
||||
{
|
||||
dirPath = resolvedDir;
|
||||
free(resolvedDir);
|
||||
}
|
||||
|
||||
// Split PATH and compare each entry
|
||||
stringstream ss(pathEnv);
|
||||
string token;
|
||||
while (getline(ss, token, ':'))
|
||||
{
|
||||
// remove any trailing slashes from the token
|
||||
while (!token.empty() && token.back() == '/')
|
||||
token.pop_back();
|
||||
|
||||
if (token.empty())
|
||||
continue;
|
||||
|
||||
// check if the directory is the same as the entry or a subdirectory
|
||||
if (dirPath == token || dirPath.find(token + "/") == 0)
|
||||
return true;
|
||||
|
||||
// handle the case where the PATH entry is a symlink
|
||||
char* resolvedEntry = realpath(token.c_str(), NULL);
|
||||
if (!resolvedEntry)
|
||||
continue; // skip to the next entry since the path does not exist
|
||||
|
||||
string entryPath(resolvedEntry);
|
||||
free(resolvedEntry);
|
||||
|
||||
// remove any trailing slashes from the token
|
||||
while (!entryPath.empty() && entryPath.back() == '/')
|
||||
entryPath.pop_back();
|
||||
|
||||
// perform check again if the resolved path is different from the original (symlink)
|
||||
if (dirPath == entryPath || dirPath.find(entryPath + "/") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user