sábado, 22 de diciembre de 2007

Cómo navegar bajo fuego sin quemarnos en el intento

Hoy en día, el navegar tranquilamente por Internet es una fuente directa de exposición a ataques e infección por malware.

Objetivos típicos

Ciertos objetivos típicos de los ataques dirigidos a los usuarios son:

(1) Inyección remota y ejecución arbitraria de código.

Aprovechándose de algún desbordamiento de buffer intermedio en la aplicación cliente, tras contactar a una contraparte maliciosa, el atacante toma control de la aplicación vulnerable. Con esto, el atacante consigue que la aplicación afectada ejecute lo que él quiera ejecutar, con las restricciones de seguridad del usuario que estaba corriendo la aplicación. Además, en el caso específico de Windows, dado que suele trabajarse con privilegios de Administrador, esto da paso directo a convertir nuestra máquina en un robot zombie de una botnet bajo las órdenes de algún delincuente informático.

Aplicaciones Cliente típicamente expuestas: navegador web, lector de correo electrónico, cliente de mensajería instantánea, visualizador/editor de documentos/imágenes, etc.

Vectores típicos de infección de las aplicaciones: Páginas web maliciosas o infectadas, correos electrónicos con adjuntos infectados (peor si son ejecutables), etc.

(2) Robo de credenciales.

Aprovechándose de alguna vulnerabilidad de tipo XSS en algún sitio web de interés para el usuario, se nos expone a ejecutar código script (típicamente JavaScript) arbitrario creado por el atacante, bajo el contexto de seguridad del nombre de dominio del sitio que estamos visitando. Mediante esta técnica, el uso típico es robar las cookies del sitio en cuestión almacenadas en el navegador, cosa que puede ser grave si aquellas almacenan información de identificación ante el mismo. Peor aún es el escenario si en el sitio explotado usan técnicas de CSRF, pues nos pueden robar las credenciales mientras navegamos por otros sitios. Dependiendo del caso, esto puede ocurrir tanto si navegamos simultáneamente por ambos, como si estuviésemos navegando solamente por el sitio de infectado.

Las credenciales (cookies de identificación) típicamente apetecidas por los atacantes son aquellas correspondientes a sitios relacionados con: cuentas bancarias, cuentas en sitios de comercio electrónico (e-commerce) "famosos" (Amazon, PayPal, eBay, etc.), cuentas de correo electrónico, cuentas en sitios de redes sociales (LinkedIn, FaceBook, MySpace, Blogger, etc.), etc.

Si además en Windows navegamos como Administrador, un ataque del tipo (1) permite capturar todo lo escrito "mediante el teclado" (KeyLoggers) y todo lo visto "mediante la pantalla" (Screen Grabbers). Valga la obviedad, pero así de extremo es.

Cómo protegernos

Es iluso esperar que las aplicaciones (tanto clientes como servidoras) nunca tengan vulnerabilidades. Entonces, dado que no podemos controlar qué tan bien nos protegen las aplicaciones y los servidores, trataremos de cortar la veta desde donde obtienen el beneficio los atacantes una vez que logran realizar el ataque con "éxito".

Estas medidas no pretenden ser "a prueba de balas", pero al menos permitirán protegernos contra las vulnerabilidades más típicas y los ataques más típicos, conservando la mayor funcionalidad, dentro de lo posible.

Comenzaremos por lo más simple:

Robo de credenciales

En el navegador web, el robo de credenciales y posterior suplantación de identidad, se reduce técnicamente a la lectura no consentida y posterior retransmisión de nuestras cookies.

Ante esto, aparecen las primeras recomendaciones más básicas:

Recomendación #1: No navegar simultáneamente por sitios de seguridad crítica y "el resto" de Internet.

Recomendación #2: Cerrar sesiones abiertas en sitios de seguridad crítica una vez que dejemos de usar el sitio.

El no hacerlo, deja las sesiones "abiertas", pues el servidor nunca recibió nuestro requerimiento de olvidarse de nosotros, dejándonos expuestos mientras no se cumpla el período de expiración de la sesión en el servidor. Ante un ataque CSRF, nuestra exposición sería directa. NO BASTA SIMPLEMENTE CON CERRAR LAS PÁGINAS/TABS/LENGÛETAS, pues las cookies validadas seguirán almacenadas en el repositorio de cookies del navegador y serán enviadas ante "forged queries".

