Exploit-DB #9985
Description: Xpdf 3.01 - Local Heap Overflow / Null Pointer Dereference
Type: local
Platform: multiple
Date: 2009-10-17
Author: Adam Zabrocki

Exploit:
Name:                 Xpdf - Integer overflow which causes heap overflow and NULL pointer derefernce
Author:               Adam Zabrocki / HISPASEC (<pi3@itsec.pl> or <adam@hispasec.com>)
Date:                 July 06, 2009


Issue:

Xpdf allows local and remote attackers to overflow buffer on heap via integer overflow vulnerability.
Xpdf is prone to NULL pointer dereference attack.


Description:

Xpdf is an open-source viewer for Portable Document Format (PDF) files.
Xpdf project also includes a PDF text extractor, PDF-to-PostScript converter, and various other
utilities. Xpdf runs under the X Window System on UNIX, VMS, and OS/2. The non-X components
(pdftops, pdftotext, etc.) also run on Win32 systems and should run on pretty much any system with a
decent C++ compiler.
Xpdf is designed to be small and efficient. It can use Type 1, TrueType, or standard X fonts. 


Details:


Let's look in code:

"./goo/gmem.cc"
void *gmalloc(int size) GMEM_EXCEP {
#ifdef DEBUG_MEM 
   ...
#else 
  void *p;

  if (size < 0) {
#if USE_EXCEPTIONS 
   ...
#else 
    fprintf(stderr, "Invalid memory allocation size\n");
    exit(1);
#endif 
  }
  if (size == 0) {
    return NULL;
  }
  if (!(p = malloc(size))) {
#if USE_EXCEPTIONS 
   ...
#else 
    fprintf(stderr, "Out of memory\n");
    exit(1);
#endif 
  }
  return p;
#endif 
}

Ok. So if we pass negative value to gmalloc() than xpdf finish work via
exit() call
and print to stderr "Invalid memory allocation size\n". If we pass 0
(zero) value
than function return NULL. In other cases there will be normal call to
malloc() func.

Ok so let's look further.

"./splash/Splash.cc"
SplashError Splash::drawImage(SplashImageSource src, void *srcData,
                              SplashColorMode srcMode, GBool srcAlpha,
                              int w, int h, SplashCoord *mat) {
...
...
  SplashClipResult clipRes, clipRes2;
  int yp, yq, yt, yStep, lastYStep;
  int xp, xq, xt, xStep, xSrc;
...
  SplashColorPtr colorBuf, p;
...
#if SPLASH_CMYK 
  int pixAcc0, pixAcc1, pixAcc2, pixAcc3;
#else 
  int pixAcc0, pixAcc1, pixAcc2;
#endif 
...
  int nComps, n, m, i, j;

...
  // check color modes
  ok = gFalse; // make gcc happy
  nComps = 0; // make gcc happy
  switch (bitmap->mode) {
  case splashModeMono1:
  case splashModeMono8:
    ok = srcMode == splashModeMono8;
    nComps = 1;
    break;
  case splashModeRGB8:
    ok = srcMode == splashModeRGB8;
    nComps = 3;
    break;
  case splashModeBGR8:
    ok = srcMode == splashModeBGR8;
    nComps = 3;
    break;
#if SPLASH_CMYK 
  case splashModeCMYK8:
    ok = srcMode == splashModeCMYK8;
    nComps = 4;
    break;
#endif 
  }
  if (!ok) {
    return splashErrModeMismatch;
  }
...
...

  // compute Bresenham parameters for x and y scaling
  yp = h / scaledHeight;
  yq = h % scaledHeight;
  xp = w / scaledWidth;
  xq = w % scaledWidth;

  colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);
<- [1] !!!
  if (srcAlpha) {
    alphaBuf = (Guchar *)gmalloc((yp + 1) * w);
  } else {
    alphaBuf = NULL;
  }

  pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy

...
...

  if (srcAlpha) {

...
<BIG code>
...
  } else {

    // init y scale Bresenham
    yt = 0;
    lastYStep = 1;

    for (y = 0; y < scaledHeight; ++y) {

      // y scale Bresenham
      yStep = yp;
      yt += yq;
      if (yt >= scaledHeight) {
        yt -= scaledHeight;
        ++yStep;
      }
      
      // read row(s) from image
      n = (yp > 0) ? yStep : lastYStep;
      if (n > 0) {
        p = colorBuf;
        for (i = 0; i < n; ++i) {
          (*src)(srcData, p, NULL);                 <- [!!]  !!!
          p += w * nComps;
        }
      }

...
<now we will be looking in code for splashModeRGB8 and splashModeBGR8>
...

      switch (srcMode) {

...
...

      case splashModeRGB8:
      case splashModeBGR8:
        for (x = 0; x < scaledWidth; ++x) {

          // x scale Bresenham
          xStep = xp;
          xt += xq;
          if (xt >= scaledWidth) {
            xt -= scaledWidth;
            ++xStep;
          }

...
...

          // compute the filtered pixel at (x,y) after the x and y
scaling
          // operations
          m = xStep > 0 ? xStep : 1;
          p = colorBuf + xSrc * 3;               <-             [2] !!!
          pixAcc0 = pixAcc1 = pixAcc2 = 0;
          for (i = 0; i < n; ++i) {
            for (j = 0; j < m; ++j) {
              pixAcc0 += *p++;                   <-             [3] !!!
              pixAcc1 += *p++;
              pixAcc2 += *p++;
            }
            p += 3 * (w - m);
          }

...
...
<BIG code>
...
...
}


We immediately control variable "w" and "h". So if we set variable "w" to value zero (0).
After that call to gmalloc (in [1]) will return NULL. There is no check what value was returned!
So in [2] we have p = NULL + xSrc*3, xSrc we can set to 0 (zero) too. So in fact we can set "p" to NULL value. In [3] we have NULL pointer dereference!

Ok let's look for other scenario. What will happen if variable "w" have BIG value? Let's look:

(yp + 1) * w * nComps

"w" we can control immediately, "nComps" have some static value and "yp" we can controle indirectly because:

  yp = h / scaledHeight;

"h" we control once again immediately! So in fact we can do integer overflow and allocate less memory than it should be. For example:

h =[PII_REDACTED]
w = 1102
nComps = 3
yp / scaledHeight = 15873015, for standard scaledHeight = 126

so it should be => (15873015+1)*1102*3 = 15873016*1102*3 =[PII_REDACTED]6
but in fact after integer overflow it is:
yp[15873015]+1)*w[1102]*nComps[3] = 936583344

So it is too little :)

In  this scenario after call to gmalloc() program will go after some
instruction to code in [!!].
in fact this is call to some pointer which r