CESA-2009-003 - rev 2
[See all my vulnerabilities at
http://scary.beasts.org/security]
[Blog if you want to subscribe to new findings is at
http://scarybeastsecurity.blogspot.com/]
LCMS (Little CMS / LittleCMS) memory corruptions
Programs affected: lcms, Firefox, OpenJDK, lcms consumers (GIMP, etc).
Fixed: lcms-1.18beta2
Severity: Arbitrarty code execution from remote.
CVE-2009-0723: integer overflows leading to buffer overflows.
CVE-2009-0581: large memory leak.
CVE-2009-0733: stack-based buffer overflow due to missing bounds check.
LittleCMS is used for colour correction and management in several critical
pieces of software. It is typical for this software to look inside JPEG files
for ICC profiles, and feed them through LittleCMS if found. Therefore,
opening or viewing a malicious JPEG can compromise your machine via
affected software.
The OpenJDK case is arguably the most critical because it can be abused directly
on the server side to take over servers. All it needs is an application that
parses untrusted JPEG files (e.g. to thumbnail them).
It is against this same threat that the memory leak is significant: taking down
server JVMs.
In addition, OpenJDK of course backs the Java appletviewer on modern Linux
desktops, so this could be abused to take over web client machines.
Furthermore, it's interesting to note that the OpenJDK package is often built
without many of the system defenses that would mitigate this bug - e.g. the
stack is often forced executable, and stack canaries / protection are disabled
for whatever reason.
The Firefox case is fortunately not as severe as it could be. Firefox 3.0 does
not parse embedded ICC profiles in the default configuration. Firefox 3.1 has
changed to parse them by default, so it's nice to get these bugs fixed before
3.1 goes production and gets widely deployed.
The code responsible for the most severe bug, CVE-2009-0733, looks like this:
static
LCMSBOOL ReadSetOfCurves(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, int nLocation)
{
LPGAMMATABLE Curves[MAXCHANNELS];
unsigned int i, nCurves;
if (Icc -> Seek(Icc, Offset)) return FALSE;
if (nLocation == 1 || nLocation == 3)
nCurves = NewLUT ->InputChan;
else
nCurves = NewLUT ->OutputChan;
for (i=0; i < nCurves; i++) {
Curves[i] = ReadCurve(Icc);
...
Where MAXCHANNELS
is 16
. However, the overflow is not
as obvious / clear cut as the above code might suggest. Some common code paths
place an upper bound on InputChan
and OutputChan
.
However, ReadLUT_A2B
and ReadLUT_B2A
do not, e.g.:
static
LCMSBOOL ReadLUT_A2B(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig)
{
icLutAtoB LUT16;
if (Icc ->Read(&LUT16, sizeof(icLutAtoB), 1, Icc) != 1) return FALSE;
NewLUT -> InputChan = LUT16.inputChan;
NewLUT -> OutputChan = LUT16.outputChan;
...
if (LUT16.offsetB != 0)
ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 2);
...
Exploitation
I don't usually exploit bugs, but there are nice subtleties here to exploit
this, so I'll post a demo exploit to my blog, with explanation, over the next
few days.
Credits
- Google - found on Google's paid time.
- oCERT (and particularly Andrea Barisani) for co-ordinating a tricky issue.
- Tomas Hoger (RedHat) for fixing up serious issues with the integer overflow detection.
- Josh Bressers (RedHat) for preventing div-by-zero errors making it into the final patch.
- Marti Maria (lcms maintainer) for being patient with multiple incremental patches.
- Jan Lieskovsky (RedHat) for help co-ordinating and expanding investigation to other open-source packages.
- Julien Tinnes for interesting discussions regarding executability of
heaps.
CESA-2009-003 - rev 2
Chris Evans
scarybeasts@gmail.com