En pocas palabras: para detectar un backdoor en mu-plugins, revisá a mano los archivos de wp-content/mu-plugins (no aparecen en la lista normal de plugins ni se desactivan desde wp-admin) y corré con WP-CLI wp core verify-checksums y wp plugin verify-checksums --all. Esos comandos comparan contra los checksums de WordPress.org, así que no alcanzan solos: hay que sumar ls, find y grep.
Un backdoor WordPress en la carpeta mu-plugins se carga en cada request sin aparecer en la lista normal de plugins, y por eso puede sobrevivir a una limpieza parcial. Para detectarlo comparás el listado de WP-CLI con el filesystem, verificás checksums y buscás PHP donde no corresponde, sobre todo en uploads.
Un backdoor en WordPress es código malicioso que deja una puerta oculta para que el atacante recupere el control del sitio sin pasar por el login. Los mu-plugins (must-use plugins) viven en wp-content/mu-plugins y WordPress los activa solos en todo el sitio, según la documentación oficial de WordPress. Para un atacante, esa combinación de carga automática y poca visibilidad es muy cómoda.
En este artículo:
- En 30 segundos
- ¿Qué son los mu-plugins y por qué los atacantes los usan para esconder un backdoor WordPress?
- ¿Cómo revisar la carpeta mu-plugins con WP-CLI y por SSH?
- ¿Cómo saber si mi WordPress tiene un backdoor con wp core verify-checksums?
- ¿Cómo encontrar archivos PHP ocultos en wp-content/uploads?
- ¿Dónde más se esconde el malware además de los archivos?
- ¿Cómo eliminar un backdoor de WordPress sin que se reinfecte?
- Checklist de prevención para evitar nuevos backdoors en WordPress
- Errores comunes al buscar un backdoor en WordPress
- Preguntas Frecuentes
- Conclusión
- Fuentes
En 30 segundos
- Los mu-plugins se cargan en orden alfabético antes que los plugins normales y no se pueden desactivar desde wp-admin.
- WP-CLI trae
wp core verify-checksumsywp plugin verify-checksums --all, que comparan archivos contra los checksums de WordPress.org. - Un archivo que nadie instaló no tiene checksum publicado contra qué compararse. Por eso conviene sumar
ls,findygrep.
¿Qué son los mu-plugins y por qué los atacantes los usan para esconder un backdoor WordPress?
Los mu-plugins son archivos PHP que WordPress ejecuta en cada carga sin que nadie los active. Aparecen en una sección Must-Use separada de la pantalla Plugins y no tienen botón de desactivar. Para un atacante eso significa persistencia: si el administrador no los busca a propósito, no los ve.
La documentación oficial, publicada el 28 de marzo de 2023 y actualizada por última vez el 23 de septiembre de 2026, detalla qué hace especial a esta carpeta:
- Carga automática. Subir un archivo PHP a
wp-content/mu-pluginsalcanza para que corra. - Orden alfabético y antes del resto. Un mu-plugin puede enganchar hooks que afecten a los plugins normales.
- Solo archivos directos. WordPress ignora las subcarpetas, salvo que un archivo PHP en la raíz las incluya con un
require. - Sin avisos de actualización. Nadie te notifica nada sobre lo que hay ahí.
La misma documentación advierte que un mu-plugin comprometido puede ser más difícil de notar que uno normal, justamente porque no se desactiva desde la lista principal. No hace falta un exploit sofisticado para aprovecharlo: alcanza con que alguien consiga escribir un archivo en esa carpeta.
¿Cómo revisar la carpeta mu-plugins con WP-CLI y por SSH?
Revisás mu-plugins en dos pasos. Primero, el listado que reporta WordPress con wp plugin list --status=must-use --fields=name,status,version. Después, el contenido real de la carpeta con ls -la wp-content/mu-plugins. Si los dos no coinciden, o si hay archivos que nadie instaló, ahí empieza la investigación.
Un listado limpio no prueba nada, porque el filesystem es la fuente de verdad. Y ojo con el orden: según la documentación de WP-CLI, --skip-plugins no saltea los mu-plugins, que se cargan igual. Cuando corrés wp plugin list sobre un sitio infectado, WordPress se carga y el código malicioso se ejecuta junto con tu comando. Es una inferencia nuestra, no un dato de la documentación, pero la consecuencia práctica es mirar primero con ls y cat y recién después lanzar comandos que cargan WordPress.
Para decidir qué merece revisión, usá estos criterios:
- Nombres que no reconocés. Cualquier archivo que no puedas atribuir a tu hosting, a tu agencia o a un plugin que instalaste.
- Fechas de modificación que no coinciden con ningún trabajo. Si nadie tocó el sitio en esa fecha, es una pista.
- Archivos que nadie documentó. Si una agencia o un host agregó algo, tendría que estar registrado. La documentación oficial recomienda llevar ese registro: por qué existe cada mu-plugin, quién lo mantiene y cómo se actualiza.
- Archivos que cargan código de otro lado. Un
requireoincludehacia uploads u otra ruta inesperada es una señal más fuerte que un nombre raro.
Ejemplo hipotético (inventado para ilustrar, no es un caso real): entrás por SSH y ls -la wp-content/mu-plugins muestra tres archivos: hosting-cache.php, que tu proveedor documenta; agencia-tools.php, que figura en el registro de tu agencia; y 0-wp-helper.php, que no está en ningún registro. Los dos primeros se explican solos. El tercero cumple varios criterios de sospecha: nadie lo documentó y, como la carga es alfabética, el prefijo 0 lo haría correr antes que los demás (deducción nuestra a partir del orden alfabético que describe la documentación). Lo razonable es abrirlo con cat, ver si incluye código de otra ruta y guardar una copia fuera del webroot antes de tocar nada.
¿Cómo saber si mi WordPress tiene un backdoor con wp core verify-checksums?
Corré wp core verify-checksums: el comando descarga los checksums md5 de tu versión desde WordPress.org y los compara con los archivos instalados. Con --include-root también advierte si encuentra elementos que no son de WordPress en el directorio raíz.
Según la documentación de WP-CLI, este comando evita cargar WordPress al verificar, una ventaja en un sitio sospechoso. Si falla por versión o idioma, pasale --version y --locale con los valores que figuran en Escritorio, Actualizaciones. Para los plugins existe wp plugin verify-checksums --all, y el ejemplo oficial devuelve algo como «Verified 8 of 8 plugins» (documentación del comando).
¿Esto alcanza para decir que el sitio está limpio? No. Es una capa de detección, no un veredicto. Esta tabla resume qué cubre cada capa; la última columna es una lectura nuestra de lo que documentan los comandos:
| Capa | Comando | Qué aporta | Qué no deberías asumir que cubre |
|---|---|---|---|
| Core | wp core verify-checksums --include-root | Archivos del core que no coinciden con WordPress.org y elementos ajenos en la raíz | Archivos agregados dentro de wp-content |
| Plugins | wp plugin verify-checksums --all | Plugins con checksums publicados que fueron alterados | Plugins fuera del repositorio y archivos sueltos que nadie instaló |
| mu-plugins | wp plugin list --status=must-use más ls -la | Lo que WordPress reporta y lo que hay en disco | Que el contenido de cada archivo sea legítimo: hay que leerlo |
| Uploads | find wp-content/uploads -type f -name "*.php" | PHP donde debería haber imágenes y documentos | Payloads guardados en la base de datos |
| Base y cron | wp option list, wp cron event list, wp user list | Opciones pesadas, tareas y admins sospechosos | Código en archivos del tema o drop-ins |

