php security image-processing gd code-injection

php - ¿Cómo evitar que las personas carguen GIF con inyecciones?



security image-processing (7)

Tengo un sitio web de PHP donde las personas pueden completar los tickets de ayuda. Les permite subir capturas de pantalla para su boleto. Permito que se carguen gif, psd, bmp, jpg, png, tif. Al recibir la carga, el script PHP ignora la extensión del archivo. Identifica el tipo de archivo utilizando solo la información MIME, que para estos tipos de archivo siempre se almacena dentro de los primeros 12 bytes del archivo.

Alguien subió varios archivos GIF, que cuando se vieron con un navegador, el navegador dijo que no era válido, y mi escáner de virus me alertó de que se trataba de una inyección (o algo así). Vea a continuación un archivo zip que contiene estos GIF.

No creo que solo verificar la información del encabezado sea adecuado. He escuchado que una imagen puede ser completamente válida, pero también contiene código de explotación.

Así que tengo dos preguntas básicas:

  1. ¿Alguien sabe cómo inyectaron cosas malas en un GIF ( manteniendo un tipo MIME GIF válido )? Si sé esto, tal vez pueda verificarlo en el momento de la carga.
  2. ¿Cómo puedo evitar que alguien suba archivos como este?
    • Estoy en un alojamiento compartido, así que no puedo instalar un escáner de virus del lado del servidor.
    • El envío de la información a un sitio web de detección de virus en línea puede ser demasiado lento.
    • ¿Hay alguna forma de comprobarme usando una clase de PHP que compruebe estas cosas?
    • ¿Cambiará el tamaño de la imagen usando GD si no es válida? ¿O aún se deslizaría la vulnerabilidad y estaría en la imagen redimensionada? Si falla, sería ideal porque entonces podría usar el cambio de tamaño como una técnica para ver si son válidas.

Actualización: Todos, gracias por responder hasta ahora. Estoy intentando buscar en el servidor los GIF que se cargaron. Voy a actualizar este post si los encuentro.

Actualización 2: localicé los GIFs para cualquier persona interesada. Los puse en un archivo zip encriptado con la contraseña "123". Se encuentra aquí (tenga cuidado, hay varios botones de "Descargar" en este sitio de alojamiento, algunos de ellos son para anuncios) http://www.filedropper.com/badgifs . El llamado 5060.gif está marcado por mi antivirus como un troyano (TR / Graftor.Q.2). Debo tener en cuenta que estos archivos se cargaron antes de que yo implementara la verificación MIME de los primeros 12 bytes. Así que ahora, estoy seguro para estos particulares. Pero todavía me gustaría saber cómo detectar una vulnerabilidad que se oculta detrás de un tipo MIME correcto.

Aclaración importante: solo me preocupa el riesgo para la PC que descarga estos archivos para mirarlos. Los archivos no son un riesgo para mi servidor. No serán ejecutados. Se almacenan usando un nombre limpio (una salida de hash hexadecimal) con la extensión ".enc" y los guardo en el disco en un estado cifrado usando un filtro fwrite:

// Generate random key to encrypt this file. $AsciiKey = ''''; for($i = 0; $i < 20; $i++) $AsciiKey .= chr(mt_rand(1, 255)); // The proper key size for the encryption mode we''re using is 256-bits (32-bytes). // That''s what "mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)" says. // So we''ll hash our key using SHA-256 and pass TRUE to the 2nd parameter, so we // get raw binary output. That will be the perfect length for the key. $BinKey = hash(''SHA256'', ''~~''.TIME_NOW.''~~''.$AsciiKey.''~~'', true); // Create Initialization Vector with block size of 128 bits (AES compliant) and CBC mode $InitVec = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND); $Args = array(''iv'' => $InitVec, ''key'' => $BinKey, ''mode'' => ''cbc''); // Save encoded file in uploads_tmp directory. $hDest = fopen(UPLOADS_DIR_TMP.''/''.$Hash.''.enc'', ''w''); stream_filter_append($hDest, ''mcrypt.rijndael-128'', STREAM_FILTER_WRITE, $Args); fwrite($hDest, $Data); fclose($hDest);


1) Nunca sabrá exactamente cuál fue el problema si eliminó el .gif y su A / V no escribió un registro.

P: ¿Está el .gif en cuestión todavía en el servidor?

P: ¿Ha revisado sus registros A / V?

2) Hay muchas posibles vulnerabilidades diferentes, que pueden o no tener nada que ver directamente con el formato de archivo .gif. Aquí hay un ejemplo:

3) Para mitigar el riesgo en este ejemplo, debe:

a) Solo carga archivos ( cualquier archivo) a un directorio seguro en el servidor

b) Solo sirve archivos con sufijos específicos (.gif, .png, etc.)

