点击蓝字 关注我们
一、写作背景
在Windows系统中,许多应用程序由于业务需求,需要将压缩包下载到本地自动解压。以下是几个常见的应用场景:
1. 版本更新:应用程序需要下载新版本的安装包文件到本地,进行版本更新。
2. 刷机操作:应用程序需要下载ROM包到本地并解压,进行设备刷机。
在这些场景中,当应用程序需要对压缩包进行解压时,必须特别注意解压过程中的安全问题。未经妥善处理的压缩包可能会导致路径穿越、ZIP炸弹等安全风险。
为了防止这些安全问题,应用程序在解压前需要对下载到本地的压缩包进行严格的检测,降低压缩包解压过程中可能带来的安全风险,保护用户的系统和数据安全。
基于以上写作背景,本文对于缓解方案的设计主要是面向Windows应用程序开发人员的。
二、Windows中的压缩包
01
什么是压缩包
压缩包是一种将多个文件和文件夹打包成一个单一文件的技术,目的是减少文件体积和便于传输与存储。常见的压缩包格式包括ZIP、RAR、7Z等。
压缩包不仅可以节省磁盘空间,还能通过减少传输数据量来提高文件传输效率。
02
压缩机制的工作原理
压缩机制通过消除文件中的冗余信息来减小文件大小。
具体来说,压缩算法会寻找文件中的重复数据,并用更短的表示方式替代,从而达到压缩的效果。
常见的压缩算法包括:
1. 无损压缩:这种压缩方式不会丢失任何原始数据,解压后可以完全还原。例如ZIP和7Z格式就采用了无损压缩算法。
2. 有损压缩:这种压缩方式会丢失一部分不太重要的数据,通常用于多媒体文件,如JPEG图片和MP3音频。
03
Windows中对压缩包的支持
在Windows操作系统中,用户可以通过多种方式创建和解压缩压缩包:
1. 内置功能:Windows自带对ZIP格式的支持,用户无需安装任何第三方软件就可以创建和解压ZIP文件。只需右键点击文件或文件夹,选择“发送到”->“压缩(zipped)文件夹”即可创建ZIP压缩包。同样,双击ZIP文件即可浏览和提取内容。
2. 第三方软件:为了支持更多格式和提供更多功能,用户可以安装第三方压缩软件,如WinRAR、7-Zip等。这些软件不仅支持多种压缩格式,还提供密码保护、分卷压缩等高级功能。
04
压缩包的用途
压缩包在日常生活和工作中有广泛的应用,包括但不限于:
1. 文件传输:通过电子邮件、云存储或其他网络传输方式发送大文件时,压缩包可以显著减少传输时间和带宽消耗。
2. 备份和归档:压缩包可以将多个文件和文件夹打包成一个文件,便于备份和归档,同时节省存储空间。
3. 组织和管理文件:通过压缩包将相关文件打包在一起,可以更方便地组织和管理文件,尤其在处理大量数据时尤为有用。
05
小结
压缩包和压缩机制为我们提供了便捷的文件管理和传输方式。
然而,正因为其广泛的使用和强大的功能,压缩包也可能成为网络攻击的载体,带来潜在的安全风险。
在接下来的部分中,我们将深入探讨这些安全问题及其缓解方案。
三、路径穿越
01
问题原理
本文中,只讨论在Windows系统(用户侧)中,因压缩包解压操作可能导致的路径穿越问题。
路径穿越(Path Traversal),也称为目录穿越(Directory Traversal),是一种利用不安全的文件路径处理方式来访问系统上任意文件或目录的攻击手段。攻击者通过在文件路径中插入特殊字符(如../或..),可以绕过预期的目录限制,访问系统上的敏感文件或执行任意代码。
当用户解压一个恶意构造的压缩包时,如果解压软件未能正确处理文件路径,攻击者可以通过路径穿越来将文件解压到任意位置,包括系统目录或用户的个人文件夹。
例如,一个恶意压缩包可能包含以下文件路径:
../../../../Windows/System32/cmd.exe
如果解压软件未能检测和处理这些路径,解压过程将会把cmd.exe文件放置到C:WindowsSystem32目录下,从而覆盖系统文件或植入恶意代码。
02
产生的影响
路径穿越攻击的危害是多方面的,具体包括但不限于:
1. 覆盖关键系统文件:攻击者可以将恶意文件覆盖系统中的关键文件,如cmd.exe、notepad.exe等,从而获得系统的控制权限。
2. 植入恶意软件:通过路径穿越,攻击者可以将恶意软件植入到系统的启动目录或其他敏感位置,使得恶意软件在系统启动时自动运行。
3. 窃取敏感信息:攻击者可以访问和窃取系统上的敏感文件,如配置文件、数据库文件、用户数据等。
4. 破坏系统稳定性:覆盖或删除关键系统文件可能导致系统无法正常工作,甚至崩溃。
03
缓解方案参考
常见的两种缓解方案:
1. 应用程序在解压前,对压缩包中的文件名进行扫描,检查是否存在包含“..”的文件名。
2. 应用程序在解压前,获取压缩包解压后文件的绝对路径,与当前工作目录的路径比较,检查解压后是否存在路径穿越的安全风险。
如果发现存在路径穿越的安全风险,应该立即禁止解压该文件,中断应用程序的解压操作,以防止潜在的安全风险。
考虑到一些应用程序的业务需求,为减少误报情况,本文采用获取解压后文件的绝对路径的方法。
关于ZIP压缩包的示例代码如下:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("请提供至少一个要检查的ZIP文件的路径或包含ZIP文件的目录路径");
return;
}
List<string> zipFiles = new List<string>();
List<string> results = new List<string>();
foreach (string path in args)
{
if (Directory.Exists(path))
{
foreach (string file in Directory.GetFiles(path, "*.zip", SearchOption.AllDirectories))
{
zipFiles.Add(file);
}
}
else if (File.Exists(path) && path.ToLower().EndsWith(".zip"))
{
zipFiles.Add(path);
}
else
{
Console.WriteLine($"路径 {path} 不是一个有效的ZIP文件或目录");
}
}
// 打印所有扫描到的ZIP文件路径
foreach (string zipFile in zipFiles)
{
Console.WriteLine($"正在扫描ZIP文件: {zipFile}");
}
// 检查ZIP文件中的文件名安全性
foreach (string zipFile in zipFiles)
{
results.AddRange(CheckZipFiles(zipFile));
}
// 打印所有检测结果
foreach (string result in results)
{
Console.WriteLine(result);
}
}
static List<string> CheckZipFiles(string zipFilePath)
{
List<string> results = new List<string>();
try
{
using (ZipArchive zip = ZipFile.OpenRead(zipFilePath))
{
foreach (ZipArchiveEntry entry in zip.Entries)
{
if (!IsSafePath(entry.FullName))
{
results.Add($"不安全的文件名: {entry.FullName} 在ZIP文件 {zipFilePath} 中被检测到");
}
}
}
}
catch (InvalidDataException)
{
results.Add($"无法打开ZIP文件: {zipFilePath}");
}
return results;
}
static bool IsSafePath(string entryName)
{
// 获取当前工作目录
string currentDirectory = Directory.GetCurrentDirectory();
// 获取绝对路径
string fullPath = Path.GetFullPath(Path.Combine(currentDirectory, entryName));
// 检查绝对路径是否在当前工作目录之下
return fullPath.StartsWith(currentDirectory, StringComparison.OrdinalIgnoreCase);
}
}
以上示例代码在执行时,可以指定一个或多个路径作为参数。
程序会对指定的一个或多个路径中的zip文件进行扫描,并在不解压的情况下检查是否有路径穿越的恶意zip文件,效果如下图:
关于7Z压缩包的示例代码如下:
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Common;
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("请提供至少一个要检查的7z文件的路径或包含7z文件的目录路径");
return;
}
List<string> sevenZipFiles = new List<string>();
List<string> results = new List<string>();
foreach (string path in args)
{
if (Directory.Exists(path))
{
foreach (string file in Directory.GetFiles(path, "*.7z", SearchOption.AllDirectories))
{
sevenZipFiles.Add(file);
}
}
else if (File.Exists(path) && path.ToLower().EndsWith(".7z"))
{
sevenZipFiles.Add(path);
}
else
{
Console.WriteLine($"路径 {path} 不是一个有效的7z文件或目录");
}
}
// 打印所有扫描到的7z文件路径
foreach (string sevenZipFile in sevenZipFiles)
{
Console.WriteLine($"正在扫描7z文件: {sevenZipFile}");
}
// 检查7z文件中的文件名安全性
foreach (string sevenZipFile in sevenZipFiles)
{
results.AddRange(CheckSevenZipFiles(sevenZipFile));
}
// 打印所有检测结果
foreach (string result in results)
{
Console.WriteLine(result);
}
}
static List<string> CheckSevenZipFiles(string sevenZipFilePath)
{
List<string> results = new List<string>();
try
{
using (var archive = SevenZipArchive.Open(sevenZipFilePath))
{
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory && !IsSafePath(entry.Key))
{
results.Add($"不安全的文件名: {entry.Key} 在7z文件 {sevenZipFilePath} 中被检测到");
}
}
}
}
catch (Exception ex)
{
results.Add($"无法打开7z文件: {sevenZipFilePath},错误: {ex.Message}");
}
return results;
}
static bool IsSafePath(string entryName)
{
// 获取当前工作目录
string currentDirectory = Directory.GetCurrentDirectory();
// 获取绝对路径
string fullPath = Path.GetFullPath(Path.Combine(currentDirectory, entryName));
// 检查绝对路径是否在当前工作目录之下
return fullPath.StartsWith(currentDirectory, StringComparison.OrdinalIgnoreCase);
}
}
参数依然是目标路径,运行效果如下图:
四、ZIP炸弹
01
问题原理
ZIP炸弹(ZIP Bomb),也称为压缩炸弹,是一种特殊构造的压缩包,其设计目的是在解压时占用大量的系统资源(如CPU、内存和磁盘空间),从而导致系统崩溃或服务中断。
这种攻击利用了压缩算法的高压缩比特性,通过嵌套压缩或重复数据等方式,将一个极小的压缩包解压成一个巨大的文件集。
ZIP炸弹的核心在于其巧妙的结构设计,使得解压后的数据量远远超过压缩前的数据量。以下是几种常见的ZIP炸弹构造方式:
1. 重复数据:通过重复相同的数据块,可以将一个小文件压缩成极小的体积。例如,1GB的重复数据可以被压缩成几KB。
2. 嵌套压缩:通过嵌套多个压缩包,可以进一步增加压缩比。例如,一个ZIP文件包含多个ZIP文件,每个ZIP文件又包含多个ZIP文件,最终解压时会产生巨量的数据。
3. 组合方式:结合重复数据和嵌套压缩,可以构造出更加复杂和难以检测的ZIP炸弹。
一个经典的ZIP炸弹示例如下:
42.zip
|
├── file1.zip (包含多个重复数据文件)
├── file2.zip (包含多个重复数据文件)
└── ... (多个嵌套的ZIP文件)
这个仅42KB的42.zip文件在解压后会生成4.5PB(Petabytes)的数据,足以耗尽大部分系统的资源。
02
产生的影响
ZIP炸弹攻击的危害主要体现在以下几个方面:
1. 系统资源耗尽:解压过程中会占用大量的CPU、内存和磁盘空间,可能导致系统崩溃或严重性能下降。
2. 服务中断:对于服务器或在线服务,ZIP炸弹攻击可能导致服务不可用,影响正常的业务运营。
3. 数据丢失:在资源耗尽的情况下,系统可能无法正常处理其他任务,导致数据丢失或损坏。
03
缓解方案
为了防范ZIP炸弹攻击,开发者和用户可以采取以下措施:
1. 使用安全的解压软件:选择那些经过安全审计和更新的解压软件,这些软件通常会包含ZIP炸弹防护机制。
2. 限制解压资源:在解压过程中限制CPU、内存和磁盘空间的使用,防止资源耗尽。
3. 扫描压缩包:在解压前使用安全扫描工具检查压缩包,识别和拒绝潜在的ZIP炸弹。
4. 分段解压:逐步解压文件,并监控系统资源使用情况,及时终止可疑的解压操作。
通过这些预防措施,可以有效降低ZIP炸弹攻击带来的风险,保护系统的稳定性和数据安全。
对于Windows应用程序的开发者,这里给出扫描压缩包方法的参考代码:
在执行解压操作之前,先对压缩包进行扫描。
using System;
using System.IO;
using System.IO.Compression;
using System.Diagnostics;
class Program
{
// 配置最大解压后的文件大小和文件数量限制
private const long MAX_UNCOMPRESSED_SIZE = 100 * 1024 * 1024; // 设置为100 MB
private const int MAX_FILE_COUNT = 100; //设置为100个文件
static void Main(string[] args)
{
// 指定要解压的ZIP文件路径
string zipFilePath = "C:\Users\Administrator\Desktop\zip-test\test.zip";
string zipFileDir = Path.GetDirectoryName(zipFilePath);
try
{
// 初步检查:检查zip文件本身的大小是否超过限制
FileInfo fileInfo = new FileInfo(zipFilePath);
Console.WriteLine("Size of zip: " + fileInfo.Length);
if (fileInfo.Length > MAX_UNCOMPRESSED_SIZE)
{
throw new Exception("File size exceeds limit");
}
Console.WriteLine("Size of zip safe!");
// 解压前检查:估算解压后的总大小和文件数量
(long totalUncompressedSize, int fileCount) = EstimateUncompressedSize(zipFilePath);
Console.WriteLine("Estimate files size: " + totalUncompressedSize);
Console.WriteLine("Estimate files count: " + fileCount);
if (totalUncompressedSize > MAX_UNCOMPRESSED_SIZE)
{
throw new Exception("Estimated uncompressed size exceeds limit");
}
if (fileCount > MAX_FILE_COUNT)
{
throw new Exception("File count exceeds limit");
}
Console.WriteLine("Size of files safe!");
Console.WriteLine("Count of files safe!");
ExtractZipSafely(zipFilePath, zipFileDir);
// 监控资源:监控解压过程中的资源消耗
MonitorResources();
Console.WriteLine("ZIP file extracted successfully!");
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
// 估算解压后的总大小和文件数量
private static (long, int) EstimateUncompressedSize(string zipFilePath)
{
long totalUncompressedSize = 0;
int fileCount = 0;
using (ZipArchive archive = ZipFile.OpenRead(zipFilePath))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
totalUncompressedSize += entry.Length;
fileCount++;
}
}
return (totalUncompressedSize, fileCount);
}
// 检查文件类型是否安全
private static bool IsSafeFileType(string fileName)
{
// 仅允许特定类型的文件(如果需要)
string[] allowedExtensions = { ".txt", ".jpg", ".png", ".pdf" };
string ext = Path.GetExtension(fileName).ToLower();
return Array.Exists(allowedExtensions, e => e == ext);
}
// 检查文件路径是否安全
private static bool IsSafePath(string filePath, string baseDir)
{
// 确保路径在指定的基目录下
return Path.GetFullPath(filePath).StartsWith(baseDir, StringComparison.OrdinalIgnoreCase);
}
// 安全地解压ZIP文件
private static void ExtractZipSafely(string zipFilePath, string extractTo)
{
using (ZipArchive archive = ZipFile.OpenRead(zipFilePath))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
string fileName = entry.FullName;
if (!IsSafeFileType(fileName))
{
throw new Exception($"Unsafe file type detected: {fileName}");
}
Console.WriteLine("File type safe!");
string extractPath = Path.Combine(extractTo, fileName);
if (!IsSafePath(extractPath, extractTo))
{
throw new Exception($"Unsafe file path detected: {extractPath}");
}
Console.WriteLine("File path safe!");
// 创建目录(如果需要)
string directoryPath = Path.GetDirectoryName(extractPath);
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
// 解压文件
entry.ExtractToFile(extractPath);
}
}
}
// 监控资源使用情况
private static void MonitorResources()
{
Process currentProcess = Process.GetCurrentProcess();
if (currentProcess.WorkingSet64 > MAX_UNCOMPRESSED_SIZE)
{
throw new Exception("Memory usage exceeds limit");
}
Console.WriteLine("Memory usage safe!");
if (currentProcess.TotalProcessorTime.TotalMilliseconds > 2000) //此处设定为2000毫秒,可根据需要设定
{
throw new Exception("CPU usage exceeds limit");
}
Console.WriteLine("CPU usage safe!");
}
}
程序会执行的操作:
1. 检查zip文件大小;
2. 估算解压后的文件大小、文件数量;
3. 检查是否是符合要求的文件类型;
4. 检查文件路径是否安全;
5. 监控解压过程中的资源消耗;
6. 安全的解压zip文件。
执行效果如下:
成功解压:
解压失败案例,检测到文件数量过多:
五、其他
在Windows系统中,除了路径穿越和ZIP炸弹之外,压缩包解压过程中还可能面临其他安全问题。以下是一些常见的安全问题及其原理和缓解方案。
01
恶意文件注入
1.1 原理
攻击者可以在压缩包中包含恶意文件,如病毒、木马或恶意脚本。当用户解压并执行这些文件时,恶意代码会在用户系统上运行,可能导致数据泄露、系统损坏或被攻击者控制。
1.2 缓解方案
1. 使用杀毒软件:在下载和解压压缩包前,使用最新的杀毒软件扫描压缩包和解压后的文件。
2. 限制执行权限:避免自动执行解压后的文件,特别是可执行文件(如.exe、.bat等),并限制这些文件的执行权限。
3. 数字签名和来源验证:确保下载的压缩包来自可信来源,并验证文件的数字签名。
02
压缩格式漏洞
2.1原理
某些压缩格式或解压软件可能存在安全漏洞,攻击者可以利用这些漏洞构造特定的压缩包,当用户解压时触发漏洞,导致代码执行或系统崩溃。
2.2缓解方案
1. 保持软件更新:定期更新解压软件和操作系统,以修复已知的安全漏洞。
2. 使用安全审计过的解压软件:选择那些经过安全审计和广泛使用的解压软件,这些软件通常会更安全。
03
文件名欺骗
3.1原理
攻击者可以通过构造特殊的文件名来欺骗用户。例如,使用双扩展名(如document.txt.exe)或看似无害的文件名(如invoice.pdf),诱使用户执行恶意文件。
3.2缓解方案
1. 显示文件扩展名:在Windows资源管理器中启用显示文件扩展名的选项,以便用户能够看到文件的真实扩展名。
2. 谨慎处理可执行文件:避免直接打开从不信任来源下载的可执行文件,特别是那些可能被伪装的文件。
六、总结
在现代计算环境中,压缩包广泛应用于数据传输和存储。然而,压缩包在提高效率的同时,也带来了诸多安全隐患。本文详细探讨了Windows系统中压缩包解压过程中可能面临的主要安全问题,并提供了相应的缓解方案。
通过本文的探讨,我们希望用户能够提高对压缩包安全问题的认识,采取适当的防范措施,保护自己的系统和数据安全。同时,为Windows应用程序的开发者在安全开发方面提供参考。
压缩包虽然方便,但在使用过程中必须保持警惕,避免因忽视安全问题而导致不必要的损失。安全无小事,希望每一位读者都能在日常使用压缩包时,做到安全第一,谨慎操作。
往期精彩合集
● pixel5内核build、pixel6 LineageOS编译、motorola救砖
● 前后端分离架构下 利用SpringBoot确保接口安全性
长
按
关
注
联想GIC全球安全实验室(中国)
原文始发于微信公众号(联想全球安全实验室):Windows中压缩包可能出现的安全问题及相关缓解方案参考