Browse code

Multiple blacklist sigs can now match with allmatch

Also, move the cert-related DCONF cfg checks to more
appropriate locations. One change in behavior:

PE_CONF_CATALOG will disable loading trusted hashes from
.cat files, but won't disable Authenticode hash checking
completely (PE_CONF_CERTS does this).

Andrew authored on 2019/02/19 06:04:46
Showing 7 changed files
... ...
@@ -1365,7 +1365,7 @@ static int asn1_parse_countersignature(fmap_t *map, const void **asn1data, unsig
1365 1365
     return 1;
1366 1366
 }
1367 1367
 
1368
-static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, char **certname)
1368
+static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, cli_ctx *ctx)
1369 1369
 {
1370 1370
     struct cli_asn1 asn1, deep, deeper;
1371 1371
     uint8_t issuer[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE];
... ...
@@ -1378,7 +1378,7 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
1378 1378
     // md is used to hold the message digest we extract from the signature
1379 1379
     uint8_t md[MAX_HASH_SIZE];
1380 1380
     cli_crt *x509;
1381
-    void *ctx;
1381
+    void *hash_ctx;
1382 1382
     int result;
1383 1383
     cl_error_t ret = CL_EPARSE;
1384 1384
 
... ...
@@ -1589,17 +1589,23 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
1589 1589
                     /* Use &(engine->cmgr) for this check, since we don't copy
1590 1590
                      * blacklist certs into cmgr and so that if there's a
1591 1591
                      * match, we have a long-lived pointer that we can pass
1592
-                     * back indicating the name of the sig that matched (we
1593
-                     * can't just malloc new space for one, since nothing above
1594
-                     * here knows to free it.) */
1592
+                     * back (via cli_append_virus) indicating the name of the
1593
+                     * sigs that matched (we can't just malloc new space for
1594
+                     * one, since nothing above here knows to free it.) */
1595 1595
                     if (NULL != (crt = crtmgr_blacklist_lookup(&(engine->cmgr), x509))) {
1596 1596
                         ret = CL_VIRUS;
1597
-                        cli_dbgmsg("asn1_parse_mscat: Found Authenticode certificate blacklisted by %s\n", (crt->name ? crt->name : "(no name)"));
1598
-                        if (NULL != certname) {
1599
-                            *certname = crt->name ? crt->name : "(no name)";
1597
+                        cli_dbgmsg("asn1_parse_mscat: Found Authenticode certificate blacklisted by %s\n", crt->name ? crt->name : "(unnamed CRB rule)");
1598
+                        if (NULL != ctx) {
1599
+                            ret = cli_append_virus(ctx, crt->name ? crt->name : "(unnamed CRB rule)");
1600
+                            if ((ret == CL_VIRUS) && !SCAN_ALLMATCHES) {
1601
+                                crtmgr_free(&newcerts);
1602
+                                goto finish;
1603
+                            }
1600 1604
                         }
1601
-                        crtmgr_free(&newcerts);
1602
-                        goto finish;
1605
+                        /* In the case where ctx is NULL, we don't care about
1606
+                         * blacklist matches - we are either using this
1607
+                         * function to parse .cat rules that were loaded in,
1608
+                         * or it's sigtool doing cert printing. */
1603 1609
                     }
1604 1610
 
1605 1611
                     /* NOTE: Since the 'issuer' cli_crt field is required for
... ...
@@ -1634,6 +1640,16 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
1634 1634
                     crtmgr_free(&newcerts);
1635 1635
                     break;
1636 1636
                 }
1637
+
1638
+                /* In the SCAN_ALLMATCHES case, we'd get here with
1639
+                 * ret == CL_VIRUS if a match occurred but we wanted
1640
+                 * to keep looping to look for other matches.  In that
1641
+                 * case, bail here. */
1642
+                if (CL_VIRUS == ret) {
1643
+                    crtmgr_free(&newcerts);
1644
+                    break;
1645
+                }
1646
+
1637 1647
                 x509 = newcerts.crts;
1638 1648
 
1639 1649
                 /* Now look for cases where embedded certs can be trusted
... ...
@@ -1928,13 +1944,13 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
1928 1928
             break;
1929 1929
         }
1930 1930
 
1931
-        if (NULL == (ctx = get_hash_ctx(hashtype))) {
1931
+        if (NULL == (hash_ctx = get_hash_ctx(hashtype))) {
1932 1932
             break;
1933 1933
         }
1934 1934
 
1935
-        cl_update_hash(ctx, "\x31", 1);
1936
-        cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1);
1937
-        cl_finish_hash(ctx, hash);
1935
+        cl_update_hash(hash_ctx, "\x31", 1);
1936
+        cl_update_hash(hash_ctx, (void *)(attrs + 1), attrs_size - 1);
1937
+        cl_finish_hash(hash_ctx, hash);
1938 1938
 
1939 1939
         if (!fmap_need_ptr_once(map, asn1.content, asn1.size)) {
1940 1940
             cli_dbgmsg("asn1_parse_mscat: failed to read encryptedDigest\n");
... ...
@@ -2267,7 +2283,7 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine)
2267 2267
  *
2268 2268
  * If CL_VIRUS is returned, certname will be set to the certname of blacklist
2269 2269
  * rule that matched (unless certname is NULL). */
2270
-cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions, char **certname)
2270
+cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions, cli_ctx *ctx)
2271 2271
 {
2272 2272
     unsigned int content_size;
2273 2273
     struct cli_asn1 c;
... ...
@@ -2277,15 +2293,9 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset
2277 2277
     const void *content;
2278 2278
     crtmgr certs;
2279 2279
     int ret;
2280
-    void *ctx;
2280
+    void *hash_ctx;
2281 2281
     unsigned int i;
2282 2282
 
2283
-    // TODO Move these into cli_check_auth_header
2284
-    if (!(engine->dconf->pe & PE_CONF_CERTS))
2285
-        return CL_EVERIFY;
2286
-    if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS)
2287
-        return CL_EVERIFY;
2288
-
2289 2283
     cli_dbgmsg("in asn1_check_mscat (offset: %llu)\n", (long long unsigned)offset);
