sábado, 7 de diciembre de 2013

El rincón de HighSec: Seguridad de servidores ­ Parte I ­ Conceptos básicos, NGINX, Jaula

Compartir este artículo:
Vamos a comenzar una serie de post sobre seguridad de servidores. Antes de empezar, vamos a definir una serie de conceptos básicos:

Servidores

Un servidor no es más que un dispositivo que responde a solicitudes de un cliente, normalmente es un ordenador que  contiene  información  para  ser  compartida  con  muchos  sistemas  de  clientes.  Por  ejemplo,  páginas  Web, documentos, bases de datos, imágenes, archivos de audio y video, etc.

Podemos y debemos pedir  autenticación de usuario para verificar si tiene permiso para realizar una operación particular. Estos servidores deben contar con una lista central de cuentas de usuarios y autorizaciones, o permisos (para realizar operaciones y de acceso a datos) otorgados a cada usuario. Por ejemplo, si quiere cargar datos a un servidor FTP, se le puede dar permiso para escribir en su carpeta personal, pero no para leer otros archivos del sitio.

Concretando: Un servidor  es  un  equipo,  normalmente  un  ordenador,  que  ejecuta  procesos  en segundo  plano, llamados servicios o demonios. Ëstos escuchan peticiones de los clientes y envían la respuesta. La comunicación se realiza siguiendo un determinado protocolo, que dependerá del servicio que estemos desplegando.

También  suele  llamarse  servidor  al  software  que  se  encarga  de  escuchar  y  responder  a  las  llamadas  de  los clientes. Por ejemplo, Apache es un software que se encarga de gestionar las peticiones de acceso a una página Web, es decir, nos provee el servicio Web. Apache es, por tanto, un servidor Web.

Atendiendo al servicio que proveen, veamos diferentes tipos de servidores:

•       Servidor Web: Es un software que se mantiene a la escucha y devuelve las páginas webs solicitadas por los clientes.

•       Servidor Proxy: Es un software que permite que, dentro de una red local, se acceda a servicios que se encuentran fuera de la misma. El ejemplo típico es un servidor proxy para permitir la salida a Internet de determinados  equipos  de  nuestra red  local.  No sólo se  utiliza  para redirigir  el servicio  web,  puede ser utilizado para redirigir cualquier servicio. Cuando la función es la inversa, es decir, que desde el exterior de nuestra red se pueda acceder a un servicio interno, se llama proxy inverso.

•       Servidor FTP: Permite acceder a archivos que se encuentran en el servidor. El acceso puede ser de sólo lectura o lectura y escritura.

•       Servidor SSH: Nos permite iniciar sesiones de usario de manera remota a un equipo, en el que se está ejecutando.

•       Servidor DNS: Dado un nombre de máquina, resuelve la dirección IP a la que corresponde.

•       Servidor de correo: Es el servicio encargado de recibir y entregar los correos de los clientes.

Cada  uno  de  estos  servidores,  utiliza  sus  protocolos  de  comunicación.  Por  el  simple  hecho  de  tenerlos  en funcionamiento, estamos dejando una “puerta” abierta en el equipo que se ejecutan. Debemos proteger esta puerta de entrada contra posibles ataques.

Para la fortificación de sistemas, debemos seguir unos principios básicos:

•       Gestión de riesgos: Tenemos que realizar un análisis para estimar la magnitud de los riesgos que está expuesto el servidor. Es decir, debemos conocer los riesgos, para poder prevenir, impedir, y controlar.

•       Mínimo punto de exposición: Cada servicio que queramos proporcionar, debe (o debería) estar en un equipo separado. Además, cada equipo debe contener el software estrictamente necesario para realizar su función.  Si  es  un servidor Web,  no  debería ser  a  la  vez  el servidor  de  base  de  datos  o  de  correo,  por ejemplo. Cada servidor debe tener su propio papel , y no debe contener más que lo imprescindible para realizar su función.

•       Mínimo privilegio posible: En un entorno multiusuario, cada programa se ejecuta con unos privilegios determinados.  Para  la  administración  del  equipo,  existe  un  usuario  administrador (root  en  los sistemas UNIX­Like),  que dispone  de privilegios  para acceder  a cualquier  parte  el sistema. Bien, los servidores deben  ejecutarse  con un usuario  específico,  que deberá  poseer  los privilegios  justos  para realizar  su función. Si un  atacante  consigue  entrar  en nuestro sistema  aprovechando una vulnerabilidad de nuestro servidor, podrá obtener los privilegios bajo los que se esté ejecutando, pero no el control total del sistema.

