From b198d85a4e7a1c74701784277f1214259e07a160 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 6 Jun 2024 08:08:31 -0700 Subject: [PATCH] blah --- libclamav/ole2_extract.c | 160 +++++++++++++++++++++++++++++++-------- 1 file changed, 128 insertions(+), 32 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 8e1ddf940a..d33bae607f 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -711,6 +711,24 @@ static inline bool is_obfuscated(const fib_base_t * const pFib){ } #pragma GCC diagnostic pop + +typedef struct { + bool velvet_sweatshop; + + bool encrypted; + + const char * encryption_type; + +} encryption_status_t; + +const char * const RC4_ENCRYPTION = "RC4Encryption"; +const char * const XOR_OBFUSCATION = "XORObfuscation"; +const char * const AES128_ENCRYPTION = "EncryptedWithAES128"; +const char * const AES192_ENCRYPTION = "EncryptedWithAES192"; +const char * const AES256_ENCRYPTION = "EncryptedWithAES256"; + + + static uint32_t get_stream_data_offset(ole2_header_t * hdr, const property_t * word_block, uint16_t sector) { uint32_t offset = (1 << hdr->log2_big_block_size); uint32_t sector_size = offset; @@ -730,23 +748,22 @@ fprintf(stderr, "%s::%d::NOT MINISTREAM!!!!!!!!!!\n", __FUNCTION__, __LINE__); } -static bool test_for_encryption(cli_ctx * ctx, const property_t * word_block, ole2_header_t * hdr) { +static bool test_for_encryption(cli_ctx * ctx, const property_t * word_block, ole2_header_t * hdr, encryption_status_t * pEncryptionStatus) { const uint8_t * ptr = NULL; fib_base_t fib = {0}; - bool bRet = false; uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - return bRet; + return false; } ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); if (NULL == ptr){ cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - return bRet; + return false; } copy_fib_base(&fib, ptr); @@ -754,13 +771,14 @@ static bool test_for_encryption(cli_ctx * ctx, const property_t * word_block, ol if (FIB_BASE_IDENTIFIER != fib.wIdent){ cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); - return bRet; + return false; } /*TODO: Look into whether or not it's possible to determine the xor key when * a document is obfuscated with xor * (is_obfuscated function) */ +#if 0 bRet = is_encrypted(&fib); if (bRet){ /*Only inserts metadata if there is encryption.*/ @@ -769,6 +787,15 @@ static bool test_for_encryption(cli_ctx * ctx, const property_t * word_block, ol } return bRet; +#else + pEncryptionStatus->encrypted = is_encrypted(&fib); + + if (is_obfuscated(&fib)) { + pEncryptionStatus->encryption_type = XOR_OBFUSCATION; + } + + return pEncryptionStatus->encrypted; +#endif } @@ -813,10 +840,9 @@ static bool find_file_pass(const uint8_t * const ptr, uint32_t ptr_size, uint32_ * Search for the FilePass structure. * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/cf9ae8d5-4e8c-40a2-95f1-3b31f16b5529 */ -static bool test_for_xls_encryption(cli_ctx * ctx, const property_t * word_block, ole2_header_t * hdr) { +static bool test_for_xls_encryption(cli_ctx * ctx, const property_t * word_block, ole2_header_t * hdr, encryption_status_t * pEncryptionStatus) { uint16_t tmp16; uint32_t idx; - bool bRet = false; uint32_t stream_data_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); @@ -824,38 +850,39 @@ static bool test_for_xls_encryption(cli_ctx * ctx, const property_t * word_block const uint8_t * const ptr = fmap_need_off_once(hdr->map, stream_data_offset, block_size); if (NULL == ptr){ cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", stream_data_offset, stream_data_offset); - return bRet; + return false; } /*Validate keyword*/ idx = 0; if (!read_uint16(ptr, block_size, &idx, &tmp16)){ - return bRet; + return false; } /*Invalid keyword*/ if (2057 != tmp16){ - return bRet; + return false; } /*Skip past this size.*/ memcpy(&tmp16, &(ptr[idx]), 2); if (!read_uint16(ptr, block_size, &idx, &tmp16)){ - return bRet; + return false; } idx += tmp16; if (!find_file_pass(ptr, block_size, &idx)){ - return bRet; + return false; } if (!read_uint16(ptr, block_size, &idx, &tmp16)){ - return bRet; + return false; } #define XLS_XOR_OBFUSCATION 0 #define XLS_RC4_ENCRYPTION 1 +#if 0 bRet = ((XLS_RC4_ENCRYPTION == tmp16) || (XLS_XOR_OBFUSCATION == tmp16)); if (bRet){ /*Only inserts metadata if there is encryption.*/ @@ -864,6 +891,19 @@ static bool test_for_xls_encryption(cli_ctx * ctx, const property_t * word_block } return bRet; +#else + + if (XLS_RC4_ENCRYPTION == tmp16) { + pEncryptionStatus->encryption_type = RC4_ENCRYPTION; + pEncryptionStatus->encrypted = true; + } else if (XLS_XOR_OBFUSCATION == tmp16) { + pEncryptionStatus->encryption_type = XOR_OBFUSCATION; + pEncryptionStatus->encrypted = true; + } + + + return (NULL != pEncryptionStatus->encryption_type); +#endif } @@ -884,7 +924,8 @@ static bool test_for_xls_encryption(cli_ctx * ctx, const property_t * word_block static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t prop_index, ole2_walk_property_tree_file_handler handler, unsigned int rec_level, unsigned int *file_count, - cli_ctx *ctx, unsigned long *scansize, void *handler_ctx) + cli_ctx *ctx, unsigned long *scansize, void *handler_ctx, + encryption_status_t * pEncryptionStatus) { property_t prop_block[4]; int32_t idx, current_block, i, curindex; @@ -969,17 +1010,25 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")){ - test_for_encryption(ctx, &(prop_block[idx]), hdr); + test_for_encryption(ctx, &(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")){ - test_for_xls_encryption(ctx, &(prop_block[idx]), hdr); + test_for_xls_encryption(ctx, &(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")){ - test_for_encryption(ctx, &(prop_block[idx]), hdr); + test_for_encryption(ctx, &(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptionInfo")){ +#if 0 insert_metadata(ctx, "Ole2Encrypted", 1); print_heuristic(ctx, "Heuristics.Encrypted.OLE2"); +#else + pEncryptionStatus->encrypted = true; +#endif } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptedPackage")){ +#if 0 insert_metadata(ctx, "Ole2Encrypted", 1); print_heuristic(ctx, "Heuristics.Encrypted.OLE2"); +#else + pEncryptionStatus->encrypted = true; +#endif } ole2_listmsg("printing ole2 property\n"); @@ -1012,7 +1061,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } hdr->sbat_root_start = prop_block[idx].start_block; if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx); + ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); return ret; @@ -1053,7 +1102,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cli_dbgmsg("OLE2: filesize exceeded\n"); } if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize, handler_ctx); + ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); return ret; @@ -1104,7 +1153,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } else dirname = NULL; if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dirname, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx); + ret = ole2_walk_property_tree(hdr, dirname, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); if (dirname) { @@ -2595,13 +2644,14 @@ static bool initialize_encryption_key( cli_ctx * ctx, const uint8_t *encryptionInfoStreamPtr, size_t remainingBytes, - encryption_key_t *encryptionKey) + encryption_key_t *encryptionKey, + encryption_status_t * pEncryptionStatus) { bool bRet = false; size_t idx = 0; encryption_key_t key; bool bAES = false; - const char * jsonKey = NULL; + //const char * jsonKey = NULL; encryption_info_stream_standard_t encryptionInfo = {0}; uint16_t *encryptionInfo_CSPName = NULL; @@ -2670,7 +2720,7 @@ static bool initialize_encryption_key( goto done; } bAES = true; - jsonKey = "EncryptedWithAES128"; + pEncryptionStatus->encryption_type = AES128_ENCRYPTION; break; case SE_HEADER_EI_AES192: // not implemented @@ -2679,7 +2729,7 @@ static bool initialize_encryption_key( goto done; } bAES = true; - jsonKey = "EncryptedWithAES192"; + pEncryptionStatus->encryption_type = AES192_ENCRYPTION; goto done; case SE_HEADER_EI_AES256: // not implemented @@ -2688,11 +2738,11 @@ static bool initialize_encryption_key( goto done; } bAES = true; - jsonKey = "EncryptedWithAES256"; + pEncryptionStatus->encryption_type = AES256_ENCRYPTION; goto done; case SE_HEADER_EI_RC4: // not implemented - jsonKey = "EncryptedWithRC4"; + pEncryptionStatus->encryption_type = RC4_ENCRYPTION; goto done; default: cli_dbgmsg("ole2: Invalid Algorithm ID: 0x%x\n", encryptionInfo.encryptionInfo.algorithmID); @@ -2783,11 +2833,13 @@ static bool initialize_encryption_key( /*Only want jsonKey to add encryption information if it is NOT encrypted with VelvetSweatshop, * since that will be added either way. */ - jsonKey = NULL; memcpy(encryptionKey, &key, sizeof(encryption_key_t)); bRet = true; done: + if (pEncryptionStatus->encryption_type){ + pEncryptionStatus->encrypted = true; + } #if 0 if (SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) { if (NULL != jsonKey) { @@ -2802,8 +2854,12 @@ static bool initialize_encryption_key( } } #else +#if 0 insert_metadata(ctx, jsonKey, true); insert_metadata(ctx, "EncryptedWithVelvetSweatshop", bRet); +#else + fprintf(stderr, "%s::%d::Move to bottom\n", __FUNCTION__, __LINE__); +#endif #endif #if 0 @@ -2814,11 +2870,15 @@ static bool initialize_encryption_key( } } #else +#if 0 if (jsonKey){ print_heuristic(ctx, "Heuristics.Encrypted.OLE2"); } else if (bRet){ print_heuristic(ctx, "Heuristics.Encrypted.OLE2.VelvetSweatshop"); } +#else + fprintf(stderr, "%s::%d::Move to bottom\n", __FUNCTION__, __LINE__); +#endif #endif return bRet; @@ -2846,6 +2906,28 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil encryption_key_t key; bool bEncrypted = false; size_t encryption_offset = 0; + encryption_status_t encryption_status = {0}; + + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::create a structure to pass to walk_property_tree that keeps track of if the file is encrpyted and if it is VelvetSweatshop\n", __FUNCTION__, __LINE__); cli_dbgmsg("in cli_ole2_extract()\n"); if (!ctx) { @@ -2947,7 +3029,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil bEncrypted = initialize_encryption_key(ctx, &(((const uint8_t *)phdr)[encryption_offset]), hdr.m_length - encryption_offset, - &key); + &key, &encryption_status); #if 0 cli_dbgmsg("Encrypted with VelvetSweatshop: %d\n", bEncrypted); @@ -2968,7 +3050,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil hdr.has_vba = false; hdr.has_xlm = false; hdr.has_image = false; - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize, NULL); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize, NULL, &encryption_status); cli_bitset_free(hdr.bitset); hdr.bitset = NULL; if (!file_count || !(hdr.bitset = cli_bitset_init())) { @@ -2996,7 +3078,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil goto done; } file_count = 0; - ole2_walk_property_tree(&hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2, NULL); + ole2_walk_property_tree(&hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2, NULL, &encryption_status); ret = CL_CLEAN; *files = hdr.U; if (has_vba) { @@ -3013,13 +3095,27 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil /* PASS 2/B : OTF scan */ file_count = 0; if (bEncrypted) { - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf_encrypted, 0, &file_count, ctx, &scansize2, &key); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf_encrypted, 0, &file_count, ctx, &scansize2, &key, &encryption_status); } else { - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2, NULL); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2, NULL, &encryption_status); } } done: + + if (encryption_status.encryption_type) { + insert_metadata(ctx, encryption_status.encryption_type, true); + } else if (!encryption_status.velvet_sweatshop) { + insert_metadata(ctx, "Encrypted", encryption_status.encrypted); + } + insert_metadata(ctx, "EncryptedWithVelvetSweatshop", encryption_status.velvet_sweatshop); + + if (encryption_status.velvet_sweatshop){ + print_heuristic(ctx, "Heuristics.Encrypted.OLE2.VelvetSweatshop"); + } else if (encryption_status.encrypted){ + print_heuristic(ctx, "Heuristics.Encrypted.OLE2"); + } + if (hdr.bitset) { cli_bitset_free(hdr.bitset); }