diff --git a/v2rayN/AmazTool/UpgradeApp.cs b/v2rayN/AmazTool/UpgradeApp.cs index caa269f4..f2ffe522 100644 --- a/v2rayN/AmazTool/UpgradeApp.cs +++ b/v2rayN/AmazTool/UpgradeApp.cs @@ -45,6 +45,8 @@ internal class UpgradeApp var thisAppOldFile = $"{Utils.GetExePath()}.tmp"; File.Delete(thisAppOldFile); var splitKey = "/"; + var allowedBaseDir = Utils.StartupPath(); + var pathComparison = Utils.GetPathComparison(); using var archive = ZipFile.OpenRead(fileName); foreach (var entry in archive.Entries) @@ -65,16 +67,21 @@ internal class UpgradeApp } var fullName = string.Join(splitKey, lst[1..lst.Length]); + var entryOutputPath = Utils.GetPath(fullName); + if (!Utils.IsPathUnderDirectory(allowedBaseDir, entryOutputPath)) + { + Console.WriteLine($"{Resx.Resource.FailedUpgrade} blocked potential path traversal: {entry.FullName}"); + return; + } - if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase)) + if (string.Equals(Utils.GetExePath(), entryOutputPath, pathComparison)) { File.Move(Utils.GetExePath(), thisAppOldFile); } - var entryOutputPath = Utils.GetPath(fullName); Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!); //In the bin folder, if the file already exists, it will be skipped - if (fullName.StartsWith("bin") && File.Exists(entryOutputPath)) + if (fullName.StartsWith("bin", pathComparison) && File.Exists(entryOutputPath)) { continue; } diff --git a/v2rayN/AmazTool/Utils.cs b/v2rayN/AmazTool/Utils.cs index df13ecf6..b7adc346 100644 --- a/v2rayN/AmazTool/Utils.cs +++ b/v2rayN/AmazTool/Utils.cs @@ -24,6 +24,28 @@ internal class Utils return Path.Combine(startupPath, fileName); } + public static bool IsPathUnderDirectory(string baseDir, string targetPath) + { + if (string.IsNullOrWhiteSpace(baseDir) || string.IsNullOrWhiteSpace(targetPath)) + { + return false; + } + + var baseFull = Path.GetFullPath(baseDir); + var targetFull = Path.GetFullPath(targetPath); + + baseFull = baseFull.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; + + return targetFull.StartsWith(baseFull, GetPathComparison()); + } + + public static StringComparison GetPathComparison() + { + return OperatingSystem.IsWindows() + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + } + public static string V2rayN => "v2rayN"; public static void StartV2RayN()