Browse code

Integrate JPEG exploit check into JPEG parser

Integrated the JPEG exploit check into the JPEG parser and removed it
from special.c.

As a happy consequence of this, the photoshop file detection and
embedded JPEG thumbnail exploit check was merged in as well, which means
that the embedded thumbnails can also be scanned as embedded JPEG files.

Micah Snyder authored on 2020/12/02 15:52:08
Showing 4 changed files
... ...
@@ -37,6 +37,7 @@
37 37
 
38 38
 #include "jpeg.h"
39 39
 #include "clamav.h"
40
+#include "scanners.h"
40 41
 
41 42
 // clang-format off
42 43
 /*
... ...
@@ -182,6 +183,7 @@ typedef enum {
182 182
      *     - SPIFF. Not a common format, see http://fileformats.archiveteam.org/wiki/SPIFF
183 183
      *   0xED:
184 184
      *     - IPTC / IMM metadata (a type of comment)
185
+     *     - Photoshop data
185 186
      *   0xEE:
186 187
      *     - AdobeRGB (as opposed to sRGB)
187 188
      */
... ...
@@ -250,14 +252,69 @@ typedef enum {
250 250
 
251 251
 // clang-format on
252 252
 
253
-cl_error_t
254
-cli_parsejpeg(cli_ctx *ctx)
253
+static cl_error_t jpeg_check_photoshop_8bim(cli_ctx *ctx, off_t *off)
254
+{
255
+    cl_error_t retval;
256
+    const unsigned char *buf;
257
+    uint16_t ntmp;
258
+    uint8_t nlength, id[2];
259
+    uint32_t size;
260
+    off_t offset = *off;
261
+    fmap_t *map  = *ctx->fmap;
262
+
263
+    if (!(buf = fmap_need_off_once(map, offset, 4 + 2 + 1))) {
264
+        cli_dbgmsg("read bim failed\n");
265
+        return CL_BREAK;
266
+    }
267
+    if (memcmp(buf, "8BIM", 4) != 0) {
268
+        cli_dbgmsg("missed 8bim\n");
269
+        return CL_BREAK;
270
+    }
271
+
272
+    id[0] = (uint8_t)buf[4];
273
+    id[1] = (uint8_t)buf[5];
274
+    cli_dbgmsg("ID: 0x%.2x%.2x\n", id[0], id[1]);
275
+    nlength = buf[6];
276
+    ntmp    = nlength + ((((uint16_t)nlength) + 1) & 0x01);
277
+    offset += 4 + 2 + 1 + ntmp;
278
+
279
+    if (fmap_readn(map, &size, offset, 4) != 4) {
280
+        return CL_BREAK;
281
+    }
282
+    size = be32_to_host(size);
283
+    if (size == 0) {
284
+        return CL_BREAK;
285
+    }
286
+    if ((size & 0x01) == 1) {
287
+        size++;
288
+    }
289
+
290
+    *off = offset + 4 + size;
291
+    /* Is it a thumbnail image: 0x0409 or 0x040c */
292
+    if ((id[0] == 0x04) && ((id[1] == 0x09) || (id[1] == 0x0c))) {
293
+        /* Yes */
294
+        cli_dbgmsg("found thumbnail\n");
295
+    } else {
296
+        /* No - Seek past record */
297
+        return CL_CLEAN;
298
+    }
299
+
300
+    /* Jump past header */
301
+    offset += 4 + 28;
302
+
303
+    /* Scan the thumbnail JPEG */
304
+    retval = cli_magic_scan_nested_fmap_type(map, offset, 0, ctx, CL_TYPE_JPEG, "photoshop-thumbnail");
305
+
306
+    return retval;
307
+}
308
+
309
+cl_error_t cli_parsejpeg(cli_ctx *ctx)
255 310
 {
256 311
     cl_error_t status = CL_CLEAN;
257 312
 
258 313
     fmap_t *map = NULL;
259 314
     jpeg_marker_t marker, prev_marker, prev_segment = JPEG_MARKER_NOT_A_MARKER_0x00;
260
-    uint8_t buff[strlen("ICC_PROFILE") + 2]; /* Using length "ICC_PROFILE" + 2 because it's the longest we'll read. */
315
+    uint8_t buff[50]; /* 50 should be sufficient for now */
261 316
     uint16_t len_u16;
262 317
     unsigned int offset = 0, i, len, segment = 0;
263 318
     bool found_comment = false;
... ...
@@ -294,8 +351,13 @@ cli_parsejpeg(cli_ctx *ctx)
294 294
             if (fmap_readn(map, &marker_u8, offset, sizeof(marker_u8)) == sizeof(marker_u8)) {
295 295
                 offset += sizeof(marker_u8);
296 296
             } else {
297
-                cli_errmsg("JPEG: Failed to read marker, file corrupted?\n");
298
-                cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadMarker");
297
+                if (SCAN_HEURISTIC_BROKEN_MEDIA) {
298
+                    cli_errmsg("JPEG: Failed to read marker, file corrupted?\n");
299
+                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadMarker");
300
+                    status = CL_EPARSE;
301
+                } else {
302
+                    cli_dbgmsg("Failed to read marker, file corrupted?\n");
303
+                }
299 304
                 goto done;
300 305
             }
301 306
             marker = (jpeg_marker_t)marker_u8;
... ...
@@ -305,30 +367,64 @@ cli_parsejpeg(cli_ctx *ctx)
305 305
             prev_marker = marker;
306 306
         }
307 307
         if (i == 16) {
308
-            cli_warnmsg("JPEG: Spurious bytes before segment %u\n", segment);
309
-            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SpuriousBytesBeforeSegment");
310
-            status = CL_EPARSE;
308
+            if (SCAN_HEURISTIC_BROKEN_MEDIA) {
309
+                cli_warnmsg("JPEG: Spurious bytes before segment %u\n", segment);
310
+                cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SpuriousBytesBeforeSegment");
311
+                status = CL_EPARSE;
312
+            } else {
313
+                cli_dbgmsg("Spurious bytes before segment %u\n", segment);
314
+            }
311 315
             goto done;
312 316
         }
313 317
 
318
+        /*
319
+         * Check for MS04-028 exploit (See: https://docs.microsoft.com/en-us/security-updates/securitybulletins/2004/ms04-028)
320
+         * You can reproduce to test with https://www.exploit-db.com/exploits/474
321
+         * Checking here because the exploit PoC will fail our length check, below.
322
+         */
323
+        if (JPEG_MARKER_SEGMENT_COM_COMMENT == marker) {
324
+            if (fmap_readn(map, buff, offset, 2) == 2) {
325
+                if (buff[0] == 0x00) {
326
+                    if ((buff[1] == 0x00) || (buff[1] == 0x01)) {
327
+                        /* Found exploit */
328
+                        status = cli_append_virus(ctx, "Heuristics.Exploit.W32.MS04-028");
329
+                        goto done;
330
+                    }
331
+                }
332
+            }
333
+        }
334
+
314 335
         if (fmap_readn(map, &len_u16, offset, sizeof(len_u16)) != sizeof(len_u16)) {
315
-            cli_errmsg("JPEG: Failed to read the segment size, file corrupted?\n");
316
-            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadSegmentSize");
336
+            if (SCAN_HEURISTIC_BROKEN_MEDIA) {
337
+                cli_errmsg("JPEG: Failed to read the segment size, file corrupted?\n");
338
+                cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadSegmentSize");
339
+                status = CL_EPARSE;
340
+            } else {
341
+                cli_dbgmsg("Failed to read the segment size, file corrupted?\n");
342
+            }
317 343
             goto done;
318 344
         }
319 345
         len = (unsigned int)be16_to_host(len_u16);
320 346
         cli_dbgmsg("segment[%d] = 0x%02x, Length %u\n", segment, marker, len);
321 347
 
322 348
         if (len < 2) {
323
-            cli_warnmsg("JPEG: Invalid segment size\n");
324
-            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.InvalidSegmentSize");
325
-            status = CL_EPARSE;
349
+            if (SCAN_HEURISTIC_BROKEN_MEDIA) {
350
+                cli_warnmsg("JPEG: Invalid segment size\n");
351
+                cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.InvalidSegmentSize");
352
+                status = CL_EPARSE;
353
+            } else {
354
+                cli_dbgmsg("Invalid segment size\n");
355
+            }
326 356
             goto done;
327 357
         }
328 358
         if (len >= map->len - offset + sizeof(len_u16)) {
329
-            cli_warnmsg("JPEG: Segment data out of file\n");
330
-            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SegmentDataOutOfFile");
331
-            status = CL_EPARSE;
359
+            if (SCAN_HEURISTIC_BROKEN_MEDIA) {
360
+                cli_warnmsg("JPEG: Segment data out of file\n");
361
+                cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SegmentDataOutOfFile");
362
+                status = CL_EPARSE;
363
+            } else {
364
+                cli_dbgmsg("Segment data out of file\n");
365
+            }
332 366
             goto done;
333 367
         }
334 368
         offset += len;
... ...
@@ -338,35 +434,39 @@ cli_parsejpeg(cli_ctx *ctx)
338 338
                 /*
339 339
                  * JFIF, maybe
340 340
                  */
341
-                if (fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("JFIF") + 1) == strlen("JFIF") + 1 && 0 == memcmp(buff, "JFIF\0", strlen("JFIF") + 1)) {
341
+                if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("JFIF") + 1) == strlen("JFIF") + 1) &&
342
+                    (0 == memcmp(buff, "JFIF\0", strlen("JFIF") + 1))) {
342 343
                     /* Found a JFIF marker */
343
-                    if (found_app && num_JFIF > 0) {
344
-                        cli_warnmsg("JPEG: Duplicate Application Marker found (JFIF)\n");
345
-                        cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
346
-                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFdupAppMarker");
347
-                        status = CL_EPARSE;
348
-                        goto done;
349
-                    }
350
-                    if (!(segment == 1 ||
351
-                          (segment == 2 && found_comment) ||
352
-                          (segment == 2 && num_Exif > 0) ||
353
-                          (segment == 3 && found_comment && num_Exif > 0))) {
354
-                        /* The JFIF segment is technically required to appear first, though it has been observed
355
-                        * appearing in segment 2 in functional images when segment 1 is a comment or an Exif segment.
356
-                        * If segment 1 wasn't a comment or Exif, then the file structure is unusual. */
357
-                        cli_warnmsg("JPEG: JFIF marker at wrong position, found in segment # %d\n", segment);
358
-                        cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
359
-                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFmarkerBadPosition");
360
-                        status = CL_EPARSE;
361
-                        goto done;
362
-                    }
363
-                    if (len < 16) {
364
-                        cli_warnmsg("JPEG: JFIF header too short\n");
365
-                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFheaderTooShort");
366
-                        status = CL_EPARSE;
367
-                        goto done;
368
-                    }
369 344
                     cli_dbgmsg(" JFIF application marker\n");
345
+
346
+                    if (SCAN_HEURISTIC_BROKEN_MEDIA) {
347
+                        if (found_app && num_JFIF > 0) {
348
+                            cli_warnmsg("JPEG: Duplicate Application Marker found (JFIF)\n");
349
+                            cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
350
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFdupAppMarker");
351
+                            status = CL_EPARSE;
352
+                            goto done;
353
+                        }
354
+                        if (!(segment == 1 ||
355
+                              (segment == 2 && found_comment) ||
356
+                              (segment == 2 && num_Exif > 0) ||
357
+                              (segment == 3 && found_comment && num_Exif > 0))) {
358
+                            /* The JFIF segment is technically required to appear first, though it has been observed
359
+                             * appearing in segment 2 in functional images when segment 1 is a comment or an Exif segment.
360
+                             * If segment 1 wasn't a comment or Exif, then the file structure is unusual. */
361
+                            cli_warnmsg("JPEG: JFIF marker at wrong position, found in segment # %d\n", segment);
362
+                            cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
363
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFmarkerBadPosition");
364
+                            status = CL_EPARSE;
365
+                            goto done;
366
+                        }
367
+                        if (len < 16) {
368
+                            cli_warnmsg("JPEG: JFIF header too short\n");
369
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFheaderTooShort");
370
+                            status = CL_EPARSE;
371
+                            goto done;
372
+                        }
373
+                    }
370 374
                     found_app = true;
371 375
                     num_JFIF += 1;
372 376
                 } else {
... ...
@@ -380,31 +480,37 @@ cli_parsejpeg(cli_ctx *ctx)
380 380
                 /*
381 381
                  * Exif, or maybe XMP data
382 382
                  */
383
-                if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Exif") + 2) == strlen("Exif") + 2) && (0 == memcmp(buff, "Exif\0\0", strlen("Exif") + 2))) {
384
-                    if (found_app && (num_Exif > 0 || num_SPIFF > 0)) {
385
-                        cli_warnmsg("JPEG: Duplicate Application Marker found (Exif)\n");
386
-                        cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
387
-                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifDupAppMarker");
388
-                        status = CL_EPARSE;
389
-                        goto done;
390
-                    }
391
-                    if (segment > 3 && !found_comment && num_JFIF > 0) {
392
-                        /* If Exif was found after segment 3 and previous segments weren't a comment or JFIF, something is unusual. */
393
-                        cli_warnmsg("JPEG: Exif marker at wrong position\n");
394
-                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderBadPosition");
395
-                        status = CL_EPARSE;
396
-                        goto done;
397
-                    }
398
-                    if (len < 16) {
399
-                        cli_warnmsg("JPEG: Exif header too short\n");
400
-                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderTooShort");
401
-                        status = CL_EPARSE;
402
-                        goto done;
403
-                    }
383
+                if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Exif") + 2) == strlen("Exif") + 2) &&
384
+                    (0 == memcmp(buff, "Exif\0\0", strlen("Exif") + 2))) {
385
+                    /* Found an Exif marker */
404 386
                     cli_dbgmsg(" Exif application marker\n");
387
+
388
+                    if (SCAN_HEURISTIC_BROKEN_MEDIA) {
389
+                        if (found_app && (num_Exif > 0 || num_SPIFF > 0)) {
390
+                            cli_warnmsg("JPEG: Duplicate Application Marker found (Exif)\n");
391
+                            cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
392
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifDupAppMarker");
393
+                            status = CL_EPARSE;
394
+                            goto done;
395
+                        }
396
+                        if (segment > 3 && !found_comment && num_JFIF > 0) {
397
+                            /* If Exif was found after segment 3 and previous segments weren't a comment or JFIF, something is unusual. */
398
+                            cli_warnmsg("JPEG: Exif marker at wrong position\n");
399
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderBadPosition");
400
+                            status = CL_EPARSE;
401
+                            goto done;
402
+                        }
403
+                        if (len < 16) {
404
+                            cli_warnmsg("JPEG: Exif header too short\n");
405
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderTooShort");
406
+                            status = CL_EPARSE;
407
+                            goto done;
408
+                        }
409
+                    }
405 410
                     found_app = true;
406 411
                     num_Exif += 1;
407
-                } else if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("http://")) == strlen("http://")) && (0 == memcmp(buff, "http://", strlen("http://")))) {
412
+                } else if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("http://")) == strlen("http://")) &&
413
+                           (0 == memcmp(buff, "http://", strlen("http://")))) {
408 414
                     cli_dbgmsg(" XMP metadata\n");
409 415
                     found_comment = true;
410 416
                 } else {
... ...
@@ -430,41 +536,64 @@ cli_parsejpeg(cli_ctx *ctx)
430 430
                 /*
431 431
                  * SPIFF
432 432
                  */
433
-                if (found_app) {
434
-                    cli_warnmsg("JPEG: Duplicate Application Marker found (SPIFF)\n");
435
-                    cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
436
-                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFdupAppMarker");
437
-                    status = CL_EPARSE;
438
-                    goto done;
439
-                }
440
-                if (segment != 1 && (segment != 2 || !found_comment)) {
441
-                    cli_warnmsg("JPEG: SPIFF marker at wrong position\n");
442
-                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFmarkerBadPosition");
443
-                    status = CL_EPARSE;
444
-                    goto done;
445
-                }
446
-                if (fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("SPIFF") + 1) != strlen("SPIFF") + 1 || memcmp(buff, "SPIFF\0", strlen("SPIFF") + 1)) {
447
-                    cli_warnmsg("JPEG: No SPIFF marker\n");
448
-                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoSPIFFmarker");
449
-                    status = CL_EPARSE;
450
-                    goto done;
451
-                }
452
-                if (len < 16) {
453
-                    cli_warnmsg("JPEG: SPIFF header too short\n");
454
-                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFheaderTooShort");
455
-                    status = CL_EPARSE;
456
-                    goto done;
433
+                if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("SPIFF") + 1) == strlen("SPIFF") + 1) &&
434
+                    (0 == memcmp(buff, "SPIFF\0", strlen("SPIFF") + 1))) {
435
+                    /* Found SPIFF application marker */
436
+                    cli_dbgmsg(" SPIFF application marker\n");
437
+
438
+                    if (SCAN_HEURISTIC_BROKEN_MEDIA) {
439
+                        if (found_app) {
440
+                            cli_warnmsg("JPEG: Duplicate Application Marker found (SPIFF)\n");
441
+                            cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
442
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFdupAppMarker");
443
+                            status = CL_EPARSE;
444
+                            goto done;
445
+                        }
446
+                        if (segment != 1 && (segment != 2 || !found_comment)) {
447
+                            cli_warnmsg("JPEG: SPIFF marker at wrong position\n");
448
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFmarkerBadPosition");
449
+                            status = CL_EPARSE;
450
+                            goto done;
451
+                        }
452
+                        if (len < 16) {
453
+                            cli_warnmsg("JPEG: SPIFF header too short\n");
454
+                            cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFheaderTooShort");
455
+                            status = CL_EPARSE;
456
+                            goto done;
457
+                        }
458
+                    }
459
+                    found_app = true;
460
+                    num_SPIFF += 1;
461
+                } else {
462
+                    cli_dbgmsg(" Unfamiliar use of application marker: 0x%02x\n", marker);
457 463
                 }
