Browse code

libclamav: check file sizes for MD5 sigs in all cases

Tomasz Kojm authored on 2009/10/03 04:03:26
Showing 9 changed files
... ...
@@ -1,3 +1,8 @@
1
+Fri Oct  2 21:01:51 CEST 2009 (tk)
2
+----------------------------------
3
+ * libclamav: check file sizes for MD5 sigs in all cases
4
+	      Reported by Edwin
5
+
1 6
 Fri Oct  2 14:35:42 CEST 2009 (tk)
2 7
 ----------------------------------
3 8
  * libclamav: unify fp checking; output fp signatures in debug mode
... ...
@@ -245,7 +245,7 @@ void cli_bm_free(struct cli_matcher *root)
245 245
     }
246 246
 }
247 247
 
248
-int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, int fd, struct cli_bm_off *offdata)
248
+int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_bm_patt **patt, const struct cli_matcher *root, uint32_t offset, int fd, struct cli_bm_off *offdata)
249 249
 {
250 250
 	uint32_t i, j, off, off_min, off_max;
251 251
 	uint8_t found, pchain, shift;
... ...
@@ -370,6 +370,8 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
370 370
 		    }
371 371
 		    if(virname)
372 372
 			*virname = p->virname;
373
+		    if(patt)
374
+			*patt = p;
373 375
 		    if(info.exeinfo.section)
374 376
 			free(info.exeinfo.section);
375 377
 		    return CL_VIRUS;
... ...
@@ -35,7 +35,7 @@ struct cli_bm_patt {
35 35
     uint16_t length, prefix_length;
36 36
     uint16_t cnt;
37 37
     unsigned char pattern0;
38
-    uint32_t boundary;
38
+    uint32_t boundary, filesize;
39 39
 };
40 40
 
41 41
 struct cli_bm_off {
... ...
@@ -46,7 +46,7 @@ int cli_bm_addpatt(struct cli_matcher *root, struct cli_bm_patt *pattern, const
46 46
 int cli_bm_init(struct cli_matcher *root);
47 47
 int cli_bm_initoff(const struct cli_matcher *root, struct cli_bm_off *data, int fd);
48 48
 void cli_bm_freeoff(struct cli_bm_off *data, const struct cli_matcher *root);
49
-int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, int fd, struct cli_bm_off *offdata);
49
+int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_bm_patt **patt, const struct cli_matcher *root, uint32_t offset, int fd, struct cli_bm_off *offdata);
50 50
 void cli_bm_free(struct cli_matcher *root);
51 51
 
52 52
 #endif
... ...
@@ -76,7 +76,7 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
76 76
 	if(!acdata && (ret = cli_ac_initdata(&mdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
77 77
 	    return ret;
78 78
 
79
-	if(troot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, troot, offset, -1, NULL)) != CL_VIRUS)
79
+	if(troot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, NULL, troot, offset, -1, NULL)) != CL_VIRUS)
80 80
 	    ret = cli_ac_scanbuff(buffer, length, virname, NULL, NULL, troot, acdata ? (acdata[0]) : (&mdata), offset, ftype, NULL, AC_SCAN_VIR, NULL);
81 81
 
82 82
 	if(!acdata)
... ...
@@ -89,7 +89,7 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
89 89
     if(!acdata && (ret = cli_ac_initdata(&mdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
90 90
 	return ret;
91 91
 
92
-    if(groot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, groot, offset, -1, NULL)) != CL_VIRUS)
92
+    if(groot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, NULL, groot, offset, -1, NULL)) != CL_VIRUS)
93 93
 	ret = cli_ac_scanbuff(buffer, length, virname, NULL, NULL, groot, acdata ? (acdata[1]) : (&mdata), offset, ftype, NULL, AC_SCAN_VIR, NULL);
94 94
 
95 95
     if(!acdata)
... ...
@@ -292,6 +292,7 @@ int cli_checkfp(int fd, cli_ctx *ctx)
292 292
 	const char *virname;
293 293
 	off_t pos;
