Blog

Detección de virus y malware en Linux con ClamAV

En este artículo vamos a analizar cómo correr escaneos periódicos de ClamAV en una instancia con Debian, si tu sistema utiliza systemd podrás reproducir fácilmente los mismos pasos.

¿Qué es ClamAV?

ClamAV es un antivirus open-source pensado para la detección de troyanos, virus, malware y otro tipo de amenazas. Aunque fue lanzado en 2002 para sistemas UNIX, su versión más utilizada actualmente es la de GNU/Linux, contando también con soporte para MacOS e incluso Windows.

Creado inicialmente por Tomasz Kojm, fue posteriormente vendido a Sourcefire en 2007 y más tarde adquirido por Cisco en 2013. Actualmente es mantenido por la división de ciberseguridad de Cisco, Talos.

¿De verdad es necesario?

Si se supone que GNU/Linux no tiene virus, ¿es necesario un software antivirus? Esto depende en gran medida del uso que queramos darle.

El primer caso no afecta directamente a la máquina que está corriendo el antivirus. En caso de servir archivos a otros sistemas que utilizan Windows, se puede utilizar ClamAV para detectar posibles amenazas para estos sistemas. De hecho, uno de los usos más comunes de ClamAV es en servidores de correo, para frenar la dispersión de archivos maliciosos.

También se puede dar el caso, que a nivel de negocio, sea necesario para la obtención de alguna certificación. Esto suele ser más común cada vez y desde STR nos hemos encontrado en varias ocasiones en esta situación, motivo por el cual hacemos este artículo.

En cualquier caso, aunque por el propio diseño los sistemas UNIX-like son más seguros, no son invulnerables a posibles virus y sobre son muy usados como canal de distribución de los mismos.

Distintas formas de uso

ClamAV es un software complejo y muy completo que puede ser utilizado en multitud de formas. Por ejemplo se puede llevar a cabo un escaneo de los ficheros a los que se accede en tiempo real, función que solo está disponible en GNU/Linux. También se puede realizar una cuarentena de los ficheros detectados como infectados, moviéndose a un directorio indicado. No obstante hay que tener cuidado ya que se pueden dar falsos positivos y esto podría acabar afectando al servicio si se usa en un sistema de producción.

Por lo tanto lo que vamos a llevar a cabo nosotros es un escaneo del árbol completo de directorios, dejando reflejado en un fichero de log los resultados para que puedan ser analizados y actuar en consecuencia pero sin afectar al funcionamiento del sistema. Además vamos a aprovechar los timers de Systemd para realizar estas acciones de forma periódica y desatendida.

Instalación de ClamAV

ClamAV ofrece las fuentes de su software, pero en el caso de Debian lo tenemos disponible también mediante la paquetería del sistema por lo que simplemente tenemos que instalarlo de la siguiente forma:


sudo apt-get install clamav clamav-daemon

Con la instalación de estos paquetes se nos configuran dos servicios de systemd:

  • clamav-daemon.service: este servicio estará parado y sin habilitar. Es al que llamaremos para realizar los escaneos de ficheros y en caso de utilizar el escaneo en acceso es el que lo coordina.
  • clamav-freshclam.service: este servicio se encontrará habilitado y corriendo y es el que se encarga de actualizar la base de datos de virus.

Por defecto y gracias al servicio de freshclam se actualiza la base de datos de virus cada hora. De esta forma si en algún momento queremos hacer un escaneo ad-hoc no será necesario actualizarlo antes.

Como indicamos antes, el daemon no está todavía corriendo, por lo que tenemos que habilitarlo y arrancarlo de la siguiente manera:


sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon

Es importante tener en cuenta que aunque el servicio tiene un estado activo, puede que no haya terminado de arrancar y por lo tanto cualquier comando que haga uso del mismo, como clamdscan, puede fallar si se ejecuta momentos después de un reinicio.

