Lhaca是一款由日本开发的免费文档压缩解压工具。 Lhaca在处理畸形的LHA文档时存在缓冲区溢出漏洞,远程攻击者可能利用此漏洞通过诱使用户处理恶意文件控制用户机器。 Lhaca没有充分地验证从LHA文件读取的Extended Header Size值便将其拷贝到了Extended Header Data字节数的栈缓冲区,如果Extended Header Size值大于255的话就可能触发缓冲区溢出,导致执行任意指令;此外由于没有正确地使用strncpy()还可能导致进一步覆盖该缓冲区。有漏洞的函数如下: function_40D974(FILE *fp, char *outbuffer) { char var_1408[4096]; char overflowedBuffer[255]; //var_408 char var_309[769]; DWORD var_8; DWORD var_4; var_4 = 0; memset(outbuffer, 0, 0x12c); // clear 300-byte buffer ... ... fseek(fp, 1, SEEK_SET); ... ... if(fread(&var_1408[1], 1, 0x24, fp) < 0x24) { // exit with error "Invalid header (LHarc file ?)" } globalReadBufPtr = &var_1408[20]; // header-level byte position outbuffer[0x15] = *globalReadBufPtr; globalReadBufPtr++; if(outbuffer[0x15] != 2) // check header level { if(fread(&var_1408[0x25], 1, 2, fp) < 2) // read length of extended header { // exit with error "Invalid header (LHarc file ?)" } } if(outbuffer[0x15] >= 3) { // exit with error "Unknown level header" } globalReadBufPtr = &var_1408[2]; outbuffer[0] = 0x25; memcpy(&outbuffer[1], &var_1408[2], 5); ... ... ... // globalReadBufPtr points within the var_1408 buffer and currently points // to the filename read from the LZH header. 40DAB9_copy_filename_in_lzh_header_to_outbuffer(&outbuffer[0x16], globalReadBufPtr, length_of_filename); ... ... ... while(...) // loc_40DC67 (loop that reads all headers) { // globalReadBufPtr points within the var_1408 buffer and currently points to // the next Extended Header Size field (two-bytes) that was read from the LZH file. // The getNextExtendedHeaderSize() function will increment the "globalReadBufPtr" // pointer by two bytes. hdrSize = getNextExtendedHeaderSize(globalReadBufPtr); // stop processing if the next header size is 0 if(hdrSize == 0) break; if(outbuffer[0x15] != 2) // header level { // This code checks that there is enough space to read header. // The result of overflowedBuffer - globalReadBufPtr can be up // to 0xFD9 (4057 bytes). // So up the 4057 bytes can be read in this buffer. i.e. "hdrSize" // can be up to 0xFD9. // Note that globalReadBufPtr points within var_1408. if(overflowedBuffer - globalReadBufPtr >= hdrSize) { if(fread(globalReadBufPtr, 1, hdrSize, fp) != hdrSize) { // exit with error "Invalid header (LHa file ?)" } switch(*globalReadBufPtr) { globalReadBufPtr++; case 40: // MS-DOS attribute header : 0x40 { ... } case 2: // Directory name header : 0x02 { // BUFFER OVERFLOW. // (hdrSize - 3)-bytes of directory name in the // extended header is copied from the buffer // pointed to by globalReadBufPtr into // overflowedBuffer. // From above, it should be noted that up to // 0xFD9 (4057) bytes can be read into the buffer // pointed to by globalReadBufPtr. i.e. hdrSize // can be up to 0xFD9. // The size of overflowedBufer is only 0xFF (255). // Note that there is another buffer below // overflowedBuffer, so more than 0x408 (1032) // bytes must be copied into overflowedBuffer to // overwrite the saved EIP. This is possible since // 4057 bytes can be read into globalReadBufPtr. if(hdrSize-3 > 0) { copyloop(overflowedBuffer, globalReadBufPtr, hdrSize-3); } ... } ... } } else { // exit with error "Invalid header (LHa file ?)" } } } if(outbuffer[0x15] != 2) // header level { ... } // &outbuffer[0x16] points to the filename read from the LZH header // WRONG USE OF strncat() !!!! // "overflowedBuffer" already contains the directory name copied from // the extended header. So fixing strncat()'s length argument at // 0xFF allows more bytes to be appended into the buffer that is already // overflowed. strncat(overflowedBuffer, &outbuffer[0x16], 0xFF); strncpy(&outbuffer[0x16], overflowedBuffer, 0xFF); ... ... ... ... } 通过缓冲区溢出覆盖所保存的EIP就可以执行任意指令。 Lhaca 1.21 目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载: <a href="http://park8.wakwak.com/~app/Lhaca/" target="_blank">http://park8.wakwak.com/~app/Lhaca/</a>