458
-                cli_dbgmsg(" SPIFF application marker\n");
459
-                found_app = true;
460
-                num_SPIFF += 1;
461 464
                 break;
462 465
 
463 466
             case JPEG_MARKER_SEGMENT_APP13:
464 467
                 /*
465
-                 * IPTC / IMM metadata (comment), probably
468
+                 * Check for Photoshop information
469
+                 * Example file to test with: 2c5883a964917aa54c8b3e2c70dabf0a7b06ba8c21bcbaf6f1c19501be9d9196
466 470
                  */
467
-                cli_dbgmsg(" IPTC IMM metadata segment marker\n");
471
+                if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Photoshop 3.0") + 1) == strlen("Photoshop 3.0") + 1) &&
472
+                    (0 == memcmp(buff, "Photoshop 3.0\0", strlen("Photoshop 3.0") + 1))) {
473
+                    /* Found a Photoshop file */
474
+                    off_t photoshop_data_offset = offset - len + sizeof(len_u16) + strlen("Photoshop 3.0") + 1;
475
+                    off_t old_offset;
476
+
477
+                    cli_dbgmsg("Found Photoshop segment\n");
478
+                    do {
479
+                        old_offset = photoshop_data_offset;
480
+                        status     = jpeg_check_photoshop_8bim(ctx, &photoshop_data_offset);
481
+                        if (photoshop_data_offset <= old_offset)
482
+                            break;
483
+                    } while (status == CL_CLEAN);
484
+
485
+                    if (status == CL_BREAK) {
486
+                        status = CL_CLEAN;
487
+                    }
488
+                } else {
489
+                    cli_dbgmsg(" Unfamiliar use of application marker: 0x%02x\n", marker);
490
+                }
468 491
                 found_comment = true;