294 294
 	struct stat sb;
295
+	const struct cli_bm_patt *patt = NULL;
295 296
 
296 297
 
297 298
     if((pos = lseek(fd, 0, SEEK_CUR)) == -1) {
... ...
@@ -302,25 +303,29 @@ int cli_checkfp(int fd, cli_ctx *ctx)
302 302
     lseek(fd, 0, SEEK_SET);
303 303
 
304 304
     if(ctx->engine->md5_fp) {
305
+	if(fstat(fd, &sb) == -1) {
306
+	    cli_errmsg("cli_checkfp(): fstat(%d) failed\n", fd);
307
+	    lseek(fd, pos, SEEK_SET);
308
+	    return 0;
309
+	}
310
+
305 311
 	if(!(digest = cli_md5digest(fd))) {
306 312
 	    cli_errmsg("cli_checkfp(): Can't generate MD5 checksum\n");
307 313
 	    lseek(fd, pos, SEEK_SET);
308 314
 	    return 0;
309 315
 	}
310 316
 
311
-	if(cli_bm_scanbuff(digest, 16, &virname, ctx->engine->md5_fp, 0, -1, NULL) == CL_VIRUS) {
317
+	if(cli_bm_scanbuff(digest, 16, &virname, &patt, ctx->engine->md5_fp, 0, -1, NULL) == CL_VIRUS && patt->filesize == sb.st_size) {
312 318
 	    cli_dbgmsg("cli_checkfp(): Found false positive detection (fp sig: %s)\n", virname);
313 319
 	    free(digest);
314 320
 	    lseek(fd, pos, SEEK_SET);
315 321
 	    return 1;
316 322
 	}
317 323
 
318
-	if(fstat(fd, &sb) != -1) {
319
-	    for(i = 0; i < 16; i++)
320
-		sprintf(md5 + i * 2, "%02x", digest[i]);
321
-	    md5[32] = 0;
322
-	    cli_dbgmsg("FP SIGNATURE: %s:%u:%s\n", md5, (unsigned int) sb.st_size, *ctx->virname ? *ctx->virname : "Name");
323
-	}
324
+	for(i = 0; i < 16; i++)
325
+	    sprintf(md5 + i * 2, "%02x", digest[i]);
326
+	md5[32] = 0;
327
+	cli_dbgmsg("FP SIGNATURE: %s:%u:%s\n", md5, (unsigned int) sb.st_size, *ctx->virname ? *ctx->virname : "Name");
324 328
 
325 329
 	free(digest);
326 330
     }
... ...
@@ -425,7 +430,7 @@ int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struc
425 425
 	    length += maxpatlen;
426 426
 
427 427
 	if(troot) {
428
-	    if(troot->ac_only || (ret = cli_bm_scanbuff(upt, length, ctx->virname, troot, offset, desc, bm_offmode ? &toff : NULL)) != CL_VIRUS)
428
+	    if(troot->ac_only || (ret = cli_bm_scanbuff(upt, length, ctx->virname, NULL, troot, offset, desc, bm_offmode ? &toff : NULL)) != CL_VIRUS)
429 429
 		ret = cli_ac_scanbuff(upt, length, ctx->virname, NULL, NULL, troot, &tdata, offset, ftype, ftoffset, acmode, NULL);
430 430
 
431 431
 	    if(ret == CL_VIRUS) {
... ...
@@ -440,7 +445,7 @@ int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struc
440 440
 	}
441 441
 
442 442
 	if(!ftonly) {
443
-	    if(groot->ac_only || (ret = cli_bm_scanbuff(upt, length, ctx->virname, groot, offset, desc, NULL)) != CL_VIRUS)
443
+	    if(groot->ac_only || (ret = cli_bm_scanbuff(upt, length, ctx->virname, NULL, groot, offset, desc, NULL)) != CL_VIRUS)
444 444
 		ret = cli_ac_scanbuff(upt, length, ctx->virname, NULL, NULL, groot, &gdata, offset, ftype, ftoffset, acmode, NULL);
445 445
 
446 446
 	    if(ret == CL_VIRUS) {
... ...
@@ -514,8 +519,10 @@ int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struc
514 514
 	return CL_VIRUS;
515 515
 
516 516
     if(!ftonly && ctx->engine->md5_hdb) {
517
+	    const struct cli_bm_patt *patt;
517 518
 	cli_md5_final(digest, &md5ctx);
518
-	if(cli_bm_scanbuff(digest, 16, ctx->virname, ctx->engine->md5_hdb, 0, -1, NULL) == CL_VIRUS && (cli_bm_scanbuff(digest, 16, NULL, ctx->engine->md5_fp, 0, -1, NULL) != CL_VIRUS))
519
+	fstat(desc, &sb);
520
+	if(cli_bm_scanbuff(digest, 16, ctx->virname, &patt, ctx->engine->md5_hdb, 0, -1, NULL) == CL_VIRUS && patt->filesize == sb.st_size && (cli_bm_scanbuff(digest, 16, NULL, &patt, ctx->engine->md5_fp, 0, -1, NULL) != CL_VIRUS || patt->filesize != sb.st_size))
519 521
 	    return CL_VIRUS;
520 522
     }
521 523
 
... ...
@@ -924,8 +924,9 @@ int cli_scanpe(int desc, cli_ctx *ctx)
924 924
 		for(j = 0; j < md5_sect->soff_len && md5_sect->soff[j] <= exe_sections[i].rsz; j++) {
925 925
 		    if(md5_sect->soff[j] == exe_sections[i].rsz) {
926 926
 			unsigned char md5_dig[16];
927
-			if(cli_md5sect(desc, &exe_sections[i], md5_dig) && cli_bm_scanbuff(md5_dig, 16, ctx->virname, ctx->engine->md5_mdb, 0, -1, NULL) == CL_VIRUS) {
928
-			    if(cli_bm_scanbuff(md5_dig, 16, NULL, ctx->engine->md5_fp, 0, -1, NULL) != CL_VIRUS) {
927
+			const struct cli_bm_patt *patt;
928
+			if(cli_md5sect(desc, &exe_sections[i], md5_dig) && cli_bm_scanbuff(md5_dig, 16, ctx->virname, &patt, ctx->engine->md5_mdb, 0, -1, NULL) == CL_VIRUS && patt->filesize == exe_sections[i].rsz) {
929
+			    if(cli_bm_scanbuff(md5_dig, 16, NULL, &patt, ctx->engine->md5_fp, 0, -1, NULL) != CL_VIRUS || patt->filesize != fsize) {
929 930
 
930 931
 				free(section_hdr);
931 932
 				free(exe_sections);
... ...
@@ -1194,13 +1194,13 @@ static int hash_match(const struct regex_matcher *rlist, const char *host, size_
1194 1194
 	    h[64]='\0';
1195 1195
 	    cli_dbgmsg("Looking up hash %s for %s(%u)%s(%u)\n", h, host, (unsigned)hlen, path, (unsigned)plen);
1196 1196
 	    if (prefix_matched) {
1197
-		if (cli_bm_scanbuff(sha256_dig, 4, &virname, &rlist->hostkey_prefix,0,-1,NULL) == CL_VIRUS) {
1197
+		if (cli_bm_scanbuff(sha256_dig, 4, &virname, NULL, &rlist->hostkey_prefix,0,-1,NULL) == CL_VIRUS) {
1198 1198
 		    cli_dbgmsg("prefix matched\n");
1199 1199
 		    *prefix_matched = 1;
1200 1200
 		} else
1201 1201
 		    return CL_SUCCESS;
1202 1202
 	    }
1203
-	    if (cli_bm_scanbuff(sha256_dig, 32, &virname, &rlist->sha256_hashes,0,-1,NULL) == CL_VIRUS) {
1203
+	    if (cli_bm_scanbuff(sha256_dig, 32, &virname, NULL, &rlist->sha256_hashes,0,-1,NULL) == CL_VIRUS) {
1204 1204
 		cli_dbgmsg("This hash matched: %s\n", h);
1205 1205
 		switch(*virname) {
1206 1206
 		    case 'W':
... ...
@@ -406,7 +406,7 @@ static int cli_chkign(const struct cli_matcher *ignored, const char *signame, co
406 406
     if(!ignored || !signame || !entry)
407 407
 	return 0;
408 408
 
409
-    if(cli_bm_scanbuff(signame, strlen(signame), &md5_expected, ignored, 0, -1, NULL) == CL_VIRUS) {
409
+    if(cli_bm_scanbuff(signame, strlen(signame), &md5_expected, NULL, ignored, 0, -1, NULL) == CL_VIRUS) {
410 410
 	if(md5_expected) {
411 411
 	    cli_md5_init(&md5ctx);
412 412
             cli_md5_update(&md5ctx, entry, strlen(entry));
... ...
@@ -1289,7 +1289,6 @@ static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo,
1289 1289
 	const char *pt;
1290 1290
 	int ret = CL_SUCCESS;
1291 1291
 	unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0, tokens_count;
1292
-	uint32_t size;
1293 1292
 	struct cli_bm_patt *new;
1294 1293
 	struct cli_matcher *db = NULL;
1295 1294
 
... ...
@@ -1343,7 +1342,7 @@ static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo,
1343 1343
 	}
1344 1344
 	new->length = 16;
1345 1345
 
1346
-	size = atoi(tokens[size_field]);
1346
+	new->filesize = atoi(tokens[size_field]);
1347 1347
 
1348 1348
 	new->virname = cli_mpool_virname(engine->mempool, (char *) tokens[2], options & CL_DB_OFFICIAL);
1349 1349
 	if(!new->virname) {
... ...
@@ -1375,7 +1374,7 @@ static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo,
1375 1375
 	    if(!db->md5_sizes_hs.capacity) {
1376 1376
 		    cli_hashset_init(&db->md5_sizes_hs, 65536, 80);
1377 1377
 	    }
1378
-	    cli_hashset_addkey(&db->md5_sizes_hs, size);
1378
+	    cli_hashset_addkey(&db->md5_sizes_hs, new->filesize);
1379 1379
 	}
1380 1380
 
1381 1381
 	sigs++;
... ...
@@ -455,7 +455,7 @@ static int add_hash(struct regex_matcher *matcher, char* pattern, const char fl,
455 455
 
456 456
 	if (fl != 'W' && pat->length == 32 &&
457 457
 	    cli_hashset_contains(&matcher->sha256_pfx_set, cli_readint32(pat->pattern)) &&
458
-	    cli_bm_scanbuff(pat->pattern, 32, &vname, &matcher->sha256_hashes,0,-1,NULL) == CL_VIRUS) {
458
+	    cli_bm_scanbuff(pat->pattern, 32, &vname, NULL, &matcher->sha256_hashes,0,-1,NULL) == CL_VIRUS) {
459 459
 	    if (*vname == 'W') {
460 460
 		/* hash is whitelisted in local.gdb */
461 461
 		cli_dbgmsg("Skipping hash %s\n", pattern);
... ...
@@ -116,7 +116,7 @@ START_TEST (test_bm_scanbuff) {
116 116
     ret = cli_parse_add(root, "Sig3", "babedead", 0, 0, "*", 0, NULL, 0);
117 117
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
118 118
 
119
-    ret = cli_bm_scanbuff("blah\xde\xad\xbe\xef", 12, &virname, root, 0, -1,NULL);
119
+    ret = cli_bm_scanbuff("blah\xde\xad\xbe\xef", 12, &virname, NULL, root, 0, -1, NULL);
120 120
     fail_unless(ret == CL_VIRUS, "cli_bm_scanbuff() failed");
121 121
     fail_unless(!strncmp(virname, "Sig2", 4), "Incorrect signature matched in cli_bm_scanbuff()\n");
122 122
     cli_bm_free(root);