int asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, uint8_t *computed_sha1) { unsigned int content_size; struct cli_asn1 c; const void *content; crtmgr certs; int ret; if (!(engine->dconf->pe & PE_CONF_CERTS)) return CL_VIRUS; if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) return CL_VIRUS; cli_dbgmsg("in asn1_check_mscat (offset: %llu)\n", (long long unsigned)offset); crtmgr_init(&certs); if(crtmgr_add_roots(engine, &certs)) { crtmgr_free(&certs); return CL_VIRUS; } ret = asn1_parse_mscat(map, offset, size, &certs, 1, &content, &content_size, engine); crtmgr_free(&certs); if(ret) return CL_VIRUS; if(asn1_expect_objtype(map, content, &content_size, &c, 0x30)) return CL_VIRUS; if(asn1_expect_obj(map, &c.content, &c.size, 0x06, lenof(OID_SPC_PE_IMAGE_DATA_OBJID), OID_SPC_PE_IMAGE_DATA_OBJID)) return CL_VIRUS; if(asn1_expect_objtype(map, c.next, &content_size, &c, 0x30)) return CL_VIRUS; if(content_size) { cli_dbgmsg("asn1_check_mscat: extra data in content\n"); return CL_VIRUS; } if(asn1_expect_algo(map, &c.content, &c.size, lenof(OID_sha1), OID_sha1)) return CL_VIRUS; if(asn1_expect_obj(map, &c.content, &c.size, 0x04, SHA1_HASH_SIZE, computed_sha1)) return CL_VIRUS; cli_dbgmsg("asn1_check_mscat: file with valid authenicode signature, whitelisted\n"); return CL_CLEAN; }
int crtmgr_add_roots(struct cl_engine *engine, crtmgr *m) { cli_crt *crt; /* * Certs are cached in engine->cmgr. Copy from there. */ if (m != &(engine->cmgr)) { for (crt = engine->cmgr.crts; crt != NULL; crt = crt->next) { if (crtmgr_add(m, crt)) { crtmgr_free(m); return 1; } } return 0; } return 0; }
static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, struct cl_engine *engine) { struct cli_asn1 asn1, deep, deeper; uint8_t sha1[SHA1_HASH_SIZE], issuer[SHA1_HASH_SIZE], md[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE]; const uint8_t *message, *attrs; unsigned int dsize, message_size, attrs_size; cli_crt_hashtype hashtype; cli_crt *x509; void *ctx; int result; int isBlacklisted = 0; cli_dbgmsg("in asn1_parse_mscat\n"); do { if(!(message = fmap_need_off_once(map, offset, 1))) { cli_dbgmsg("asn1_parse_mscat: failed to read pkcs#7 entry\n"); break; } if(asn1_expect_objtype(map, message, &size, &asn1, 0x30)) /* SEQUENCE */ break; /* if(size) { */ /* cli_dbgmsg("asn1_parse_mscat: found extra data after pkcs#7 %u\n", size); */ /* break; */ /* } */ size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x06, lenof(OID_signedData), OID_signedData)) /* OBJECT 1.2.840.113549.1.7.2 - contentType = signedData */ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0xa0)) /* [0] - content */ break; if(size) { cli_dbgmsg("asn1_parse_mscat: found extra data in pkcs#7\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* SEQUENCE */ break; if(size) { cli_dbgmsg("asn1_parse_mscat: found extra data in signedData\n"); break; } size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* INTEGER - VERSION 1 */ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x31)) /* SET OF DigestAlgorithmIdentifier */ break; if(asn1_expect_algo(map, &asn1.content, &asn1.size, lenof(OID_sha1), OID_sha1)) /* DigestAlgorithmIdentifier[0] == sha1 */ break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: only one digestAlgorithmIdentifier is allowed\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* SEQUENCE - contentInfo */ break; /* Here there is either a PKCS #7 ContentType Object Identifier for Certificate Trust List (szOID_CTL) * or a single SPC_INDIRECT_DATA_OBJID */ if( (!embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, 0x06, lenof(OID_szOID_CTL), OID_szOID_CTL)) || (embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, 0x06, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID)) ) break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0xa0)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: found extra data in contentInfo\n"); break; } dsize = deep.size; if(asn1_expect_objtype(map, deep.content, &dsize, &deep, 0x30)) break; if(dsize) { cli_dbgmsg("asn1_parse_mscat: found extra data in content\n"); break; } *hashes = deep.content; *hashes_size = deep.size; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* certificates */ break; dsize = asn1.size; if(dsize) { crtmgr newcerts; crtmgr_init(&newcerts); while(dsize) { if(asn1_get_x509(map, &asn1.content, &dsize, cmgr, &newcerts)) { dsize = 1; break; } } if(dsize) break; if(newcerts.crts) { x509 = newcerts.crts; cli_dbgmsg("asn1_parse_mscat: %u new certificates collected\n", newcerts.items); while(x509) { cli_crt *parent = crtmgr_verify_crt(cmgr, x509); /* Dump the cert if requested before anything happens to it */ if (engine->dconf->pe & PE_CONF_DUMPCERT) { char issuer[SHA1_HASH_SIZE*2+1], subject[SHA1_HASH_SIZE*2+1], serial[SHA1_HASH_SIZE*2+1]; char mod[1024], exp[1024]; int j=1024; fp_toradix_n(&x509->n, mod, 16, j); fp_toradix_n(&x509->e, exp, 16, j); for (j=0; j < SHA1_HASH_SIZE; j++) { sprintf(&issuer[j*2], "%02x", x509->issuer[j]); sprintf(&subject[j*2], "%02x", x509->subject[j]); sprintf(&serial[j*2], "%02x", x509->serial[j]); } cli_dbgmsg_internal("cert subject:%s serial:%s pubkey:%s i:%s %lu->%lu %s %s %s\n", subject, serial, mod, issuer, (unsigned long)x509->not_before, (unsigned long)x509->not_after, x509->certSign ? "cert" : "", x509->codeSign ? "code" : "", x509->timeSign ? "time" : ""); } if(parent) { if (parent->isBlacklisted) { isBlacklisted = 1; cli_dbgmsg("asn1_parse_mscat: Authenticode certificate %s is revoked. Flagging sample as virus.\n", (parent->name ? parent->name : "(no name)")); } x509->codeSign &= parent->codeSign; x509->timeSign &= parent->timeSign; if(crtmgr_add(cmgr, x509)) break; crtmgr_del(&newcerts, x509); x509 = newcerts.crts; continue; } x509 = x509->next; } if(x509) break; if(newcerts.items) cli_dbgmsg("asn1_parse_mscat: %u certificates did not verify\n", newcerts.items); crtmgr_free(&newcerts); } } if(asn1_get_obj(map, asn1.next, &size, &asn1)) break; if(asn1.type == 0xa1 && asn1_get_obj(map, asn1.next, &size, &asn1)) /* crls - unused shouldn't be present */ break; if(asn1.type != 0x31) { /* signerInfos */ cli_dbgmsg("asn1_parse_mscat: unexpected type %02x for signerInfos\n", asn1.type); break; } if(size) { cli_dbgmsg("asn1_parse_mscat: unexpected extra data after signerInfos\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: only one signerInfo shall be present\n"); break; } size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* Version = 1 */ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* issuerAndSerialNumber */ break; dsize = asn1.size; if(asn1_expect_objtype(map, asn1.content, &dsize, &deep, 0x30)) /* issuer */ break; if(map_sha1(map, deep.content, deep.size, issuer)) break; if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x02)) /* serial */ break; if(map_sha1(map, deep.content, deep.size, serial)) break; if(dsize) { cli_dbgmsg("asn1_parse_mscat: extra data inside issuerAndSerialNumber\n"); break; } if(asn1_expect_algo(map, &asn1.next, &size, lenof(OID_sha1), OID_sha1)) /* digestAlgorithm == sha1 */ break; attrs = asn1.next; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* authenticatedAttributes */ break; attrs_size = (uint8_t *)(asn1.next) - attrs; if(attrs_size < 2) { cli_dbgmsg("asn1_parse_mscat: authenticatedAttributes size is too small\n"); break; } dsize = asn1.size; deep.next = asn1.content; result = 0; while(dsize) { struct cli_asn1 cobj; int content; if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x30)) { /* attribute */ dsize = 1; break; } if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, 0x06)) { /* attribute type */ dsize = 1; break; } if(deeper.size != lenof(OID_contentType)) continue; if(!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n"); dsize = 1; break; } if(!memcmp(deeper.content, OID_contentType, lenof(OID_contentType))) content = 0; /* contentType */ else if(!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest))) content = 1; /* messageDigest */ else continue; if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, 0x31)) { /* set - contents */ dsize = 1; break; } if(deep.size) { cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attributes\n"); dsize = 1; break; } if(result & (1<<content)) { cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest appear twice\n"); dsize = 1; break; } if(content == 0) { /* contentType */ if( (!embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, 0x06, lenof(OID_szOID_CTL), OID_szOID_CTL)) || /* cat file */ (embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, 0x06, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID)) /* embedded cat */ ) { dsize = 1; break; } result |= 1; } else { /* messageDigest */ if(asn1_expect_objtype(map, deeper.content, &deeper.size, &cobj, 0x04)) { dsize = 1; break; } if(cobj.size != SHA1_HASH_SIZE) { cli_dbgmsg("asn1_parse_mscat: messageDigest attribute has got the wrong size (%u)\n", cobj.size); dsize = 1; break; } if(!fmap_need_ptr_once(map, cobj.content, SHA1_HASH_SIZE)) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n"); dsize = 1; break; } memcpy(md, cobj.content, SHA1_HASH_SIZE); result |= 2; } if(deeper.size) { cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attribute\n"); dsize = 1; break; } } if(dsize) break; if(result != 3) { cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest are missing\n"); break; } if(asn1_expect_algo(map, &asn1.next, &size, lenof(OID_rsaEncryption), OID_rsaEncryption)) /* digestEncryptionAlgorithm == sha1 */ break; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x04)) /* encryptedDigest */ break; if(asn1.size > 513) { cli_dbgmsg("asn1_parse_mscat: encryptedDigest too long\n"); break; } if(map_sha1(map, *hashes, *hashes_size, sha1)) break; if(memcmp(sha1, md, sizeof(sha1))) { cli_dbgmsg("asn1_parse_mscat: messageDigest mismatch\n"); break; } if(!fmap_need_ptr_once(map, attrs, attrs_size)) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticatedAttributes\n"); break; } ctx = cl_hash_init("sha1"); if (!(ctx)) break; cl_update_hash(ctx, "\x31", 1); cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1); cl_finish_hash(ctx, sha1); if(!fmap_need_ptr_once(map, asn1.content, asn1.size)) { cli_dbgmsg("asn1_parse_mscat: failed to read encryptedDigest\n"); break; } if(!(x509 = crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, CLI_SHA1RSA, sha1, VRFY_CODE))) { cli_dbgmsg("asn1_parse_mscat: pkcs7 signature verification failed\n"); break; } message = asn1.content; message_size = asn1.size; if(!size) { cli_dbgmsg("asn1_parse_mscat: countersignature is missing\n"); break; } if(size && asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa1)) /* unauthenticatedAttributes */ break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside signerInfo\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside unauthenticatedAttributes\n"); break; } size = asn1.size; /* 1.2.840.113549.1.9.6 - counterSignature */ if(asn1_expect_obj(map, &asn1.content, &size, 0x06, lenof(OID_countersignature), OID_countersignature)) break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x31)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside counterSignature\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside unauthenticatedAttributes\n"); break; } size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* Version = 1*/ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* issuerAndSerialNumber */ break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x30)) /* issuer */ break; if(map_sha1(map, deep.content, deep.size, issuer)) break; if(asn1_expect_objtype(map, deep.next, &asn1.size, &deep, 0x02)) /* serial */ break; if(map_sha1(map, deep.content, deep.size, serial)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: extra data inside countersignature issuer\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* digestAlgorithm */ break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x06)) break; if(deep.size != lenof(OID_sha1) && deep.size != lenof(OID_md5)) { cli_dbgmsg("asn1_parse_mscat: wrong digestAlgorithm size\n"); break; } if(!fmap_need_ptr_once(map, deep.content, deep.size)) { cli_dbgmsg("asn1_parse_mscat: failed to read digestAlgorithm OID\n"); break; } if(deep.size == lenof(OID_sha1) && !memcmp(deep.content, OID_sha1, lenof(OID_sha1))) { hashtype = CLI_SHA1RSA; if(map_sha1(map, message, message_size, md)) break; } else if(deep.size == lenof(OID_md5) && !memcmp(deep.content, OID_md5, lenof(OID_md5))) { hashtype = CLI_MD5RSA; if(map_md5(map, message, message_size, md)) break; } else { cli_dbgmsg("asn1_parse_mscat: unknown digest oid in countersignature\n"); break; } if(asn1.size && asn1_expect_obj(map, &deep.next, &asn1.size, 0x05, 0, NULL)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: extra data in countersignature oid\n"); break; } attrs = asn1.next; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* authenticatedAttributes */ break; attrs_size = (uint8_t *)(asn1.next) - attrs; if(attrs_size < 2) { cli_dbgmsg("asn1_parse_mscat: countersignature authenticatedAttributes are too small\n"); break; } result = 0; dsize = asn1.size; deep.next = asn1.content; while(dsize) { int content; if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x30)) { /* attribute */ dsize = 1; break; } if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, 0x06)) { /* attribute type */ dsize = 1; break; } if(deeper.size != lenof(OID_contentType)) /* lenof(contentType) = lenof(messageDigest) = lenof(signingTime) = 9 */ continue; if(!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) { dsize = 1; break; } if(!memcmp(deeper.content, OID_contentType, lenof(OID_contentType))) content = 0; /* contentType */ else if(!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest))) content = 1; /* messageDigest */ else if(!memcmp(deeper.content, OID_signingTime, lenof(OID_signingTime))) content = 2; /* signingTime */ else continue; if(result & (1<<content)) { cli_dbgmsg("asn1_parse_mscat: duplicate field in countersignature\n"); dsize = 1; break; } result |= (1<<content); if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, 0x31)) { /* attribute type */ dsize = 1; break; } if(deep.size) { cli_dbgmsg("asn1_parse_mscat: extra data in countersignature value\n"); dsize = 1; break; } deep.size = deeper.size; switch(content) { case 0: /* contentType = pkcs7-data */ if(asn1_expect_obj(map, &deeper.content, &deep.size, 0x06, lenof(OID_pkcs7_data), OID_pkcs7_data)) deep.size = 1; else if(deep.size) cli_dbgmsg("asn1_parse_mscat: extra data in countersignature content-type\n"); break; case 1: /* messageDigest */ if(asn1_expect_obj(map, &deeper.content, &deep.size, 0x04, (hashtype == CLI_SHA1RSA) ? SHA1_HASH_SIZE : 16, md)) { deep.size = 1; cli_dbgmsg("asn1_parse_mscat: countersignature hash mismatch\n"); } else if(deep.size) cli_dbgmsg("asn1_parse_mscat: extra data in countersignature message-digest\n"); break; case 2: /* signingTime */ { time_t sigdate; /* FIXME shall i use it?! */ if(asn1_get_time(map, &deeper.content, &deep.size, &sigdate)) deep.size = 1; else if(deep.size) cli_dbgmsg("asn1_parse_mscat: extra data in countersignature signing-time\n"); else if(sigdate < x509->not_before || sigdate > x509->not_after) { cli_dbgmsg("asn1_parse_mscat: countersignature timestamp outside cert validity\n"); deep.size = 1; } break; } } if(deep.size) { dsize = 1; break; } } if(dsize) break; if(result != 7) { cli_dbgmsg("asn1_parse_mscat: some important attributes are missing in countersignature\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* digestEncryptionAlgorithm == sha1 */ break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x06)) /* digestEncryptionAlgorithm == sha1 */ break; if(deep.size != lenof(OID_rsaEncryption)) { /* lenof(OID_rsaEncryption) = lenof(OID_sha1WithRSAEncryption) = 9 */ cli_dbgmsg("asn1_parse_mscat: wrong digestEncryptionAlgorithm size in countersignature\n"); break; } if(!fmap_need_ptr_once(map, deep.content, lenof(OID_rsaEncryption))) { cli_dbgmsg("asn1_parse_mscat: cannot read digestEncryptionAlgorithm in countersignature\n"); break; } /* rsaEncryption or sha1withRSAEncryption */ if(memcmp(deep.content, OID_rsaEncryption, lenof(OID_rsaEncryption)) && memcmp(deep.content, OID_sha1WithRSAEncryption, lenof(OID_sha1WithRSAEncryption))) { cli_dbgmsg("asn1_parse_mscat: digestEncryptionAlgorithm in countersignature is not sha1\n"); break; } if(asn1.size && asn1_expect_obj(map, &deep.next, &asn1.size, 0x05, 0, NULL)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: extra data in digestEncryptionAlgorithm in countersignature\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x04)) /* encryptedDigest */ break; if(asn1.size > 513) { cli_dbgmsg("asn1_parse_mscat: countersignature encryptedDigest too long\n"); break; } if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside countersignature\n"); break; } if(!fmap_need_ptr_once(map, attrs, attrs_size)) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticatedAttributes\n"); break; } if(hashtype == CLI_SHA1RSA) { ctx = cl_hash_init("sha1"); if (!(ctx)) break; cl_update_hash(ctx, "\x31", 1); cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1); cl_finish_hash(ctx, sha1); } else { ctx = cl_hash_init("md5"); if (!(ctx)) break; cl_update_hash(ctx, "\x31", 1); cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1); cl_finish_hash(ctx, sha1); } if(!fmap_need_ptr_once(map, asn1.content, asn1.size)) { cli_dbgmsg("asn1_parse_mscat: failed to read countersignature encryptedDigest\n"); break; } if(!crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, hashtype, sha1, VRFY_TIME)) { cli_dbgmsg("asn1_parse_mscat: pkcs7 countersignature verification failed\n"); break; } cli_dbgmsg("asn1_parse_mscat: catalog successfully parsed\n"); if (isBlacklisted) { return 1; } return 0; } while(0); cli_dbgmsg("asn1_parse_mscat: failed to parse catalog\n"); return 1; }