•       Defensa en profundidad: Debemos implantar todas las medidas de seguridad posibles. Para hacer esto hay que tener en cuenta que, si una medida de seguridad no permite que el servicio responda en un tiempo razonable,  no  estaremos  ofrenciéndolo. Esa medida  no  es  válida. Además,  hay  que tener  presente  que, algunas medidas anulan a otras. En ese caso, habrá que plantearse cuál de las dos hay que implantar.

En  el  caso  de  una  gran  corporación,  la  utilización  de  diferentes  equipos  para  la  ejecución  de  los  diferentes servicios, será  viable.  ¿Qué  ocurre si  esto  no  es  así?  ¿Y si sólo  disponemos  de  un  equipo?  En  este  caso,  la división de los diferentes servicios puede hacerse mediante virtualización. Creamos una máquina virtual que será la encargada de ejectuar un servicio determinado.

Debemos tener en cuenta que, si vamos a levantar un servidor Web, probablemente necesitemos más servicios. 

Por  ejemplo,  necesitaremos  también  un  servidor  de  correo  electrónico.  Además,  seguramente  necesitemos realizar inicios de sesión en remoto para poder administrar el equipo o subir determinados archivos. A esto le añadimos la necesidad de tener un servidor de base de datos, para poder desplegar las aplicaciones Web.

Esto  hace  que,  en  muchos  casos,  en  un  mismo  equipo,  estén  corriendo  varios  servicios  a  la  vez.  Debemos proteger los mismos y, siempre que sea posible, separarlos mediante virtualización o técnicas de “enjaulado”.

Veamos tipos de ataques básicos a los que nos enfrentaremos:

•       Malware: Es un software malicioso que se ejecuta en nuestro equipo. Este software puede estar diseñado para realizar diferentes acciones, sin que nos percatemos. Estas acciones pueden ser de diversa índole, por ejemplo, permitir acceso a nuestro equipo, enviar correos con malware, enviar información almacenada, etc.

•       DDoS: Son ataques de denegación de servicio. Saturan con peticiones falsas el servicio, de forma que impiden atender las peticiones legítimas. Para hacer este tipo de ataques, normalmente, se utilizan equipos infectados con malware que realizan las peticiones sin que el usuario se de cuenta que está formando parte de una BotNet (una red de equipos “zombies” que se “despiertan” cuando el atacante les indica y realizan las peticiones masivas al objetivo).

•       Fuerza bruta: Se intenta acceder a un servicio probando múltiples contraseñas y usuarios, hasta que se consigue.

¿Cómo podemos protegernos de estos ataques?

Para  el  caso  del Malware,  debemos  utilizar  antivirus  y  detectores  de rootkits.  Para  los sistemas  GNU/Linux, disponemos de múltiples opciones, por  ejemplo,  chkrootkit, rkhunter, unhide, tiger o lynis. Estas herramientas nos permiten detectar rootkits y nos informan de posibles vulnerabilidades en nuestro sistema (binarios con el bit SUID activado y configuración permisiva para el usuario root de determitados servicios, por ejemplo). Además, podemos utilizar SpamAssanssin y ClamAV, sobre todo, para el servidor de correo.

En el caso de los ataques DDoS, podemos mitigarlos utilizando scripts que limiten el número de conexiones por IP  o  mediante  la  limitación  de  conexiones,  si  el  servicio  lo  requiere.  Una  herramienta  interesante  es  DDoS Deflate, que bloquea IP's si exceden un determinado número de conexiones.

Fuerza  bruta,  en  el  caso  de  servicios  como  SSH,  es  interesante  utilizar  SSH­Keys,  que  proporcionan  una solución elegante para ataques de fuerza bruta. Si no queda más remedio que utilizar la autencación clásica de usuario/contraseña y para proteger más servicios que requieren esta autenticación, disponemos de herramientas como  Fail2Ban  o  SSHGuard.  Estas  herramientas  realizan  una  comprobación  de  los  intentos  de  autencación fallidos y, a un número de fallos en un período determinado, banean la IP.

Además de esto, es conveniente bloquear los escaneos de puertos. Normalmente, antes de ser atacados, nuestro equipo será escaneado para comprobar qué puertos están abiertos y qué servicios están activados. Siempre que sea posible, es una buena idea cambiar los puertos por defecto, por ejemplo, en conexiones SSH. Una herramienta que  nos  permite  bloquear  los  escaneos  de  puertos  a  IPs  que  no  se  encuentren  en  una  lista  blanca  de  IPs permitidas, es PortSentry. No siempre nos interesa bloquear todas las consultas, que también es posible realizarlo mediante cortafuegos, de ahí la conveniencia de utilizar PortSentry.

