Cómo saber si atacaron tu WordPress: revisa los registros del servidor paso a paso

Cuando sale una vulnerabilidad grave, como PHP2Shell en octubre de 2026, lo primero es actualizar. Lo segundo es averiguar si atacaron tu WordPress antes de que lo hicieras. La respuesta está en los registros del servidor: unos archivos de texto donde Apache, Nginx y PHP anotan cada visita y cada error. Esta guía explica, paso a paso, dónde están, cómo leerlos con unas pocas órdenes y cómo distinguir el ruido de siempre de lo que de verdad debería preocuparte.

Los ejemplos de esta guía son reales. Montamos un servidor de laboratorio con Amazon Linux 2023, Apache, PHP-FPM y WordPress 7.1.3, con un sitio inventado, mitienda.test, y le lanzamos los ataques típicos: fuerza bruta contra el inicio de sesión, una red de bots contra xmlrpc.php y un escáner buscando archivos. Las direcciones IP son de las reservadas para documentación (203.0.113.x): en tu servidor verás otras. Todas las órdenes se ejecutaron como un usuario normal con sudo, igual que lo harías tú.

Antes de empezar

  • Necesitas entrar en el servidor por SSH con un usuario que pueda usar sudo. En una instancia EC2 de Amazon es ec2-user; en Ubuntu, ubuntu. Si no sabes cómo, empieza por la guía de SSH.
  • Si tienes un alojamiento compartido (sin acceso por SSH), busca en el panel de control un apartado de registros de acceso o «logs» que permita descargarlos. Si no lo hay, pídeselos a tu proveedor: las mismas órdenes funcionan en tu ordenador con el archivo descargado (en macOS y Linux, tal cual; en Windows, desde WSL).
  • Las órdenes solo leen. Ninguna de las de esta guía cambia nada en el servidor hasta el apartado de cerrar puertas, que es opcional.

Qué registro anota qué

Una petición pasa por varias piezas, y cada una apunta cosas distintas. Apache (o Nginx) anota todas las peticiones en el registro de acceso, y en el registro de errores solo las que fallan. Si la petición es para un archivo PHP, se la pasa a PHP-FPM, que tiene sus propios registros para los errores de PHP y para los procesos que se caen.

Visitante o bot POST /wp-login.php Apache recibe la petición Registro de acceso una línea por petición: quién, cuándo, qué y qué le respondió Registro de errores solo si algo falla: carpetas, permisos, rutas imposibles si es un archivo .php PHP-FPM ejecuta el código PHP Errores de PHP memoria agotada, errores fatales www-error.log Registro de FPM procesos que se caen o reinician error.log WordPress decide la respuesta La respuesta vuelve por el mismo camino, y su código (200, 302, 404…) queda en el registro de acceso.

Esquema: decodigo.com.

Paso 1: encontrar los registros

Dependen del sistema y de cómo se configuró el sitio. Los lugares habituales son estos:

  • Amazon Linux, Red Hat, Rocky, Alma o Fedora (Apache se llama httpd): la carpeta /var/log/httpd/. Si el sitio tiene su propio host virtual, sus registros suelen llevar el nombre del dominio, como tusitio.com-access.log, y los genéricos access_log y error_log pueden estar casi vacíos. Si usas HTTPS, puede haber un par aparte con ssl en el nombre: es donde estará casi todo el tráfico.
  • Debian y Ubuntu (Apache se llama apache2): la carpeta /var/log/apache2/, con access.log, error.log y other_vhosts_access.log. Ojo con este último: usa un formato en el que el primer campo es el nombre del sitio y la IP pasa a ser el segundo, así que en las órdenes de abajo habría que cambiar $1 por $2 y sumar uno a los demás números.
  • Nginx: /var/log/nginx/access.log y error.log. Por defecto usa el mismo formato que Apache, así que las órdenes de esta guía sirven igual.
  • PHP-FPM: en Amazon Linux, /var/log/php-fpm/ (error.log para los procesos y www-error.log para los errores de PHP); en Debian y Ubuntu, /var/log/php8.4-fpm.log o el de tu versión.

Si no los encuentras, pregúntale a la configuración de Apache dónde escribe:

sudo grep -RiE '^\s*(CustomLog|ErrorLog)' /etc/httpd/ /etc/apache2/ 2>/dev/null

Una ruta como logs/access_log es relativa a la carpeta de Apache: en Amazon Linux, /etc/httpd/logs apunta a /var/log/httpd.

