¿Cómo IndoXploit hackea a WordPress?

Voy a contar mi experiencia con un sitio web en WordPress que fue hackeado hace ya un buen tiempo, a pesar de tener todas las configuraciones posibles de seguridad fue hackeado. Así que me puse a investigar y les voy a explicar como es que pudo ingresar a WordPress.

Este son las características del servidor y del sitio web:

  • Servidor: Hosting Compartido (Linux)
  • Servicios: Apache, PHP, MySQL
  • 3 WordPress, 1 Joomla y 1 sitio web desarrollado en Laravel funcionando en este Hosting

Todo empezó cuando uno de los WordPress fue hackeado por un Defacer (Alguien que cambia o modifica el Home del sitio web). La seguridad de ese sitio web tenia una seguridad básica o la que viene por defecto en el WordPress, los demás sitios web en WordPress, Joomla y Laravel no habían sido afectados. Se recuperó el WordPress que fue hackeado y se configuró todas las medidas de seguridad posibles a todos los sitios que tenia en ese Hosting. Al final de revisar no pude determinar por donde había ingresado el malware, solo encontré los archivos que subieron y que habían cambiado un usuario y su contraseña del WordPress por indoxploit.

Luego de un par de meses reviso el WordPress y encuentro que devuelta habían cambiado un usuario y su contraseña del WordPress a indoxploit, pero no pudieron modificar el Home del WordPress, esto también les había ocurrido a los demás WordPress y al Joomla, el único que se salvo fue el sitio web desarrollado en Laravel. Molesto y fastidiado por esto decidí investigar por donde había ingresado este malware.

En este proceso de investigar tuve un inconveniente con el log del Apache del Hosting, solo se almacenaba un día de historial, así que no podía ver en que momento atacaban y que petición HTTP hacían. Para solucionar esto cree un script que se ejecuta cada vez que hacen una petición HTTP en el WordPress a través de un archivo PHP colocado en la carpeta wp-content/mu-plugins, cualquier archivo PHP que se coloque en esta carpeta, WordPress lo ejecutará, más información.

El código lo que hace es guardar en un archivo todas las peticiones HTTP que se ejecuta en el WordPress, de esta manera podría saber que petición HTTP hacen para realizar el ataque.

<?php
function nhk_alldump()
{
    if (isset($_SERVER)) {
        $fecha = new DateTime(null, new DateTimeZone('UTC'));
        $data = [];
        $dir_log = $_SERVER['DOCUMENT_ROOT'].'/../nhk';
        if( !realpath($dir_log) ) mkdir($dir_log, 0750);
        $file_log = $dir_log.'/nhk_alldump.log';
        $data["REMOTE_ADDR"] = (array_key_exists('REMOTE_ADDR', $_SERVER)) ? $_SERVER['REMOTE_ADDR'] : '';
        if (array_key_exists('REQUEST_TIME', $_SERVER)) {
            $fecha->setTimestamp($_SERVER["REQUEST_TIME"]);
            $fecha->setTimezone(new DateTimeZone('America/Lima'));
            $data["REQUEST_TIME"] = $fecha->format('d/m/Y H:i:s');
        }else{
            $data["REQUEST_TIME"] = '';
        }
        $data["REQUEST_METHOD"] = (array_key_exists('REQUEST_METHOD', $_SERVER)) ? $_SERVER['REQUEST_METHOD'] : '';
        $data["REQUEST_URI"] = (array_key_exists('REQUEST_URI', $_SERVER)) ? $_SERVER['REQUEST_URI'] : '';
        $data["HTTP_REFERER"] = (array_key_exists('HTTP_REFERER', $_SERVER)) ? $_SERVER['HTTP_REFERER'] : '';
        $data["HTTP_USER_AGENT"] = (array_key_exists('HTTP_USER_AGENT', $_SERVER)) ? $_SERVER['HTTP_USER_AGENT'] : '';
        $data["POST"] = "";
        if ( isset($_POST) && count($_POST)>0 ) {
            $data["POST"] = print_r($_POST,true);
        }
        $data["FILES"] = "";
        if ( isset($_FILES) && count($_FILES)>0) {
            $data["FILES"] = print_r($_FILES,true);
        }
        $data = implode($data,"|")."\n";
        file_put_contents($file_log, $data, FILE_APPEND | LOCK_EX);
    }
}
add_action( 'wp_loaded', 'nhk_alldump' );
?>

Pasando un mes reviso el WordPress y otra vez cambiaron el usuario a indoxploit, esta vez reviso el log que generaba mi código y solo aparece el momento en que el atacante se loguea, pero no aparece el momento en que cambia el usuario del WordPress, entonces el cambio de usuario no lo hacia a través de una petición HTTP, por lo tanto no usaban el WordPress para cambiar los datos de la base de datos. Ahora mi pregunta era ¿Cómo modificaron la base de datos?, la única forma es a través de una consulta directa a la base de datos, pero para hacer eso tendrían que saber el usuario y contraseña del MySQL o con otro usuario con mayor privilegio.

