<<< Date Index >>>     <<< Thread Index >>>

[Overflow.pl] Clam AntiVirus Win32-UPX Heap Overflow



Overflow.pl Security Advisory #6

Clam AntiVirus Win32-UPX Heap Overflow

Vendor: Clam AntiVirus
Affected version: Prior to 0.88.4
Vendor status: Fixed version released (0.88.4)

Author: Damian Put <pucik@xxxxxxxxxxx>
URL: http://www.overflow.pl/adv/clamav_upx_heap.txt
Date: 09.08.2006

1. Background

"Clam AntiVirus is a GPL anti-virus toolkit for UNIX. The main purpose of this
software is the integration with mail servers (attachment scanning). The package provides a flexible and scalable multi-threaded daemon, a command line scanner,
and a tool for automatic updating via Internet. The programs are based on a
shared library distributed with the Clam AntiVirus package, which you can use
with your own software. Most importantly, the virus database is kept up to date"

http://www.clamav.net


2. Description

Remote exploitation of a heap overflow vulnerability could allow execution of
arbitrary code or cause denial of service.

Vulnerability exists in pefromupx() function, that is used to buil Win32 PE file
from UPX packed file.

The vulnerable code is:

libclamav/upx.c:
------------
int pefromupx (char *src, char *dst, uint32_t *dsize, uint32_t ep, uint32_t
upx0, uint32_t upx1, uint32_t magic)
{
  char *imports, *sections, *pehdr, *newbuf;
  int sectcnt, upd=1;
  uint32_t realstuffsz;
  uint32_t foffset=0xd0+0xf8;

  imports = dst + cli_readint32(src + ep - upx1 + magic);
  realstuffsz = imports-dst;

  if (realstuffsz >= *dsize ) {
    cli_dbgmsg("UPX: wrong realstuff size - giving up rebuild\n");
    return 0;
  }
....

OK first we check that realstuffsz is not larger than dsize.

....
  foffset+=0x28*sectcnt;

  if (!CLI_ISCONTAINED(dst, *dsize, sections, 0x28*sectcnt)) {
    cli_dbgmsg("UPX: Not enough space for all sects - giving up rebuild\n");
    return 0;
  }
....

Now we check that we have enough space for section headers.

....

  for (upd = 0; upd <sectcnt ; upd++) {
    uint32_t vsize=cli_readint32(sections+8)-1;
    uint32_t rsize=cli_readint32(sections+16);
    uint32_t urva=cli_readint32(sections+12);

    .....

    cli_writeint32(sections+8, vsize);
    cli_writeint32(sections+20, foffset);
    foffset+=rsize;
    sections+=0x28;
  }

....

Now, we add to foffset rsize value of all sections and we DON`T check
that we have enough space in *dst.

....

  /* CBA restoring the imports they'll look different from the originals
anyway... */
  /* ...and yeap i miss the icon too :P */

  memcpy(dst, newbuf, foffset);
  *dsize = foffset;
  free(newbuf);

  cli_dbgmsg("UPX: PE structure rebuilt from compressed file\n");
  return 1;
}

....

And there is our heap overflow. We copy from newbuf to dst pointer foffset
bytes, but we don`t check that foffset > *dsize.


3. PoC

The example of crafted upx file: http://overflow.pl/poc/clamav_upx_heap.exe


[pucik@overflow UPX]$ clamscan clamav_upx_heap.exe
*** glibc detected *** double free or corruption (out): 0x08bcbbc0 ***
Przerwane (core dumped)

You can control value of foffset changing "SizeOfRawData" of section 1.