Asumiendo que el atacante ya consiguió ejecutar código script arbitrario en nuestro navegador para robar nuestras cookies, no le demos lo que quiere. ¿Cómo? Simple: no teniéndolo. Naturalmente, no estoy proponiendo desistir del uso de cookies, pues hoy en día poco se puede hacer sin ellas y perderíamos mucha funcionalidad. Simplemente almacenémoslas en perfiles distintos, de modo que el compromiso de un repositorio de cookies no implique el compromiso del otro, y así trabajamos con un repositorio para transacciones de seguridad crítica y con otro para "el resto" de Internet. Esto da pie a la siguiente recomendación:

Recomendación #3: Usar perfiles (y, por ende, repositorios de cookies) distintos para navegación de seguridad crítica que para navegación "normal" (o, derechamente, "suicida", lo que requeriría un 3er perfil ;P ).

En Firefox, las opciones -P profile y -ProfileManager pueden ser de utilidad.

Sin embargo, ante un ataque de tipo (1), esta prevención también puede ser vulnerada, pues se muda de perfil, pero no de aplicación ni permisos. Ante ello, una posible mejora contra ataques "a ciegas" (en contraposición con los "dirigidos") es utilizar un navegador para "todo lo demás" y otro para navegación de seguridad crítica.

Por cierto que no todo es blanco/negro, así que es perfectamente posible tener más de 2 perfiles. No es lo mismo navegar por el sitio del banco que por un webmail o por sitios de redes sociales.

Recomendación #4: Usar programas distintos para navegación simultánea de seguridad crítica y para navegación normal (y no mezclarlos).


En mi caso, uso Mozilla Prism para navegación de seguridad crítica y dejo mi Firefox híper-enchulado para navegación normal. La gracia es que funcionan como programas separados, cada uno con su propio repositorio de cookies.

Esto ya requeriría ser vulnerado con un ataque del tipo (1), situación que analizamos a continuación:

Inyección remota y ejecución arbitraria de código.

Puesto que tampoco podemos esperar que los agentes de usuario (o sus plugins y extensiones) no tengan desbordamientos de buffer, tampoco podemos evitar que tomen control del proceso de la aplicación con la que navegamos. Como tal proceso corre bajo nuestros permisos de usuario, queda virtualmente libre para acceder/realizar todo lo que nuestro perfil de usuario del sistema nos permite. ¿Cómo lo enfrentamos? Pues nuevamente, con separación de privilegios.

La primera recomendación básica es NO trabajar cotidianamente con privilegios globales sobre el sistema.

Recomendación #5: Utilizar permisos restringidos para aplicacio'nes de riesgo.


En los sistemas basados en Unix/Linux, esto es práctica normal y está todo diseñado para que se pueda realizar sin inconvenientes. En los sistemas basados en Windows, la experiencia puede ser más frustrante pues se suele obtener ejecuciones entorpecidas de las aplicaciones de aplicarse estas restricciones. Si bien en la actualidad esto ha ido mejorando (aunque todavía falta bastante para equiparar a Unix al respecto), aún persiste la costumbre (y la configuración por omisión del sistema) de hacer todo bajo privilegios de superusuario. Considerando que al menos al 90% de los usuarios no les interesa preocuparse por la seguridad de sus sistemas y que el 90% de los usuarios de Internet usa Windows, no es de extrañar el descomunal tamaño que están alcanzando las botnets hoy en día, situación que ha contribuido enormemente a poner en grave riesgo la seguridad en Internet durante los últimos años.

La implementación siguiente es específica para Linux (en mi caso, KUbuntu), pero sus ideas pueden ser extrapoladas para otros sistemas. Quienes no puedan evitar tener que usar Windows, pueden pedirle asesoría técnica de cómo hacerlo al vendor a quien le compraron la licencia }:-] (broma).