2290 2284
     crtmgr_init(&certs);
2291 2285
     /* Get a copy of all certs in the trust store, excluding blacklist certs */
... ...
@@ -2293,7 +2303,7 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset
2293 2293
         crtmgr_free(&certs);
2294 2294
         return CL_EVERIFY;
2295 2295
     }
2296
-    ret = asn1_parse_mscat(engine, map, offset, size, &certs, 1, &content, &content_size, certname);
2296
+    ret = asn1_parse_mscat(engine, map, offset, size, &certs, 1, &content, &content_size, ctx);
2297 2297
     crtmgr_free(&certs);
2298 2298
     if (CL_CLEAN != ret)
2299 2299
         return ret;
... ...
@@ -2325,7 +2335,7 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset
2325 2325
         return CL_EPARSE;
2326 2326
     }
2327 2327
 
2328
-    if (NULL == (ctx = get_hash_ctx(hashtype))) {
2328
+    if (NULL == (hash_ctx = get_hash_ctx(hashtype))) {
2329 2329
         return CL_EPARSE;
2330 2330
     }
2331 2331
 
... ...
@@ -2340,10 +2350,10 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset
2340 2340
             return CL_EVERIFY;
2341 2341
         }
2342 2342
 
2343
-        cl_update_hash(ctx, hptr, regions[i].size);
2343
+        cl_update_hash(hash_ctx, hptr, regions[i].size);
2344 2344
     }
2345 2345
 
2346
-    cl_finish_hash(ctx, hash);
2346
+    cl_finish_hash(hash_ctx, hash);
2347 2347
 
2348 2348
     if (cli_debug_flag) {
2349 2349
         char hashtxt[MAX_HASH_SIZE * 2 + 1];
... ...
@@ -31,6 +31,6 @@ struct cli_mapped_region {
31 31
 };
32 32
 
33 33
 int asn1_load_mscat(fmap_t *map, struct cl_engine *engine);
34
-cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions, char **certname);
34
+cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions, cli_ctx *ctx);
35 35
 
36 36
 #endif
... ...
@@ -983,12 +983,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
983 983
      * If we want to add support for more signature parsing in the future
984 984
      * (Ex: MachO sigs), do that here too. */
