Detectar código PHP malicioso oculto en presuntos PDFs (o TTF, JPG, PNG, ICO…) atacando WordPress

En el ataque a instalaciones WordPress comprometidas, es habitual que, además de usar una brecha de seguridad que les permite inyectar un fichero de entrada, luego el grueso del código del webshell (aplicación maliciosa que se instala oculta) vaya en ficheros que a priori no son ejecutables y son de extensiones conocidas: documentos PDFs, imágenes JPG o PNG, ficheros de fuente TTF, o incluso iconos ICO. Esta técnica evade filtros básicos en busca de código malicioso y persiste tras limpiezas superficiales. Para hacer una limpieza completa y detectar si el sistema ha sido infectado habrá que buscar estos ficheros. Vamos a ver que comandos Linux utilizo para ello.

¿Por qué pasa en WordPress?

Por una combinación de estas tres circunstancias (que suele ser bastante habitual):

  • Plugins o themes desactualizados que permiten subidas de ficheros y/o que permiten ejecución PHP en la carpeta uploads; un plugin o theme desactualizado puede tener un agujero de seguridad que ya se ha subsanado en versiones posteriores.
  • .htaccess ausente o débil no fuerza octet-stream, con lo que acceder a estos ficheros puede ejecutar el código php en su interior.
  • Malware que se inyecta en ficheros habituales de WordPress como functions.php o wp-head.php y luego carga el código de estos «PDFs» que no son tal.

¿Un fichero no de PHP con código PHP dentro?

Dentro del fichero, en lugar de la información habitual de estos ficheros, esconden <?php eval(base64_decode(...)); ?>, o sea, código PHP ofuscado que se ejecuta si el servidor lo interpreta como PHP.

Así que, como el código PHP, al menos su etiqueta de apertura, va en claro podemos utilizar el siguiente comando linux:

grep -RIn --include="*.pdf" -e "<?php" /ruta/a/wp-content/uploads/
  • -R: Recursivo en subdirectorios (uploads suele tenerlos).
  • -I: Ignora binarios puros, pero procesa ficheros híbridos.
  • -n: Muestra línea exacta.
  • --include="*.pdf": Solo PDFs.

Ejemplo de salida sospechosa:

wp-content/uploads/2026/04/malicious.pdf:154:<?php eval(gzuncompress(base64_decode('3vilc0d3-3vilc0d33vilc0d3-3vilc0d3')));?>

Podemos hacer una búsqueda más amplia si no sabemos en que extensiones se pueden ocultar, aunque esto puede dar mucho falso positivo:

grep -RIn --exclude="*.php" -e "<?php" /ruta/a/wp-content/uploads/

--exclude="*.php": Todos los ficheros que no sean PHP.

Lo anterior podemos replantearlo con un pipe para borrar lo encontrado directamente. ATENCIÓN esto borra todo lo que encuentra, incluidos falsos positivos.

grep -RIlZ --include="*.pdf" -e "<?php" /ruta/a/uploads/ | xargs -0 rm -f
  • -l esta opción de grep nos devuelve solo nombres de archivo, listos para pasar por el pipe.

Principalmente el vector de ataque en WordPress suele ser el directorio /wp-content/uploads/ específicamente; otros directorios son menos comunes.

Prevención básica: .htaccess

Por otro lado, revisa que la carpeta uploads tiene prohibido ejecutar ficheros PHP que se encuentren allí, cualquier código PHP no se interpreta.

# En .htaccess de /uploads/
<Files "*.php">
Order Deny,Allow
Deny from all
</Files>
php_flag engine off

Pero no confiemos solo en .htaccess: mejor usar open_basedir en php.ini y validar cualquier subida a uploads.

Aún así, también es interesante hacer el mismo grep por toda la instalación desde la raíz y quizá ampliar a buscar cosas como <?=, <% o eval() ofuscado. Prueba con grep -P '(?i)(<\?php|eval\()'.

Fuente: wpsecurityninja

¿Cómo puedo borrar un archivo de linux que no se deja?

Cuando en Linux no puedes borrar un archivo, el problema casi siempre no está en los permisos del fichero sino en los del directorio que lo contiene.

Qué permisos controlan el borrado

En Unix/Linux, para borrar un fichero hace falta permiso de escritura (w) y ejecución (x) sobre el directorio que lo contiene, no sobre el propio fichero. Eso significa que puedes tener un fichero 777 dentro de un directorio 555 y aún así rm dará Permission denied, porque el directorio no es escribible.

Motivos típicos de “Permission denied” al borrar

  1. Directorio sin permiso de escritura
    El usuario no tiene w sobre el directorio que contiene el fichero.
  2. Directorio sin permiso de ejecución
    Aunque parezca raro, sin x no puedes “navegar” la entrada del fichero en el directorio, por lo que rm falla.
  3. Mezcla de permisos y sudo
    Si el fichero o los directorios son propiedad de otro usuario, normalmente necesitas sudo o cambiar propietario/permisos antes de borrar.
  4. Atributos especiales (immutable, etc.)
    En algunos casos el fichero tiene atributos especiales (+i) que impiden borrarlo incluso con permisos de escritura.

Cambiar permisos del directorio antes de borrar

La solución básica es asegurarse de que el usuario tiene permisos de escritura sobre el directorio:

chmod u+w /ruta/al/directorio
rm -f /ruta/al/directorio/archivo

Si quieres borrar todo el contenido de un directorio, suele ser necesario:

chmod -R u+w /ruta/al/directorio
rm -rf /ruta/al/directorio

Bola EXTRA: Hagamos una función de terminal para borrar y chmod en una sola línea

Sabiendo lo anterior, y ante la necesidad de usar varias veces ambos comandos sobre un mismo directorio, he creado una función:

chmodrm() { chmod -R u+w "$1" && rm -rf "$1"}
  • chmod -R u+w "$1" da permiso de escritura al propietario (u) sobre el directorio y todo su contenido.
  • rm -rf "$1" borra el directorio y su contenido recursivamente solo si el chmod tiene éxito (por el &&).

Consideraciones de seguridad

  • Usa esta función solo en directorios donde realmente tengas autoridad para borrar; rm -rf es irreversible.
  • Si el directorio es propiedad de otro usuario, necesitas sudo o cambiar el propietario (chown) antes de chmod -R.
  • En entornos compartidos o de producción, considera usar rm -ri (interactivo) o loggear antes de borrar en lugar de un rm -rf ciego.

Fuentes: StackOverflow | RedHat