Advisory: chmlib exploitable buffer overflow Product: chmlib Affected Version: <=0.36 Immune Version: >0.36 OS: Tested on linux 2.4 probably other OS affected as well Date: 26.10.2005 Author: Sven Tantau - http://www.sven-tantau.de/ Vendor-URL: http://morte.jedrea.com/%7Ejedwin/projects/chmlib/ Vendor-Status: informed, vendor released a fixed version Advisory-URL: http://www.sven-tantau.de/public_files/chmlib/chmlib_20051126.txt Product ======= >> From chmlib/README chmlib is a small library designed for accessing MS ITSS files. The ITSS file format is used for Microsoft Html Help files (.chm), which have been the predominant medium for software documentation from Microsoft during the past several years, having superceded the previously used .hlp file format. ... You can download the source code releases from http://morte.jedrea.com/%7Ejedwin/projects/chmlib/ Details ======= 0. chm_lib.c: _chm_decompress_block(struct chmFile *h, UInt64 block, UChar **ubuffer) 1. see cmpLen and cbuffer declaration 2. call to _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen) to set cmpLen 3. cmpLen is used to offset write operations in cbuffer (_chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen)) 4. if cmpLen > h->reset_table.block_len + 6144 the buffer overflows 5. as we can supply the fd data, exploitation is possible and an attacker can execute arbitrary code. (I am not going to release an exploit for this vulnerability to the public.) /* decompress the block. must have lzx_mutex. */ static Int64 _chm_decompress_block(struct chmFile *h, UInt64 block, UChar **ubuffer) { ... UChar cbuffer[h->reset_table.block_len + 6144]; /* compressed buffer */ Int64 cmpLen; /* compressed len */ ... ... ... ... if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen) || _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen || LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen, (int)h->reset_table.block_len) != DECR_OK) { ... ----------------------------- /* get the bounds of a compressed block. return 0 on failure */ static int _chm_get_cmpblock_bounds(struct chmFile *h, UInt64 block, UInt64 *start, Int64 *len) { UChar buffer[8], *dummy; unsigned int remain; /* for all but the last block, use the reset table */ if (block < h->reset_table.block_count-1) { ... ... /* unpack the end address */ dummy = buffer; remain = 8; if (_chm_fetch_bytes(h, buffer, (UInt64)h->data_offset + (UInt64)h->rt_unit.start + (UInt64)h->reset_table.table_offset + (UInt64)block*8 + 8, remain) != remain || !_unmarshal_int64(&dummy, &remain, len)) return 0; } /* for the last block, use the span in addition to the reset table */ else { ... } /* compute the length and absolute start address */ *len -= *start; *start += h->data_offset + h->cn_unit.start; return 1; } -------------- /* * dest(len) is read out of the fd */ static int _unmarshal_int64(unsigned char **pData, unsigned int *pLenRemain, Int64 *dest) { Int64 temp; int i; if (8 > *pLenRemain) return 0; temp=0; for(i=8; i>0; i--) { temp <<= 8; temp |= (*pData)[i-1]; } *dest = temp; *pData += 8; *pLenRemain -= 8; return 1; } --------------- /* utility function to handle differences between {pread,read}(64)? */ static Int64 _chm_fetch_bytes(struct chmFile *h, UChar *buf, UInt64 os, Int64 len) { Int64 readLen=0, oldOs=0; if (h->fd == CHM_NULL_FD) return readLen; CHM_ACQUIRE_LOCK(h->mutex); ... readLen = pread(h->fd, buf, (long)len, (unsigned int)os); ... CHM_RELEASE_LOCK(h->mutex); return readLen; } Solution ======== Update! chmlib maintainer Jed Wing released a new version 0.37 You can download the source code releases from: http://morte.jedrea.com/%7Ejedwin/projects/chmlib/ No need for my quick and dirty patch. History ======= 2005-10-24 issue found by Sven Tantau 2005-10-25 contacted chmlib maintainer 2005-10-25 quick reaction with confirmation 2005-10-26 new release of chmlib and public disclosure -- Sven Tantau +49 177 7824828 http://www.sven-tantau.de/ *** http://www.beastiebytes.de/ http://twe.sven-tantau.de/ *** http://www.bewiso.de/ _______________________________________________ Full-Disclosure - We believe in it. Charter: http://lists.grok.org.uk/full-disclosure-charter.html Hosted and sponsored by Secunia - http://secunia.com/