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.
pull/142/head^2
Micah Snyder 5 years ago
parent 1ae678c945
commit 205614b403
  1. 356
      libclamav/jpeg.c
  2. 15
      libclamav/scanners.c
  3. 148
      libclamav/special.c
  4. 1
      libclamav/special.h

@ -37,6 +37,7 @@
#include "jpeg.h"
#include "clamav.h"
#include "scanners.h"
// clang-format off
/*
@ -182,6 +183,7 @@ typedef enum {
* - SPIFF. Not a common format, see http://fileformats.archiveteam.org/wiki/SPIFF
* 0xED:
* - IPTC / IMM metadata (a type of comment)
* - Photoshop data
* 0xEE:
* - AdobeRGB (as opposed to sRGB)
*/
@ -250,14 +252,69 @@ typedef enum {
// clang-format on
cl_error_t
cli_parsejpeg(cli_ctx *ctx)
static cl_error_t jpeg_check_photoshop_8bim(cli_ctx *ctx, off_t *off)
{
cl_error_t retval;
const unsigned char *buf;
uint16_t ntmp;
uint8_t nlength, id[2];
uint32_t size;
off_t offset = *off;
fmap_t *map = *ctx->fmap;
if (!(buf = fmap_need_off_once(map, offset, 4 + 2 + 1))) {
cli_dbgmsg("read bim failed\n");
return CL_BREAK;
}
if (memcmp(buf, "8BIM", 4) != 0) {
cli_dbgmsg("missed 8bim\n");
return CL_BREAK;
}
id[0] = (uint8_t)buf[4];
id[1] = (uint8_t)buf[5];
cli_dbgmsg("ID: 0x%.2x%.2x\n", id[0], id[1]);
nlength = buf[6];
ntmp = nlength + ((((uint16_t)nlength) + 1) & 0x01);
offset += 4 + 2 + 1 + ntmp;
if (fmap_readn(map, &size, offset, 4) != 4) {
return CL_BREAK;
}
size = be32_to_host(size);
if (size == 0) {
return CL_BREAK;
}
if ((size & 0x01) == 1) {
size++;
}
*off = offset + 4 + size;
/* Is it a thumbnail image: 0x0409 or 0x040c */
if ((id[0] == 0x04) && ((id[1] == 0x09) || (id[1] == 0x0c))) {
/* Yes */
cli_dbgmsg("found thumbnail\n");
} else {
/* No - Seek past record */
return CL_CLEAN;
}
/* Jump past header */
offset += 4 + 28;
/* Scan the thumbnail JPEG */
retval = cli_magic_scan_nested_fmap_type(map, offset, 0, ctx, CL_TYPE_JPEG, "photoshop-thumbnail");
return retval;
}
cl_error_t cli_parsejpeg(cli_ctx *ctx)
{
cl_error_t status = CL_CLEAN;
fmap_t *map = NULL;
jpeg_marker_t marker, prev_marker, prev_segment = JPEG_MARKER_NOT_A_MARKER_0x00;
uint8_t buff[strlen("ICC_PROFILE") + 2]; /* Using length "ICC_PROFILE" + 2 because it's the longest we'll read. */
uint8_t buff[50]; /* 50 should be sufficient for now */
uint16_t len_u16;
unsigned int offset = 0, i, len, segment = 0;
bool found_comment = false;
@ -294,8 +351,13 @@ cli_parsejpeg(cli_ctx *ctx)
if (fmap_readn(map, &marker_u8, offset, sizeof(marker_u8)) == sizeof(marker_u8)) {
offset += sizeof(marker_u8);
} else {
cli_errmsg("JPEG: Failed to read marker, file corrupted?\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadMarker");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_errmsg("JPEG: Failed to read marker, file corrupted?\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadMarker");
status = CL_EPARSE;
} else {
cli_dbgmsg("Failed to read marker, file corrupted?\n");
}
goto done;
}
marker = (jpeg_marker_t)marker_u8;
@ -305,30 +367,64 @@ cli_parsejpeg(cli_ctx *ctx)
prev_marker = marker;
}
if (i == 16) {
cli_warnmsg("JPEG: Spurious bytes before segment %u\n", segment);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SpuriousBytesBeforeSegment");
status = CL_EPARSE;
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Spurious bytes before segment %u\n", segment);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SpuriousBytesBeforeSegment");
status = CL_EPARSE;
} else {
cli_dbgmsg("Spurious bytes before segment %u\n", segment);
}
goto done;
}
/*
* Check for MS04-028 exploit (See: https://docs.microsoft.com/en-us/security-updates/securitybulletins/2004/ms04-028)
* You can reproduce to test with https://www.exploit-db.com/exploits/474
* Checking here because the exploit PoC will fail our length check, below.
*/
if (JPEG_MARKER_SEGMENT_COM_COMMENT == marker) {
if (fmap_readn(map, buff, offset, 2) == 2) {
if (buff[0] == 0x00) {
if ((buff[1] == 0x00) || (buff[1] == 0x01)) {
/* Found exploit */
status = cli_append_virus(ctx, "Heuristics.Exploit.W32.MS04-028");
goto done;
}
}
}
}
if (fmap_readn(map, &len_u16, offset, sizeof(len_u16)) != sizeof(len_u16)) {
cli_errmsg("JPEG: Failed to read the segment size, file corrupted?\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadSegmentSize");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_errmsg("JPEG: Failed to read the segment size, file corrupted?\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadSegmentSize");
status = CL_EPARSE;
} else {
cli_dbgmsg("Failed to read the segment size, file corrupted?\n");
}
goto done;
}
len = (unsigned int)be16_to_host(len_u16);
cli_dbgmsg("segment[%d] = 0x%02x, Length %u\n", segment, marker, len);
if (len < 2) {
cli_warnmsg("JPEG: Invalid segment size\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.InvalidSegmentSize");
status = CL_EPARSE;
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Invalid segment size\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.InvalidSegmentSize");
status = CL_EPARSE;
} else {
cli_dbgmsg("Invalid segment size\n");
}
goto done;
}
if (len >= map->len - offset + sizeof(len_u16)) {
cli_warnmsg("JPEG: Segment data out of file\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SegmentDataOutOfFile");
status = CL_EPARSE;
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Segment data out of file\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SegmentDataOutOfFile");
status = CL_EPARSE;
} else {
cli_dbgmsg("Segment data out of file\n");
}
goto done;
}
offset += len;
@ -338,35 +434,39 @@ cli_parsejpeg(cli_ctx *ctx)
/*
* JFIF, maybe
*/
if (fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("JFIF") + 1) == strlen("JFIF") + 1 && 0 == memcmp(buff, "JFIF\0", strlen("JFIF") + 1)) {
if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("JFIF") + 1) == strlen("JFIF") + 1) &&
(0 == memcmp(buff, "JFIF\0", strlen("JFIF") + 1))) {
/* Found a JFIF marker */
if (found_app && num_JFIF > 0) {
cli_warnmsg("JPEG: Duplicate Application Marker found (JFIF)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFdupAppMarker");
status = CL_EPARSE;
goto done;
}
if (!(segment == 1 ||
(segment == 2 && found_comment) ||
(segment == 2 && num_Exif > 0) ||
(segment == 3 && found_comment && num_Exif > 0))) {
/* The JFIF segment is technically required to appear first, though it has been observed
* appearing in segment 2 in functional images when segment 1 is a comment or an Exif segment.
* If segment 1 wasn't a comment or Exif, then the file structure is unusual. */
cli_warnmsg("JPEG: JFIF marker at wrong position, found in segment # %d\n", segment);
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFmarkerBadPosition");
status = CL_EPARSE;
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: JFIF header too short\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFheaderTooShort");
status = CL_EPARSE;
goto done;
}
cli_dbgmsg(" JFIF application marker\n");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
if (found_app && num_JFIF > 0) {
cli_warnmsg("JPEG: Duplicate Application Marker found (JFIF)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFdupAppMarker");
status = CL_EPARSE;
goto done;
}
if (!(segment == 1 ||
(segment == 2 && found_comment) ||
(segment == 2 && num_Exif > 0) ||
(segment == 3 && found_comment && num_Exif > 0))) {
/* The JFIF segment is technically required to appear first, though it has been observed
* appearing in segment 2 in functional images when segment 1 is a comment or an Exif segment.
* If segment 1 wasn't a comment or Exif, then the file structure is unusual. */
cli_warnmsg("JPEG: JFIF marker at wrong position, found in segment # %d\n", segment);
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFmarkerBadPosition");
status = CL_EPARSE;
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: JFIF header too short\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFheaderTooShort");
status = CL_EPARSE;
goto done;
}
}
found_app = true;
num_JFIF += 1;
} else {
@ -380,31 +480,37 @@ cli_parsejpeg(cli_ctx *ctx)
/*
* Exif, or maybe XMP data
*/
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))) {
if (found_app && (num_Exif > 0 || num_SPIFF > 0)) {
cli_warnmsg("JPEG: Duplicate Application Marker found (Exif)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifDupAppMarker");
status = CL_EPARSE;
goto done;
}
if (segment > 3 && !found_comment && num_JFIF > 0) {
/* If Exif was found after segment 3 and previous segments weren't a comment or JFIF, something is unusual. */
cli_warnmsg("JPEG: Exif marker at wrong position\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderBadPosition");
status = CL_EPARSE;
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: Exif header too short\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderTooShort");
status = CL_EPARSE;
goto done;
}
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))) {
/* Found an Exif marker */
cli_dbgmsg(" Exif application marker\n");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
if (found_app && (num_Exif > 0 || num_SPIFF > 0)) {
cli_warnmsg("JPEG: Duplicate Application Marker found (Exif)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifDupAppMarker");
status = CL_EPARSE;
goto done;
}
if (segment > 3 && !found_comment && num_JFIF > 0) {
/* If Exif was found after segment 3 and previous segments weren't a comment or JFIF, something is unusual. */
cli_warnmsg("JPEG: Exif marker at wrong position\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderBadPosition");
status = CL_EPARSE;
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: Exif header too short\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderTooShort");
status = CL_EPARSE;
goto done;
}
}
found_app = true;
num_Exif += 1;
} else if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("http://")) == strlen("http://")) && (0 == memcmp(buff, "http://", strlen("http://")))) {
} else if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("http://")) == strlen("http://")) &&
(0 == memcmp(buff, "http://", strlen("http://")))) {
cli_dbgmsg(" XMP metadata\n");
found_comment = true;
} else {
@ -430,41 +536,64 @@ cli_parsejpeg(cli_ctx *ctx)
/*
* SPIFF
*/
if (found_app) {
cli_warnmsg("JPEG: Duplicate Application Marker found (SPIFF)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFdupAppMarker");
status = CL_EPARSE;
goto done;
}
if (segment != 1 && (segment != 2 || !found_comment)) {
cli_warnmsg("JPEG: SPIFF marker at wrong position\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFmarkerBadPosition");
status = CL_EPARSE;
goto done;
}
if (fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("SPIFF") + 1) != strlen("SPIFF") + 1 || memcmp(buff, "SPIFF\0", strlen("SPIFF") + 1)) {
cli_warnmsg("JPEG: No SPIFF marker\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoSPIFFmarker");
status = CL_EPARSE;
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: SPIFF header too short\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFheaderTooShort");
status = CL_EPARSE;
goto done;
if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("SPIFF") + 1) == strlen("SPIFF") + 1) &&
(0 == memcmp(buff, "SPIFF\0", strlen("SPIFF") + 1))) {
/* Found SPIFF application marker */
cli_dbgmsg(" SPIFF application marker\n");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
if (found_app) {
cli_warnmsg("JPEG: Duplicate Application Marker found (SPIFF)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFdupAppMarker");
status = CL_EPARSE;
goto done;
}
if (segment != 1 && (segment != 2 || !found_comment)) {
cli_warnmsg("JPEG: SPIFF marker at wrong position\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFmarkerBadPosition");
status = CL_EPARSE;
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: SPIFF header too short\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFheaderTooShort");
status = CL_EPARSE;
goto done;
}
}
found_app = true;
num_SPIFF += 1;
} else {
cli_dbgmsg(" Unfamiliar use of application marker: 0x%02x\n", marker);
}
cli_dbgmsg(" SPIFF application marker\n");
found_app = true;
num_SPIFF += 1;
break;
case JPEG_MARKER_SEGMENT_APP13:
/*
* IPTC / IMM metadata (comment), probably
* Check for Photoshop information
* Example file to test with: 2c5883a964917aa54c8b3e2c70dabf0a7b06ba8c21bcbaf6f1c19501be9d9196
*/
cli_dbgmsg(" IPTC IMM metadata segment marker\n");
if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Photoshop 3.0") + 1) == strlen("Photoshop 3.0") + 1) &&
(0 == memcmp(buff, "Photoshop 3.0\0", strlen("Photoshop 3.0") + 1))) {
/* Found a Photoshop file */
off_t photoshop_data_offset = offset - len + sizeof(len_u16) + strlen("Photoshop 3.0") + 1;
off_t old_offset;
cli_dbgmsg("Found Photoshop segment\n");
do {
old_offset = photoshop_data_offset;
status = jpeg_check_photoshop_8bim(ctx, &photoshop_data_offset);
if (photoshop_data_offset <= old_offset)
break;
} while (status == CL_CLEAN);
if (status == CL_BREAK) {
status = CL_CLEAN;
}
} else {
cli_dbgmsg(" Unfamiliar use of application marker: 0x%02x\n", marker);
}
found_comment = true;
break;
@ -472,12 +601,13 @@ cli_parsejpeg(cli_ctx *ctx)
/*
* Adobe RGB, probably
*/
if (fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Adobe") + 1) != strlen("Adobe") + 1 || 0 != memcmp(buff, "Adobe\0", strlen("Adobe") + 1)) {
if ((fmap_readn(map, buff, offset - len + sizeof(len_u16), strlen("Adobe") + 1) == strlen("Adobe") + 1) &&
(0 == memcmp(buff, "Adobe\0", strlen("Adobe") + 1))) {
cli_dbgmsg(" AdobeRGB application marker\n");
} else {
/* Not Adobe, dunno what this is. */
cli_dbgmsg(" Unfamiliar application marker: 0x%02x\n", marker);
break;
cli_dbgmsg(" Unfamiliar use of application marker: 0x%02x\n", marker);
}
cli_dbgmsg(" AdobeRGB application marker\n");
break;
case JPEG_MARKER_SEGMENT_APP3:
@ -524,10 +654,12 @@ cli_parsejpeg(cli_ctx *ctx)
case JPEG_MARKER_SEGMENT_JPG7: /* JPG7 */
cli_dbgmsg(" JPG7 segment marker\n");
if (found_app) {
cli_warnmsg("JPEG: Application Marker before JPG7\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.AppMarkerBeforeJPG7");
status = CL_EPARSE;
goto done;
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Application Marker before JPG7\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.AppMarkerBeforeJPG7");
status = CL_EPARSE;
goto done;
}
}
goto done;
@ -546,9 +678,11 @@ cli_parsejpeg(cli_ctx *ctx)
/*
* We shouldn't reach this marker because we exit out when we hit the Start of Scan marker.
*/
cli_warnmsg("JPEG: No image in jpeg\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoImages");
status = CL_EPARSE;
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: No image in jpeg\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoImages");
status = CL_EPARSE;
}
goto done;
case JPEG_MARKER_SEGMENT_COM_COMMENT: /* COM (comment) */
@ -562,11 +696,13 @@ cli_parsejpeg(cli_ctx *ctx)
case JPEG_MARKER_SEGMENT_DTT: /* DTT */
cli_dbgmsg(" DTT segment marker\n");
if (prev_segment != JPEG_MARKER_SEGMENT_DTI) {
cli_warnmsg("JPEG: No DTI segment before DTT\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.DTTMissingDTISegment");
status = CL_EPARSE;
goto done;
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
if (prev_segment != JPEG_MARKER_SEGMENT_DTI) {
cli_warnmsg("JPEG: No DTI segment before DTT\n");
cli_append_possibly_unwanted(ctx, "Heuristics.Broken.Media.JPEG.DTTMissingDTISegment");
status = CL_EPARSE;
goto done;
}
}
break;