Dos límites más. Los checksums requieren conexión a WordPress.org, y un plugin que no está en el repositorio (premium, a medida) no tiene contra qué compararse. Un archivo que nadie instaló tampoco tiene checksum publicado. El comando de plugins ofrece una opción --exclude-mu-plugins, lo que sugiere que por defecto los incluye, pero eso no lo convierte en una herramienta para cazar mu-plugins truchos: sin referencia oficial no hay contra qué comparar.
¿Cómo encontrar archivos PHP ocultos en wp-content/uploads?
Ejecutá find wp-content/uploads -type f -name "*.php". La carpeta uploads debería tener imágenes, PDFs y otros documentos, no código ejecutable, así que cualquier resultado merece revisión. Después buscá funciones de ofuscación con grep y mirá las fechas de modificación.
Tomá estos comandos como punto de partida, porque rutas y patrones dependen de tu instalación:
- PHP en uploads:
find wp-content/uploads -type f \( -iname "*.php" -o -iname "*.phtml" \) - Funciones sospechosas:
grep -rlE "eval\(|base64_decode|gzinflate|str_rot13" wp-content/mu-plugins wp-content/uploads - Cambios recientes:
find wp-content -type f -name "*.php" -mtime -7
Las funciones eval, base64_decode, str_rot13 y gzinflate son indicadores posibles de ofuscación, pero también aparecen en código legítimo. Un hit en grep es una pista, no una condena. Lo mismo vale para uploads: algunos plugins legítimos dejan un index.php casi vacío para evitar el listado de directorios. Abrí el archivo y fijate si tiene contenido real.
Si tenés acceso a los logs del servidor, buscá también pedidos POST a archivos PHP dentro de uploads. Es una inferencia razonable (un webshell suele recibir órdenes por ahí), pero no hay un patrón único, así que no la tomes como regla.
¿Cómo bloquear la ejecución de PHP en uploads?
Se bloquea desde la configuración del servidor, negando la ejecución de archivos .php en wp-content/uploads. En Apache se suele hacer con un .htaccess en esa carpeta y en Nginx con una regla de location. La sintaxis exacta depende de tu servidor y de tu hosting, así que probala primero en un entorno de prueba. Es una barrera complementaria: no reemplaza buscar y eliminar los archivos que ya están.
¿Dónde más se esconde el malware además de los archivos?
También puede esconderse en la base de datos, en drop-ins, en archivos de configuración de PHP y en tareas programadas. Parte del código malicioso puede estar guardado en la base mientras el archivo en disco es apenas un loader. Por eso podés borrar todos los archivos sospechosos, ver un escaneo en verde y tener el mismo problema horas después.
Estos puntos no están en las fuentes citadas: son lugares habituales de persistencia que conviene revisar como parte de una limpieza completa.
- Opciones en la base. Probá
wp option list --fields=option_name, buscá nombres que no reconozcas y mirá el tamaño de lo sospechoso conwp option get NOMBRE | wc -c. - Drop-ins. Archivos como
db.phpyadvanced-cache.phpen wp-content, que WordPress carga solo. Si no usás un plugin de caché u objeto que los requiera, desconfiá. - .user.ini con auto_prepend_file. Hace que PHP cargue un archivo antes que cualquier script.
- functions.php del tema. Una inyección ahí se ejecuta en cada página.
- Cron. Revisá
wp cron event list. - Admins falsos.
wp user list --role=administratormuestra quién tiene acceso total.
¿Cómo eliminar un backdoor de WordPress sin que se reinfecte?
Se elimina cortando todas las rutas de ejecución antes de borrar los archivos visibles. Si queda otra pieza capaz de regenerar el malware, borrar solo el archivo que ves deja el sitio reinfectado. El orden de la limpieza importa tanto como la limpieza.
- Poné el sitio en mantenimiento u offline. Mientras esté expuesto, el atacante puede volver a escribir archivos.
- Guardá una copia de lo sospechoso fuera del webroot, para poder analizar cómo entraron.
- Neutralizá las rutas de ejecución. Primero
.user.ini, drop-ins y loaders, para que nada se regenere mientras limpiás. - Eliminá las copias fuera del disco: opciones sospechosas en la base y tareas de cron.
- Borrá los archivos maliciosos: mu-plugins, PHP en uploads, inyecciones en el tema.
- Quitá los usuarios administradores falsos.
- Rotá credenciales y salts. Contraseñas de admins, de la base, de SFTP y las claves secretas de
wp-config.php. Con WP-CLI podés regenerar las claves conwp config shuffle-salts. - Volvé a escanear. Repetí los comandos de las secciones anteriores y dejá el sitio en observación unos días.
¿Limpiar a mano o restaurar un backup?
Estos criterios ayudan a decidir. Son una guía nuestra, no una regla de las fuentes:
| Situación | Opción más prudente | Por qué |
|---|---|---|
| Tenés un backup anterior a la infección y sabés su fecha | Restaurar y luego actualizar y rotar credenciales | No hay que adivinar qué piezas se escaparon |
| No sabés desde cuándo está comprometido el sitio | Limpiar con el orden de arriba y, si hay duda, reconstruir desde core y plugins limpios | Un backup puede ya contener el backdoor |
| Hay un único archivo, sin loaders, opciones ni cron asociados | Limpieza manual, con escaneo posterior | La superficie a revisar es chica y verificable |
| Encontrás varias piezas que se referencian entre sí | Restaurar o reconstruir | El riesgo de que algo se reconstruya es alto |
Si optás por restaurar, confirmá en tu panel de hosting de qué fecha son tus copias y cuántos días hacia atrás llegan. Para armar una estrategia de backups que resista este tipo de incidentes, mirá la guía de copias de seguridad a prueba de ransomware. Pero el backup solo no cierra el problema: hay que descubrir cómo entró el atacante, sea un plugin vulnerable o credenciales robadas. Si no cerrás esa puerta, restaurás un sitio limpio que vuelve a caer.
Checklist de prevención para evitar nuevos backdoors en WordPress
Prevenir es mantener chica la superficie de ataque y mirar los cambios. Ninguna de estas medidas es nueva, pero alcanza con que una esté floja para dejar una entrada abierta.
- Actualizá plugins y temas. Un plugin vulnerable es una de las entradas posibles, junto con las credenciales robadas.
- Borrá lo que no usás. Un plugin desactivado sigue en disco y puede seguir siendo vulnerable.
- Contraseñas únicas y 2FA para admins. Si no lo tenés activo, seguí la guía para activar la verificación en dos pasos.
- Permisos mínimos. Dale rol de administrador solo a quien lo necesita.
- Monitoreo de integridad. Alertas cuando cambie algo en mu-plugins, una carpeta que casi nunca debería cambiar.
- Bloqueo de PHP en uploads a nivel servidor, como vimos arriba.
- Otras capas de hardening. Las cabeceras HTTP de seguridad y la guía de hardening de WordPress suman protección, aunque ninguna reemplaza revisar lo que hay en disco.
- Backups probados. Un backup que nunca restauraste es una esperanza, no un backup.
Errores comunes al buscar un backdoor en WordPress
- Confiar en la lista de Plugins de wp-admin. Los mu-plugins no figuran en la lista principal y un listado limpio no prueba nada. Corrección: contrastá siempre con
ls -la wp-content/mu-plugins. - Tomar el «Success» de verify-checksums como sitio limpio. Solo indica que los archivos con checksum publicado coinciden. Corrección: usá el comando como una capa más, junto con
findygrep. - Ejecutar WP-CLI con WordPress cargado en un sitio infectado antes de mirar los archivos. Los mu-plugins se cargan aun con
--skip-plugins. Corrección: inspeccioná primero conlsycat. - Borrar solo el archivo que encontraste. Si hay loaders, opciones en la base o cron, el malware puede reconstruirse. Corrección: neutralizá primero las rutas de ejecución.
- Olvidarse de rotar los salts y las contraseñas. Con credenciales robadas, el atacante vuelve a entrar. Corrección: rotá todo y borrá usuarios admin desconocidos.
- Marcar todo
base64_decodecomo malicioso. Hay código legítimo que lo usa. Corrección: abrí el archivo y evaluá el contexto, la fecha y quién lo instaló.
Preguntas Frecuentes
¿Por qué los mu-plugins no aparecen en la lista de plugins de WordPress?
Los mu-plugins no aparecen en la lista principal porque WordPress los muestra en una sección Must-Use aparte, y no se pueden desactivar desde wp-admin. Se cargan solos desde wp-content/mu-plugins, según la documentación oficial. Para quitar uno, hay que borrar su archivo.
¿Se puede usar WP-CLI para buscar malware en WordPress?
Sí, pero WP-CLI no es un escáner de malware. Con wp core verify-checksums y wp plugin verify-checksums --all detectás archivos modificados del core y de plugins con checksums publicados, y con wp option list, wp cron event list y wp user list revisás la base y los admins. Para archivos extra en wp-content necesitás find y grep.
¿Wp plugin verify-checksums revisa los mu-plugins?
El comando tiene una opción --exclude-mu-plugins, lo que sugiere que por defecto los considera. Aun así, no sirve para encontrar mu-plugins maliciosos: verifica contra los checksums de WordPress.org, y un archivo que nadie instaló no tiene checksum contra qué compararse.
¿Por qué mi WordPress se vuelve a infectar después de borrar el malware?
Probablemente quedó algún componente que reconstruye al resto (loaders, drop-ins o código guardado en la base), o el acceso original (un plugin vulnerable o credenciales robadas) sigue abierto. Hay que limpiar todas las rutas y cerrar la entrada.
¿Qué archivos de uploads son sospechosos?
Cualquier archivo .php o .phtml en wp-content/uploads es sospechoso hasta que se demuestre lo contrario, porque esa carpeta debería tener imágenes y documentos. La excepción habitual es un index.php casi vacío que dejan algunos plugins legítimos. Revisá el contenido y la fecha de modificación.
Conclusión
El backdoor en mu-plugins funciona porque aprovecha una característica legítima: la carpeta se carga sola y casi nadie la mira. Conviene asumir que el malware puede no vivir en un solo archivo, sino repartirse en varias piezas que se reconstruyen entre sí.
Para hoy: entrá por SSH, listá wp-content/mu-plugins con ls -la y leé lo que no reconozcas antes de cargar WordPress. Después corré wp core verify-checksums --include-root, buscá PHP en uploads y revisá admins, opciones y cron. Si encontrás algo, limpiá en el orden indicado, rotá credenciales y averiguá cómo entraron. Los comandos son una capa de detección, no un certificado de salud.