Por supuesto, debemos huir de repositiorios no oficiales a la hora de instalar aplicaciones.

Una  vez vistos  estos  conceptos  básicos,  veamos  un ejemplo  de enjaulado  de un servidor web. En este caso, vamos a hacerlo con NGiNX.

NGiNX es un servidor web, proxy inverso de alto rendimiento y un proxy para protocolos de correo electrónico.

Su desarrollo comenzó en el 2002 por Igor Sysoev para solventar un problema llamado C10K (los servidores no soportaban más de 10000 conexiones a la vez).

Entre sus ventajas más importantes es su estabilidad, bajo consumo de recursos y su sencilla configuración. Es utilizado por Github, Wikipedia, Wordpress y muchos más.

Vamos a instalar los paquetes necesarios:

Arch

# pacman ­Syu nginx

Debian/ Ubuntu

# apt­get install nginx­full

Jaula en Debian

#!/bin/bash ­­verbose

#######################################

# Empezamos

#######################################

# Creamos la estructura de directorios

#######################################

mkdir ­vp /srv/nginx/dev

mkdir ­vp /srv/nginx/etc/nginx/

mkdir ­vp /srv/nginx/usr/{lib,sbin,bin}

mkdir ­vp /srv/nginx/usr/share/nginx

mkdir ­vp /srv/nginx/var/{log,lib}/nginx

mkdir ­vp /srv/nginx/srv/http

mkdir ­vp /srv/nginx/{run,tmp}

cd /srv/nginx/

ln ­s usr/lib lib

cd lib

ln ­s ../lib i386­linux­gnu

ln ­s i386­linux­gnu i686

ln ­s i386­linux­gnu/i686 cmov

[[ `uname ­m` == "x86_64" ]] && {

ln ­s usr/lib lib64 ;

cd usr ;

ln ­s lib lib64 ;

}

########################################

#Creamos los dispositivos

########################################

mknod ­m 0666 /srv/nginx/dev/null c 1 3

mknod ­m 0666 /srv/nginx/dev/random c 1 8

mknod ­m 0444 /srv/nginx/dev/urandom c 1 9

########################################

#Copiamos los binarios

########################################

cp ­vr /usr/share/nginx/* /srv/nginx/usr/share/nginx

cp ­v /usr/sbin/nginx /srv/nginx/usr/sbin/

cp ­vrp /var/lib/nginx /srv/nginx/var/lib/

touch /srv/nginx/etc/shells

#########################################

#Copiamos la bibliotecas necesarias

#########################################

cp ­v $(ldd /usr/sbin/nginx |grep /usr/lib|sed ­r 's/(.+) (\/.*) (.*)/\2/') /srv/nginx/usr/lib

cp ­v /lib/i386­linux­gnu/i686/cmov/libnsl* /srv/nginx/lib

cp ­v $(ldd /usr/sbin/nginx |grep /lib/ | grep ­v /usr/lib|sed ­r 's/(.+)(\/lib\/.*) .*/\2/') /srv/nginx/lib

cp ­v /lib/libnss_* /srv/nginx/lib

cp ­v /lib/i386­linux­gnu/libnss* /srv/nginx/lib

cp ­rfvL /etc/{services,localtime,nsswitch.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx}

/srv/nginx/etc

#########################################

#Creamos los usuarios

#########################################

echo ­e "www­data:x:33:\nnogroup:x:65534:" > /srv/nginx/etc/group

echo ­e "www­data:x:33:33:www­data:/var/www:/bin/sh\nnobody:x:65534:65534:nobody:/nonexistent:/bin/sh" >

/srv/nginx/etc/passwd

echo ­e "www­data:*:15836:0:99999:7:::\nnobody:*:15836:0:99999:7:::"  > /srv/nginx/etc/shadow

echo ­e "www­data:*::\nnogroup:*::" > /srv/nginx/etc/gshadow

############################################################

#Para poder utilizar puertos hasta el 1024 dentro del chroot

############################################################

setcap 'cap_net_bind_service=+ep' /srv/nginx/usr/sbin/nginx

############################################################

#Estableciendo permisos y montando los dispositivos

############################################################

chmod ugo+rw /srv/nginx/tmp

chmod ugo+rw /srv/nginx/run

chown www­data:www­data /srv/nginx/var/log/nginx