root@overkill:~# systemctl restart clamav-daemon && systemctl status clamav-daemon && /usr/bin/clamdscan --fdpass . ● clamav-daemon.service - Clam AntiVirus userspace daemon Loaded: loaded (/lib/systemd/system/clamav-daemon.service; enabled; vendor preset: enabled) Drop-In: /etc/systemd/system/clamav-daemon.service.d └─extend.conf Active: active (running) since Wed 2024-09-04 19:21:21 CEST; 5ms ago Docs: man:clamd(8) man:clamd.conf(5) https://docs.clamav.net/ Process: 39942 ExecStartPre=/bin/mkdir -p /run/clamav (code=exited, status=0/SUCCESS) Process: 39943 ExecStartPre=/bin/chown clamav /run/clamav (code=exited, status=0/SUCCESS) Main PID: 39944 (clamd) Tasks: 1 (limit: 38301) Memory: 4.7M CPU: 6ms CGroup: /system.slice/clamav-daemon.service └─39944 /usr/sbin/clamd --foreground=true  Sep 04 19:21:21 overkill systemd[1]: Starting Clam AntiVirus userspace daemon... Sep 04 19:21:21 overkill systemd[1]: Started Clam AntiVirus userspace daemon. ERROR: Could not connect to clamd on LocalSocket /var/run/clamav/clamd.ctl: No such file or directory LibClamAV Error: File tree walk aborted.  ----------- SCAN SUMMARY ----------- Infected files: 0 Total errors: 1 Time: 0.000 sec (0 m 0 s) Start Date: 2024:09:04 19:21:21 End Date: 2024:09:04 19:21:21

Para comprobar si el servicio está levantado deberemos validar que el socket del daemon está levantado, esto lo podemos hacer con:


ls -l /var/run/clamav/clamd.ctl

 

root@overkill:~# systemctl restart clamav-daemon && ls -l /var/run/clamav/clamd.ctl ls: cannot access '/var/run/clamav/clamd.ctl': No such file or directory root@overkill:~# ls -l /var/run/clamav/clamd.ctl srw-rw-rw- 1 clamav clamav 0 Sep 4 19:25 /var/run/clamav/clamd.ctl

A parte de esta comprobación también podemos consultar el log del demonio en /var/log/clamav/clamav.log, donde podremos ver cómo elimina o levanta el socket.

Configuración ClamAV

En este punto ya tenemos levantado el servicio de ClamAV y listo para realizar escaneos, pero si lanzamos un escaneo sin más, lo más probable es que nos encontremos con múltiples warnings o errores.

Si probamos por ejemplo a lanzar un escaneo del home de root como el propio usuario root, nos encontraremos con que no tenemos permisos. Esto se debe a que por defecto el usuario que se usa es clamav. Para escalar privilegios y poder analizar todos los ficheros del sistema tendremos que ejecutar el comando con la opción --fdpass y no olvidarnos de hacerlo con el usuario root o utilizando sudo.

root@overkill:~# /usr/bin/clamdscan /root /root: File path check failure: Permission denied. ERROR  ----------- SCAN SUMMARY ----------- Infected files: 0 Total errors: 1 Time: 0.000 sec (0 m 0 s) Start Date: 2024:09:04 19:35:52 End Date: 2024:09:04 19:35:52 root@overkill:~# /usr/bin/clamdscan --fdpass /root /root: OK  ----------- SCAN SUMMARY ----------- Infected files: 0 Time: 0.986 sec (0 m 0 s) Start Date: 2024:09:04 19:36:04 End Date: 2024:09:04 19:36:05

Otro de los problemas que nos podemos encontrar es la aparición de warnings al escanear directorios que contengan sockets, ya que este tipo de ficheros no pueden ser analizados.

fig4.png

Para evitar este tipo de warnings lo que podemos hacer es excluir todos los directorios que los contengan. La siguiente es una lista de los más habituales:

  • /proc
  • /sys
  • /run
  • /dev
  • /snap
  • /var/lib/lxcfs/cgroup
  • /var/spool/postfix/private
  • /var/spool/postfix/public
  • /var/spool/postfix/dev

Para excluirlos utilizaremos la opción ExcludePath de la siguiente forma:


printf "ExcludePath ^/proc\nExcludePath ^/sys\nExcludePath ^/run\nExcludePath ^/dev\nExcludePath ^/snap\nExcludePath ^/var/lib/lxcfs/cgroup\nExcludePath ^/var/spool/postfix/private\nExcludePath ^/var/spool/postfix/public\nExcludePath ^/var/spool/postfix/dev" | sudo tee -a /etc/clamav/clamd.conf

En función de la cantidad de directorios anidados que tengamos en nuestro sistema también es posible que recibamos un error como el siguiente:


Failed to determine real filename of (null)

