在趋势科技漏洞研究服务漏洞报告的摘录中,趋势科技研究团队的 Guy Lederfein 和 Jason McFadyen 详细介绍了 Microsoft Windows 中最近修补的远程代码执行漏洞。该漏洞最初是由微软进攻研究与安全工程团队发现的。成功利用该漏洞可能会导致在使用易受攻击的库的应用程序上下文中执行任意代码。以下是他们涵盖 CVE-2024-20697 的文章的一部分,并进行了一些最小的修改。
Microsoft Windows 中包含的 Libarchive 库中存在整数溢出漏洞。该漏洞是由于对用于 Intel E8 预处理(包含在 RAR 存档的压缩数据中)的 RARVM 过滤器的块长度的边界检查不足造成的。
远程攻击者可以通过诱使目标用户提取精心设计的 RAR 存档来利用此漏洞。成功利用该漏洞可能会导致在使用易受攻击的库的应用程序上下文中执行任意代码。
漏洞
RAR 文件格式支持数据压缩、错误恢复和多卷跨越。RAR 格式存在多个版本:RAR1.3、RAR1.5、RAR2、RAR3 和最新版本 RAR5。不同版本的 RAR 使用不同的压缩和解压缩算法。
下面介绍版本 1.5、2.x 和 3.x 使用的 RAR 格式。RAR 存档由一系列可变长度块组成。
每个块都以标头开始。下表是RAR块头的常见结构:
Offset Size Name Description
------ -------- ------------ -------------------------------------------------------------
0x00 2 HeadCRC CRC of block header
0x02 1 HeaderType Block type:
RarBlock Marker (0x72)
ArcHeader (0x73)
FileHeader(0x74)
OldStyle Comment Header(0x75)
OldStyle Authenticity Information (0x76)
OldStyle SubBlock (0x77)
OldStyle Recovery Record (0x78)
OldStyle Authenticity Information (0x79)
SubBlock (0x7a)
EndBlock (0x7b)
0x03 2 Flags Block flags:
SKIP_IF_UNKNOWN (0x4000)
LONG_BLOCK (0x8000)
0x05 2 HeadSize Header size
RarBlock 标记是RAR 存档的第一个块,用作 RAR 格式文件的签名:
Offset Size Name Description
-------- -------- ------------ --------------------------------------------
0x00 2 HeadCRC CRC of block header = 0x6152
0x02 1 HeaderType Block type = 0x72
0x03 2 Flags Block flags = 0x1A21
0x05 2 HeadSize Header size = 0x0007
该块始终在每个 RAR 文件的开头包含以下字节序列:
0x52 0x61 0x72 0x21 0x1A 0x07 0x00 (ASCII: "Rar!x1Ax07x00")
ArcHeader是 RAR 文件中的第二个块,具有以下结构:
Offset Size Name Description
-------- -------- ------------ ---------------------------------
0x00 2 HeadCRC CRC of block header
0x02 1 HeaderType Block type = 0x73
0x03 2 Flags Block flags:
MHD_VOLUME (0x0001)
MHD_COMMENT (0x0002)
MHD_LOCK (0x0004)
MHD_SOLID (0x0008)
MHD_NEWNUMBERING (0x0010)
MHD_AV (0x0020)
MHD_PROTECT (0x0040)
MHD_PASSWORD (0x0080)
MHD_FIRSTVOLUME (0x0100)
0x05 2 HeadSize Block size = 0x000D
0x07 2 HighPosAV
0x09 4 PosAV
ArcHeader块后面跟着一个或多个FileHeader块。这些块具有以下结构:
Offset Size Name Description
-------- -------- ------------ ---------------------------------
0x00 2 HeadCRC CRC of block header
0x02 1 HeaderType Block type = 0x74
0x03 2 Flags Block flags:
LHD_SPLIT_BEFORE (0x0001)
LHD_SPLIT_AFTER (0x0002)
LHD_PASSWORD (0x0004)
LHD_COMMENT (0x0008)
LHD_SOLID (0x0010)
LHD_WINDOWMASK (0x00E0)
LHD_LARGE (0x0100)
LHD_UNICODE (0x0200)
LHD_SALT (0x0400)
LHD_VERSION (0x0800)
LHD_EXTTIME (0x1000)
0x05 2 HeadSize Full header size, not including file data
0x07 4 PackSize Compressed file size (P)
0x0B 4 UnpSize Uncompressed file size
0x0F 1 HostOS Operating system used for archiving:
HOST_MSDOS (0x00)
HOST_OS2 (0x01)
HOST_WIN32 (0x02)
HOST_UNIX (0x03)
HOST_MACOS (0x04)
HOST_BEOS (0x05)
0x10 4 FileHash CRC32 hash of file
0x14 4 FileTime Date and time in MS-DOS format
0x18 1 UnpVer RAR version needed to extract the file
0x19 1 Method Packing method:
Store (0x30)
Fastest (0x31)
Fast (0x32)
Normal (0x33)
Good (0x34)
Best (0x35)
0x1A 2 NameSize Filename size (N)
0x1C 4 FileAttr File attributes (OS-dependant)
0x20 4 HighPackSize High 4 bytes of 64-bit value of compressed file size
Optional - only present if LHD_LARGE flag is set
0x24 4 HighUnpSize High 4 bytes of 64-bit value of uncompressed file size
Optional - only present if LHD_LARGE flag is set
0x28 N FileName File name
0x28+N 8 Salt Salt value
Optional - only present if LHD_SALT flag is set
0x30+N. M ExtTime Precise timing for mtime, ctime, atime, and arctime
Optional - only present if LHD_EXTTIME flag is set
0x30+N+M P Data File data
Data will be compressed if Method > 0x30
请注意,上述偏移量是相对于可选字段的存在而言的。
EndBlock块将表示 RAR 存档的结束。该块具有以下结构:
Offset Size Name Description
-------- -------- ------------ ---------------------------------
0x00 2 HeadCRC CRC of block header
0x02 1 HeaderType Block type = 0x7B
0x03 2 Flags Block flags:
EARC_NEXT_VOLUME (0x0001)
EARC_DATACRC (0x0002)
EARC_REVSPACE (0x0004)
EARC_VOLNUMBER (0x0008)
0x05 2 HeadSize Header size
0x07 4 ArcDataCRC CRC32 hash of RAR archive
Optional - only present if EARC_DATACRC flag is set
0x0B 2 VolNumber Volume number
Optional - only present if EARC_VOLNUMBER flag is set
对于RAR 存档中的每个FileHeader块,如果Method字段未设置为“Store”( 0x30),则 Data 字段将包含压缩文件数据。解压缩方法取决于用于压缩数据的 RAR 版本。提取压缩数据所需的 RAR 版本记录在FileHeader块的UnpVer字段中。
与本报告相关的是 RAR 格式版本 2.9(又名 RAR4)使用的 RAR 提取方法,该方法在UnpVer字段设置为 29 时使用。压缩数据可以使用 Lempel-Ziv (LZ) 算法或使用部分匹配预测 (PPM) 压缩。本报告不会详细描述提取算法,而只是总结了了解漏洞的相关部分。有关提取算法的参考实现,请参阅Unpack::Unpack29()UnRAR源代码中的函数。
当libarchive库尝试从RAR压缩文件中提取文件内容时,如果文件数据被压缩(即Method字段未设置为“Store”),read_data_compressed()则将调用该函数来提取压缩数据。压缩数据由多个块组成,每个块可以使用LZ算法(以块的第一位设置为0表示)或使用PPM压缩(以块的第一位设置为1表示)进行压缩。最初,parse_codes()将调用该函数来解码提取文件数据所需的表。如果遇到使用LZ算法压缩的数据块,expand()将调用该函数来解压缩数据。在该函数中,通过循环expand()调用从压缩数据中读取符号。read_next_symbol()在函数中read_next_symbol(),符号将根据函数中解码的哈夫曼表进行解码parse_codes()。
如果解码后的符号为257,read_filter()则会调用该函数读取RARVM过滤器,其结构如下:
Offset Size Name Description
-------- -------- ------------ ---------------------------------
0x00 1 Flags Filter flags:
LENGTH (0x07)
READ_GLOBAL_DATA (0x08)
READ_REGISTERS (0x10)
READ_BLOCK_LENGTH (0x20)
ADD_258_BLOCK_START (0x40)
READ_FILTER_NUM (0x80)
0x01 1 LengthExt1 Length Extension #1
Optional - only present if LENGTH is set to 6 or 7
0x02 1 LengthExt2 Length Extension #2
Optional - only present if Length is set to 7
0x03 var Code Filter code
请注意,上述偏移量是相对于可选字段的存在而言的。
Code字段大小的计算如下:如果Flags字段的最低3位(称为LENGTH)小于6,则代码大小为(LENGTH+1)。如果LENGTH设置为 6,则代码大小为 ( LengthExt1 + 7 )。如果LENGTH设置为 7,则代码大小为(LengthExt1 << 8) |长度Ext2。计算出代码长度并将代码本身复制到缓冲区后,代码、其长度和过滤器标志将被发送到函数parse_filter()以解析代码部分。
在代码部分中,通过调用函数 来解析数字membr_next_rarvm_number()。该函数读取2位,并根据它们的值,确定读取多少位来解析该值。如果前2位为0,则读取4个值位;如果为1,则读取8个值位;如果为2,则读取16个值位;如果为 3,则将读取 32 个值位。
函数parse_filter()将解析代码部分,其结构如下:
Offset Size Name Description
-------- -------- ---------------- --------------------------------------------------------
0x00 var FilterNum Filter number
Optional - only present if READ_FILTER_NUM flag is set
var var BlockStart Block Start Position
If flag ADD_258_BLOCK_START flag is set,
258 will be added to this value
var var BlockLength Optional - only present if READ_BLOCK_LENGTH flag is set
var var Registers Contains a bit mask of registers of length 7 bits
followed by up to 7 register values
Optional - only present if READ_REGISTERS flag is set
var var ByteCodeLen (L) Length of the following ByteCode field in bytes
var L ByteCode ByteCode to be compiled
请注意,如果未设置READ_REGISTERS标志,则寄存器将被初始化,以便将第 5 个寄存器设置为块长度,该长度可以从代码段读取(如果设置了READ_BLOCK_LENGTH标志),也可以从前一个过滤器的块长度。
解析这些字段后parse_filter(),ByteCode字段及其长度将被发送到该函数compile_program()。在此函数中,验证字节码的第一个字节是否等于字节码中所有其他字节的异或。如果为 true,它将将该结构的指纹字段设置rar_program_code为在完整字节码上运行的 CRC-32 算法的值,并结合左移 32 位的字节码长度。
回到函数中parse_filter(),计算过滤器的所有字段后,将通过调用包含指纹字段和计算的寄存器值的结构来rar_filter初始化该结构。这些值将分别设置为结构体的prog 字段和initialregisters字段。create_filter()rar_program_coderar_filter
一旦过滤器的处理完成,run_filters()就会调用函数来运行解析后的过滤器。该函数使用rar_virtual_machine类型的结构初始化rar_filters结构的vm字段。该结构包含一个寄存器字段(一个 8 个整数的数组)和一个大小为 的内存字段。然后,通过调用 来执行每个过滤器。如果与执行的过滤器关联的rar_program_code结构的指纹字段等于 或,则调用该函数。该函数从初始寄存器数组的第 5 个字段读取块长度。然后,运行循环来替换VM内存中的实例和/或VM内存中的实例,并将块长度用作循环退出条件。0x40004execute_filter()0x35AD5768870x393CD7E57Eexecute_filter_e8()0xE80xE9
Microsoft Windows 中包含的 Libarchive 库中存在整数溢出漏洞。该漏洞是由于对用于 Intel E8 预处理(包含在 RAR 存档的压缩数据中)的 RARVM 过滤器的块长度的边界检查不足造成的。具体来说,如果存档包含一个 RARVM 过滤器,其指纹字段计算为0x35AD576887或0x393CD7E57E,则它将通过调用 来执行execute_filter_e8()。如果过滤器的第 5 个寄存器设置为块长度 4,则该函数中设置为块长度减 5 的循环条件将溢出到0xFFFFFFFF。由于 VM 内存的大小为0x40004,这将导致内存访问超出代表 VM 内存的基于堆的缓冲区的范围。
远程攻击者可以通过诱使目标用户提取精心设计的 RAR 存档来利用此漏洞,其中包含将第 5 个寄存器设置为 4 的 RARVM 过滤器。成功利用该漏洞可能会导致在使用易受攻击的库的应用程序上下文中执行任意代码。
笔记:
• 所有多字节整数均采用小端字节顺序。
• 除非另有说明,所有偏移量和大小均以字节为单位。
• 由于RAR4 格式没有官方文档,因此描述基于UnRAR 和libarchive 源代码。字段名称要么从源代码复制,要么根据功能给出。
检测指导
要检测利用此漏洞的攻击,检测设备必须监视和解析可能发送 RAR 存档的常用端口上的流量,例如 FTP、HTTP、SMTP、IMAP、SMB 和 POP3。
检测设备必须寻找RAR文件的传输并能够解析RAR文件格式。目前,还没有 RAR 文件格式的官方文档。该检测指南基于UnRAR 程序和libarchive 库提供的用于提取 RAR 存档的源代码。
上面详细介绍了 RAR 块头的常见结构。检测设备必须首先查找RarBlock标记,它是 RAR 存档的第一个块,并充当 RAR 格式文件的签名:
Offset Size Name Description
-------- -------- ------------ --------------------------------------------
0x00 2 HeadCRC CRC of block header = 0x6152
0x02 1 HeaderType Block type = 0x72
0x03 2 Flags Block flags = 0x1A21
0x05 2 HeadSize Header size = 0x0007
检测设备可以通过查找以下字节序列来识别该块:
0x52 0x61 0x72 0x21 0x1A 0x07 0x00 ("Rar!x1Ax07x00")
如果找到,设备必须识别 ArcHeader ,它是 RAR 文件中的第二个块,如上所述。ArcHeader块后面跟着一个或多个FileHeader块,其结构也已在上面详细介绍。请注意,上述偏移量是相对于可选字段的存在而言的。
检测设备必须解析每个FileHeader块并检查其 Method 字段。如果 Method 字段的值大于0x30,则检测设备必须检查FileHeader块的 Data 字段,其中包含压缩文件数据。可以使用 Lempel-Ziv (LZ) 算法或使用部分匹配预测(PPM) 压缩来压缩压缩数据。本检测指南不会详细描述提取算法。有关提取算法的参考实现,请参阅UnRAR 源代码中的Unpack::Unpack29()函数。
压缩数据由多个块组成,每个块可以使用LZ算法(以块的第一位设置为0表示)或使用PPM压缩(以块的第一位设置为1表示)进行压缩。检测设备必须根据用于压缩它的算法来提取每个块。如果遇到使用LZ算法压缩的块,则检测设备必须从压缩数据的开头解码霍夫曼表。然后,检测设备必须迭代剩余的压缩数据,并根据生成的霍夫曼表解码每个符号。如果遇到符号257,则必须将以下数据解析为RARVM过滤器,其结构如下:
Offset Size Name Description
-------- -------- ------------ ---------------------------------
0x00 1 Flags Filter flags:
LENGTH (0x07)
READ_GLOBAL_DATA (0x08)
READ_REGISTERS (0x10)
READ_BLOCK_LENGTH (0x20)
ADD_258_BLOCK_START (0x40)
READ_FILTER_NUM (0x80)
0x01 1 LengthExt1 Length Extension #1
Optional - only present if LENGTH is set to 6 or 7
0x02 1 LengthExt2 Length Extension #2
Optional - only present if Length is set to 7
0x03 var Code Filter code
请注意,上述偏移量是相对于可选字段的存在而言的。
然后检测设备必须计算代码字段的大小。Code字段大小的计算如下:如果 Flags 字段的最低 3 位(将称为LENGTH)小于 6,则代码大小为(LENGTH + 1)。如果LENGTH设置为 6,则代码大小为(LengthExt1 + 7)。如果LENGTH设置为 7,则代码大小为(LengthExt1 << 8) |长度Ext2。计算出Code字段的大小后,必须按照以下结构解析Code字段:
Offset Size Name Description
-------- -------- ---------------- --------------------------------------------------------
0x00 var FilterNum Filter number
Optional - only present if READ_FILTER_NUM flag is set
var var BlockStart Block Start Position
If flag ADD_258_BLOCK_START flag is set,
258 will be added to this value
var var BlockLength Optional - only present if READ_BLOCK_LENGTH flag is set
var var Registers Contains a bit mask of registers of length 7 bits
followed by up to 7 register values
Optional - only present if READ_REGISTERS flag is set
var var ByteCodeLen (L) Length of the following ByteCode field in bytes
var L ByteCode ByteCode to be compiled
该结构中的所有数字字段(FilterNum、BlockStart、BlockLength、寄存器值和ByteCodeLen)必须根据RarVM::ReadData()UnRAR 源代码函数中实现的算法读取。该算法读取 2 位数据,表示包含数值的数据位数。请注意,此结构中的某些字段是可选的,并且取决于RARVM 过滤器结构的Flags字段中设置的标志。
提取所有必要字段后,检测设备必须检查以下条件:
• ByteCode字段的 CRC-32 校验0xAD576887和为 且ByteCodeLen字段为OR ByteCode字段0x35的 CRC-32 校验和为 且ByteCodeLen字段为。
• 设置READ_REGISTERS标志,并将 Registers 字段的第 5 个寄存器的值设置为 4 U,或者设置 READ_BLOCK_LENGTH 标志,并将 BlockLength 字段的值设置为 4 。如果同时满足这两个条件,则应将流量视为可疑流量。利用此漏洞的攻击可能正在进行中。
笔记:
• 所有多字节整数均采用小端字节顺序。
• 除非另有说明,所有偏移量和大小均以字节为单位。
结论
Microsoft 于 2024 年 1 月修复了此漏洞,并为其分配了CVE-2024-20697。虽然他们没有建议任何缓解因素,但您可以采取一些额外的措施来帮助防止此错误被利用。这包括不从不受信任的来源提取 RAR 存档文件以及使用本博客“检测指南”部分中提供的指南过滤流量。尽管如此,还是建议应用供应商补丁来彻底解决这个问题。
原文地址:
https://www.zerodayinitiative.com/blog/2024/4/17/cve-2024-20697-windows-libarchive-remote-code-execution-vulnerability
文章参考:
https://clearbluejar.github.io/posts/patch-tuesday-diffing-cve-2024-20696-windows-libarchive-rce/
原文始发于微信公众号(Ots安全):CVE-2024-20697:WINDOWS LIBARCHIVE 远程代码执行漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论