469 492
                 break;
470 493
 
... ...
@@ -472,12 +601,13 @@ cli_parsejpeg(cli_ctx *ctx)
472 472
                 /*
473 473
                  * Adobe RGB, probably
474 474
                  */
475
-                if (fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Adobe") + 1) != strlen("Adobe") + 1 || 0 != memcmp(buff, "Adobe\0", strlen("Adobe") + 1)) {
475
+                if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Adobe") + 1) == strlen("Adobe") + 1) &&
476
+                    (0 == memcmp(buff, "Adobe\0", strlen("Adobe") + 1))) {
477
+                    cli_dbgmsg(" AdobeRGB application marker\n");
478
+                } else {
476 479
                     /* Not Adobe, dunno what this is. */
477
-                    cli_dbgmsg(" Unfamiliar application marker: 0x%02x\n", marker);
478
-                    break;
480
+                    cli_dbgmsg(" Unfamiliar use of application marker: 0x%02x\n", marker);
479 481
                 }
480
-                cli_dbgmsg(" AdobeRGB application marker\n");
481 482
                 break;
482 483
 
483 484
             case JPEG_MARKER_SEGMENT_APP3:
... ...
@@ -524,10 +654,12 @@ cli_parsejpeg(cli_ctx *ctx)
524 524
             case JPEG_MARKER_SEGMENT_JPG7: /* JPG7 */