Para comprobar con que usuario modifican la base de datos, hice otra prueba, para esto cree un trigger a la tabla wp_users.

Lo que hace el trigger es guardar en otra tabla la IP, el usuario MySQL y la consulta SQL, que se producen en la tabla wp_users cuando se ingresa o actualiza un registro.

CREATE TABLE `wp_mysqlog` (
  `id` INT(11) NOT NULL,
  `tabla` VARCHAR(100) DEFAULT NULL,
  `event` VARCHAR(50) DEFAULT NULL,
  `user` VARCHAR(250) NOT NULL,
  `db` VARCHAR(100) DEFAULT NULL,
  `info` longtext,
  `fecha` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 
 
CREATE TRIGGER `log_queries_before_insert` BEFORE INSERT ON `wp_users` FOR EACH ROW BEGIN
    DECLARE field_user VARCHAR(250);
    DECLARE field_db VARCHAR(100);
    DECLARE field_info longtext;
    SET field_info = (SELECT info FROM INFORMATION_SCHEMA.PROCESSLIST WHERE id = CONNECTION_ID());
    SET field_user = (SELECT USER());
    SET field_db = (SELECT DATABASE());
    INSERT INTO `wp_mysqlog`(`tabla`,`event`,`user`,`db`,`info`,`fecha`) VALUES ('users','INSERT',field_user, field_db, field_info, NOW());
END
$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER `log_queries_before_update` BEFORE UPDATE ON `wp_users` FOR EACH ROW BEGIN
    DECLARE field_user VARCHAR(250);
    DECLARE field_db VARCHAR(100);
    DECLARE field_info longtext;
    SET field_info = (SELECT info FROM INFORMATION_SCHEMA.PROCESSLIST WHERE id = CONNECTION_ID());
    SET field_user = (SELECT USER());
    SET field_db = (SELECT DATABASE());
    INSERT INTO `wp_mysqlog`(`tabla`,`event`,`user`,`db`,`info`,`fecha`) VALUES ('users','UPDATE',field_user, field_db, field_info, NOW());
END

Pasado un tiempo vuelven a cambiar al usuario del WordPress y en mi tabla donde guardo todos los cambios aparece que lo hacen con el mismo usuario e IP (localhost) que tengo en el archivo wp-config.php. Con esta información no cabe duda que los accesos lo leían del archivo wp-config.php.

Acceder al archivo wp-config.php

En este punto me hacia una pregunta ¿Cómo alguien podría acceder al archivo wp-config.php de mi cuenta de Hosting sin tener acceso a mi cuenta?
La respuesta estaba en los permisos del archivo wp-config.php por defecto los permisos son 644, que quiere decir para el propietario leer y escribir, para el grupo leer y para otros leer. Si le decimos que otros lean el archivo wp-config.php cualquier usuario de este Hosting podría leer el contenido del archivo y por ende cualquier usuario puede tener acceso a la base de datos del WordPress. Como decía Cantinflas “Ahí está el detalle”.

¿Cómo saber la ruta del archivo wp-config.php de otra cuenta?

En un Hosting compartido (Linux) el archivo de configuración del WordPress se encontraría en /home/NOMBRE_USUARIO/public_html/wp-config.php, lo que faltaría es saber cuales son los nombres de todos los usuarios de un servidor Linux, esos datos se pueden obtener del archivo /etc/passwd.

// Obtenemos los usuarios
$passwd = file_get_contents('/etc/passwd');
 
// Con el usuario obtenido obtenemos el contenido del archivo de configuración del WordPress
$config = file_get_contents('/home/NOMBRE_USUARIO/public_html/wp-config.php');

Una vez que se tiene los accesos a la base de datos, se puede cambiar el usuario y contraseña a la tabla wp_users.

¿Cómo modifican el WordPress?

Ya teniendo un usuario en el WordPress, se loguean y suben un archivo PHP donde está todo el código para hacer todo lo que desee el atacante.

La Solución

Para que otros usuarios no tengan acceso al archivo wp-config.php hay que cambiar los permisos a 640 o si somos más paranoicos a 440. En algunos Posts hablan de mover el archivo wp-config.php a fuera de la carpeta pública, todavía no he probado este método, pero cambiando los permisos el problema hasta el momento se ha solucionado.
También hay que aplicar todas las configuraciones de seguridad al WordPress. Muy importante es desactivar que el WordPress edite archivos PHP e instale plugins, así evitamos que suban algún archivo PHP.

Más información de como mejorar la seguridad en WordPress https://gmartinez.pe/seguridad-en-wordpress/

(Visited 2.308 times, 1 visits today)
Puedes saltar al final y dejar una respuesta. Hacer ping no está permitido actualmente.