Browse code

improve scanning of handcrafted zip archives

git-svn: trunk@2640

Tomasz Kojm authored on 2007/01/26 08:41:28
Showing 4 changed files
... ...
@@ -1,3 +1,7 @@
1
+Fri Jan 26 00:36:13 CET 2007 (tk)
2
+---------------------------------
3
+  * libclamav: improve scanning of handcrafted zip archives
4
+
1 5
 Thu Jan 25 14:00:27 GMT 2007 (njh)
2 6
 ----------------------------------
3 7
   * libclamav:	Use BLOCKMAX (suggestion from TK)
... ...
@@ -226,7 +226,6 @@ static int cli_unrar_scanmetadata(int desc, rar_metadata_t *metadata, cli_ctx *c
226 226
 static int cli_scanrar(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_check)
227 227
 {
228 228
 	int ret = CL_CLEAN;
229
-	unsigned int files = 0;
230 229
 	rar_metadata_t *metadata, *metadata_tmp;
231 230
 	char *dir;
232 231
 	rar_state_t rar_state;
... ...
@@ -312,11 +311,11 @@ static int cli_scanzip(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_c
312 312
 	char *tmpname = NULL, *buff;
313 313
 	int fd, bytes, ret = CL_CLEAN;
314 314
 	unsigned long int size = 0;
315
-	unsigned int files = 0, encrypted;
315
+	unsigned int files = 0, encrypted, bfcnt;
316 316
 	struct stat source;
317 317
 	struct cli_meta_node *mdata;
318 318
 	int err;
319
-	uint8_t swarning = 0;
319
+	uint8_t swarning = 0, fail, success;
320 320
 
321 321
 
322 322
     cli_dbgmsg("in scanzip()\n");
... ...
@@ -483,65 +482,86 @@ static int cli_scanzip(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_c
483 483
 	    }
484 484
 	}
485 485
 
486
-	/* generate temporary file and get its descriptor */
487
-	if((tmpname = cli_gentempstream(NULL, &tmp)) == NULL) {
488
-	    cli_dbgmsg("Zip: Can't generate tmpfile().\n");
489
-	    zip_file_close(zfp);
490
-	    ret = CL_ETMPFILE;
491
-	    break;
492
-	}
486
+	bfcnt = 0;
487
+	success = 0;
488
+	while(1) {
489
+	    fail = 0;
490
+
491
+	    /* generate temporary file and get its descriptor */
492
+	    if((tmpname = cli_gentempstream(NULL, &tmp)) == NULL) {
493
+		cli_dbgmsg("Zip: Can't generate tmpfile().\n");
494
+		ret = CL_ETMPFILE;
495
+		break;
496
+	    }
497
+
498
+	    size = 0;
499
+	    while((bytes = zip_file_read(zfp, buff, FILEBUFF)) > 0) {
500
+		size += bytes;
501
+		if(fwrite(buff, 1, bytes, tmp) != (size_t) bytes) {
502
+		    cli_dbgmsg("Zip: Can't write to file.\n");
503
+		    ret = CL_EIO;
504
+		    break;
505
+		}
506
+	    }
507
+
508
+	    if(!encrypted) {
509
+		if(size != zdirent.st_size) {
510
+		    cli_dbgmsg("Zip: Incorrectly decompressed (%d != %d)\n", size, zdirent.st_size);
511
+		    if(zfp->bf[0] == -1) {
512
+			ret = CL_EZIP;
513
+			break;
514
+		    } else {
515
+			fail = 1;
516
+		    }
517
+		} else {
518
+		    cli_dbgmsg("Zip: File decompressed to %s\n", tmpname);
519
+		    success = 1;
520
+		}
521
+	    }
522
+
523
+	    if(!fail) {
524
+		if(fflush(tmp) != 0) {
525
+		    cli_dbgmsg("Zip: fflush() failed\n");
526
+		    ret = CL_EFSYNC;
527
+		    break;
528
+		}
529
+
530
+		fd = fileno(tmp);
531
+
532
+		lseek(fd, 0, SEEK_SET);
533
+
534
+		if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS ) {
535
+		    cli_dbgmsg("Zip: Infected with %s\n", *ctx->virname);
536
+		    ret = CL_VIRUS;
537
+		    break;
538
+		}
493 539
 
494
-	size = 0;
495
-	while((bytes = zip_file_read(zfp, buff, FILEBUFF)) > 0) {
496
-	    size += bytes;
497
-	    if(fwrite(buff, 1, bytes, tmp) != (size_t) bytes) {
498
-		cli_dbgmsg("Zip: Can't write to file.\n");
499
-		zip_file_close(zfp);
500
-		zip_dir_close(zdir);
540
+	    }
541
+
542
+	    if(tmp) {
501 543
 		fclose(tmp);
502 544
 		if(!cli_leavetemps_flag)
503 545
 		    unlink(tmpname);
504 546
 		free(tmpname);
505
-		free(buff);
506
-		return CL_EIO;
547
+		tmp = NULL;
507 548
 	    }
508
-	}
509
-
510
-	zip_file_close(zfp);
511 549
 
512
-	if(!encrypted) {
513
-	    if(size != zdirent.st_size) {
514
-		cli_dbgmsg("Zip: Incorrectly decompressed (%d != %d)\n", size, zdirent.st_size);
515
-		ret = CL_EZIP;
550
+	    if(zfp->bf[bfcnt] == -1)
516 551
 		break;
517 552
 
518
-	    } else {
519
-		cli_dbgmsg("Zip: File decompressed to %s\n", tmpname);
520
-	    }
553
+	    zfp->method = (uint16_t) zfp->bf[bfcnt];
554
+	    cli_dbgmsg("Zip: Brute force mode - checking compression method %u\n", zfp->method);
555
+	    bfcnt++;
521 556
 	}
557
+	zip_file_close(zfp);
522 558
 
523
-	if(fflush(tmp) != 0) {
524
-	    cli_dbgmsg("Zip: fflush() failed\n");
525
-	    ret = CL_EFSYNC;
526
-	    break;
559
+	if(!ret && !success) { /* brute-force decompression failed */
560
+	    cli_dbgmsg("Zip: All attempts to decompress file failed\n");
561
+	    ret = CL_EZIP;
527 562
 	}
528 563
 
529
-	fd = fileno(tmp);
530
-
531
-	lseek(fd, 0, SEEK_SET);
532
-	if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS ) {
533
-	    cli_dbgmsg("Zip: Infected with %s\n", *ctx->virname);
534
-	    ret = CL_VIRUS;
564
+	if(ret) 
535 565
 	    break;
536
-	}
537
-
538
-	if(tmp) {
539
-	    fclose(tmp);
540
-	    if(!cli_leavetemps_flag)
541
-		unlink(tmpname);
542
-	    free(tmpname);
543
-	    tmp = NULL;
544
-	}
545 566
     }
546 567
 
547 568
     zip_dir_close(zdir);
... ...
@@ -176,7 +176,7 @@ int __zip_parse_root_directory(int fd, struct zip_disk_trailer *trailer, zip_dir
176 176
 	uint32_t u_rootsize = EC32(trailer->z_rootsize);  
177 177
 	uint32_t u_rootseek = EC32(trailer->z_rootseek) + start;
178 178
 	uint16_t u_extras, u_comment, u_namlen, u_flags;
179
-	uint8_t clone_entry;
179
+	unsigned int bfcnt;
180 180
 	char *pt;
181 181
 
182 182
 
... ...
@@ -185,6 +185,11 @@ int __zip_parse_root_directory(int fd, struct zip_disk_trailer *trailer, zip_dir
185 185
 	return CL_EIO;
186 186
     }
187 187
 
188
+    if(!u_entries) {
189
+	cli_errmsg("Unzip: __zip_parse_root_directory: File contains no entries\n");
190
+	return CL_EFORMAT;
191
+    }
192
+
188 193
     if(u_rootsize > (uint32_t) sb.st_size) {
189 194
 	cli_errmsg("Unzip: __zip_parse_root_directory: Incorrect root size\n");
190 195
 	return CL_EFORMAT;
... ...
@@ -197,7 +202,6 @@ int __zip_parse_root_directory(int fd, struct zip_disk_trailer *trailer, zip_dir
197 197
     hdr = hdr0;
198 198
 
199 199
     for(entries = u_entries, offset = 0; entries > 0; entries--) {
200
-	clone_entry = 0;
201 200
 
202 201
 	if(lseek(fd, u_rootseek + offset, SEEK_SET) < 0) {
203 202
 	    free(hdr0);
... ...
@@ -239,16 +243,20 @@ int __zip_parse_root_directory(int fd, struct zip_disk_trailer *trailer, zip_dir
239 239
         hdr->d_off   = EC32(d->z_off) + start;
240 240
 
241 241
         hdr->d_compr = EC16(d->z_compr);
242
+
243
+	bfcnt = 0;
242 244
 	if(!hdr->d_compr && hdr->d_csize != hdr->d_usize) {
243 245
 	    cli_warnmsg("Unzip: __zip_parse_root_directory: File claims to be stored but csize != usize\n");
244
-	    cli_dbgmsg("Unzip: __zip_parse_root_directory: Assuming method 'inflate'\n");
245
-	    hdr->d_compr = 8;
246
+	    cli_dbgmsg("Unzip: __zip_parse_root_directory: Also checking for method 'deflated'\n");
247
+	    hdr->d_bf[bfcnt] = ZIP_METHOD_DEFLATED;
248
+	    bfcnt++;
246 249
 	} else if(hdr->d_compr && hdr->d_csize == hdr->d_usize) {
247 250
 	    cli_dbgmsg("Unzip: __zip_parse_root_directory: File claims to be deflated but csize == usize\n");
248
-	    cli_dbgmsg("Unzip: __zip_parse_root_directory: Assuming method 'stored'\n");
249
-	    hdr->d_compr = 0;
250
-	    clone_entry = 1;
251
+	    cli_dbgmsg("Unzip: __zip_parse_root_directory: Also checking for method 'stored'\n");
252
+	    hdr->d_bf[bfcnt] = ZIP_METHOD_STORED;
253
+	    bfcnt++;
251 254
 	}
255
+	hdr->d_bf[bfcnt] = -1;
252 256
 
253 257
 	hdr->d_flags = u_flags;
254 258
 
... ...
@@ -280,18 +288,6 @@ int __zip_parse_root_directory(int fd, struct zip_disk_trailer *trailer, zip_dir
280 280
 
281 281
 	prev_hdr = hdr;
282 282
 	hdr = (zip_dir_hdr *) ((char *) hdr + hdr->d_reclen);
283
-
284
-	if(clone_entry) {
285
-	    hdr0 = (zip_dir_hdr *) cli_realloc(hdr0, u_rootsize + *p_reclen);
286
-	    if(!hdr0)
287
-		return CL_EMEM;
288
-	    memcpy((zip_dir_hdr *) hdr, (zip_dir_hdr *) prev_hdr, *p_reclen);
289
-	    hdr->d_compr = 8;
290
-	    if(u_namlen)
291
-		hdr->d_name[strlen(hdr->d_name) - 1]++;
292
-	    p_reclen = &hdr->d_reclen;
293
-	    hdr = (zip_dir_hdr *) ((char *) hdr + hdr->d_reclen);
294
-	}
295 283
     }
296 284
 
297 285
     if(p_reclen) {
... ...
@@ -561,6 +557,8 @@ zip_file *zip_file_open(zip_dir *dir, const char *name, int d_off)
561 561
             fp->usize = hdr->d_usize;
562 562
             fp->csize = hdr->d_csize;
563 563
 
564
+	    fp->bf = hdr->d_bf;
565
+
564 566
             ret = __zip_inflate_init(fp, hdr);
565 567
 
566 568
 	    if(ret) {
... ...
@@ -147,6 +147,7 @@ struct __zip_file
147 147
 {
148 148
     struct __zip_dir *dir; 
149 149
     uint16_t method;
150
+    int16_t *bf;
150 151
     size_t restlen;
151 152
     size_t crestlen;
152 153
     size_t usize;
... ...
@@ -164,6 +165,7 @@ struct __zip_dir_hdr
164 164
     uint16_t    d_reclen;       /* next dir_hdr structure offset */
165 165
     uint16_t    d_namlen;       /* explicit namelen of d_name */
166 166
     uint16_t    d_compr;        /* compression type */
167
+    int16_t	d_bf[2];	/* compression type/brute force */
167 168
     uint16_t	d_flags;	/* general purpose flags */
168 169
     char        d_name[1];      /* actual name of the entry */
169 170
 };