985 985
     if (1 == info.status && i == 1) {
986
-        char *certname = NULL;
987
-        ret            = cli_check_auth_header(ctx, &(info.exeinfo), &certname);
988
-
989
-        if (CL_VIRUS == ret) {
990
-            ret = cli_append_virus(ctx, certname);
991
-        }
986
+        ret = cli_check_auth_header(ctx, &(info.exeinfo));
992 987
 
993 988
         if ((ret == CL_VIRUS || ret == CL_VERIFIED) && !SCAN_ALLMATCHES) {
994 989
             cli_targetinfo_destroy(&info);
... ...
@@ -5510,11 +5510,11 @@ static int sort_sects(const void *first, const void *second)
5510 5510
  * 
5511 5511
  * CL_VERIFIED will be returned if the file was whitelisted based on its
5512 5512
  * signature.  CL_VIRUS will be returned if the file was blacklisted based on
5513
- * its signature.  Otherwise, an cl_error_t error value will be returned.
5513
+ * its signature.  Otherwise, a cl_error_t error value will be returned.
5514 5514
  * 
5515
- * If CL_VIRUS is returned, certname will be set to the certname of blacklist
5516
- * rule that matched (unless certname is NULL). */
5517
-cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char **certname)
5515
+ * If CL_VIRUS is returned, cli_append_virus will get called, adding the
5516
+ * name associated with the blacklist CRB rules to the list of found viruses.*/
5517
+cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo)
5518 5518
 {
5519 5519
     size_t at;
5520 5520
     unsigned int i, hlen;
... ...
@@ -5530,12 +5530,13 @@ cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char
5530 5530
     uint32_t sec_dir_size;
5531 5531
     struct cli_exe_info _peinfo;
5532 5532
 
5533
-    // If Authenticode parsing has been disabled via DCONF, then don't
5534
-    // continue on.
5535
-    // TODO This should probably be named PE_CONF_AUTHENTICODE instead
5536
-    // of PE_CONF_CATALOG
5537
-    if (!(DCONF & PE_CONF_CATALOG))
5538
-        return CL_EFORMAT;
5533
+    // If Authenticode parsing has been disabled via DCONF or an engine
5534
+    // option, then don't continue on.
5535
+    if (!(DCONF & PE_CONF_CERTS))
5536
+        return CL_EVERIFY;
5537
+
5538
+    if (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS)
5539
+        return CL_EVERIFY;
5539 5540
 
5540 5541
     // If peinfo is NULL, initialize one.  This makes it so that this function
5541 5542
     // can be used easily by sigtool
... ...
@@ -5659,7 +5660,7 @@ cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char
5659 5659
         at = sec_dir_offset + sizeof(cert_hdr);
5660 5660
         hlen -= sizeof(cert_hdr);
5661 5661
 
5662
-        ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions, certname);
5662
+        ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions, ctx);
5663 5663
 
5664 5664
         if (CL_VERIFIED == ret) {
5665 5665
             // We validated the embedded signature.  Hooray!
... ...
@@ -98,7 +98,7 @@ enum {
98 98
 int cli_pe_targetinfo(fmap_t *map, struct cli_exe_info *peinfo);
99 99
 int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx);
100 100
 
101
-cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char **certname);
101
+cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo);
102 102
 int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type, stats_section_t *hashes);
103 103
 
104 104
 uint32_t cli_rawaddr(uint32_t, const struct cli_exe_section *, uint16_t, unsigned int *, size_t, uint32_t);
... ...
@@ -2939,6 +2939,16 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
2939 2939
     char *subject = NULL, *pubkey = NULL, *serial = NULL;
2940 2940
     const uint8_t exp[] = "\x01\x00\x01";
2941 2941
 
2942
+    if (!(engine->dconf->pe & PE_CONF_CERTS)) {
2943
+        cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to DCONF configuration\n");
2944
+        return ret;
2945
+    }
2946
+
2947
+    if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) {
2948
+        cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to engine options\n");
2949
+        return ret;
2950
+    }
2951
+
2942 2952
     cli_crt_init(&ca);
2943 2953
     memset(ca.issuer, 0xca, sizeof(ca.issuer));
2944 2954
 
... ...
@@ -3105,6 +3115,19 @@ static int cli_loadmscat(FILE *fs, const char *dbname, struct cl_engine *engine,
3105 3105
     UNUSEDPARAM(options);
3106 3106
     UNUSEDPARAM(dbio);
3107 3107
 
3108
+    /* If loading in signatures stored in .cat files is disabled, then skip.
3109
+     * If Authenticoded signature parsing in general is disabled, then also
3110
+     * skip. */
3111
+    if (!(engine->dconf->pe & PE_CONF_CATALOG) || !(engine->dconf->pe & PE_CONF_CERTS)) {
3112
+        cli_dbgmsg("cli_loadmscat: Ignoring .cat sigs due to DCONF configuration\n");
3113
+        return 0;
3114
+    }
3115
+
3116
+    if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) {
3117
+        cli_dbgmsg("cli_loadmscat: Ignoring .cat sigs due to engine options\n");
3118
+        return 0;
3119
+    }
3120
+
3108 3121
     if (!(map = fmap(fileno(fs), 0, 0))) {
3109 3122
         cli_dbgmsg("Can't map cat: %s\n", dbname);
3110 3123
         return 0;
... ...
@@ -3455,7 +3455,7 @@ static int dumpcerts(const struct optstruct *opts)
3455 3455
         return -1;
3456 3456
     }
3457 3457
 
3458
-    ret = cli_check_auth_header(&ctx, NULL, NULL);
3458
+    ret = cli_check_auth_header(&ctx, NULL);
3459 3459
 
3460 3460
     switch (ret) {
3461 3461
         case CL_VERIFIED: