oCERT #2009-009 CamlImages integer overflows
CamlImages, an open source image processing library, suffers from several integer overflows which may lead to a potentially exploitable heap overflow and result in arbitrary code execution.
The vulnerability is triggered by PNG image parsing, the read_png_file and read_png_file_as_rgb24 functions do not properly validate the width and height of the image. Specific PNG images with large width and height can be crafted to trigger the vulnerability.
CamlImages <= 2.2
Unfortunately oCERT has been unable to get feedback from CamlImages maintainers and the package seems unmaintained, it's therefore suggested to avoid CamlImages usage on production or any environment where strong security is needed.
Credit: vulnerability report and PoC code received from Tielei Wang <wangtielei [at] icst [dot] pku [dot] edu [dot] cn>, ICST-ERCIS.
2009-05-21: vulnerability reported received
2009-05-21: contacted camlimages maintainers
2009-06-30: due to lack of feedback oCERT asks reporter to disclose the issue
2009-07-01: reporter agrees to disclosure
2009-07-02: assigned CVE
2009-07-02: advisory release
ml: app-text/active-dvi seems to be using this library, so in case of removal, both would have to go. Do you have contact to upstream?
There is a new version (3.0.1) at http://gallium.inria.fr/camlimages/ (it seems it has been released a long time ago: 2007-10-11 according to the tarball mtime)
Is it also exploitable?
Considering the timeline I'd suspect it is, but the advisory is about <=2.2.
The advisory has been updates to reflect that 3.0.1 is also affected. Alexis, I forwarded the reproducer to you. The email to upstream bounced for one of the two authors (Jun.Furuse).
Multiple integer overflows in CamlImages 2.2 and earlier might allow
context-dependent attackers to execute arbitrary code via a crafted
PNG image with large width and height values that trigger a
heap-based buffer overflow in the (1) read_png_file or (2)
3.0.1 in tree with upstream patches; it checks for oversized images in gif, jpeg and png so maybe there were other vulns. in jpeg & gif readers.
Created attachment 199108 [details, diff]
Fixes for CVE-2009-2295 and gif/jpeg overflows.
Arches, please test and mark stable:
Target keywords : "ppc x86"
this will break it's only reverse-dep app-text/active-dvi-1.7.3:
/usr/bin/ocamlc.opt -I /usr/lib/ocaml/lablgtk2 -I /usr/lib/ocaml -I /usr/lib/ocaml/site-packages/camlimages -o dvi.cmo -c dvi.ml
/usr/bin/ocamlc.opt -I /usr/lib/ocaml/lablgtk2 -I /usr/lib/ocaml -I /usr/lib/ocaml/site-packages/camlimages -o drawimage.cmo -c drawimage.ml
File "drawimage.ml", line 304, characters 8-18:
Unbound value Ps.load_ps
make: *** [drawimage.cmo] Error 2
make: *** Waiting for unfinished jobs....
make: Leaving directory `/var/tmp/portage/app-text/active-dvi-1.7.3/work/advi-1.7.3/src'
make: *** [all] Error 2
make: Leaving directory `/var/tmp/portage/app-text/active-dvi-1.7.3/work/advi-1.7.3/src'
make: *** [all-recursive] Error 1
* ERROR: app-text/active-dvi-1.7.3 failed.
* Call stack:
* ebuild.sh, line 49: Called src_compile
* environment, line 2672: Called die
* The specific snippet of code:
* emake || die "emake failed";
* The die message:
* emake failed
[ebuild R ] dev-ml/camlimages-3.0.1 USE="gif gtk jpeg tiff truetype -doc -gs -xpm" 0 kB
[ebuild R ] app-text/active-dvi-1.7.3 USE="ocamlopt -tk" 0 kB
Portage 126.96.36.199 (default/linux/x86/2008.0/desktop, gcc-4.3.2, glibc-2.9_p20081201-r2, 188.8.131.52 i686)
System uname: Linux-184.108.40.206-i686-Intel-R-_Core-TM-2_Duo_CPU_T8300_@_2.40GHz-with-glibc2.0
Timestamp of tree: Mon, 27 Jul 2009 21:00:01 +0000
sys-devel/autoconf: 2.13, 2.63-r1
sys-devel/automake: 1.4_p6, 1.5, 1.7.9-r1, 1.9.6-r2, 1.10.2
CFLAGS="-O2 -march=i686 -pipe"
CONFIG_PROTECT="/etc /usr/kde/3.5/env /usr/kde/3.5/share/config /usr/kde/3.5/shutdown /usr/share/config"
CONFIG_PROTECT_MASK="/etc/ca-certificates.conf /etc/env.d /etc/env.d/java/ /etc/fonts/fonts.conf /etc/gconf /etc/php/apache2-php5/ext-active/ /etc/php/cgi-php5/ext-active/ /etc/php/cli-php5/ext-active/ /etc/revdep-rebuild /etc/sandbox.d /etc/terminfo /etc/texmf/language.dat.d /etc/texmf/language.def.d /etc/texmf/updmap.d /etc/texmf/web2c /etc/udev/rules.d"
CXXFLAGS="-O2 -march=i686 -pipe"
FEATURES="collision-protect distlocks fixpackages parallel-fetch protect-owned sandbox sfperms strict test unmerge-orphans userfetch userpriv usersandbox"
LINGUAS="en en_GB de"
PORTAGE_RSYNC_OPTS="--recursive --links --safe-links --perms --times --compress --force --whole-file --delete --stats --timeout=180 --exclude=/distfiles --exclude=/local --exclude=/packages"
USE="X acl acpi alsa apache2 avahi berkdb bluetooth branding bzip2 cairo cdr cli cracklib crypt cups dbus dri dvd dvdr dvdread eds emboss encode esd evo examples fam firefox fortran gdbm gif gnome gpm gstreamer gtk hal iconv ipv6 isdnlog jpeg kde ldap libnotify mad midi mikmod mp3 mpeg mudflap ncurses nls nptl nptlonly ogg opengl openmp pam pcre pdf perl png ppds pppd python qt3 qt3support qt4 quicktime readline reflection sdl session source spell spl ssl startup-notification svg sysfs tcpd test tiff truetype unicode usb vorbis win32codecs x86 xml xorg xulrunner xv zlib" ALSA_CARDS="ali5451 als4000 atiixp atiixp-modem bt87x ca0106 cmipci emu10k1 emu10k1x ens1370 ens1371 es1938 es1968 fm801 hda-intel intel8x0 intel8x0m maestro3 trident usb-audio via82xx via82xx-modem ymfpci" ALSA_PCM_PLUGINS="adpcm alaw asym copy dmix dshare dsnoop empty extplug file hooks iec958 ioplug ladspa lfloat linear meter mmap_emul mulaw multi null plug rate route share shm softvol" APACHE2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias" ELIBC="glibc" INPUT_DEVICES="keyboard mouse evdev" KERNEL="linux" LCD_DEVICES="bayrad cfontz cfontz633 glk hd44780 lb216 lcdm001 mtxorb ncurses text" LINGUAS="en en_GB de" USERLAND="GNU" VIDEO_CARDS="fbdev glint i810 intel mach64 mga neomagic nv r128 radeon savage sis tdfx trident vesa vga via vmware voodoo"
Unset: CPPFLAGS, CTARGET, EMERGE_DEFAULT_OPTS, FFLAGS, INSTALL_MASK, LANG, LC_ALL, PORTAGE_COMPRESS, PORTAGE_COMPRESS_FLAGS, PORTAGE_RSYNC_EXTRA_OPTS, PORTDIR_OVERLAY
Its just missing usedeps from advi part; added active-dvi-1.7.3-r1 that fixes that. Please mark it stable together with camlimages 3.0.1 and remove the old active-dvi revision while at it.
Multiple integer overflows in CamlImages 2.2 might allow
context-dependent attackers to execute arbitrary code via images
containing large width and height values that trigger a heap-based
buffer overflow, related to (1) crafted GIF files (gifread.c) and (2)
crafted JPEG files (jpegread.c), a different vulnerability than
The PNG patch appears to be incomplete (rowbytes itself is never checked, unless in libpng; I personally would always include my own, explicit check), and oversized() seems unnecessarily complex (why not check for x and y <= 0 and avoid the explicit "y != 0" in the third clause? images with zero-dimensions aren't valid, either). The GIF and JPEG checks appear to depend on third-party data structures and may break in the future if those ever switch to unsigned values (though I imagine that's unlikely).
(In reply to comment #13)
> The PNG patch appears to be incomplete (rowbytes itself is never checked,
> unless in libpng; I personally would always include my own, explicit check),
I'm not sure I'm following. rowbytes is declared as size_t and libpng's png_get_rowbytes() returns a uint_32, which is an identical type. What else do you want to check?
rowbytes is only used in memory allocations or access in multiplications with height, and that case is checked.
> and oversized() seems unnecessarily complex (why not check for x and y <= 0 and
> avoid the explicit "y != 0" in the third clause? images with zero-dimensions
> aren't valid, either).
0 height or width are certainly not errors to cause an overflow message, so the additional case of oversized() returning false for x=0 or y=0 seems sane to me.
> The GIF and JPEG checks appear to depend on third-party
> data structures and may break in the future if those ever switch to unsigned
> values (though I imagine that's unlikely).
The members of the GifFileType struct and the jpeg_decompress_struct that are accessed are explicitly not marked private. How else should camlimages get access to these values?
(In reply to comment #14)
>> The PNG patch appears to be incomplete (rowbytes itself is never checked,
>> unless in libpng; I personally would always include my own, explicit check),
> I'm not sure I'm following. rowbytes is declared as size_t and libpng's
> png_get_rowbytes() returns a uint_32, which is an identical type. What
> else do you want to check? rowbytes is only used in memory allocations
> or access in multiplications with height, and that case is checked.
rowbytes itself is a product, and you never check that. Consider an RGB
image with w = 1431655766: rowbytes = 2. Does libpng catch the overflow?
Maybe so, maybe not, or maybe now but not in the future due to an oversight;
the point is that you're depending on it to do so, and I'm saying that's not
a very good approach to security. (As you noted, you're using rowbytes in
memory allocations, but none of your rowbytes*h checks will catch the problem.
>> and oversized() seems unnecessarily complex (why not check for x and y <= 0 and
>> avoid the explicit "y != 0" in the third clause? images with zero-dimensions
>> aren't valid, either).
> 0 height or width are certainly not errors to cause an overflow message,
> so the additional case of oversized() returning false for x=0 or y=0
> seems sane to me.
The point is simply that zero-dimensioned images serve no valid purpose, and
you can save a few clock cycles by disallowing them:
#define oversized(x, y) ((x) <= 0 || (y) <= 0 || (x) > INT_MAX / (y))
Cheap optimization, though undoubtedly not measurable...
>> The GIF and JPEG checks appear to depend on third-party
>> data structures and may break in the future if those ever switch to unsigned
>> values (though I imagine that's unlikely).
> The members of the GifFileType struct and the jpeg_decompress_struct
> that are accessed are explicitly not marked private. How else should
> camlimages get access to these values?
Simply by copying to your own integer variables. I'm not claiming you
necessarily gain anything by doing so--nothing in the patch screams at me
"I will break if x is unsigned"--but I have seen other, similar (security)
code depend on the signedness of integers that turned out not to be signed.
You may want to do a quick mini-audit and make sure there are no signed/
unsigned dependencies in nearby code.
(My first post to gentoo's bugzilla :) )
Just a general remark on this bug, has anyone checked tiffread.c? I can't find any validation checking in there and was wondering, whether it is required.
Also, I am still getting a segmentation fault for the crafted jpeg image that was sent as a PoC via vendor-sec (I can hand that out via private email).
Just wondering, whether someone checked it. I am attaching some code to test it, provided by our camlimages maintainer, maybe it's useful.
Created attachment 201702 [details]
testsuite for PoC
(In reply to comment #16)
> Just a general remark on this bug, has anyone checked tiffread.c?
I think those issues are confirmed now, bug 290222. ;)