mount ­t tmpfs none /srv/nginx/run ­o 'noexec,size=1M'

mount ­t tmpfs none /srv/nginx/tmp ­o 'noexec,size=100M'

Jaula en Archlinux

#!/bin/bash ­­verbose

#######################################

# Empezamos

#######################################

# Creamos la estructura de directorios

#######################################

mkdir ­p /srv/nginx/dev

mkdir ­p /srv/nginx/etc/nginx

mkdir ­p /srv/nginx/usr/{lib,bin}

mkdir ­p /srv/nginx/usr/share/nginx

mkdir ­p /srv/nginx/var/{log,lib}/nginx

mkdir ­p /srv/nginx/srv/http

mkdir ­p /srv/nginx/{run,tmp}

cd /srv/nginx/

ln ­s usr/lib lib

[[ `uname ­m` == "x86_64" ]] && {

ln ­s usr/lib lib64

cd usr

ln ­s lib lib64

}

########################################

#Creamos los dispositivos

########################################

mknod ­m 0666 /srv/nginx/dev/null c 1 3

mknod ­m 0666 /srv/nginx/dev/random c 1 8

mknod ­m 0444 /srv/nginx/dev/urandom c 1 9

########################################

#Copiamos los binarios

########################################

cp ­r /usr/share/nginx/* /srv/nginx/usr/share/nginx

cp /usr/bin/nginx /srv/nginx/usr/bin/

cp ­rp /var/lib/nginx /srv/nginx/var/lib/

touch /srv/nginx/etc/shells

#########################################

#Copiamos la bibliotecas necesarias

#########################################

cp $(ldd /usr/bin/nginx |grep /usr/lib|sed ­r 's/(.+) (\/.*) (.*)/\2/') /srv/nginx/usr/lib

cp /lib/ld­linux­x86­64.so.2 /srv/nginx/lib

cp /usr/lib/libnss_* /srv/nginx/usr/lib

cp ­rfvL

/etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx}

/srv/nginx/etc

#########################################

#Creamos los usuarios

#########################################

echo ­e "http:x:33: \nnobody:x:99:" > /srv/nginx/etc/group

echo ­e "http:x:33:33:http:/:/bin/false \nnobody:x:99:99:nobody:/:/bin/false" > /srv/nginx/etc/passwd

echo ­e "http:::\nnobody:::" > /srv/nginx/etc/gshadow

############################################################

#Para poder utilizar puertos hasta el 1024 dentro del chroot

############################################################

setcap 'cap_net_bind_service=+ep' /srv/nginx/usr/bin/nginx

############################################################

#Estableciendo permisos y montando los dispositivos

############################################################

chmod ugo+rw /srv/nginx/tmp

chmod ugo+rw /srv/nginx/run

mount ­t tmpfs none /srv/nginx/run ­o 'noexec,size=1M'

mount ­t tmpfs none /srv/nginx/tmp ­o 'noexec,size=100M'

chown http:log /srv/nginx/var/log/nginx

El problema que tendremos es que, para actualizar NGiNX, no basta con actualizar los paquetes instalados, hay que volver a copiarlos dentro de la jaula. Las aplicaciones deberán ser copiadas también dentro de ésta, para que sean accesibles.

Cómo podemos ver, no dejamos ningún shell dentro de ella, con lo que impedimos que se pueda abrir una sesión interactiva. Además, sólo están los usuarios con los que se ejecuta NGiNX, con lo que también impedimos una escalada de privilegios.

Para que todo esto funcione, hay que modificar, en el caso de ArchLinux, el servicio de SystemD que levanta NGiNX y, en el caso de Debian, el script que realiza esta tarea:

Debian

Editamos /etc/init.d/nginx, y debe quedar como se muestra en el ejemplo:



Arch

Creamos /usr/lib/systemd/nginx­jail.service , y tiene que contener estás líneas:


En los siguientes posts iremos viendo el resto de herramientas, así como un ejemplo también con Apache.

Espero que os haya gustado, happy hacking!!



Mª José Montes – mjose@highsec.es – @MMontesDiaz

1 comentario:

  1. Gracias por tu post, me estoy adentrando en el mundo fascinante de la seguridad, he contratado un servidor VPS virtualizado con openvz, y no me planteo virtualizar sobre lo virtualizado, tendría que saber exactamente los recursos casi exactos que demandarían cada servicio, pero lo del enjaulado me es interesante seguire indagando sobre el tema.

    Saludos desde Canarias.

    ResponderEliminar

Related Posts Plugin for WordPress, Blogger...