525 525
                 cli_dbgmsg(" JPG7 segment marker\n");
526 526
                 if (found_app) {
527
-                    cli_warnmsg("JPEG: Application Marker before JPG7\n");
528
-                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.AppMarkerBeforeJPG7");
529
-                    status = CL_EPARSE;
530
-                    goto done;
527
+                    if (SCAN_HEURISTIC_BROKEN_MEDIA) {
528
+                        cli_warnmsg("JPEG: Application Marker before JPG7\n");
529
+                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.AppMarkerBeforeJPG7");
530
+                        status = CL_EPARSE;
531
+                        goto done;
532
+                    }
531 533
                 }
532 534
                 goto done;
533 535
 
... ...
@@ -546,9 +678,11 @@ cli_parsejpeg(cli_ctx *ctx)
546 546
                 /*
547 547
                  * We shouldn't reach this marker because we exit out when we hit the Start of Scan marker.
548 548
                  */
549
-                cli_warnmsg("JPEG: No image in jpeg\n");
550
-                cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoImages");
551
-                status = CL_EPARSE;
549
+                if (SCAN_HEURISTIC_BROKEN_MEDIA) {
550
+                    cli_warnmsg("JPEG: No image in jpeg\n");
551
+                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoImages");
552
+                    status = CL_EPARSE;
553
+                }
552 554
                 goto done;
