From 078d1410dd3307956638d9c90f40ec15032ef01f Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sat, 11 Jan 2025 23:22:40 +0100 Subject: 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 --- src/Core/Unix/CoreService.cpp | 14 ++++- src/Core/Unix/CoreServiceRequest.cpp | 6 ++ src/Core/Unix/CoreServiceRequest.h | 5 +- src/Core/Unix/CoreUnix.cpp | 107 +++++++++++++++++++++++++++++++++++ src/Core/Unix/CoreUnix.h | 2 + 5 files changed, 131 insertions(+), 3 deletions(-) (limited to 'src/Core/Unix') diff --git a/src/Core/Unix/CoreService.cpp b/src/Core/Unix/CoreService.cpp index c2eb2bf0..712dbab4 100644 --- a/src/Core/Unix/CoreService.cpp +++ b/src/Core/Unix/CoreService.cpp @@ -99,6 +99,11 @@ namespace VeraCrypt { shared_ptr request = Serializable::DeserializeNew (inputStream); + // Update Core properties based on the received request + Core->SetUserEnvPATH (request->UserEnvPATH); + Core->ForceUseDummySudoPassword(request->UseDummySudoPassword); + Core->SetAllowInsecureMount(request->AllowInsecureMount); + try { // ExitRequest @@ -283,12 +288,17 @@ namespace VeraCrypt static Mutex mutex; ScopeLock lock (mutex); + // Copy Core properties to the request so that they can be transferred to the elevated process + request.ApplicationExecutablePath = Core->GetApplicationExecutablePath(); + request.UserEnvPATH = Core->GetUserEnvPATH(); + request.UseDummySudoPassword = Core->GetUseDummySudoPassword(); + request.AllowInsecureMount = Core->GetAllowInsecureMount(); + if (request.RequiresElevation()) { request.ElevateUserPrivileges = true; request.FastElevation = !ElevatedServiceAvailable; - request.ApplicationExecutablePath = Core->GetApplicationExecutablePath(); - + while (!ElevatedServiceAvailable) { // Test if the user has an active "sudo" session. diff --git a/src/Core/Unix/CoreServiceRequest.cpp b/src/Core/Unix/CoreServiceRequest.cpp index 900fbcc4..14e2ec28 100644 --- a/src/Core/Unix/CoreServiceRequest.cpp +++ b/src/Core/Unix/CoreServiceRequest.cpp @@ -23,6 +23,9 @@ namespace VeraCrypt ApplicationExecutablePath = sr.DeserializeWString ("ApplicationExecutablePath"); sr.Deserialize ("ElevateUserPrivileges", ElevateUserPrivileges); sr.Deserialize ("FastElevation", FastElevation); + sr.Deserialize ("UserEnvPATH", UserEnvPATH); + sr.Deserialize ("UseDummySudoPassword", UseDummySudoPassword); + sr.Deserialize ("AllowInsecureMount", AllowInsecureMount); } void CoreServiceRequest::Serialize (shared_ptr stream) const @@ -33,6 +36,9 @@ namespace VeraCrypt sr.Serialize ("ApplicationExecutablePath", wstring (ApplicationExecutablePath)); sr.Serialize ("ElevateUserPrivileges", ElevateUserPrivileges); sr.Serialize ("FastElevation", FastElevation); + sr.Serialize ("UserEnvPATH", UserEnvPATH); + sr.Serialize ("UseDummySudoPassword", UseDummySudoPassword); + sr.Serialize ("AllowInsecureMount", AllowInsecureMount); } // CheckFilesystemRequest diff --git a/src/Core/Unix/CoreServiceRequest.h b/src/Core/Unix/CoreServiceRequest.h index c00a865d..77778ca2 100644 --- a/src/Core/Unix/CoreServiceRequest.h +++ b/src/Core/Unix/CoreServiceRequest.h @@ -20,7 +20,7 @@ namespace VeraCrypt { struct CoreServiceRequest : public Serializable { - CoreServiceRequest () : ElevateUserPrivileges (false), FastElevation (false) { } + CoreServiceRequest () : ElevateUserPrivileges (false), FastElevation (false), UseDummySudoPassword (false), AllowInsecureMount (false) { } TC_SERIALIZABLE (CoreServiceRequest); virtual bool RequiresElevation () const { return false; } @@ -29,6 +29,9 @@ namespace VeraCrypt FilePath ApplicationExecutablePath; bool ElevateUserPrivileges; bool FastElevation; + string UserEnvPATH; + bool UseDummySudoPassword; + bool AllowInsecureMount; }; struct CheckFilesystemRequest : CoreServiceRequest diff --git a/src/Core/Unix/CoreUnix.cpp b/src/Core/Unix/CoreUnix.cpp index f6827806..1f2d3125 100644 --- a/src/Core/Unix/CoreUnix.cpp +++ b/src/Core/Unix/CoreUnix.cpp @@ -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; @@ -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; + } } diff --git a/src/Core/Unix/CoreUnix.h b/src/Core/Unix/CoreUnix.h index 0f1ab344..ae26bf0a 100644 --- a/src/Core/Unix/CoreUnix.h +++ b/src/Core/Unix/CoreUnix.h @@ -48,6 +48,8 @@ namespace VeraCrypt virtual void SetFileOwner (const FilesystemPath &path, const UserId &owner) const; virtual DirectoryPath SlotNumberToMountPoint (VolumeSlotNumber slotNumber) const; virtual void WipePasswordCache () const { throw NotApplicable (SRC_POS); } + virtual bool IsProtectedSystemDirectory (const DirectoryPath &directory) const; + virtual bool IsDirectoryOnUserPath(const DirectoryPath &directory) const; protected: virtual DevicePath AttachFileToLoopDevice (const FilePath &filePath, bool readOnly) const { throw NotApplicable (SRC_POS); } -- cgit v1.2.3