Esto es debido a que por defecto Calmav viene configurado con un máximo de recursividad (30) que puede no ser apropiado para nuestro sistema. Podemos validar el máximo de directorios anidados con el siguiente comando:


find / | awk 'FS="/" {print(NF)}' | sort --general-numeric-sort | tail --lines 1

Tras esto solo tendremos que modificar el valor de MaxDirectoryRecursion.

La ejecución de un escaneo puede ser muy larga y por defecto se ejecuta un solo proceso. Por lo tanto, es recomendable incluir en el comando las opciones --fdpass --multiscan para poder paralelizar y por lo tanto reducir el tiempo de ejecución.

Ejecución programada

Habitualmente se requiere realizar múltiples ejecuciones de los escaneos de forma periódica. Para llevar a cabo esto en un sistema GNU/Linux, la forma más fácil es configurar un cron. No obstante hay una forma mucho más flexible y con más opciones que es los timers de systemd.

Con los timers de systemd podemos configurar una periodicidad para la ejecución pero además incluir un delay aleatorio con un máximo en segundos. Esto puede ser muy útil por ejemplo a la hora de realizar tareas sobre múltiples instancias paralelas que ofrecen el mismo servicio. 

Para lanzar el comando de escaneo de clamav con un timer de systemd, primero tenemos que configurar un servicio de systemd. En nuestro caso vamos a crear un escaneo semanal, para ello creamos el fichero /etc/systemd/system/clamavweekly.service con el contenido:


[Unit]
Description="Execution of full system ClamAV scan"

[Service]
ExecStart=/usr/bin/clamdscan --fdpass --multiscan --log=/var/log/clamav/clamdscan.log /
User=root

Tras esto creamos el fichero del timer que ejecutará este servicio en /etc/systemd/system/clamavweekly.timer:


[Unit]
Description="run clamavweekly.service"

[Timer]
OnCalendar=Sun *-*-* 23:05:00
RandomizedDelaySec=3600
Unit=clamavweekly.service

[Install]
WantedBy=multi-user.target

Una vez creados estos ficheros tenemos que recargar la configuración de systemd con el comando systemctl daemon-reload y ya tendremos programada la ejecución del escaneo. Podemos comprobar la siguiente ejecución comprobando el listado de timer con el comando systemctl list-timers.

Recibir notificaciones de fallo

Tal y como está configurado ahora, no nos enteraríamos de un fallo en la ejecución a no ser que accedamos a ver el estado del servicio o a leer el log. Con los timers de systemd al contrario de cron, no hay una manera integrada de enviar una notificación por correo cuando se produce un fallo. No obstante, en todos los servicios se puede configurar una acción cuando se produce un error. Por lo tanto lo que podemos hacer es configurar un servicio que se encargue de enviar correo.

Para ello creamos un script que se encargue de recoger información sobre el fallo y enviar el correo, aceptando como variables los datos de la dirección de envío y servicio. En nuestro caso lo hemos creado en /usr/local/bin/failnotification.sh:


#!/bin/sh
systemctl status --full "$2" | mail -s "Fallo en el servicio $2" $1

Este script lo llamamos desde el nuevo servicio /etc/systemd/system/failnotification@.service:


[Unit]
Description=Send fail notification email

[Service]
Type=oneshot
ExecStart=/usr/local/bin/failnotification.sh mymail@example.com %i
User=root
Group=systemd-journal

Por último, solo tenemos que añadir a la configuración del servicio encargado de ejecutar el escaneo la siguiente linea en el apartado Unit:


OnFailure=failnotification@%n.service

De esta forma cada vez que el servicio falle recibiremos una notificación por correo que incluye el output del estado del servicio.

 

En este artículo hemos visto cómo utilizar de forma básica ClamAV para escanear nuestro sistema de forma eficiente. Además también hemos visto cómo programar los escaneos con notificaciones de fallo mediante timers de systemd, algo que se puede utilizar en multitud de otros casos.

 

Newsletter de STR Sistemas

Suscríbete a nuestra newsletter para recibir contenido interesante del mundo DevOps y artículos escritos por nuestros técnicos

¡Usamos cookies propias y de terceros para mejorar tu experiencia en esta web! Si sigues navegando, consientes y aceptas estas cookies en tu ordenador, móvil o tablet.

Más información sobre las cookies y cómo cambiar su configuración en tu navegador aquí.

x