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@overflow.pl>
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 th=
is
software is the integration with mail servers (attachment scanning). =20
The package
provides a flexible and scalable multi-threaded daemon, a command line =20
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 us=
e
with your own software. Most importantly, the virus database is kept =20
up to date"

http://www.clamav.net


2. Description

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

Vulnerability exists in pefromupx() function, that is used to buil =20
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=3D1;
uint32_t realstuffsz;
uint32_t foffset=3D0xd0+0xf8;

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

if (realstuffsz >=3D *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+=3D0x28*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 =3D 0; upd <sectcnt ; upd++) {
uint32_t vsize=3Dcli_readint32(sections+8)-1;
uint32_t rsize=3Dcli_readint32(sections+16);
uint32_t urva=3Dcli_readint32(sections+12);

.....

cli_writeint32(sections+8, vsize);
cli_writeint32(sections+20, foffset);
foffset+=3Drsize;
sections+=3D0x28;
}

.....

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 =3D 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.