Algo de esto ya ha incorporado Windows Vista al implementar el modo de permisos reducidos expecíficamente en Explorer 7, pero acá vamos a ver cómo hacerlo en forma más general. La gracia es que no sólo protegeremos nuestros datos de usuario de inyecciones al navegador web, sino que ante inyecciones a cualquier visualizador de documentos potencialmente malignos: e-mails, PDFs, macros de Office, fotografías, música, video, etc.

Esta solución está pensada para usuarios hogareños incluso en sistemas multiusuario (por ejemplo, con cuentas para familiares, amigos y visitas). Para servidores, si bien también puede aplicarse, puede ser poco práctico y es más recomendable SELinux.

Usuario trinchera

La idea es que dejemos nuestra cuenta de usuario normal solamente para navegaciones/visualizaciones de seguridad crítica (banco, quizás webmail) y creemos otra cuenta de usuario todavía más restringida, bajo nuestro control, que dejaremos para mandar a la guerra a la aplicaciones bajo sus permisos.

Supongamos que mi usuario:grupo es lux(1000):lux(1000). Procedemos entonces a crear otro usuario:grupo que usaremos como trinchera para navegación insegura:


% sudo addgroup --gid 1001 xul
% sudo adduser --home /home/xul --uid 1001 xul
% sudo addgroup xul xul


No debemos obviar la calidad de la contraseña que le definiremos, la cual, muy preferentemente, debe ser distinta a la nuestra.

La idea es que hagamos todo lo inseguro usando al usuario xul, el cual no deberá poder mirar nada de nuestro usuario oficial lux, pero cuyos archivos deben poder ser accesibles por lux. Para esto, agregamos a nuestro usuario oficial (¡y sólo a él!) al grupo del usuario trinchera:

% sudo addgroup lux xul


Con esto, nuestro usuario oficial tendrá acceso a todos los archivos que el usuario trinchera permita dentro de su grupo. Para que por omisión queden todos sus archivos disponibles para nuestro usuario oficial, necesitamos modificar su UMASK en el archivo de configuración de su login shell:


lux % su xul
xul % vim ~/.bash_profile


Y donde dice umask 077 debe decir umask 007.

Y no olvidemos proteger el acceso a su $HOME así como protegemos el nuestro. De paso, damos los permisos al usuario oficial vía los permisos de grupo:

xul % chmod -R u=rwX,g=rwX,o-rwx $HOME


Listo. Ya tenemos configurada la cuenta del usuario trinchera. Ahora trabajaremos en la cuenta del usuario oficial para acceder fácilmente a las aplicaciones inseguras vía el usuario trinchera desde el usuario oficial.

En nuestra cuenta podemos crear un subdirectorio ~/bin/ y, dentro de él, dejar el siguiente archivo (que llamaremos cage.sh, pues es algo más básico que un jail :P).

Archivo cage.sh:

#!/bin/bash

if [ -z "$CAGE_USER" ]; then
echo Environment variable CAGE_USER not defined.
exit 1
fi

CMD=$(basename $0)

SU=`which sux`
if [ _$SU == _ ] ; then # SU
SU=`which su`
exec "$SU" "$CAGE_USER" --command="$CMD $@"
else # SUX
exec "$SU" "$CAGE_USER" "$CMD" $@
fi


Este programa se encarga de ejecutar en forma genérica los programas que queramos como usuario trinchera, dependiendo del nombre con el que lo llamemos al invocarlo con el usuario oficial.

Como pueden ver, el nombre del usuario trinchera lo obtiene a partir de la variable de ambiente CAGE_USER. Para el ejemplo, en algún lugar de nuestro profile deberemos configurarlo así: (por ejemplo, para Bash, en ~/.bash_profile)

export CAGE_USER=xul


Y por comodidad, el nombre cage es conveniente ante cage.sh:

% ln -s cage.sh ~/bin/cage


Este archivo debe tener permisos de ejecución:

% chmod u=rwx,og=rx ~/bin/cage


También deben notar que intenta usar primero al comando sux y, si no lo encuentra, intenta después con el comando su.

Atrincherando aplicaciones

Ahora estamos casi listos. Para cada programa que queramos ejecutar con los privilegios del usuario trinchera, creamos un link simbólico a cage. Por ejemplo, para correr un Firefox "atrincherado", haríamos:

% ln -s cage ~/bin/firefox


Luego, si ejecutamos ~/bin/firefox, tendremos a nuestro querido navegador full-featured andando con cadenas, mientras que si ejecutamos directamente firefox, lo ejecutaremos como usuario oficial.

Esto puede ir más allá y podemos atrincherar todas las aplicaciones que encontremos peligrosas. Por ejemplo, si encontramos riesgoso abrir archivos PDF recién descargados, podemos atrincherar nuestro visualizador de PDF, como lo es el caso de KPDF:

% ln -s cage ~/bin/kpdf


Así, para ver un PDF potencialmente maligno, ejecutamos:

% ~/bin/kpdf archivo.pdf


O si queremos ver planillas con macros virulentas (salvando incompatibilidades de formato):


% ln -s cage ~/bin/oocalc
% ~/bin/oocalc planilla.xls


Y así con cada aplicación que queramos "black-listear". Ciertamente que una lista negra no es la mejor solución de seguridad, pero si lo limitamos al universo (generalmente reducido) de aplicaciones que utilizamos regularmente, puede ser de mucha utilidad, sin perder funcionalidad.

Algunas simplificaciones

Para evitar tener que escribir la ruta completa de la aplicación atrincherada cada vez que necesitemos ejecutarla, surge la tentación de anteponer el directorio de cage a nuestro PATH, configurándolo en alguno de nuestros archivos de inicialización:

export PATH=$HOME/bin:$PATH


El problema de esto es que cage entraría en un ciclo infinito de auto-invocaciones, pues invocaría una y otra vez a la aplicación atrincherada y nunca pasaría a ejecutar a la aplicación deseada (gracias al uso de exec, esto se limita a un sólo proceso y no botará el sistema, bastando interrumpir su ejecución con Control+C).

Una alternativa es crear un mini script que invoque a las aplicaciones atrincheradas por nosotros:

Archivo ~/bin/trinchera.sh:

#!/bin/bash

APP=$1
shift

if [ _ == _$APP ] ; then
echo Uso: `basename $0` 'programa [ argumentos ]' ;
exit 1 ;
fi

exec "~/bin/$APP" $@


Le damos permisos de ejecución con:

% chmod u=rwx,go=rx ~/bin/trinchera.sh


Acortamos su uso con:

% ln -s trinchera.sh ~/bin/trinchera


Y así ejecutamos simplemente (con ~/bin en nuestro PATH):

% trinchera firefox


Lo cual podría ser más cómodo si la ruta es engorrosa.

Ahora, a crear íconos en vuestros menúes y Desktops y ¡a la guerra! :)

De vuelta a lo oficial

Una vez que hayamos terminado de trabajar con la aplicación atrincherada y queramos, por ejemplo, rescatar los documentos que hayamos descargado dentro de la cuenta del usuario trinchera, procedemos a:

(1) Escanear con antivirus lo descargado:


% ln -s cage ~/bin/clamscan
% trinchera clamscan -r ~xul/descagas/


(2) Recuperar los documentos:

% mv ~xul/archivo_descargado ~/mis_descargas


Esto último se puede hacer así de simple porque nuestro usuario oficial pertenece al mismo grupo que el usuario trinchera y éste último nos dio permisos de escritura al grupo.

(3) Enviar documentos:


% ln -s $PWD/mi_documento ~xul/uploads
% trinchera firefox
# ... enviarlo
% rm ~xul/uploads/mi_documento


Limitaciones

El uso de este usuario trinchera no protege ante ataques de elevación de privilegios, pues aquellas vulnerabilidades radican o bien en el kernel o bien en otros procesos que ya están corriendo como superusuario. Al menos, la gracia es que se reduce la probabilidad de ataque, pues se requiere tanto una vulnerabilidad de inyección de código como una vulnerabilidad de elevación de privilegios.


Espero que les guste y les sirva. Cualquier comentario o mejora es bienvenido.

Have a nice bit.

Autor: Luis León Cárdenas Graide
Licenciado bajo Creative Commons. Algunos derechos reservados.

No hay comentarios.: