xorl %eax, %eax

CVE-2008-1389: ClamAV CHM Parser DoS

leave a comment »

ClamAV is clearly the most popular open source anti-virus software. It supports numerous platforms including Windows and BSD derivatives but it is mainly used on servers for centralized virus scanning. This bug was found by Hanno Böck of schokokeks.org webhosting on 9 August 2008 and it was finally patched on ClamAV 0.94 release. The code presented here is from the last vulnerable release of ClamAV which was 0.93. The vulnerable function is located at libclamav/chmunpack.c where the CHM unpacker code is.

857  int cli_chm_open(int fd, const char *dirname, chm_metadata_t *metadata)
858  {
859          struct stat statbuf;
860          int retval;
       ...
864          if ((retval = chm_init_metadata(metadata)) != CL_SUCCESS) {
       ...
909          while (metadata->num_chunks) {
910                  if (read_chunk(metadata, fd) != CL_SUCCESS) {
911                          cli_dbgmsg("read_chunk failed");
912                          goto abort;
913                  }
914                  read_control_entries(metadata);
915                  metadata->num_chunks--;
916                  metadata->chunk_offset += metadata->itsp_hdr.block_len;
917          }
       ...
934          return CL_SUCCESS;
935
936  abort:
       ...
942          return CL_EFORMAT;
943  }

This is not so obvious if you are not familiar with internal ClamAV library functions. At line 864 it initializes the metadata structure which is basically the meta-data contained in the CHM file scanned by ClamAV. Next, at lines 909-917 it attempts to read iteratively the number of chunks contained in metadata structure. If it fails on reading the metadata from file (line 910) it directly jumps to abort label. Then it attempts to read the control entries included in meta-data section of the CHM file. Here is the problem. Regardless of the action of this routine, the while loop re-attempts to read the rest of the meta-data control entries! A quick look at this function reveals that it could however fail…

375  /* Read control entries */
376  static int read_control_entries(chm_metadata_t *metadata)
377  {
        ...
382                  if (metadata->chunk_current > metadata->chunk_end) {
383                          cli_dbgmsg("read chunk entries failed\n");
384                          return FALSE;
385                  }
        ...
388                  if (((metadata->chunk_current + name_len) > metadata->chunk_end) || ((metadata->chunk_current + name_len) < metadata->chunk_data)) {
389                          cli_dbgmsg("Bad CHM name_len detected\n");
390                          return FALSE;
391                  }
        ...
416          return TRUE;
417  }

So if you can construct a CHM file that it drops into one of those states, you can have an inconsistency where the CHM should be handled as corrupted but instead, it is treated as normal file by cli_chm_open() and attempts to access the rest of the chunks even though they are corrupted. The patch for this vulnerability was:

                        cli_dbgmsg("read_chunk failed");
                        goto abort;
                }
-               read_control_entries(metadata);
+               if (read_control_entries(metadata) == FALSE) {
+                       goto abort;
+               }
                metadata->num_chunks--;
                metadata->chunk_offset += metadata->itsp_hdr.block_len;

Finally, Hanno Böck also provided a couple of PoC CHM files that trigger this vulnerability. You can download them here. If you are really curious about what values do you need to construct this file maybe you should code a small application that just fills this structure:

typedef struct chm_metadata_tag {
        uint64_t file_length;
        uint64_t file_offset;
        chm_sys_entry_t sys_control;
        chm_sys_entry_t sys_content;
        chm_sys_entry_t sys_reset;
        off_t m_length;
        char *m_area;
        chm_itsf_header_t itsf_hdr;
        chm_itsp_header_t itsp_hdr;
        int ufd;
        int ofd;
        uint32_t num_chunks;
        off_t chunk_offset;
        char *chunk_data;
        char *chunk_current;
        char *chunk_end;
        uint16_t chunk_entries;
} chm_metadata_t;


I haven’t done this and of course it might (almost certainly it will) require additional parsing. Anyway, it was kind of interesting vulnerability.

Written by xorl

March 12, 2009 at 14:04

Posted in bugs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s