Cómo limpiar y reforzar un sitio WordPress tras una infección de malware

Cuando un sitio WordPress es comprometido, muchas veces no basta con eliminar archivos sospechosos: los scripts maliciosos suelen regenerarse automáticamente gracias a puertas traseras ocultas en el servidor. En este artículo documentamos un proceso técnico completo de limpieza y refuerzo, dividido en dos fases: forense en servidor (SSH) y endurecimiento en WordPress y WAF.

1. Detección inicial

  • Aparición de redirecciones a sitios externos.
  • Archivos como .htaccess regenerándose solos con reglas sospechosas.
  • Carpetas ocultas con nombres aleatorios (/e0a8cc, /fecf2, etc.).
  • Procesos PHP inusuales ejecutándose de manera persistente.
  • Resultados alterados en buscadores (Google mostrando títulos en otros idiomas).

Para confirmar, se buscaron patrones comunes de malware:

grep -R --line-number --color "base64_decode\|gzinflate\|shell_exec\|system\|eval" public_html

2. Limpieza en servidor (SSH con PuTTY)

2.1. Identificación y eliminación de procesos maliciosos

ps -u "$(whoami)" -o pid,etime,cmd | grep -E 'php|bash|sh|wget|curl'
ps -u "$(whoami)" -o pid | xargs -r kill -9

2.2. Desbloqueo y borrado de carpetas que se regeneran

for dir in .tmb contactos e0a8cc fecf2 images permisos tawk; do
  chattr -i public_html/$dir 2>/dev/null || true
  chmod -R 777 public_html/$dir 2>/dev/null || true
  rm -rf public_html/$dir 2>/dev/null || true
done

2.3. Neutralización de .user.ini y .htaccess

find public_html -type f -name '.user.ini' -exec sh -c '
  chattr -i "$1" 2>/dev/null || true
  echo "[neutralizado]" > "$1"
  chmod 644 "$1"
' sh {} \;

Se regeneró un .htaccess limpio:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

2.4. Reinstalación del core de WordPress

wget https://wordpress.org/latest.zip
unzip -q latest.zip
mv public_html/wp-admin ~/quarantine/wp-admin_$(date +%F_%H%M)
mv public_html/wp-includes ~/quarantine/wp-includes_$(date +%F_%H%M)
cp -a wordpress/wp-admin wordpress/wp-includes public_html/

3. Refuerzo en WordPress (wp-admin)

3.1. Plugins de seguridad

Se instaló y configuró All In One WP Security (AIOS):

  • Bloqueo de wp-login.php con slug personalizado.
  • Deshabilitar editor de archivos desde wp-config.php:
define('DISALLOW_FILE_EDIT', true);
  • Lista negra de IPs sospechosas.
  • Protección contra fuerza bruta (renombrar login + honeypot).

3.2. Permisos correctos de archivos

find public_html -type d -exec chmod 755 {} \;
find public_html -type f -exec chmod 644 {} \;
chmod 600 public_html/wp-config.php

3.3. Borrado de spam en base de datos

DELETE FROM wp_comments WHERE comment_approved = 'spam';
DELETE FROM wp_comments WHERE comment_content LIKE '%http%';

4. Refuerzo en Cloudflare

Aunque el plan gratuito no incluye WAF completo, se aplicaron medidas:

  • Reglas personalizadas de firewall para bloquear países con actividad sospechosa.
  • Bot Fight Mode activado para detener scrapers.
  • Rate Limiting configurado para proteger wp-login.php y xmlrpc.php.

Ejemplo de regla:

(http.request.uri.path contains "/wp-login.php")

Acción: Block o Challenge.

5. Lecciones aprendidas

  • Nunca dejar credenciales débiles: contraseñas simples fueron la causa inicial.
  • Los atacantes suelen instalar plugins falsos para mantener persistencia.
  • La limpieza debe ser en capas: servidor, core de WordPress, base de datos, plugins y capa de red (Cloudflare).
  • Monitorear constantemente con:
find public_html -mmin -5 -ls

Conclusión

Un ataque a WordPress no se soluciona borrando archivos manualmente: requiere un proceso forense completo, reinstalación de componentes limpios, bloqueo de persistencia y endurecimiento en varios niveles.

Con esta metodología, cualquier administrador puede detectar, limpiar y reforzar un sitio comprometido.