@ -2562,16 +2562,6 @@ static cl_error_t cli_scanriff(cli_ctx *ctx)
return ret;
}
static cl_error_t cli_scanjpeg(cli_ctx *ctx)
{
cl_error_t ret = CL_CLEAN;
if (cli_check_jpeg_exploit(ctx, 0) == 1)
ret = cli_append_virus(ctx, "Heuristics.Exploit.W32.MS04-028");
return ret;
}
static cl_error_t cli_scancryptff(cli_ctx *ctx)
{
cl_error_t ret = CL_CLEAN, ndesc;
@ -4190,10 +4180,7 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
case CL_TYPE_JPEG:
if (SCAN_HEURISTICS && (DCONF_OTHER & OTHER_CONF_JPEG))
ret = cli_scanjpeg(ctx); /* This one has some Exploit detection. */
if (SCAN_HEURISTICS && SCAN_HEURISTIC_BROKEN_MEDIA && (DCONF_OTHER & OTHER_CONF_JPEG) && ret != CL_VIRUS)
ret = cli_parsejpeg(ctx);
ret = cli_parsejpeg(ctx); /* JPG parser detects MS04-028 exploits as well as Broken.Media */
break;
case CL_TYPE_TIFF:

@ -92,154 +92,6 @@ int cli_check_mydoom_log(cli_ctx *ctx)
return cli_append_virus(ctx, "Heuristics.Worm.Mydoom.M.log");
}
static int jpeg_check_photoshop_8bim(cli_ctx *ctx, off_t *off)
{
const unsigned char *buf;
uint16_t ntmp;
uint8_t nlength, id[2];
uint32_t size;
off_t offset = *off;
int retval;
fmap_t *map = *ctx->fmap;
if (!(buf = fmap_need_off_once(map, offset, 4 + 2 + 1))) {
cli_dbgmsg("read bim failed\n");
return -1;
}
if (memcmp(buf, "8BIM", 4) != 0) {
cli_dbgmsg("missed 8bim\n");
return -1;
}
id[0] = (uint8_t)buf[4];
id[1] = (uint8_t)buf[5];
cli_dbgmsg("ID: 0x%.2x%.2x\n", id[0], id[1]);
nlength = buf[6];
ntmp = nlength + ((((uint16_t)nlength) + 1) & 0x01);
offset += 4 + 2 + 1 + ntmp;
if (fmap_readn(map, &size, offset, 4) != 4) {
return -1;
}
size = special_endian_convert_32(size);
if (size == 0) {
return -1;
}
if ((size & 0x01) == 1) {
size++;
}
*off = offset + 4 + size;
/* Is it a thumbnail image: 0x0409 or 0x040c */
if ((id[0] == 0x04) && ((id[1] == 0x09) || (id[1] == 0x0c))) {
/* Yes */
cli_dbgmsg("found thumbnail\n");
} else {
/* No - Seek past record */
return 0;
}
/* Jump past header */
offset += 4 + 28;
retval = cli_check_jpeg_exploit(ctx, offset);
if (retval == 1) {
cli_dbgmsg("Exploit found in thumbnail\n");
}
return retval;
}
static int jpeg_check_photoshop(cli_ctx *ctx, off_t offset)
{
int retval;
const unsigned char *buffer;
off_t old;
fmap_t *map = *ctx->fmap;
if (!(buffer = fmap_need_off_once(map, offset, 14))) {
return 0;
}
if (memcmp(buffer, "Photoshop 3.0", 14) != 0) {
return 0;
}
offset += 14;
cli_dbgmsg("Found Photoshop segment\n");
do {
old = offset;
retval = jpeg_check_photoshop_8bim(ctx, &offset);
if (offset <= old)
break;
} while (retval == 0);
if (retval == -1) {
retval = 0;
}
return retval;
}
int cli_check_jpeg_exploit(cli_ctx *ctx, off_t offset)
{
const unsigned char *buffer;
int retval;
fmap_t *map = *ctx->fmap;
cli_dbgmsg("in cli_check_jpeg_exploit()\n");
if (ctx->recursion > ctx->engine->maxreclevel)
return CL_EMAXREC;
if (!(buffer = fmap_need_off_once(map, offset, 2)))
return 0;
if ((buffer[0] != 0xff) || (buffer[1] != 0xd8)) {
return 0;
}
offset += 2;
for (;;) {
off_t new_off;
if (!(buffer = fmap_need_off_once(map, offset, 4))) {
return 0;
}
/* Check for multiple 0xFF values, we need to skip them */
if ((buffer[0] == 0xff) && (buffer[1] == 0xff)) {
offset++;
continue;
}
offset += 4;
if ((buffer[0] == 0xff) && (buffer[1] == 0xfe)) {
if (buffer[2] == 0x00) {
if ((buffer[3] == 0x00) || (buffer[3] == 0x01)) {
return 1;
}
}
}
if (buffer[0] != 0xff) {
return -1;
}
if (buffer[1] == 0xda) {
/* End of Image marker */
return 0;
}
new_off = ((unsigned int)buffer[2] << 8) + buffer[3];
if (new_off < 2) {
return -1;
}
new_off -= 2;
new_off += offset;
if (buffer[1] == 0xed) {
/* Possible Photoshop file */
ctx->recursion++;
retval = jpeg_check_photoshop(ctx, offset);
ctx->recursion--;
if (retval != 0)
return retval;
}
offset = new_off;
}
}
static uint32_t riff_endian_convert_32(uint32_t value, int big_endian)
{
if (big_endian)

@ -35,7 +35,6 @@ struct swizz_stats {
};
int cli_check_mydoom_log(cli_ctx *ctx);
int cli_check_jpeg_exploit(cli_ctx *ctx, off_t offset);
int cli_check_riff_exploit(cli_ctx *ctx);
void cli_detect_swizz_str(const unsigned char *str, uint32_t len, struct swizz_stats *stats, int blob);
int cli_detect_swizz(struct swizz_stats *stats);

Loading…
Cancel
Save