Warehouse hackeado, problemas con la plantilla de Prestashop
Hace unas semanas algunas tiendas online de Prestashop que utilizan la famosa plantilla Warehouse, de IQIT-COMMERCE comenzaron a sufrir ataques. Los desarrolladores encontraron pronto la vulnerabilidad, y realizaron un parche para ponerle solución. El problema lo tienen principalmente las tiendas con una versión de la plantilla anterior a la 3.7.7 , ya que en las posteriores versiones de Warehouse esta solución ya estaba aplicada.
Pero, ¿cómo saber si mi tienda Prestashop está en peligro?
Sencillo. Para saber si tu tienda es vulnerable, y por tanto, está en peligro de sufrir este ataque, bastará con que realices algunas comprobaciones en tu navegador. Para ello deberás escribir en él, sustituyendo el término “midominio” por el dominio correspondiente al sitio que deseas comprobar, alguna de las siguientes opciones:
- http://tudominio.com/modules/simpleslideshow/uploadimage.php
- http://tudominio.com/modules/homepageadvertise/uploadimage.php
- http://tudominio.com/modules/productpageadverts/uploadimage.php
- http://tudominio.com/modules/columnadverts/uploadimage.php
El navegador debe devolverte un texto del estilo: “User is not logged in”, “error” o “no file”. Si no lo hace, tienes un problema y debes aplicarle solución.
¿Cuál es la solución?¿Qué módulos de la plantilla se han visto afectados?
Como has podido deducir de la comprobación anterior, los módulos de la plantilla que se han visto afectados por la vulnerabilidad son los siguientes:
- modules/simpleslideshow/slides
- modules/homepageadvertise/slides
- modules/productpageadverts/slides
- modules/columnadverts/slides
Si tu tienda todavía no ha sido atacada, tienes dos opciones:
- Aplicar el parche de seguridad publicado por los desarrolladores y que puedes descargar aquí: https://drive.google.com/file/d/0B6yfaCTJqFdeYldDNmg0d0Iwd1U/view?usp=sharing . Se trata de un fichero .zip con un PDF explicativo y los archivos que debes sustituir en el directorio modules de tu tienda. En el fichero encontrarás los ficheros tanto para Pestrashop 1.5 como para 1.6.
Security update for @PrestaShop Warehouse theme is avaiable. Recommanded to use security hotfix from package or update to latest version
— IQIT-COMMERCE (@agencja_iqit) 16 de junio de 2016
- Actualizar la plantilla. Como ya hemos señalado antes, el problema está solucionado a partir de la versión 3.7.7 de Warehouse.
Si no has tenido suerte, y tu tienda ha sido atacada, debes seguir los siguientes pasos:
- Restaurar una copia de seguridad de tu tienda online, anterior al hackeo, y cambiar todas la contraseñas de usuarios, FTP, administradores de la tienda, bases de datos, etc.
- Revisar si existen ficheros con contenidos tipo “base64”, “eval”. Para buscar de forma rápida en nuestros ficheros, bastará con conectarnos por ssh a nuestro servidor, y realizar una búsqueda tipo:
find . -type f | xargs grep -E "base64_decode|eval"
(teniendo en cuenta que nuestro directorio activo sea la raíz del directorio público html, en caso contrario, sustituyendo “.” por el directorio público html). También debes tener en cuenta, que no todos los resultados que nos devuelva esta búsqueda son ficheros malignos, ya que estas funciones (como base64_decode), son usadas por módulos o el mismo PrestaShop, por lo que habría que revisarlas individualmente, si se tienen dudas. Además, estas funciones también podrían estar ofuscadas, por lo que no es un solución única para nuestro problema. Próximamente publicaremos un script que nos avisará de la creación de nuevos ficheros php, y nos mantendrá alerta de posibles ataques.
- Proceder con los puntos del apartado anterior: aplicar el parche o actualizar nuestro tema.
Si quiere más información sobre el tema, puede ver el siguiente vídeo:
Explicación en formato texto del ataque:
Ejemplo práctico de Prestashop atacado:
Hace un par de semana nos llegó un cliente que había recibido un correo electrónico de certsi, en el que le indicaban que su web había sido modificada, al encontrarse un fichero llamado aan.htm en la raíz de su directorio público (Defacement detectado en página web xxx).
Lo primero que hicimos fue revisar el fichero de logs del servidor, que en su caso, era un Apache:
36.70.107.117 - - [12/Jun/2016:11:48:45 +0200] "GET /tienda/modules/columnadverts/slides/paypal.png HTTP/1.1" 200 43134 "https://www.google.com" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 292 36.70.107.117 - - [12/Jun/2016:11:49:02 +0200] "GET /tienda/modules/columnadverts/uploadimage.php HTTP/1.1" 200 297 "-" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 317 36.70.107.117 - - [12/Jun/2016:11:49:02 +0200] "GET /favicon.ico HTTP/1.1" 200 233 "-" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 284 36.70.107.117 - - [12/Jun/2016:11:49:03 +0200] "GET /favicon.ico HTTP/1.1" 200 233 "-" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 284 36.70.107.117 - - [12/Jun/2016:11:50:33 +0200] "POST /tienda/modules/columnadverts/uploadimage.php HTTP/1.1" 200 306 "http://localheart.esy.es/public_html/public_html/tools/?tools=csrf" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 13978 36.70.107.117 - - [12/Jun/2016:11:50:38 +0200] "GET /tienda/modules/columnadverts/slides/t.php HTTP/1.1" 200 833 "-" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 314 36.70.107.117 - - [12/Jun/2016:11:50:40 +0200] "POST /tienda/modules/columnadverts/slides/t.php HTTP/1.1" 200 11098 "http://sitio.com/tienda/modules/columnadverts/slides/t.php" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 479 36.70.107.117 - - [12/Jun/2016:11:50:44 +0200] "GET /tienda/modules/columnadverts/slides/t.php?path=/directorio_publico_html HTTP/1.1" 200 55781 "http://sitio.com/tienda/modules/columnadverts/slides/t.php" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 453 36.70.107.117 - - [12/Jun/2016:11:50:53 +0200] "POST /tienda/modules/columnadverts/slides/t.php?path=/directorio_publico_html HTTP/1.1" 200 57468 "http://sitio.com/tienda/modules/columnadverts/slides/t.php?path=/directorio_publico_html" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 1335 36.70.107.117 - - [12/Jun/2016:11:50:58 +0200] "GET /tienda/modules/columnadverts/slides/t.php HTTP/1.1" 200 11098 "-" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 360 36.70.107.117 - - [12/Jun/2016:11:51:01 +0200] "GET /tienda/modules/columnadverts/slides/t.php?option&path=/directorio_publico_html/tienda/modules/columnadverts/slides&opt=delete&type=file&name=t.php HTTP/1.1" 200 9262 "http://sitio.com/tienda/modules/columnadverts/slides/t.php" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 528 36.70.107.117 - - [12/Jun/2016:11:51:05 +0200] "GET /aan.htm HTTP/1.1" 200 1000 "-" "Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0" 326 69.42.220.27 - - [12/Jun/2016:11:55:57 +0200] "GET /aan.htm HTTP/1.1" 200 1000 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)" 184 69.42.220.27 - - [12/Jun/2016:11:55:59 +0200] "HEAD / HTTP/1.1" 200 390 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)" 127
Como pudimos ver, con la ayuda de una herramienta on-line (http://localheart.esy.es/public_html/public_html/tools/?tools=csrf), se hizo un POST al fichero vulnerable uploadimage.php, subiendo un fichero llamado t.php (el cual quedó almacenado en el directorio slides del módulo vulnerable columnadverts), el cual, repasaba ficheros del servidor, así como abría una consola remota (entre otras cosas).
Una vez detectado esto, ya podíamos decir que el sitio había sido crackeado, por lo que tuvimos que limpiarlo a fondo, recuperando copias anteriores, y cambiando las contraseñas de las aplicaciones web, así como de terminales, ssh, bbdd, etc.
Con el siguiente comando podremos saber si han atacado nuestro sitio (ya que en todos los módulos vulnerables, éste es el fichero con el problema):
cat fichero_de_logs.log | "grep uploadimage.php" | grep "POST"
En este caso en concreto, el ataque provenía de Indonesia, aunque sólo podemos saber el último salto desde el cual atacaron (podría ser un proxy, vpn, o cualquier otro sitio atacado previamente).
Explicación de la vulnerabilidad
Todos los módulos vulnerables, contienen un fichero llamado uploadimage.php, el cual acepta un fichero subido ($_FILES[‘userfile’][‘name’];) y lo mueve a un directorio concreto (en caso de columnadverts, el directorio el slides). El problema es que no se comprueba en ningún caso que el fichero subido sea un tipo de imagen, y aunque se comprobara (es fácil subir un fichero camuflándolo por otro tipo), no usa las funciones de PrestaShop para comprobar si el usuario que lo está subiendo es o no un empleado (employee del backoffice), por lo que uploadimage.php siempre subirá un fichero al subdirectorio indicado, independientemente de PrestaShop. La solución rápida a esto, es añadir un error 404 después de abrir la etiqueta de php, y luego acabando la ejecución del mismo:
header("HTTP/1.0 404 Not Found"); echo "¡Atrasss!"; die();
Pero ésta no es la mejor solución, ya que estos módulos no funcionarían. La solución es antes de subir el fichero, comprobar que el usuario ha iniciado sesión en el backoffice de PrestaShop, y que tiene los privilegios que queramos. Esto lo podemos hacer usando el objeto contexto de PrestaShop, por ejemplo, usando:
if ($this->context->employee->isLoggedBack()){Aquí el código a ejecutar si es empleado}
Hay que recordar, que para usar las funciones de PrestaShop en un script php, antes hay que incluir el fichero config/config.inc.php de PrestaShop (por ejemplo con un include_once).
Fichero vulnerable de estos módulos (en este caso, de columnadverts):
<?php function MakeFriendlyUrl($sString) { $asCharacters = Array( //WIN "\xb9" => "a", "\xa5" => "A", "\xe6" => "c", "\xc6" => "C", "\xea" => "e", "\xca" => "E", "\xb3" => "l", "\xa3" => "L", "\xf3" => "o", "\xd3" => "O", "\x9c" => "s", "\x8c" => "S", "\x9f" => "z", "\xaf" => "Z", "\xbf" => "z", "\xac" => "Z", "\xf1" => "n", "\xd1" => "N", //UTF "\xc4\x85" => "a", "\xc4\x84" => "A", "\xc4\x87" => "c", "\xc4\x86" => "C", "\xc4\x99" => "e", "\xc4\x98" => "E", "\xc5\x82" => "l", "\xc5\x81" => "L", "\xc3\xb3" => "o", "\xc3\x93" => "O", "\xc5\x9b" => "s", "\xc5\x9a" => "S", "\xc5\xbc" => "z", "\xc5\xbb" => "Z", "\xc5\xba" => "z", "\xc5\xb9" => "Z", "\xc5\x84" => "n", "\xc5\x83" => "N", //ISO "\xb1" => "a", "\xa1" => "A", "\xe6" => "c", "\xc6" => "C", "\xea" => "e", "\xca" => "E", "\xb3" => "l", "\xa3" => "L", "\xf3" => "o", "\xd3" => "O", "\xb6" => "s", "\xa6" => "S", "\xbc" => "z", "\xac" => "Z", "\xbf" => "z", "\xaf" => "Z", "\xf1" => "n", "\xd1" => "N"); $sString = strtr($sString, $asCharacters); $sString = strtr($sString, 'ĄŚŹąśź','ASZasz'); $sString = preg_replace("'[[:punct:][:space:]]'",'-',$sString); $sString = strtolower($sString); $sChar = '-'; $nRepeats = 1; $sString = preg_replace_callback('#(['.$sChar.'])\1{'.$nRepeats.',}#', create_function('$a', 'return substr($a[0], 0,'.$nRepeats.');'), $sString); return $sString; } $plik1=$_FILES['userfile']['name']; $nazwa_plik=$_FILES['userfile']['tmp_name']; $file_extension = pathinfo($plik1, PATHINFO_EXTENSION); $plik1= str_replace($file_extension,'',$plik1); $plik1= substr($plik1,0,-1); $plik1= MakeFriendlyUrl($plik1); $plik1 .= '.'; $plik1 .= $file_extension; $i=1; while(file_exists('./slides/'.$plik1)) { $plik1= str_replace($file_extension,'',$plik1); $plik1= substr($plik1,0,-1); $plik1 .= '-'; $plik1 .= $i; $plik1 .= '.'; $plik1 .= $file_extension; $i++; } $uploaddir = './slides/'; //<-- Changed this to my directory for storing images $uploadfile = $uploaddir . basename($plik1); //<-- IMPORTANT function createThumbnail($filename) { $final_width_of_image = 244; $path_to_image_directory = './slides/'; $path_to_thumbs_directory = './thumbs/'; if(preg_match('/[.](jpg)$/', $filename)) { $im = imagecreatefromjpeg($path_to_image_directory . $filename); } else if (preg_match('/[.](gif)$/', $filename)) { $im = imagecreatefromgif($path_to_image_directory . $filename); } else if (preg_match('/[.](png)$/', $filename)) { $im = imagecreatefrompng($path_to_image_directory . $filename); } $ox = imagesx($im); $oy = imagesy($im); if($ox > $final_width_of_image ){ $nx = $final_width_of_image; $ny = floor($oy * ($final_width_of_image / $ox)); $nm = imagecreatetruecolor($nx, $ny); imagecopyresampled($nm, $im, 0,0,0,0,$nx,$ny,$ox,$oy); if(!file_exists($path_to_thumbs_directory)) { if(!mkdir($path_to_thumbs_directory)) { die("There was a problem. Please try again!"); } } imagejpeg($nm, $path_to_thumbs_directory . $filename); } } if (move_uploaded_file($nazwa_plik, $uploadfile)) { createThumbnail($plik1); echo "success:".$plik1; // IMPORTANT } else { // WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE! // Otherwise onSubmit event will not be fired echo "error"; } ?>
Hola en artículo comentáis que publicaréis un script que alerte de las modificaciones que haya en el prestashop. Lo podéis publicar? Gracias
Hola Jordi,
Es un script que usamos nosotros con nuestros clientes, pero que queremos pulir y mejorar, avisando también de inyecciones en los php’s ya creados (encode, decode, evals, …).
No te puedo dar fecha para la liberación del mismo, ya que lo vamos haciendo a ratos. En cuanto lo tengamos todo listo, lo publicaremos en la web y en las rrss.
Saludos
Buenos días. En primer lugar gracias por la información.
He tenido que limpiar un e-commerce afectado por este problema, con caídas constantes, redirecciones extrañas y calificando google el site como sitio comprometido. Al fin hemos podido solucionar el problema entre el proveedor de hosting y yo tras mucho investigar, probar y dolores de cabeza. Tuvimos que limpiar y eliminar ficheros infectados, actualizar prestashop de 1.6.0.9 a 1.6.1.8, cambiar todas las contraseñas y enviar de nuevo solicitud a google, quitando al fin el dichoso mensaje al cabo de 2 semanas. Mi duda es que he aplicado el parche recomendado y al escribir en el navegador tudominio.com/modules/simpleslideshow/uploadimage.php no me muestra el mensaje “User is not logged in”, “error” o “no file” . ¿a que puede deberse?. Pero el fichero que actualiza (ajax_advancedsliderUpload.php) si lleva en su interior esa línea de código (User is not logged in) a diferencia del original., Gracias de antemano.
Hola José Pascual,
deberíamos poder ver el contenido del fichero php para poder responderte, no obstante, si sabes PHP, puedes hacer un “debbug” de lo que sucede.
Lo mejor es que hicieras una petición GET para ver el código de error que devuelve, o una petición REST.
Saludos