[SECURITY] Backdoors detectados en WordPress de producción (health-check.php + wp-highlits) #183
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Incidente de seguridad: backdoors WordPress detectados en producción
Severidad: crítica — pendiente de contención.
Hallazgos confirmados (solo lectura)
MU-plugin no estándar y cargado automáticamente:
/web/wp-content/mu-plugins/health-check.phpbbaeb9a2f7574d8ae6233c5bbb9cc18340dd0c6e86043460f4d9c0baf766e65f23.42, autor “Team Yoast”.admintoolque itera administradores y ejecutawp_set_current_user()+wp_set_auth_cookie(); también intenta bloquear su desactivación.do_action('wp_login', $username)con un único argumento frente a LLAR 3.3.1, que requiere dos.Plugin activo sospechoso:
/web/wp-content/plugins/wp-highlits/wp-highlits.phpd34b4e9b29ecef811dabf52bb47b1415f00fdc2a7784fcb6dffdc668408bd0c3wp-highlits/wp-highlits.php.7.499776, código ofuscado y referencias engañosas a Wordfence.Correlación temporal:
health-check.php: 2026-07-29 05:02:53 UTCwp-highlits.php: 2026-07-29 05:03:37 UTCUsuario
andrey:1087, loginandrey, emailandrey@pabloarias.eu2018-02-05 19:03:42administratorAcciones pendientes
health-check.phpy desactivar/aislarwp-highlitssin borrarlos hasta preservar evidencia.No se han aplicado cambios en producción durante este diagnóstico.
Contención inicial — cuentas legacy deshabilitadas
Autorización explícita recibida para deshabilitar andrey, pabloarias y josek.
Aplicado y verificado server-side
Mecanismo: MU-plugin limitado a estos tres IDs (
fea-security-blocked-users.php), contraseñas aleatorias rotadas, tokens de sesión y application passwords revocados. Verificación: los tres devuelvenlogin_blocked=true,reset_allowed=false, sin sesiones persistentes.Evidencia
/web/wp-content/uploads/forensics/incident-183/disabled-users-before.json/web/wp-content/mu-plugins/fea-security-blocked-users.phplogs/incident-183/fea-security-blocked-users.phpIdentidad de
calvoRafael Calvo Beca,info@feadulta.com, administrador desde 2012, 275 posts. No se ha tocado.Logs
El acceso disponible desde la cuenta de hosting solo expone
/logs/antimalware.log(49 MB), no access logs web. Su cola llega hasta 10-jun y registra un scan que detectó malware JS en/home/feadulta.com/web/media/com_rsform/js/jquerycalendar/jquery.datetimepicker.js; no permite atribuir los accesos recientes ni la subida de los dos backdoors. Se requiere al proveedor del hosting export de access/error logs del intervalo de modificación.Contención de artefactos completada
Evidencia preservada
Copias byte a byte y manifest SHA-256 guardados localmente:
/home/rafa/joomla-migration/logs/incident-183/evidence/health-check.php— SHA-256bbaeb9a2f7574d8ae6233c5bbb9cc18340dd0c6e86043460f4d9c0baf766e65fwp-highlits.php— SHA-256d34b4e9b29ecef811dabf52bb47b1415f00fdc2a7784fcb6dffdc668408bd0c3Cuarentena reversible aplicada
/web/wp-content/mu-plugins/health-check.php→health-check.php.quarantined-incident-183/web/wp-content/plugins/wp-highlits/→wp-highlits.quarantined-incident-183/No se borraron archivos.
Verificación posterior server-side
health-check.phpausente de la carga: PASSwp-highlitsausente de la carga: PASSadmin_login_check: ausente/wp-login.phpvía WordPress HTTP: 301 (respuesta normal de redirección)Siguiente fase: atribución/vector de entrada, revisión de logs del proveedor, escaneo de persistencia adicional y rotación ordenada de secretos.
Escaneo de persistencia adicional — primera pasada (solo lectura)
Artefactos y estado de las comprobaciones:
uploads, MU-plugins, drop-ins y archivos con mtime correlacionado: no hay un tercer candidato con patrones de ofuscación/ejecución equivalente a los dos artefactos ya aislados.advanced-cache.php,db.php,object-cache.php,sunrise.php)..phpbajowp-content/uploadsson runners internos de avataresimport_prod_62.phpyimport_prod_90.php; inspeccionados: scripts declarados para los issues de importación de avatar, sin ofuscación ni funcionalidad de backdoor./web/antiguo(Joomla legacy) genera cache PHP continuamente; queda como superficie separada a inventariar y decidir si debe permanecer expuesta. Sus archivos cache no se han tratado como evidencia de compromiso por mtime.wp-highlits/wp-highlits.phpaún aparece en la opciónactive_pluginsporque se aisló por filesystem; no se carga ya. Pendiente limpiar esa referencia de configuración como parte del cierre controlado.Artefactos locales:
logs/incident-183/filesystem-scan-window.jsonlogs/incident-183/wp-state-scan.jsonLa atribución sigue bloqueada hasta recibir access/error logs del proveedor.
Aporte desde el lado de los bots (Inma)
Verificación externa post-contención (solo lectura, 29-jul tarde)
301 → antiguo.feadulta.com(tanto en www como en wp-nuevo), y ambos artefactos (wp-content/plugins/wp-highlits/wp-highlits.phpywp-content/mu-plugins/health-check.php) dan exactamente esa respuesta. En cambiowp-content/mu-plugins/fea-security-blocked-users.phpdevuelve 500 (existe y casca al invocarlo a pelo, como debe). Conclusión: retirada de ficheros confirmada de forma independiente desde fuera.Línea temporal — descartado el PC de los bots como vector
El 29-jul las automatizaciones locales corrieron a las 03:00 (teologasbot) y a las 08:00 (escritoresbot, recordatoriobot), hora local. En el intervalo de subida de los backdoors (07:02–07:03 local / 05:02–05:03 UTC) no hubo ninguna actividad contra WordPress desde este equipo.
Usuario de las automatizaciones
Los bots publican con el usuario
icalvotorre(application password, contra wp-nuevo). Al inventariar/validar administradores: es legítimo y debe conservarse. Si hay que rotar application passwords en bloque, avisad antes para actualizar el.enven el momento y que no se pare la cadena de la carta.Cuentas legacy
Confirmado (Inma, por WhatsApp):
andrey(pabloarias.eu) yjosekfueron usuarios legítimos de la etapa Joomla. Correcto mantenerlos deshabilitados hoy.Duda que queda viva
¿El escaneo del resto del árbol WordPress (persistencia adicional) está hecho o pendiente? Es el punto del checklist que más nos preocupa de cara a dar la web por limpia, junto con los access logs del hosting para atribuir el vector.
Joomla
sys.php: evidencia preservada y shell retirada de cargaConfirmación del artefacto
El fichero detectado por CDmon era una web shell funcional, no un falso positivo:
/web/antiguo/modules/mod_menu/sys.php8152c22e26a7135b81d47d1de1c8454d1f4ed566193eaf2038af39152e33092emtime:2020-10-25T12:25:32Z(fecha del contenido; no atribuye el acceso actual)Se preservó copia forense local y se ha puesto en cuarentena reversible:
Logs recuperados por SSH
SSH sí da acceso de lectura a la misma carpeta
/errorsque CDmon expone por FTP. Se preservaron localmente los ficheros de 27 y 28-jul:errors.log.20260727.bz2yerrors.log.20260728.bz2php_errors.log.20260727.bz2yphp_errors.log.20260728.bz2Son principalmente logs de error/ModSecurity, no access logs completos. Contienen intentos RCE bloqueados por ModSecurity a través de IPs Cloudflare, pero ninguna petición a
sys.phpni evidencia de la subida/creación de los backdoors. Siguen faltando access logs HTTP y logs FTP/SFTP/panel para atribución.Avance de atribución: vector de entrada confirmado por el log web
Los registros aportados por Rafa cierran una parte crítica de la cronología. La hora del error log está en CEST (UTC+2), por lo que
07:02local equivale a05:02 UTC, exactamente la ventana de mtime de los artefactos WordPress.Secuencia observada (29-jul)
07:02:16 CEST— petición a una shell temporal/rr.php, conpath=/usr/home/feadulta.com/web; ModSecurity detecta intento de subirwp-cl.php.07:02:53 CEST— misma/rr.php, ahora conpath=/usr/home/feadulta.com/web/wp-content/mu-plugins; ModSecurity detecta la subida dehealth-check.php.health-check.phptienemtime 05:02:53 UTCywp-highlits.php05:03:37 UTC, coherentes con esos requests.El WAF registró las subidas pero su score fue 5 frente a umbral de bloqueo 25: alertó, no bloqueó. Las IP remotas (
162.158.122.21) son de Cloudflare, no atribuyen al atacante.Estado de
/rr.phpywp-cl.phpYa no existen en el árbol
/web: quien operó la shell temporal la eliminó después de usarla. Un escaneo de la ventana UTC04:55–05:10no encuentra otros archivos no-cache además de los dos artefactos WordPress ya aislados.Conclusión actual
El actor utilizó
/rr.phpcomo web shell de subida para plantar los backdoors WordPress. La shell Joomlasys.phpera un mecanismo plausible para crear/operar esa shell, pero el log disponible todavía no muestra la petición inicial asys.phpni el momento de creación derr.php; esa parte sigue pendiente de access logs completos/Cloudflare.sys.phpsigue en cuarentena yrr.phpno está presente.Ticket abierto con CDmon para logs forenses
#cdmoncc274423info@feadulta.com)Solicitud enviada: access logs HTTP completos de
feadulta.comyantiguo.feadulta.com, IP real tras Cloudflare cuando exista, y logs FTP/SFTP/SSH/cPanel/File Manager para atribuir la subida derr.php,wp-cl.phpyhealth-check.php. También se solicita explicación y bloqueo efectivo de subidas PHP detectadas por ModSecurity.Trabajo paralelo mientras esperamos CDmon
sys.phpya confirmado permanece en cuarentena.com_foxcontact(incluye uploader),mod_foxcontact/upr.php, K2 y librerías elFinder. Son componentes de subida presentes en una Joomla 3.10 EOL; no se atribuye aún la entrada a ninguno sin logs de la petición inicial.wp-highlits/wp-highlits.phpdeactive_plugins, con snapshot previo en el expediente. El directorio ya estaba en cuarentena y no se vuelve a cargar.Siguiente paso de contención pendiente de decisión: restringir temporalmente el administrador Joomla y endpoints de subida legacy de forma reversible, o retirar el subdominio legacy mientras se construye el mirror estático.
Hardening temporal Joomla aplicado y verificado
Aplicado bajo autorización de Rafa, con backup reversible del
.htaccessraíz:/web/antiguo/administrator/.htaccess.components/com_foxcontact/;modules/mod_foxcontact/upr.php;option=com_foxcontact.Verificación desde el origen:
Backup remoto:
/web/antiguo/.htaccess.pre-incident-183-20260729T110202Z.Este cierre es intencionadamente reversible. La función de contacto/upload de Fox Contact queda deshabilitada durante el incidente; el contenido histórico, enlaces e imágenes no se han tocado.
Decisión de remediación estructural — 29-jul-2026
Rafa confirma que el riesgo estructural es mantener Joomla EOL ejecutando PHP dentro de la misma cuenta de hosting. La contención actual reduce la exposición, pero no sustituye la retirada definitiva de esa superficie.
Decisión: iniciar hoy la preparación de un mirror HTML estático y de solo lectura de
antiguo.feadulta.comen Hetzner. Se preservará el archivo histórico público; no se eliminará contenido ni se apagará la web legacy hasta validar paridad pública y acordar el cutover.Orden de trabajo:
La investigación de atribución y la rotación de secretos continúan en paralelo. El ticket CDmon
#cdmoncc274423sigue pendiente de logs forenses.Plan operativo del mirror estático publicado en #180
En respuesta a la decisión de remediación de este issue (comment-455), el plan por fases para el
mirror HTML read-only de
antiguo.feadulta.comestá publicado en #180: #180 (comment)Resumen para este hilo (lo que afecta al incidente):
wgetcontra el origen), nunca copiando el filesystem.Por construcción es imposible arrastrar
sys.php,rr.phpni ningún.phpejecutable aHetzner: se captura lo que Joomla renderiza, no lo que hay en disco.
hosts externos en
<script>, patrones de inyección, páginas de challenge congeladas), con reglade parada explícita. Capturar por HTTP evita el código del atacante, no su output — este es
el punto que suele olvidarse.
/web/antiguo+ dump) se preserva pero NO viaja aHetzner: se queda en almacenamiento local etiquetado como cuarentena.
antiguo.feadulta.com, que sigue sirviendo Joomla durante toda lavalidación. Sin DNS, sin cutover, sin retirada. La contención de comment-452 se mantiene.
tras 7-14 días estables.
Dependencia crítica que cruza los dos issues: el mirror se construye crawleando el origen, así
que si el hosting de CDMON se apaga el 07/08 perdemos la fuente. La continuidad del hosting
deja de ser solo una cuestión de rollback de la migración y pasa a ser prerrequisito de la
remediación de seguridad.
Hallazgo forense:
sys.phpYA estaba en la copia local — la shell precede a la intrusión del 29-julSalió al evaluar si podíamos usar la copia local de Joomla como fuente del mirror (detalle completo
en #180: #180 (comment)).
Hecho verificado
La copia local de Joomla (contenedor
joomla-web, construida desde un backup) contienemodules/mod_menu/sys.phpcon el mismo SHA-256 exacto que la evidencia preservada deproducción en comment-445:
Datación de esa copia local:
configuration.phpde 1-mar-2026, contenido hastaenero/marzo 2026.
Conclusión
La web shell ya estaba en el sitio meses antes de la intrusión del 29-jul — coherente con su
mtimede 2020-10-25, que hasta ahora no se podía distinguir de una fecha falsificada. El actor deesta semana no la plantó: la encontró ya puesta. Esto refuerza la hipótesis de comment-446 de
que
sys.phpfue el mecanismo plausible para crear/rr.php.Implicación que hay que verificar ya
En
N:\Backup\Joomla_dbestán los backups del incidente de malware de mayo-2026 (34detecciones, "Fase E pendiente"), incluido
feadulta-clean-20260525— el posterior a aquellalimpieza.
Esto no depende de CDmon
La atribución está bloqueada esperando los access logs del proveedor. Esta línea no. Se puede
datar la aparición del shell bisecando los backups que ya tenemos, offline y sin tocar producción:
feadulta-20260111-pre-incidente.jpafeadulta-20260306-akeeba.jpafeadulta-20260525-INFECTED.tar.gzfeadulta-clean-20260525.tar.gz(post-limpieza)mod_menu/sys.phpen los cuatro. Prioridad:feadulta-clean-20260525.sobrevivió a aquella pasada.
Pendiente de autorización de Rafa para lanzar la bisección (es solo lectura sobre backups locales).
Posible exposición de datos: backups completos (sitio + BD) dentro del árbol web
Detectado al inventariar
/web/antiguopara el mirror (detalle en #180: #180 (comment)).Hecho
8,6 GB de backups completos del sitio — incluido el dump de
fejoomla3— dentro del directorioservido por web. Única protección: un
.htaccessde 821 bytes en esa carpeta.Por qué es relevante para este incidente
sys.phpincluye subida vía POST(comment-445) y
rr.phpse usó para plantar los backdoors (comment-446). Con capacidad deescritura, ese
.htaccessse sobrescribe o se borra y los.jpaquedan descargables por HTTP.y datos de la etapa Joomla.
sys.phplleva en el sitio desde 2020 (comment-468) — la ventana de oportunidad no son días,son años.
No hay evidencia de que ocurriera. Pero tampoco hay forma de descartarlo sin los access logs,
que siguen pendientes en
#cdmoncc274423.Acciones propuestas
*.jpa/*.j01bajo/administrator/components/com_akeeba/backup/en los access logs solicitados..jpa/.j01del árbol web. Los tres están ya duplicados en almacenamientolocal (
N:\Backup\Joomla_db), así que no se pierde nada. Reducción de superficie inmediatay sin coste. (Es escritura en producción — pendiente de autorización de Rafa.)
hashes de contraseña de los 1.182 usuarios.
Nota relacionada
El directorio de Akeeba solo tiene 3 backups completos (11-ene, 6-mar, 26-may) separados ~2
meses. No hay backup automático diario ni semanal operativo en el legacy, en contra de lo que
se asumía.
Recalibrado: los 1.182 usuarios no son cuentas reales — baja la severidad de comment-479
Aporte de Rafa: los 1.182 usuarios no son personas que entren al sitio. Existen como registros
de autoría (firman artículos migrados de la etapa Joomla), pero nadie hace login salvo los
webmasters e Inma.
Lo que se cae de comment-479
esas cuentas nunca se usaron para entrar, sus hashes son en la práctica inertes: no hay
contraseñas reales que reutilizar en otros servicios, que es el daño típico de una fuga de
hashes.
Lo que sigue en pie (y ahora es lo que de verdad importa)
El problema no era el volumen de cuentas, sino qué más hay en esos mismos
.jpa:calvo(396) ylas legacy
andrey(1087) /pabloarias(1047) /josek(1049). Son exactamente lasprivilegiadas, y sus hashes viajan en esos archivos. Un puñado de admins pesa más que 1.182
registros inertes.
configuration.phpva dentro del backup: credenciales de la BD y cualquier secreto deconfiguración del Joomla (SMTP, claves de componentes). Para un atacante suele valer más que
una tabla de usuarios.
direcciones de personas reales. Es dato personal, de menor gravedad que un hash pero no cero.
Efecto sobre las acciones propuestas
.jpadel árbol web*.jpa/*.j01configuration.phpy los admins reales, no las 1.182 cuentasSin prisa, pero la retirada de los archivos del directorio público sigue siendo la acción de mejor
relación coste/beneficio: están duplicados en
N:y no se pierde nada.CDmon: access log recibido — cronología ampliada y IP origen reportada
Evidencia original preservada localmente:
Secuencia confirmada (29-jul-2026, CEST / UTC+2)
Los registros de CDmon aportan como IP de origen reportada tras Cloudflare
130.195.250.105. La IP162.158.122.21es el nodo Cloudflare que conectó con el origen. La IP, user-agent y cabeceras son indicadores técnicos, no una identificación personal concluyente.rr.phpcon destino/webwp-cl.phpwp-cl.phprr.phppor/web,wp-contentymu-pluginsrr.phpparamu-pluginshealth-check.phprr.phpEsto confirma actividad interactiva desde la IP de origen reportada: el actor no solo subió archivos por
rr.php, sino que accedió y ejecutówp-cl.phpantes de navegar y plantar el MU-plugin. ElPOST302 dewp-cl.phpno permite por sí solo determinar su payload o finalidad exacta, porque no disponemos del cuerpo de la petición.La línea posterior de
cic.esno pertenece afeadulta.comni a esta ventana y se excluye de la cronología.Gaps que siguen abiertos
rr.phpoperativo: sigue faltando la petición que creó/subió esa shell y cualquier acceso inicial asys.php.rr.php,sys.phpy cualquier subida/escritura asociada, para acotar la implantación inicial.Ventana ampliada de access log: 07:02:00–07:02:59 CEST
Se preservó el fichero íntegro aportado por Rafa, separado de las líneas de ruido de usuarios/bots:
Hallazgos nuevos
rr.phpya estaba operativa antes de la primera línea de ataque recibida. A las07:02:07ya se navega por/web; además suRefererapunta arr.php?path=.../wp-content, request previa que no aparece en esta ventana. Por tanto, este extracto no contiene la implantación inicial derr.php.Cronología operativa consolidada desde
130.195.250.105:07:02:07–08: navegación de la shell por/weby acción75706c6f6164=opet.07:02:16: POST arr.phpsobre/web(correlacionado con la subida dewp-cl.php).07:02:19:wp-cl.phpresponde200y 67.634 bytes.07:02:25: POST awp-cl.phpdevuelve302.07:02:26–29: se solicita/wp-admin/, que redirige awp-login.php?reauth=1; se carga la página de login y sus assets.07:02:32–53: nueva navegación derr.phphastawp-content/mu-pluginsy POST final, correlacionado conhealth-check.php.07:02:55: nueva carga dewp-login.php?reauth=1.No hay evidencia de login WordPress estándar exitoso en esta ventana. Tras el POST a
wp-cl.php, la sesión llega al formulario dewp-loginconreauth=1; el extracto no contiene un POST posterior awp-login.php. Esto no descarta abuso mediante código/backdoor, pero descarta afirmar que el actor obtuvo acceso por un login WordPress convencional en estos 59 segundos.Implicación
La hipótesis se afina: el actor llegó con
rr.phpya desplegada, creó/operówp-cl.php, comprobó o intentó el acceso administrativo y luego plantó el MU-plugin. Sigue faltando el evento anterior que creórr.phpy cualquier interacción inicial con la shell Joomlasys.php; hay que pedir a CDmon un intervalo anterior, no solo alrededor de07:02.CDmon: coincidencias
wp-cl.phpdel 27-jul — interpretación cautaEvidencia del correo preservada:
CDmon informa de varios GET desde la IP de origen reportada
149.102.236.228entre12:07:55y12:09:16 CESTdel 27-jul:GET /wp-cl.php→ 301;GET /es/wp-cl.php→ 404 y carga de la página de error Joomla;Conclusión
Esto prueba que esa IP sondeó la ruta
wp-cl.phpdos días antes. No prueba quewp-cl.phpexistiera el 27-jul: la respuesta 301 y el posterior 404 en la ruta Joomla son compatibles con el comportamiento de redirección/routing para una ruta inexistente. Es distinto de la evidencia del 29-jul, dondewp-cl.phprespondió 200 con 67.634 bytes y fue objeto de POST desde otra IP (130.195.250.105).Por ahora se tratan como dos actividades potencialmente relacionadas por el nombre de ruta, pero no se atribuyen al mismo actor ni se adelanta la fecha de creación del fichero sin una respuesta 200, hash, mtime o request de escritura.
CDmon indica que adjunta el log completo; ese adjunto no llegó al expediente de chat. Al recibirlo hay que preservarlo, hashearlo y buscar de forma reproducible
rr.php,sys.php,wp-cl.php,health-check.php,wp-highlits.phpy las IPs130.195.250.105/149.102.236.228en el intervalo disponible.LOG COMPLETO 29-jul recibido — compromiso WordPress administrativo confirmado
Preservación
Este log corrige y amplía los comentarios previos, que solo cubrían hasta
07:02:59.Cronología confirmada (CEST / UTC+2)
130.195.250.105license.txtrr.phpya está operativa/web/wp-contentrr.phpen/webwp-cl.phpwp-cl.phpse sirve (67.634 bytes) y recibe POSTrr.phpbajowp-content/mu-pluginshealth-check.phpwp-login.php/wp-admin/yusers/me?context=editresponden 200wp-highlits/wp-highlits.phpupload-pluginPOST, activación con noncewp-admin/options.phpsettings-updated=truerr.phpConclusiones corregidas
07:03:00hubo POST awp-login.phpcon 500 y, segundos después, una sesión WordPress autenticada. La evidencia posterior (/wp-admin/200, RESTusers/me?context=edit200, instalación/activación de plugin) confirma acceso administrativo efectivo.wp-highlits; no fue solo un fichero abandonado en disco.rr.phpya existía a las 06:59:10. Este archivo no contiene su request de creación. Tampoco contiene una request del atacante a la ruta desys.php; la hipótesis de Joomla como vector sigue siendo plausible por el artefacto hallado, pero no está probada por este access log.Acciones que pasan a ser obligatorias antes de declarar el WordPress limpio
130.195.250.105/149.102.236.228;rr.php.Pausa operativa temporal — 29-jul-2026
La investigación forense queda pausada, no cerrada, para priorizar la publicación editorial urgente de la carta.
Estado preservado:
logs/incident-183/evidence/cdmon-accesslog-20260729-full-received.txt(SHA-256 24dae1ed3f1a3c99670fc63c3fdb0615c15f5c890088f37225fd22bcc453fc9a);Al reanudar, el orden es: recuperar/auditar acceso administrativo, rotar secretos WP, reconstruir/verificar Wordfence y solicitar/correlacionar logs FTP/SFTP/SSH/panel para localizar la creación inicial de
rr.php.D1 ejecutada: el Joomla ya no es superficie de ataque — se puede apagar tras la ventana
La remediación acordada en D1 (#180 comment-442) está desplegada. Desde el 30-jul ~20:30 UTC,
antiguo.feadulta.comya no sirve el Joomla: sirve un mirror HTML estático desde el servidorde Hetzner. Detalle técnico en #180 comment-510 y comment-511.
Lo que esto significa para este incidente: el archivo histórico deja de ejecutar PHP. El mirror
se construyó únicamente por HTTP, nunca copiando el filesystem, precisamente para no arrastrar
ningún
.phpcomprometido. Se verificó antes de publicarlo: 0 ficheros con PHP, 0 páginas dechallenge congeladas, y la paridad de contenido salió limpia (25.383 páginas, 0 diferencias de
título ni de texto — #180 comment-509).
Cuándo se puede apagar el Joomla
A partir del 1-2 de agosto, al cerrarse la ventana de 48-72 h de tráfico real que arrancó con
el cambio de DNS.
El motivo de esperar no es técnico del mirror, es de reversibilidad: mientras dura la ventana, el
plan de vuelta atrás es revertir el registro A, y eso exige que el Joomla siga respondiendo al otro
lado. Cerrada la ventana, esa red de seguridad ya no hace falta.
Cómo apagarlo — y qué NO hacer
Sin borrar nada. Tres razones, y las tres importan en este issue:
la web shell
sys.phpy el resto de artefactos en cuarentena reversible. Borrarlos destruyeevidencia de un incidente abierto.
denylodevuelve a la vida. Borrar convierte una puerta de ida y vuelta en un camino sin retorno.
(#180 comment-502)—, pero para reabrir la investigación sí.
⚠️ Esto pone fecha límite a la evidencia
El hosting de CDMON caduca el 07/08/2026. Si no se renueva o degrada a un plan barato,
desaparecen a la vez el rollback y todo el material forense de este incidente: el árbol
comprometido, los artefactos en cuarentena y los logs del panel que aún no se han solicitado.
En #180 la renovación de CDMON estaba justificada como seguro de la migración. Aquí es además un
requisito de preservación de evidencia, y con una fecha mucho más dura: quedan 8 días.
Al reanudar la investigación (orden fijado en comment-494: recuperar/auditar acceso administrativo,
rotar secretos WP, reconstruir/verificar Wordfence, y correlacionar logs FTP/SFTP/SSH/panel para
localizar la creación inicial de
rr.php), conviene sacar copia de lo que haga falta antes del07/08, no después.
Lo que este cambio NO resuelve
Nada de esto toca el WordPress, que es el objeto de este issue. Los backdoors
(
health-check.php,wp-highlits) y el vector de entrada siguen exactamente donde estaban. Loúnico que cambia es que la mitad Joomla del sitio deja de poder ejecutar código, que era el
objetivo de D1.
Evidencia preservada en tres copias — y corrección al comentario anterior
Corrección
En comment-513 escribí que al caducar CDMON el 07/08 desaparecería "todo el material forense de
este incidente". No es cierto, y conviene aclararlo antes de que empuje a decidir por miedo.
Los artefactos y los logs ya recibidos estaban en local desde el principio. Lo que sí se perdería
al apagarse CDMON es distinto y más acotado:
harían falta para localizar la creación inicial de
rr.php.La renovación de CDMON sigue siendo necesaria por el rollback de la migración (#180 §1). Como
argumento de preservación de evidencia, es mucho más débil de lo que dije.
Lo que estaba en local y ya está respaldado
logs/incident-183/no estaba en gitea:logs/figura en el.gitignore(línea 43), así queninguno de esos ficheros se ha subido nunca. Vivía en una única copia, en el disco de la WSL.
Ya no. Empaquetado y replicado:
~/joomla-migration/logs/incident-183/(WSL)N:\Backup\incident-183\— fuera de la WSL, junto al resto de backups/data/backups/incident-183/Las tres verificadas contra el mismo SHA-256.
Va en
.tar.gza propósito. Dentro hay malware real (health-check.php,wp-highlits.php,joomla-sys.php): comprimido no puede ejecutarse por accidente ni acabar servido si el árbol sedespliega alguna vez en un webroot. Por el mismo motivo no se ha subido al repo suelto, aunque
logs/esté ignorado y bastara con ungit add -f.Contenido preservado: los tres artefactos, el log de acceso completo del 29-jul
(
cdmon-accesslog-20260729-full-received.txt), loserrors.log/php_errors.logdel 27, 28 y 29 dejulio, el extracto del correo de CDmon sobre
wp-cl.phpdel 27-jul, y elmanifest.jsoncon loshashes.
Estado del Joomla
Retirado como superficie de ataque: desde el 30-jul
antiguo.feadulta.comsirve un mirror estáticodesde Hetzner y no ejecuta PHP (#180 comment-510). El Joomla de CDMON sigue vivo e intacto hasta
que se cierre la ventana de 48-72 h; al apagarlo,
.htaccessdeny + parar, sin borrar.Copia íntegra del Joomla en local, por si hiciera falta volver a él:
mirror-antiguo/source/— 2,7 GB de ficheros + 66 MB de base de datos, conMANIFEST-source.sha256.Lo que este incidente aporta a la migración del 03-ago
El WordPress que se migra el lunes es el que tiene los backdoors. El criterio de migración limpia
—instalación nueva, plugins desde su origen, y de producción solo base de datos y
uploadsprevioescaneo— queda documentado en #180, junto con la parte que un cambio de servidor no corta:
las cuentas de administrador, sus hashes y las application passwords viajan dentro del dump. Ahí
está también el recordatorio de resolver la cuenta
andrey(ID 1087), que sigue pendiente desdeeste issue.