Bug 524222 - (CVE-2009-3292) CVE-2009-3292 php: exif extension: Multiple missing sanity checks in EXIF file processing
CVE-2009-3292 php: exif extension: Multiple missing sanity checks in EXIF fil...
Product: Security Response
Classification: Other
Component: vulnerability (Show other bugs)
All Linux
low Severity low
: ---
: ---
Assigned To: Red Hat Product Security
: Security
Depends On: 541594 541595 541596 541597 541598
  Show dependency treegraph
Reported: 2009-09-18 08:58 EDT by Jan Lieskovsky
Modified: 2010-10-23 08:17 EDT (History)
6 users (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Last Closed: 2010-01-14 04:24:39 EST
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---

Attachments (Terms of Use)
Local copy of PHP-5.2.11 EXIF sanity patch. (3.05 KB, patch)
2009-09-18 09:01 EDT, Jan Lieskovsky
no flags Details | Diff

  None (edit)
Description Jan Lieskovsky 2009-09-18 08:58:38 EDT
Multiple missing sanity checks for proper file form were found in php,
when processing Exchangeable Image File Format (EXIF) image files.
A remote attacker could provide a specially-crafted EXIF image file,
leading to php application, using the EXIF extension, crash, once


Upstream patch:
Comment 1 Jan Lieskovsky 2009-09-18 09:01:55 EDT
Created attachment 361646 [details]
Local copy of PHP-5.2.11 EXIF sanity patch.
Comment 2 Jan Lieskovsky 2009-09-18 09:02:30 EDT
This issue affects the versions of php package, as shipped with Red Hat
Enterprise Linux 3, 4, and 5.
Comment 3 Jan Lieskovsky 2009-09-22 04:56:06 EDT
MITRE's CVE-2009-3292 record:

Unspecified vulnerability in PHP before 5.2.11 has unknown impact and
attack vectors related to "missing sanity checks around exif

Comment 4 Tomas Hoger 2009-09-25 09:55:47 EDT
Upstream patch introduces 3 changes:

- length check in exif_process_APP1()

-	if (memcmp(CharBuf+2, ExifHeader, 6)) {
+	if (length <= 8 || memcmp(CharBuf+2, ExifHeader, 6)) {

This change is not very interesting for memcmp() call on the same line, as possible buffer over-read is rather short here.

More interesting is a subsequent call to exif_process_TIFF_in_JPEG():

exif_process_TIFF_in_JPEG(ImageInfo, CharBuf + 8, length - 8, displacement+8 TSRMLS_CC);

There, length - 8 can integer underflow, passing negative (or actually very large positive value, as unsigned size_t type is used for length parameters) is passed to the function.  Later on, this length value is used as an upper bound when reading more data from CharBuf buffer, that contains EXIF info loaded from the file.  It should be noted that when length <= 8, all data later read in exif_process_TIFF_in_JPEG() are not reliably controlled by an attacker, so EXIF parsing may fail on any of the checks done in the function (e.g. where EXIF data format is determined).  Theoretically, memory may contain desired data from some previous calls, so the checks may pass and offset_of_ifd may get set to a large value, allow further buffer over-reads, that may cause interpreter crash or (quite unlikely) cause PHP exif functions to leak some data from interpreters memory.

So impact and low severity rating identical to similar problem fixed in PHP 5.2.10 - see bug #506896.

- EOF checking when reading lh / ll

-		lh = php_stream_getc(ImageInfo->infile);
-		ll = php_stream_getc(ImageInfo->infile);
+		if ((lh = php_stream_getc(ImageInfo->infile)) == EOF) {
+			return FALSE;
+		}
+		if ((ll = php_stream_getc(ImageInfo->infile)) == EOF) {
+			return FALSE;
+		}

We can ignore lh case completely, as EOF in reading lh means EOF for ll.  EOF is defined as (-1), so when assigned to unsigned lh / ll, those variables will hold UINT_MAX (0xFFFFFFFF on both 32 and 64 bit arches).

lh and ll is used to calculate itemlen:
  itemlen = (lh << 8) | ll;

Given the "| ll", if ll is 0xFFFFFFFF, we can ignore lh completely.

itemlen is then used passed to exif_file_sections_add() as:
  sn = exif_file_sections_add(ImageInfo, marker, itemlen+1, NULL);

size argument of exif_file_sections_add() is size_t, so it will be 0 on 32 bit arches, 2^32 on 64 bits.  This size is used as data buffer size:

    if (!size) {
        data = NULL;
    } else if (data == NULL) {
        data = safe_emalloc(size, 1, 0);

On 32 bits, no buffer is allocated, which leads to a NULL pointer dereference back in exif_scan_JPEG_header():

        Data = ImageInfo->file.list[sn].data;  // data is NULL

        /* Store first two pre-read bytes. */
        Data[0] = (uchar)lh;
        Data[1] = (uchar)ll;

On 64 bits, there's an attempt to allocate large amount of memory (4G).  This is likely to fail on PHP's memory_limit (values between 32M (default) and 128M are common) or system memory limit, or allocate large buffer if no limits are set (unlikely).

- MAX_IFD_NESTING_LEVEL check for TIFF exif header

It seems this may cause PHP to process EXIF data in loop until execution time limit is hit.
Comment 5 Tomas Hoger 2009-09-29 09:32:47 EDT
(In reply to comment #4)
> - MAX_IFD_NESTING_LEVEL check for TIFF exif header
> It seems this may cause PHP to process EXIF data in loop until execution time
> limit is hit.  

To correct myself...  This problem causes exif_process_IFD_in_TIFF() to be called recursively without limiting the depth of recursion.  Hence this exhausts all stack memory and results in php process crash in a fraction of the default execution time limit.  Hence impact=low too.
Comment 8 errata-xmlrpc 2010-01-13 13:10:21 EST
This issue has been addressed in following products:

  Red Hat Enterprise Linux 5
  Red Hat Enterprise Linux 4
  Red Hat Enterprise Linux 3

Via RHSA-2010:0040 https://rhn.redhat.com/errata/RHSA-2010-0040.html

Note You need to log in before you can comment on or make changes to this bug.