From ef4355acf8106f7b25ac5ad319f440f05de502c1 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Wed, 27 Aug 2014 23:11:54 +0200 Subject: Windows vulnerability fix : make boot-loader decompressor more robust and secure by adding multiple checks and validation code. Note that we had to switch to the slow implementation of the function decode in order to keep the size of the decompressor code under 2K. --- src/Boot/Windows/BootCommon.h | 2 +- src/Boot/Windows/BootSector.asm | 4 ++- src/Boot/Windows/Decompressor.c | 66 +++++++++++++++++++++++++++++++---------- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/Boot/Windows/BootCommon.h b/src/Boot/Windows/BootCommon.h index 75346da7..50031cfe 100644 --- a/src/Boot/Windows/BootCommon.h +++ b/src/Boot/Windows/BootCommon.h @@ -13,7 +13,7 @@ #include "BootDefs.h" // The user will be advised to upgrade the rescue disk if upgrading from the following or any previous version -#define TC_RESCUE_DISK_UPGRADE_NOTICE_MAX_VERSION 0x010a +#define TC_RESCUE_DISK_UPGRADE_NOTICE_MAX_VERSION 0x010d #define TC_BOOT_LOADER_AREA_SIZE (TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS) diff --git a/src/Boot/Windows/BootSector.asm b/src/Boot/Windows/BootSector.asm index 74e8381b..1daaadac 100644 --- a/src/Boot/Windows/BootSector.asm +++ b/src/Boot/Windows/BootSector.asm @@ -134,6 +134,8 @@ checksum_ok: push dx ; Decompress boot loader + mov cx, word ptr [start + TC_BOOT_SECTOR_LOADER_LENGTH_OFFSET] + push cx ; Compressed data size push TC_BOOT_LOADER_COMPRESSED_BUFFER_OFFSET + TC_GZIP_HEADER_SIZE ; Compressed data push TC_MAX_BOOT_LOADER_DECOMPRESSED_SIZE ; Output buffer size push TC_BOOT_LOADER_DECOMPRESSOR_MEMORY_SIZE + TC_COM_EXECUTABLE_OFFSET ; Output buffer @@ -145,7 +147,7 @@ checksum_ok: retf decompressor_ret: - add sp, 6 + add sp, 8 pop dx ; Restore boot sector segment diff --git a/src/Boot/Windows/Decompressor.c b/src/Boot/Windows/Decompressor.c index efdbed91..c3883352 100644 --- a/src/Boot/Windows/Decompressor.c +++ b/src/Boot/Windows/Decompressor.c @@ -47,6 +47,7 @@ struct state { /* input state */ unsigned char *in; /* input buffer */ + unsigned int inlen; /* available input at in */ unsigned int incnt; /* bytes read so far */ int bitbuf; /* bit buffer */ int bitcnt; /* number of bits in bit buffer */ @@ -80,6 +81,9 @@ local int stored(struct state *s) /* discard leftover bits from current byte (assumes s->bitcnt < 8) */ s->bitbuf = 0; s->bitcnt = 0; + + if (s->incnt + 4 > s->inlen) + return 2; /* not enough input */ /* get length and check against its one's complement */ len = s->in[s->incnt++]; @@ -87,6 +91,9 @@ local int stored(struct state *s) if (s->in[s->incnt++] != (~len & 0xff) || s->in[s->incnt++] != ((~len >> 8) & 0xff)) return -2; /* didn't match complement! */ + + if (s->incnt + len > s->inlen) + return 2; /* not enough input */ /* copy len bytes from in to out */ if (s->out != NIL) { @@ -110,6 +117,8 @@ struct huffman { short *symbol; /* canonically ordered symbols */ }; +/* use slow version of decode to save code space */ +#define SLOW #ifdef SLOW local int decode(struct state *s, struct huffman *h) @@ -363,28 +372,39 @@ local int dynamic(struct state *s) int len; /* last length to repeat */ symbol = decode(s, &lencode); + if (symbol < 0) return symbol; if (symbol < 16) /* length in 0..15 */ lengths[index++] = symbol; else { /* repeat instruction */ len = 0; /* assume repeating zeros */ - if (symbol == 16) { /* repeat last length 3..6 times */ - if (index == 0) return -5; /* no last length! */ - len = lengths[index - 1]; /* last length */ - symbol = 3 + bits(s, 2); - } - else if (symbol == 17) /* repeat zero 3..10 times */ - symbol = 3 + bits(s, 3); - else /* == 18, repeat zero 11..138 times */ - symbol = 11 + bits(s, 7); - if (index + symbol > nlen + ndist) + switch(symbol) + { + case 16: { /* repeat last length 3..6 times */ + if (index == 0) return -5; /* no last length! */ + len = lengths[index - 1]; /* last length */ + symbol = 3 + bits(s, 2); + break; + } + case 17: /* repeat zero 3..10 times */ + symbol = 3 + bits(s, 3); + break; + default: /* == 18, repeat zero 11..138 times */ + symbol = 11 + bits(s, 7); + break; + } + if ((index + symbol > nlen + ndist)) return -6; /* too many lengths! */ while (symbol--) /* repeat last or zero symbol times */ lengths[index++] = len; } } + /* check for end-of-block code -- there better be one! */ + if (lengths[256] == 0) + return -9; + /* build huffman table for literal/length codes */ - err = construct(&lencode, lengths, nlen); + err = construct(&lencode, lengths, nlen); if (err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7; /* only allow incomplete codes if just one code */ @@ -404,7 +424,8 @@ void _acrtused () { } int far main ( unsigned char *dest, /* pointer to destination pointer */ unsigned int destlen, /* amount of output space */ - unsigned char *source) /* pointer to source data pointer */ + unsigned char *source, /* pointer to source data pointer */ + unsigned int sourcelen) { struct state s; /* input/output state */ int last, type; /* block information */ @@ -417,6 +438,7 @@ int far main ( /* initialize input state */ s.in = source; + s.inlen = sourcelen; s.incnt = 0; s.bitbuf = 0; s.bitcnt = 0; @@ -425,10 +447,22 @@ int far main ( do { last = bits(&s, 1); /* one if last block */ type = bits(&s, 2); /* block type 0..3 */ - err = type == 0 ? stored(&s) : - (type == 1 ? fixed(&s) : - (type == 2 ? dynamic(&s) : - -1)); /* type == 3, invalid */ + switch(type) + { + case 0: + err = stored(&s); + break; + case 1: + err = fixed(&s); + break; + case 2: + err = dynamic(&s); + break; + default: + err = -1; /* type == 3, invalid */ + break; + } + if (err != 0) break; /* return with error */ } while (!last); -- cgit v1.2.3