553 555
 
554 556
             case JPEG_MARKER_SEGMENT_COM_COMMENT: /* COM (comment) */
... ...
@@ -562,11 +696,13 @@ cli_parsejpeg(cli_ctx *ctx)
562 562
 
563 563
             case JPEG_MARKER_SEGMENT_DTT: /* DTT */
564 564
                 cli_dbgmsg(" DTT segment marker\n");
565
-                if (prev_segment != JPEG_MARKER_SEGMENT_DTI) {
566
-                    cli_warnmsg("JPEG: No DTI segment before DTT\n");
567
-                    cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.DTTMissingDTISegment");
568
-                    status = CL_EPARSE;
569
-                    goto done;
565
+                if (SCAN_HEURISTIC_BROKEN_MEDIA) {
566
+                    if (prev_segment != JPEG_MARKER_SEGMENT_DTI) {
567
+                        cli_warnmsg("JPEG: No DTI segment before DTT\n");
568
+                        cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.DTTMissingDTISegment");
569
+                        status = CL_EPARSE;
570
+                        goto done;
571
+                    }
570 572
                 }
571 573
                 break;
572 574
 
... ...
@@ -2562,16 +2562,6 @@ static cl_error_t cli_scanriff(cli_ctx *ctx)
2562 2562
     return ret;
2563 2563
 }