En el servidor del laboratorio, la carpeta tiene esta pinta. Fíjate en el archivo que termina en -20261022: es un registro rotado. Para que no crezcan sin fin, el sistema los cierra cada cierto tiempo (cada semana en Amazon Linux, cada día en Debian y Ubuntu) y empieza uno nuevo, así que lo de hace unos días puede estar en el archivo anterior. En Debian y Ubuntu, además, los antiguos se guardan comprimidos (.gz).

sudo ls -la /var/log/httpd/
salidatotal 112
drwx------ 2 root root  4096 Oct 22 14:46 .
drwxr-xr-x 1 root root  4096 Oct 22 14:45 ..
-rw-r--r-- 1 root root     0 Oct 22 14:44 access_log
-rw-r--r-- 1 root root   491 Oct 22 14:46 error_log
-rw-r--r-- 1 root root 13045 Oct 22 14:47 mitienda.test-access.log
-rw-r--r-- 1 root root 79696 Oct 22 14:46 mitienda.test-access.log-20261022
-rw-r--r-- 1 root root     0 Oct 22 14:46 mitienda.test-error.log
-rw-r--r-- 1 root root  1728 Oct 22 14:46 mitienda.test-error.log-20261022

Paso 2: el tropiezo del asterisco con sudo

Lo normal es querer leer todos los registros de una vez con un asterisco, como *access*. Pero en Amazon Linux y otros sistemas, la carpeta de registros solo la puede abrir root, y pasa esto:

ls /var/log/httpd/
sudo grep -c POST /var/log/httpd/*access*
salidals: cannot open directory '/var/log/httpd/': Permission denied
grep: /var/log/httpd/*access*: No such file or directory

¿Por qué falla si va con sudo? Porque el asterisco no lo interpreta grep, sino tu terminal, antes de ejecutar la orden, y tu usuario no puede ver qué archivos hay en esa carpeta. Como no encuentra ninguno, le pasa a grep el texto *access* tal cual, y grep busca un archivo con ese nombre. La solución es meter la orden dentro de sudo sh -c '…': así es root quien interpreta el asterisco.

sudo sh -c 'grep -c POST /var/log/httpd/*access*'
salida/var/log/httpd/access_log:0
/var/log/httpd/mitienda.test-access.log:122
/var/log/httpd/mitienda.test-access.log-20261022:767

Ese mismo truco va en todas las órdenes que siguen. Y si tus registros antiguos están comprimidos, cambia cat por zcat -f, que lee igual los comprimidos y los normales.

Paso 3: leer una línea del registro

Cada petición ocupa una línea. Esta es una del laboratorio, de la IP que intentaba adivinar la contraseña:

una línea del registro de acceso203.0.113.66 - - [22/Oct/2026:14:45:58 +0000] "POST /wp-login.php HTTP/1.1" 200 6034 "-" "Mozilla/5.0"

Una línea del registro de acceso, por partes 1 203.0.113.66 Quién: la IP que hizo la petición 2 – – Usuario: casi siempre vacío: ignóralo 3 [22/Oct/2026:14:45:58 +0000] Cuándo: fecha, hora y zona horaria 4 "POST /wp-login.php HTTP/1.1" Qué pidió: método, ruta y protocolo 5 200 Qué le respondió: el código de respuesta 6 6034 Tamaño: bytes de la respuesta 7 "-" De dónde venía: la página anterior (Referer) 8 "Mozilla/5.0" Con qué: el navegador que dice usar

Esquema: decodigo.com, con el formato «combined» de Apache, el habitual. Ojo: awk no cuenta estas partes, sino los trozos separados por espacios.

Para las órdenes de esta guía importan cuatro campos. Contando los trozos separados por espacios, la IP es el campo 1, el método (GET, POST) el 6, la ruta el 7 y el código de respuesta el 9. Por eso verás $1, $6, $7 y $9 en las órdenes de awk.

Si tu sitio está detrás de Cloudflare, un balanceador de carga u otro proxy, la IP que verás será la del proxy, no la del visitante. En ese caso, el registro tiene que configurarse para guardar la IP real (por ejemplo, con mod_remoteip en Apache), o tendrás que mirar los registros del propio proxy.

Paso 4: quién te manda más peticiones POST

Las visitas normales son casi todas GET: pedir una página. Los ataques que importan, en cambio, suelen ser POST: enviar un formulario de inicio de sesión, una llamada a xmlrpc.php o un archivo. Así que lo primero es contar los POST por IP en el periodo que te interesa. Esta orden toma del 17 al 22 de octubre de 2026 (cambia el filtro por tus fechas):

sudo sh -c 'cat /var/log/httpd/mitienda.test-access.log-* /var/log/httpd/mitienda.test-access.log' | grep -E '\[(1[7-9]|2[0-2])/Oct/2026' | awk '$6 ~ /POST/ {print $1}' | sort | uniq -c | sort -rn | head -20
salida    421 203.0.113.66
    151 203.0.113.77
    150 203.0.113.79
    150 203.0.113.78
     14 203.0.113.5
      2 203.0.113.25
      1 203.0.113.10

Ya se intuye la historia: una IP con cientos de POST, tres con exactamente 150 (un reparto así de redondo es típico de una red de bots que se divide el trabajo) y unas pocas con un puñado. La última, 203.0.113.10, es el propio servidor: lo vemos en el paso 6.

¿Por qué no comparar las fechas con >? Es tentador filtrar con algo como $4 >= "[17/Oct/2026", pero awk compararía el texto letra por letra, y [20/Sep/2026 saldría «mayor» que [17/Oct/2026 porque el 2 va después del 1. Por eso el filtro usa grep con los días concretos.

Paso 5: a dónde iban y qué recibieron

Ahora, para cada IP, a qué ruta mandaba los POST y qué código le devolvió el servidor:

sudo sh -c 'cat /var/log/httpd/mitienda.test-access.log-* /var/log/httpd/mitienda.test-access.log' | grep -E '\[(1[7-9]|2[0-2])/Oct/2026' | awk '$6 ~ /POST/ {sub(/\?.*/, "", $7); print $1, $7, $9}' | sort | uniq -c | sort -rn | head -20
salida    421 203.0.113.66 /wp-login.php 200
    151 203.0.113.77 /xmlrpc.php 200
    150 203.0.113.79 /xmlrpc.php 200
    150 203.0.113.78 /xmlrpc.php 200
     12 203.0.113.5 /wp-admin/admin-ajax.php 200
      2 203.0.113.5 /wp-login.php 302
      1 203.0.113.25 /alfa.php 404
      1 203.0.113.25 / 200
      1 203.0.113.10 /wp-cron.php 200