c) Sea extremadamente paranoico con cualquier cosa que se haya cargado en su sitio (¡especialmente si luego permite que otras personas lo descarguen de su sitio!)


En cuanto a la primera pregunta , nunca sabrás realmente si no puedes recuperar ningún registro o las imágenes en cuestión, porque hay muchas cosas a las que puede haber apuntado este exploit y, dependiendo de cuál sea el objetivo, la forma en que se puso el exploit. en el archivo puede ser completamente diferente.

Edición: W32 / Graftor es un nombre genérico para programas que parecen tener características similares a troyanos.

Después de abrir el archivo 5060.gif en un editor hexadecimal, noté que el programa es en realidad un programa de Windows 5060.gif nombre ha cambiado . Aunque no es un exploit del navegador y, por lo tanto, es inofensivo, a menos que se abra y se ejecute realmente, tendrá que asegurarse de que no se sirva con el tipo MIME definido por el cargador porque un usuario aún puede ser engañado para que abra el programa; Ver la respuesta a la segunda pregunta.

En cuanto a la segunda pregunta : para evitar que se ejecute cualquier código de explotación o un usuario, deberá asegurarse de que todos los archivos se almacenen con una extensión segura en el nombre del archivo para que se sirvan con el tipo MIME correcto. Por ejemplo, puede usar esta expresión regular para verificar el nombre del archivo:

if(!preg_match ( ''///.(gif|p(sd|ng)|tiff?|jpg)$/'' , $fileName)){ header("415 Unsupported Media Type"); die("File type not allowed."); }

También asegúrese de que está sirviendo los archivos con el tipo de contenido correcto; asegúrese de no usar el tipo de contenido especificado con el archivo cargado al entregar el archivo al usuario. Si confía en el tipo de contenido especificado por el cargador, el archivo puede servirse como text/html o algo similar y será analizado por el navegador del usuario como tal.

Tenga en cuenta que esto solo protege contra archivos maliciosos que explotan vulnerabilidades en el navegador de los usuarios, excluyendo el analizador de imágenes.

Si está intentando evitar explotaciones en el servidor, deberá asegurarse de que no permitirá que el analizador PHP ejecute el contenido de la imagen y que la biblioteca de imágenes que está utilizando para procesar la imagen no se conoce. vulnerabilidades

También tenga en cuenta que este código no lo defiende contra las imágenes que contienen un exploit para el analizador de imágenes utilizado por el navegador de los usuarios; para defenderse de esto, puede verificar si getimagesize() evalúa como verdadero según lo sugerido por Jeroen.

Tenga en cuenta que usar getimagesize() solo no es suficiente si no verifica los nombres de los archivos y se asegura de que los archivos se entreguen con el encabezado de Content-Type correcto, ya que las imágenes completamente válidas pueden tener un código HTML / PHP incrustado en los comentarios.


No sé mucho acerca de los formatos de imagen, pero al recrear las imágenes y luego almacenar el resultado, creo que es muy probable que se eliminen cosas innecesarias. Especialmente si elimina todos los metadatos como comentarios y todos los demás tipos de campos integrados opcionales que admiten algunos formatos de imagen.


Puedes probar phpMussel en cualquier script php que acepte subir archivos. El archivo se escaneará utilizando firmas ClamAV, además de algunas firmas heurísticas internas que buscan este tipo de intrusión específicamente.


Puedes usar la función getimagesize() para esto. Si la imagen no es válida, simplemente devolverá false .

if (getimagesize($filename)) { // valid image } else { // not a valid image }

Vale la pena señalar que esto tampoco es 100% seguro, pero es lo mejor que puedes hacer por lo que sé.

Lea más sobre esto aquí .


Respuesta tardía, pero puede ser útil para alguien. Puede intentar este enfoque:

//saves filtered $image to specified $path function save($image,$path,$mime) { switch($mime) { case "image/jpeg" : return imagejpeg(imagecreatefromjpeg($image),$path); case "image/gif" : return imagegif(imagecreatefromgif($image),$path); case "image/png" : return imagepng(imagecreatefrompng($image),$path); } return false; };


Un consejo muy útil para prevenir problemas con PHP inyectado provino del administrador del sistema de mi host: Tengo un sitio donde las personas pueden subir su propio contenido. Quería asegurarme de que el directorio desde donde se sirven las imágenes cargadas no ejecuta ningún PHP. De esa manera, alguien podría incluso publicar una imagen llamada "test.php" y aún NUNCA la analizaría PHP si estuviera en el directorio de carga. La solución fue simple: en la carpeta desde la que se sirve el contenido cargado, coloque el siguiente archivo .htacess:

RewriteEngine On RewriteRule /.$ - [NC] php_flag engine off

Esto apagará el motor de PHP para la carpeta, deteniendo así cualquier intento de iniciar cualquier PHP para explotar las vulnerabilidades del lado del servidor.