2564 2564
 
2565
-static cl_error_t cli_scanjpeg(cli_ctx *ctx)
2566
-{
2567
-    cl_error_t ret = CL_CLEAN;
2568
-
2569
-    if (cli_check_jpeg_exploit(ctx, 0) == 1)
2570
-        ret = cli_append_virus(ctx, "Heuristics.Exploit.W32.MS04-028");
2571
-
2572
-    return ret;
2573
-}
2574
-
2575 2565
 static cl_error_t cli_scancryptff(cli_ctx *ctx)
2576 2566
 {
2577 2567
     cl_error_t ret = CL_CLEAN, ndesc;
... ...
@@ -4190,10 +4180,7 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
4190 4190
 
4191 4191
         case CL_TYPE_JPEG:
4192 4192
             if (SCAN_HEURISTICS && (DCONF_OTHER & OTHER_CONF_JPEG))
4193
-                ret = cli_scanjpeg(ctx); /* This one has some Exploit detection. */
4194
-
4195
-            if (SCAN_HEURISTICS && SCAN_HEURISTIC_BROKEN_MEDIA && (DCONF_OTHER & OTHER_CONF_JPEG) && ret != CL_VIRUS)
4196
-                ret = cli_parsejpeg(ctx);
4193
+                ret = cli_parsejpeg(ctx); /* JPG parser detects MS04-028 exploits as well as Broken.Media */
4197 4194
             break;
4198 4195
 
4199 4196
         case CL_TYPE_TIFF:
... ...
@@ -92,154 +92,6 @@ int cli_check_mydoom_log(cli_ctx *ctx)
92 92
     return cli_append_virus(ctx, "Heuristics.Worm.Mydoom.M.log");
93 93
 }