Con esto ya se puede leer qué pasó. La clave es saber qué responde WordPress en cada caso, porque el código no siempre significa lo que parece:

  • POST /wp-login.php con 200: intento fallido. Cuando la contraseña es incorrecta, WordPress vuelve a mostrar el formulario con el mensaje de error, y eso es una respuesta 200 («todo bien»… para el servidor). Los 421 intentos de 203.0.113.66 son una fuerza bruta que no acertó ni una.
  • POST /wp-login.php con 302: alguien entró. Cuando la contraseña es buena, WordPress redirige al panel, y eso es un 302. Es el código que hay que vigilar (paso 6).
  • POST /xmlrpc.php con 200: tampoco es un acceso. xmlrpc.php es una puerta antigua para publicar desde otras aplicaciones, y siempre responde 200, aunque la contraseña sea mala: el error va dentro de la respuesta. En el laboratorio, cada intento recibió un «Incorrect username or password».
  • POST /wp-admin/admin-ajax.php con 200, desde tu IP: normal. Con el panel abierto, WordPress manda un «latido» cada 15 a 60 segundos para guardar borradores y avisar si otro está editando lo mismo.
  • 403: el servidor se negó (por ejemplo, porque bloqueaste esa ruta). 404: lo que pidió no existe, típico de los escáneres.
  • 503 o 429: si tienes un límite de intentos (un plugin de seguridad, un módulo del servidor o un cortafuegos), así es como suele frenar a quien insiste. Que aparezcan es buena señal: la protección funciona.

Paso 6: ¿entró alguien?

Es la pregunta importante, y se responde buscando los 302 de wp-login.php:

sudo sh -c 'cat /var/log/httpd/mitienda.test-access.log-* /var/log/httpd/mitienda.test-access.log' | awk '$6 ~ /POST/ && $7 ~ /wp-login\.php/ && $9 == 302 {print $1, $4}'
salida203.0.113.5 [22/Oct/2026:14:45:57
203.0.113.5 [22/Oct/2026:14:46:29

Solo aparece 203.0.113.5, que en el laboratorio era la administradora entrando con su contraseña. En tu caso deberían salir solo tus IP (la de casa, la de la oficina, la del móvil). Para saber cuál es la tuya, abre checkip.amazonaws.com desde tu ordenador.

Si aparece una IP que no reconoces, ve directamente al apartado «Si encuentras algo grave».

Lo que esta orden no ve: las publicaciones por la API REST con contraseñas de aplicación, que no pasan por wp-login.php, ni un acceso por xmlrpc.php con la contraseña correcta, que también da 200. Si usas alguna de las dos cosas, revisa además los usuarios y las contraseñas de aplicación en el panel (Usuarios → tu perfil).

Paso 7: reconocer el tráfico normal

Antes de alarmarte por una IP con muchas peticiones, descarta las de casa:

  • Tu propio servidor. WordPress ejecuta sus tareas programadas (publicar entradas programadas, buscar actualizaciones) llamándose a sí mismo a wp-cron.php, con su IP pública y el navegador «WordPress». En un sitio con tráfico pueden ser cientos al día. Para saber la IP pública de tu servidor, ejecuta en él curl -s https://checkip.amazonaws.com.
sudo sh -c 'cat /var/log/httpd/mitienda.test-access.log-* /var/log/httpd/mitienda.test-access.log' | grep 'wp-cron.php' | head -2
salida203.0.113.10 - - [22/Oct/2026:14:47:27 +0000] "POST /wp-cron.php?doing_wp_cron=1792680446.9913299083709716796875 HTTP/1.1" 200 - "-" "WordPress/7.1.3;
  • Tú, con el panel abierto: muchos POST a admin-ajax.php desde tu IP.
  • Los buscadores: Google, Bing y compañía hacen muchos GET, pero casi nunca POST.
  • Los servicios que conectaste: Jetpack, copias de seguridad o la app de WordPress pueden usar xmlrpc.php o la API desde sus propias IP.

Paso 8: los escáneres y el registro de errores

Además de probar contraseñas, los bots recorren miles de sitios buscando archivos olvidados o puertas traseras de otros ataques. Dejan un rastro de 404:

sudo sh -c 'cat /var/log/httpd/mitienda.test-access.log-* /var/log/httpd/mitienda.test-access.log' | awk '$9 == 404 {print $1, $7}' | sort | uniq -c | sort -rn | head
salida      2 203.0.113.25 /alfa.php
      1 203.0.113.25 /wp-xx.php
      1 203.0.113.25 /wp-content/plugins/x/up.php
      1 203.0.113.25 /shell.php
      1 203.0.113.25 /.git/config
      1 203.0.113.25 /.env

Buscaba un .env (donde muchas aplicaciones guardan contraseñas), la configuración de Git y archivos con nombres típicos de puertas traseras (shell.php, alfa.php). Que respondan 404 es lo que quieres. Este otro resumen agrupa los mensajes del registro de errores:

sudo sh -c 'cat /var/log/httpd/mitienda.test-error.log*' | awk -F'] ' '{print $NF}' | cut -c1-90 | sort | uniq -c | sort -rn | head -15
salida      1 AH01276: Cannot serve directory /var/www/mitienda.test/public_html/wp-includes/: No matchi
      1 AH01276: Cannot serve directory /var/www/mitienda.test/public_html/wp-content/uploads/: No
      1 AH01276: Cannot serve directory /var/www/mitienda.test/public_html/wp-admin/js/: No matchi
      1 AH00036: access to /aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Los mensajes más comunes, traducidos:

  • AH01276, «Cannot serve directory»: alguien pidió el listado de una carpeta y Apache se negó porque los listados están desactivados. Es lo correcto.
  • AH00036 o AH00127: rutas absurdas o demasiado largas. Alguien está probando si el servidor falla con entradas raras.
  • AH01071, «Primary script unknown»: se pidió a PHP-FPM un archivo .php que no existe. Otro escáner buscando puertas traseras.
  • AH01630, «client denied by server configuration»: una regla tuya bloqueó la petición, como la de xmlrpc.php que verás más abajo.

Paso 9: caídas y errores de PHP

Muchos ataques contra PHP, como PHP2Shell, provocan que PHP se quede sin memoria o que uno de sus procesos se caiga, aunque el ataque no llegue a funcionar. Eso queda en los registros de PHP-FPM. En el laboratorio provocamos las dos cosas a propósito para ver cómo se ven:

sudo sh -c 'cat /var/log/php-fpm/*error*.log*' | grep -iE 'SIGSEGV|SIGBUS|exited on signal|Allowed memory size|segfault' | tail -30
salida[22-Oct-2026 14:45:23] WARNING: [pool www] child 580 exited on signal 11 (SIGSEGV - core dumped) after 81.270932 seconds from start
[22-Oct-2026 14:45:12 UTC] PHP Fatal error:  Allowed memory size of 16777216 bytes exhausted (tried to allocate 1052672 bytes) in /var/www/mitienda.test/public_html/prueba-memoria.php on line 1

En un servidor sano, esta orden no debería mostrar nada, o casi nada. Un error de memoria suelto puede ser un plugin mal hecho, pero muchas caídas (exited on signal 11) o errores de memoria en fechas en las que no tocaste nada merecen una investigación. Antes de fiarte de un resultado vacío, comprueba con sudo ls -la /var/log/php-fpm/ que los archivos existen y tienen contenido.

Lo que los registros no ven

El registro de acceso guarda la línea de la petición, pero no lo que se envió dentro. En el laboratorio mandamos una subida de archivo con el campo que usan los ataques de PHP2Shell, PHP_SESSION_UPLOAD_PROGRESS, y esto es lo que se puede encontrar:

sudo sh -c 'cat /var/log/httpd/mitienda.test-access.log-* /var/log/httpd/mitienda.test-access.log' | grep -c PHP_SESSION_UPLOAD_PROGRESS
sudo sh -c 'cat /var/log/httpd/mitienda.test-access.log-* /var/log/httpd/mitienda.test-access.log' | grep '^203.0.113.25 .*"POST / '
salida0
203.0.113.25 - - [22/Oct/2026:14:46:17 +0000] "POST / HTTP/1.1" 200 69633 "-" "curl/8.21.0"

Cero coincidencias: la petición está, pero como un simple POST /. Para ver el contenido hace falta un cortafuegos de aplicaciones web (como AWS WAF, Cloudflare o ModSecurity) que lo inspeccione y lo registre. Por eso los registros sirven para ver patrones (quién insiste, a qué ruta, con qué resultado), y la defensa de verdad sigue siendo actualizar.

Si encuentras algo grave

Si ves un inicio de sesión que no es tuyo, caídas de PHP que no te explicas o archivos que no deberían estar, actúa en este orden:

  1. Cambia las contraseñas de todos los administradores y borra las contraseñas de aplicación que no reconozcas. Cambia también la de la base de datos (en wp-config.php) y las claves de seguridad de ese archivo, lo que cierra todas las sesiones abiertas.
  2. Revisa la lista de usuarios en el panel: un atacante suele crear un administrador nuevo con un nombre discreto.
  3. Busca archivos PHP donde no deberían estar. En la carpeta de subidas de WordPress solo debería haber imágenes y documentos: un .php ahí es casi siempre una puerta trasera. En el laboratorio dejamos uno a propósito:
sudo find /var/www -path '*/wp-content/uploads/*' -name '*.php'
salida/var/www/mitienda.test/public_html/wp-content/uploads/2026/10/cache.php
  1. Busca archivos PHP modificados recientemente, por ejemplo los que son más nuevos que tu wp-config.php. Las actualizaciones de WordPress y de los plugins también cambian archivos, así que compara las fechas con las de tus actualizaciones: sudo find /var/www -name '*.php' -newer /ruta/a/wp-config.php.
  2. Si encuentras una puerta trasera, no te limites a borrarla. Lo más seguro es restaurar una copia de seguridad anterior al ataque, reinstalar WordPress y los plugins desde sus fuentes oficiales y, después, actualizar todo.

Mantén PHP al día

De nada sirve revisar los registros si el agujero sigue abierto. En Amazon Linux 2023, el repositorio está fijado a una versión del sistema, y un dnf upgrade normal puede no ver los parches nuevos. En el laboratorio, la imagen traía PHP 8.3.33 y solo al pedir la última versión del repositorio apareció la corregida:

rpm -qa | grep php8.3-fpm
dnf check-update --releasever=latest 'php8.3-fpm'
salidaphp8.3-fpm-8.3.33-1.amzn2023.0.1.x86_64
php8.3-fpm.x86_64               8.3.36-1.amzn2023.0.1                amazonlinux
sudo dnf upgrade --refresh --releasever=latest 'php8.3*'
sudo systemctl restart php-fpm
systemctl show php-fpm -p ActiveEnterTimestamp

El último paso importa: los procesos de PHP-FPM que ya están en marcha siguen usando la versión vieja hasta que se reinician. La hora que muestra ActiveEnterTimestamp tiene que ser posterior a la actualización. En Debian y Ubuntu, el equivalente es sudo apt update && sudo apt upgrade y reiniciar el servicio php8.4-fpm (o el de tu versión).

Cierra las puertas que no usas

La fuerza bruta contra wp-login.php y xmlrpc.php es el ruido de fondo de cualquier WordPress. La documentación oficial de WordPress recomienda desactivar xmlrpc.php si no lo usas y limitar los intentos de inicio de sesión, mejor en el servidor o en un cortafuegos que con un plugin, porque un plugin gasta PHP en cada intento.

  • Bloquear xmlrpc.php con Apache 2.4: añade esto al final del .htaccess de tu sitio, fuera de las líneas de WordPress. Antes, comprueba que no lo necesitas: lo usan Jetpack y algunas aplicaciones antiguas para publicar desde el móvil.
<Files "xmlrpc.php">
    Require all denied
</Files>
en el laboratorio, después de añadirloxmlrpc tras el bloqueo: 403
portada: 200
  • Limitar los intentos de inicio de sesión con un plugin de seguridad, con el cortafuegos de tu proveedor o, en Nginx, con limit_req.
  • Usar contraseñas largas y únicas y, si puedes, la verificación en dos pasos para los administradores. Con eso, la fuerza bruta se queda en ruido.
  • No bloquear IP una a una: las redes de bots cambian de dirección constantemente, así que suele servir de poco. Si quieres hacerlo, mejor con herramientas que lo automatizan, como fail2ban.

Resumen: las órdenes en un solo bloque

Para Amazon Linux y Apache, con registros con el nombre del sitio. Cambia las rutas y las fechas por las tuyas y, en Debian y Ubuntu, /var/log/httpd por /var/log/apache2. zcat -f lee igual los registros comprimidos y los normales.

# 1. Dónde están los registros
sudo ls -la /var/log/httpd/ /var/log/php-fpm/

# 2. Quién manda más POST (todos los registros de acceso, también los rotados)
sudo sh -c 'zcat -f /var/log/httpd/*access*' | awk '$6 ~ /POST/ {print $1}' | sort | uniq -c | sort -rn | head -20

# 3. A qué rutas y con qué respuesta
sudo sh -c 'zcat -f /var/log/httpd/*access*' | awk '$6 ~ /POST/ {sub(/\?.*/, "", $7); print $1, $7, $9}' | sort | uniq -c | sort -rn | head -20

# 4. Inicios de sesión correctos: solo deberían salir tus IP
sudo sh -c 'zcat -f /var/log/httpd/*access*' | awk '$6 ~ /POST/ && $7 ~ /wp-login\.php/ && $9 == 302 {print $1}' | sort | uniq -c

# 5. Caídas y errores de memoria de PHP
sudo sh -c 'zcat -f /var/log/php-fpm/*error*.log*' | grep -iE 'SIGSEGV|exited on signal|Allowed memory size' | tail -30

# 6. Archivos PHP en la carpeta de subidas (no debería haber ninguno)
sudo find /var/www -path '*/wp-content/uploads/*' -name '*.php'

# 7. Tu IP pública (en el servidor) y la versión de PHP
curl -s https://checkip.amazonaws.com
rpm -qa | grep php

Si te interesa el porqué de todo esto, puedes seguir con la guía de cómo funciona HTTPS por dentro o la de SSH. Y si tu servidor usa PHP, lee qué pasó con PHP2Shell: es el mejor ejemplo de por qué conviene saber leer los registros.

Fuentes: formato de los registros de Apache, en su documentación oficial; recomendaciones contra la fuerza bruta y sobre xmlrpc.php, en la documentación de WordPress; actualizaciones de Amazon Linux 2023 con --releasever, en la guía de AWS; señales de PHP2Shell (PHP_SESSION_UPLOAD_PROGRESS, investigar desde el 17 de octubre), en el blog de seguridad de AWS; direcciones de documentación, en el RFC 5737. Las salidas se obtuvieron en un laboratorio con Amazon Linux 2023, Apache 2.4.68, PHP 8.3 y WordPress 7.1.3; las rutas de Debian se comprobaron en Debian 13.