Archivo de la categoría: php

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

Yoda conditions, código menos legible pero menos propenso a errores

El otro día me encontré leyendo código ajeno de PHP una condición «invertida»: primero se presentaba el valor buscado y luego se comparaba con la variable:

if('aguja' == $pajar){ // la encontraste!

Me dejó completamente descolocado porque supone leer al revés, y es en honor a ese personaje de Star Wars que anteponía predicado a sujeto que se ha nombrado esta manera de hacer las condiciones.

¿Los motivos para usarla? Con operadores de igualdad normales (==) en algunos lenguajes podrías escribir por error if ( $pajar = 'aguja' ), lo que asignaría el valor en vez de comparar (y siempre daría true); sin embargo, si el literal está a la izquierda ('aguja' == $pajar), esa asignación daría un error de sintaxis y el bug saltaría automáticamente.

Fuente: Yoda conditions