94 94
 
95
-static int jpeg_check_photoshop_8bim(cli_ctx *ctx, off_t *off)
96
-{
97
-    const unsigned char *buf;
98
-    uint16_t ntmp;
99
-    uint8_t nlength, id[2];
100
-    uint32_t size;
101
-    off_t offset = *off;
102
-    int retval;
103
-    fmap_t *map = *ctx->fmap;
104
-
105
-    if (!(buf = fmap_need_off_once(map, offset, 4 + 2 + 1))) {
106
-        cli_dbgmsg("read bim failed\n");
107
-        return -1;
108
-    }
109
-    if (memcmp(buf, "8BIM", 4) != 0) {
110
-        cli_dbgmsg("missed 8bim\n");
111
-        return -1;
112
-    }
113
-
114
-    id[0] = (uint8_t)buf[4];
115
-    id[1] = (uint8_t)buf[5];
116
-    cli_dbgmsg("ID: 0x%.2x%.2x\n", id[0], id[1]);
117
-    nlength = buf[6];
118
-    ntmp    = nlength + ((((uint16_t)nlength) + 1) & 0x01);
119
-    offset += 4 + 2 + 1 + ntmp;
120
-
121
-    if (fmap_readn(map, &size, offset, 4) != 4) {
122
-        return -1;
123
-    }
124
-    size = special_endian_convert_32(size);
125
-    if (size == 0) {
126
-        return -1;
127
-    }
128
-    if ((size & 0x01) == 1) {
129
-        size++;
130
-    }
131
-
132
-    *off = offset + 4 + size;
133
-    /* Is it a thumbnail image: 0x0409 or 0x040c */
134
-    if ((id[0] == 0x04) && ((id[1] == 0x09) || (id[1] == 0x0c))) {
135
-        /* Yes */
136
-        cli_dbgmsg("found thumbnail\n");
137
-    } else {
138
-        /* No - Seek past record */
139
-        return 0;
140
-    }
141
-
142
-    /* Jump past header */
143
-    offset += 4 + 28;
144
-
145
-    retval = cli_check_jpeg_exploit(ctx, offset);
146
-    if (retval == 1) {
147
-        cli_dbgmsg("Exploit found in thumbnail\n");
148
-    }
149
-    return retval;
150
-}
151
-
152
-static int jpeg_check_photoshop(cli_ctx *ctx, off_t offset)
153
-{
154
-    int retval;
155
-    const unsigned char *buffer;
156
-    off_t old;
157
-    fmap_t *map = *ctx->fmap;
158
-
159
-    if (!(buffer = fmap_need_off_once(map, offset, 14))) {
160
-        return 0;
161
-    }
162
-
163
-    if (memcmp(buffer, "Photoshop 3.0", 14) != 0) {
164
-        return 0;
165
-    }
166
-    offset += 14;
167
-
168
-    cli_dbgmsg("Found Photoshop segment\n");
169
-    do {
170
-        old    = offset;
171
-        retval = jpeg_check_photoshop_8bim(ctx, &offset);
172
-        if (offset <= old)
173
-            break;
174
-    } while (retval == 0);
175
-
176
-    if (retval == -1) {
177
-        retval = 0;
178
-    }
179
-    return retval;
180
-}
181
-
182
-int cli_check_jpeg_exploit(cli_ctx *ctx, off_t offset)
183
-{
184
-    const unsigned char *buffer;
185
-    int retval;
186
-    fmap_t *map = *ctx->fmap;
187
-
188
-    cli_dbgmsg("in cli_check_jpeg_exploit()\n");
189
-    if (ctx->recursion > ctx->engine->maxreclevel)
190
-        return CL_EMAXREC;
191
-
192
-    if (!(buffer = fmap_need_off_once(map, offset, 2)))
193
-        return 0;
194
-    if ((buffer[0] != 0xff) || (buffer[1] != 0xd8)) {
195
-        return 0;
196
-    }
197
-    offset += 2;
198
-    for (;;) {
199
-        off_t new_off;
200
-        if (!(buffer = fmap_need_off_once(map, offset, 4))) {
201
-            return 0;
202
-        }
203
-        /* Check for multiple 0xFF values, we need to skip them */
204
-        if ((buffer[0] == 0xff) && (buffer[1] == 0xff)) {
205
-            offset++;
206
-            continue;
207
-        }
208
-        offset += 4;
209
-        if ((buffer[0] == 0xff) && (buffer[1] == 0xfe)) {
210
-            if (buffer[2] == 0x00) {
211
-                if ((buffer[3] == 0x00) || (buffer[3] == 0x01)) {
212
-                    return 1;
213
-                }
214
-            }
215
-        }
216
-        if (buffer[0] != 0xff) {
217
-            return -1;
218
-        }
219
-        if (buffer[1] == 0xda) {
220
-            /* End of Image marker */
221
-            return 0;
222
-        }
223
-
224
-        new_off = ((unsigned int)buffer[2] << 8) + buffer[3];
225
-        if (new_off < 2) {
226
-            return -1;
227
-        }
228
-        new_off -= 2;
229
-        new_off += offset;
230
-
231
-        if (buffer[1] == 0xed) {
232
-            /* Possible Photoshop file */
233
-            ctx->recursion++;
234
-            retval = jpeg_check_photoshop(ctx, offset);
235
-            ctx->recursion--;
236
-            if (retval != 0)
237
-                return retval;
238
-        }
239
-        offset = new_off;
240
-    }
241
-}
242
-
243 95
 static uint32_t riff_endian_convert_32(uint32_t value, int big_endian)
244 96
 {
245 97
     if (big_endian)
... ...
@@ -35,7 +35,6 @@ struct swizz_stats {
35 35
 };
36 36
 
37 37
 int cli_check_mydoom_log(cli_ctx *ctx);
38
-int cli_check_jpeg_exploit(cli_ctx *ctx, off_t offset);
39 38
 int cli_check_riff_exploit(cli_ctx *ctx);
40 39
 void cli_detect_swizz_str(const unsigned char *str, uint32_t len, struct swizz_stats *stats, int blob);
41 40
 int cli_detect_swizz(struct swizz_stats *stats);