# rm-rf.es | Administración de sistemas

Bitácora personal de un SysAdmin Gnu/Linux, Windows, BSD...

Nmap Online para tu IP o Clase C

Nmap Online nos permite utilizar Nmap directamente desde el navegador para poder analizar los puertos abiertos de nuestro equipo y toda la información que sabéis que puede ofrecer Nmap. Eso sí, la limitación es que podemos lanzarlo únicamente contra nuestra IP pública de salida y toda su clase C (/24) correspondiente.

Básicamente, la idea es utilizarlo como herramienta durante la securización de nuestro sistema cuando no tenemos la posibilidad de utilizar una máquina remota para lanzar pruebas de seguridad de red y escaneos de puertos, algo necesario cuando estamos realizando configuraciones en nuestro firewall. No voy a entrar en posibles usos maliciosos que se le puedan dar a la herramienta. Es recomendable leer los TOS para tener claro que y que no se puede hacer, lo que se guardan en los registros de ese servidor, el tiempo que permanecen ahí esos datos, si son públicos, etc.

Como veis en la imagen, podemos realizar un escaneo rápido, completo o personalizado, especificando los parámetros de Nmap igual que lo haríamos en la línea de comandos.

Nmap Online

Vulnerabilidad crítica en instalaciones de PHP basadas en CGI

Todas las instalaciones de PHP en modo CGI están afectadas, al parecer se trata de una vulnerabilidad de la cual nadie se había dado cuenta en nada menos que ocho años.

Básicamente, la vulnerabilidad consiste en que en este tipo de instalaciones (sólo CGI, FastCGI o DSO no se ven afectadas), si la petición/request contiene ?-s permite volcar por pantalla el código fuente PHP en lugar de interpretarlo.

Para verificar si la vulnerabilidad nos afecta, sólo es necesario añadir ?-s a la URL y ver si descargamos el código fuente, ejemplo:

dominio.com/test.php?-s

Por suerte, ya podemos descargar las versiones parcheadas desde el sitio web de PHP, concretamente la PHP 5.3.12 o PHP 5.4.2. Si no queremos o podemos actualizar tan sencillamente, podemos aplicar un workaround mediante el cual configuramos unos rewrite, ya sea en el virtualhost o a nivel de .htaccess para bloquear este tipo de peticiones. Para Apache con mod_rewrite:

         RewriteCond %{QUERY_STRING} ^(%2d|-)[^=]+$ [NC]
         RewriteRule ^(.*) $1? [L]

Más información en www.php.net

Consultar y exportar certificados SSL en Sun Web Server (certutil y pkcs12)

Sun Web Server (en este artículo trabajamos sobre la versión 6.1) almacena los certificados y llaves privadas en ficheros .db (cert8.db y key3.db). Vamos a hacer uso de los comandos certutil y pkcs12 para poder hacer operaciones de listado y exportación de certificados (con o sin private key).

Para listar los certificados que tiene instalados el servidor web debemos usar el comando certutil, que se encuentra en la ruta /bin/https/admin/bin/. Los ficheros .db que comentaba antes se encuentran en la carpeta “alias” por defecto. Podéis copiarlos a una ruta temporal para trabajar con ellos en lugar de directamente en la ruta de producción. Una vez realizado, nos colocamos en esa carpeta y ejecutamos el siguiente comando:

$ certutil -L -d .

    CERTIFICADO-WEB                                                CTu,u,u

Como podéis comprobar, en este servidor web tenemos instalado el certificado CERTIFICADO-WEB. Si queremos exportarlo en formato ASCII sin su private key podemos usar el mismo comando pero especificado el certificado y el parámetro -a (ASCII). En formato binario usamos el parámetro -d (DER):

$ certutil -L -d . -n "CERTIFICADO-WEB" -a
    -----BEGIN CERTIFICATE-----
    MIICTjCCAbAxasdhljkads8akjasdolasdYjEfMB0GA1UEChMWU3Vu
    ...
    ...
    asd3asd23sadasdadsasdasd
    -----END CERTIFICATE-----
$ certutil -L -d . -n "CERTIFICADO-WEB" -d

Finalmente, si lo queremos exportar en formato legible:

$ certutil -L -d . -n "CERTIFICADO-WEB"
     Certificate:
         Data:
             Version: 3 (0x2)
             Serial Number: ...
             Signature Algorithm: PKCS #1 SHA-1 With RSA Encryption
             Issuer:
...
...

En caso de necesitar exportar también la private key del certificado, hacemos uso del comando pkcs12. En este caso necesitaremos la password del “NSS Certificate DB” para poder acceder a los certificados del servidor así como especificar una nueva para el fichero resultante PKCS12. Seguimos ubicados en la carpeta donde están los ficheros *.db. Especificamos el fichero de salida (certificado.p12, y el nombre del certificado.

$ pk12util -o certificado.p12 -n 'CERTIFICADO-WEB' -d .
Enter Password or Pin for "NSS Certificate DB":
Enter password for PKCS12 file:
Re-enter password:
pk12util: PKCS12 EXPORT SUCCESSFUL

Cómo limitar el acceso root de forma local y por tty

Pueden existir circunstancias en las que queramos, además de lo típico que es desactivar el acceso root por ssh, desactivar el acceso local para el usuario root.

Para ello tenemos el fichero /etc/securetty, en el cual aparece un listado de las consolas virtuales disponibles en el sistema (tty). Aparecerán todas las que puedan ser utilizadas, pero no tienen porque estar activas. En sistemas RedHat y derivados se especifican en /etc/init/start-ttys.conf.

Bien, para desactivar entonces el acceso root a cualquiera de esas tty, o a todas, únicamente hay que eliminarla o comentarla en ese fichero. A partir del momento que guardemos los cambios “root” ya no podrá acceder por los accesos locales/ttys desactivadas:

# cat /etc/securetty
console
vc/1
vc/2
vc/3
...
vc/9
vc/10
vc/11
#tty1
#tty2
#tty3
#tty4
#tty5
#tty6
#tty7
#tty8
#tty9
#tty10
#tty11

Si necesitamos mayor detalle en las restricciones, o hacer lo mismo con otros usuarios, debemos revisar el fichero /etc/security/access.conf. Veréis que está perfectamente documentado y que podemos realizar configuraciones como las siguientes:

# Disallow non-root logins on tty1
#
#-:ALL EXCEPT root:tty1
#
# Disallow console logins to all but a few accounts.
#
#-:ALL EXCEPT wheel shutdown sync:LOCAL
#
# Same, but make sure that really the group wheel and not the user
# wheel is used (use nodefgroup argument, too):
#
#-:ALL EXCEPT (wheel) shutdown sync:LOCAL
#
# Disallow non-local logins to privileged accounts (group wheel).
#
#-:wheel:ALL EXCEPT LOCAL .win.tue.nl
#
# Some accounts are not allowed to login from anywhere:
#
#-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL
#
# All other accounts are allowed to login from anywhere.
...
...
...

Verificar la integridad y seguridad de ficheros con RPM

rpmRPM nos ofrece una utilidad muy valiosa que nos permite verificar la integridad y seguridad de todos los paquetes y aplicaciones instaladas en el sistema (con yum o rpm). Básicamente, lo que vamos a hacer es verificar todos los ficheros de todos los paquetes instalados en el sistema. La verificación consiste en comprobar la información de estos ficheros con la que hay almacenada en la base de datos rpm. Vamos a verificar el MD5 checksum, los permisos, el propietario y grupo, tamaño del fichero, etc. Para ello utilizaremos los parámetros –verify o -V.

De primeras podemos descartar los ficheros de configuración, ya que lo más normal es que su contenido cambie, aunque todavía podríamos revisar su configuración de permisos, propietario, etc.

Si queremos hacer una revisión de todos los ficheros del sistema, algo que lógicamente puede tardar bastante tiempo, utilizaremos el siguiente comando. Hay que tener en cuenta que en caso de no haber salida todo es correcto:

# rpm -Va

Si queremos hacer la revisión de un único paquete lo indicamos y quitamos el parámetro “a”:

# rpm -V telnet

Y si quisiéramos revisar un único fichero:

# rpm --verify --file /usr/bin/chcon

¿Y qué sucede si se detecta algún problema? Vamos a verlo. Voy a hacer un backup del binario /usr/sbin/apachectl y a reescribirlo:

# mv /usr/sbin/apachectl /usr/sbin/apachectl.BAK
# touch /usr/sbin/apachectl
# rpm -V --file /usr/sbin/apachectl
S.5....T.  c /etc/httpd/conf/httpd.conf
S.5....T.    /etc/rc.d/init.d/httpd
S.5....T.  c /etc/sysconfig/httpd
SM5....T.    /usr/sbin/apachectl

Como véis ahora sí que hay salida. Tenemos 8 puntos que se pueden convertir en otros caracteres cuando hay algo que no cuadra:

  • 5: fallo en el checksum MD5
  • S: discrepancia en el tamaño del fichero
  • T: fecha de modificación distinta al original
  • L: enlace simbólico
  • D: dispositivo
  • U: usuario
  • G: grupo
  • M: permisos

Así pues, en el ejemplo anterior vemos como el binario apachectl tiene un checksum MD5 distinto, distinto tamaño, fecha de modificación y de permisos. La “c” es de fichero de configuración.

Si queremos comprobar de una tacada la integridad de todos los binarios de /usr/sbin, es tan sencillo como:

# rpm -Va | grep "/usr/sbin"
S.5....T.    /usr/sbin/apachectl

Podemos añadir un mayor nivel de debug con el parámetro “v”, “vv” más debug aún:

# rpm -Vv --file /usr/sbin/apachectl
# rpm -Vvv --file /usr/sbin/apachectl

Como veis, esta es una buena y sencilla forma de asegurarnos que los binarios del sistema no han sido alterados y también de tener un control del estado de los ficheros de configuración, etc.

Instalar y configurar TigerVNC server y utilizarlo con un túnel SSH

Instalar y configurar el servidor VNC TigerVNC es realmente sencillo. Lo que muchas veces no nos paramos a pensar es que el protocolo en sí no es seguro por lo que el tráfico no está cifrado al conectar al escritorio remoto. Vamos a ver como solucionarlo.

Lo primero es instalar TigerVNC con yum (RHEL, CentOS, Scifi linux, etc). En este caso instalo también el cliente para hacer las pruebas en local:

# yum install tigervnc-server tigervnc

Una vez instalado debemos saber que la configuración se realiza en el fichero /etc/sysconfig/vncservers. Los comentarios del fichero nos indican claramente como configurarlo, también hice un artículo hace un tiempo, echadle un vistazo: Instalar y configurar vnc-server en CentOS/RHEL/Fedora

He creado un usuario “alex” al que únicamente se le permite el acceso local por seguridad:

# The VNCSERVERS variable is a list of display:user pairs.
#
# Uncomment the lines below to start a VNC server on display :2
# as my 'myusername' (adjust this to your own).  You will also
# need to set a VNC password; run 'man vncpasswd' to see how
# to do that.
#
# DO NOT RUN THIS SERVICE if your local area network is
# untrusted!  For a secure way of using VNC, see this URL:
# http://kbase.redhat.com/faq/docs/DOC-7028

# Use "-nolisten tcp" to prevent X connections to your VNC server via TCP.

# Use "-localhost" to prevent remote VNC clients connecting except when
# doing so through a secure tunnel.  See the "-via" option in the
# `man vncviewer' manual page.

# VNCSERVERS="2:myusername"
# VNCSERVERARGS[2]="-geometry 800x600 -nolisten tcp -localhost"
VNCSERVERS="2:ale
VNCSERVERARGS[2]="-geometry 800x600 -nolisten tcp -localhost"

Creamos el password VNC para el usuario del siguiente modo:

# su alex -c "vncpasswd"
Password: XXXX
Verify: XXXX

Arrancamos el servidor VNC para el usuario alex:

[alex@localhost /]$ vncserver
xauth:  creating new authority file /home/alex/.Xauthority

New 'rhcsa:1 (alex)' desktop is rhcsa:1

Starting applications specified in /home/alex/.vnc/xstartup
Log file is /home/alex/.vnc/rhcsa:1.log

Llegados a este punto podemos acceder sin problemas en local al escritorio remoto con el comando vncviewer:

$ vncviewer localhost:1

Desde fuera no, por motivos de seguridad. Para ello vamos a crear un tunel SSH de modo que las conexiones VNC con el exterior sí que estén cifradas. Vamos a usar por ejemplo el puerto 6922 para el tunel. El servidor VNC se encuentra en la IP 10.0.0.100, desde el equipo remoto con el que queremos conectar al servidor creamos el tunel:

$ ssh alex@10.0.0.100 -L 6922:10.0.0.100:5901 -N

Y ya está, podemos acceder remotamente al servidor VNC de forma segura con el nuevo puerto:

$ vncviewer localhost:6922

Si queréis usar el estandar podéis mantener los puertos de VNC:

$ ssh alex@10.0.0.100 -L 5901:10.0.0.100:5901 -N
$ vncviewer localhost:1

Encriptar un filesystem con LUKS y cryptsetup

luksLUKS (Linux Unified Key Setup) es un cifrado que trabaja a nivel de bloque de fichero sobre la partición o volumen lógico y que permite encriptar sistemas de ficheros, para los cuales es necesaria una passphrase o keyfile para su desencriptado. Encriptar un sistema de ficheros es especialmente útil cuando trabajamos con información sensible en dispositivos físicos portatiles, como por ejemplo pendrives, ordenadores portátiles, etc.

Vamos a encriptar un sistema de ficheros de un disco secundario sobre un sistema RHEL 6 (CentOS). Para encriptar particiones de sistema (filesystem / o /home por ejemplo) es preferible hacerlo en el momento de la instalación, ya que resulta más sencillo y evitamos la pérdida de datos posterior.

Preparación del sistema

Para la encriptación con LUKS necesitamos tener cargado el módulo de kernel dm_crypt así como el paquete cryptsetup-luks:

# lsmod | grep dm_cry
dm_crypt               10848  0
dm_mod                 63859  5 dm_crypt,dm_mirror,dm_log

Si no está cargado en vuestro sistema hacedlo con modprobe:

# modprobe dm_crypt

Acordaos de configurarlo (por defecto es probable que con modprobe sea suficiente) para que sea persistente a reinicios.

Instalamos cryptsetup-luks en caso de que no esté instalado:

# yum install cryptsetup-luks
# rpm -qa | grep cryptse
cryptsetup-luks-libs-1.1.2-2.el6.i686
cryptsetup-luks-1.1.2-2.el6.i686

Preparación del sistema de ficheros/partición

Vamos a asumir que tenemos ya el sistema de ficheros creado. Podéis revisar los siguientes artículos si tenéis dudas, tanto de crear una partición estándar como un LVM:

Algo recomendable pero que suele llevar bastante tiempo es, lo primero de todo, llenar de datos aleatorios la partición a encriptar. Repito que puede llevar bastante tiempo dependiendo del tamaño de la partición. Si sólo estáis haciendo pruebas no es necesario que lo hagáis (sustituid /dev/volgroup1/logvol1 por vuestra partición o volumen):

# dd if=/dev/urandom of=/dev/volgroup1/logvol1

Encriptación del filesystem

Llegados a este punto ya tenemos el sistema de ficheros preparado para su encriptación. Haremos uso del comando cryptsetup. Lo primero que haremos es asignar la passphrase. Cuanto más compleja sea mejor, igual que cualquier password:

Nota: se eliminará cualquier dato del filesystem

# cryptsetup luksFormat /dev/volgroup1/logvol1

WARNING!
========
This will overwrite data on /dev/volgroup1/logvol1 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter LUKS passphrase: *********
Verify passphrase: *********

Al tratarse de un filesystem encriptado debemos mapearlo ya que no se puede leer directamente. Esa unidad mapeada será la que montaremos y utilicemos como un filesystem estándar. Vamos a sacar el UUID del sistema de ficheros encriptado para utilizarlo si queremos en lugar de la etiqueta (LABEL):

# cryptsetup luksUUID /dev/volgroup1/logvol1
79da86ea-70f2-4a80-82cb-6c614a885a37

Mapeamos el sistema de ficheros contra encrypted-fs:

# cryptsetup luksOpen /dev/volgroup1/logvol1 encrypted-fs
Enter passphrase for /dev/volgroup1/logvol1:

Y ya tenemos el volumen listo para particionar en /dev/mapper/encrypted-fs. Le damos formato y lo montamos:

# mkfs.ext4 /dev/mapper/encrypted-fs

Finalmente, lo añadimos a fstab para el arranque al inicio del sistema. Podemos hacerlo con el UUID o con la etiqueta (LABEL) del fs:

# tune2fs -l /dev/mapper/encrypted-fs | grep UUID
Filesystem UUID:          9578a03e-949b-4fe8-a16c-57024b01fe89

Lo añadimos a /etc/fstab:

UUID=9578a03e-949b-4fe8-a16c-57024b01fe89 /encrypted-fs ext4    defaults 0 0

Y añadimos también la siguiente entrada a /etc/crypttab para que solicite la passphrase en el arranque del sistema:

encrypted-fs /dev/volgroup1/logvol1 none

Si reiniciáis, durante el arranque se os solicitará la passphrase del volumen:

logvol1 is password protected: *************

10 trucos para securizar PHP

php 5.3.6

En esta entrada vamos a ver unos cuantos puntos que nos servirán para securizar PHP en nuestro servidor web. Nos centramos en la configuración propia de PHP, hay que tener en cuenta que la securización de la capa aplicación es tanto o más importante que la de la configuración del servidor. Hay que tener siempre actualizados a la última versión estable y con los parches de seguridad correctamente aplicados cualquier cms o script de terceros tipo WordPress, Joomla, Oscommerce, etc.

Ocultar la versión de PHP

En su día hice un artículo completo sobre esto, podéis verlo aquí (ocultar la versión de PHP). Básicamente evitamos que con un simple telnet puedan averiguar la versión de PHP que hay corriendo en el servidor:

# telnet servidor 80

Connected to xxx.com (xx.xx.xx.xx).
Escape character is '^]'.
HEAD / HTTP/1.0

HTTP/1.1 200 OK
Date: Fri, 13 Aug 2010 14:18:09 GMT
Server: Apache/1.3.26 (Unix) mod_gzip/1.3.26.1a PHP/5.3.3
Last-Modified: Fri, 12 Feb 2010 12:22:56 GMT
ETag: "44967c-6f-53ca4800"
Accept-Ranges: bytes
Content-Length: 111
Connection: close
Content-Type: text/html

Connection closed by foreign host.

Para evitar esto cambiamos a off la siguiente variable en el fichero de configuración general php.ini:

expose_php Off

Deshabilitar funciones peligrosas

También hice un artículo hace un tiempo: PHP: Deshabilitar funciones peligrosas. Existen funciones que en servidores con aplicaciones estándar rara vez se usan, pero que por contra pueden generar graves problemas de seguridad si el atacante consigue utilizarlas. Es preferible desactivar el mayor número posible y en caso de que una de ellas sea necesario valorar su activación o proponer alternativas.

Las funciones a deshabilitar se añaden en la directiva “disable_functions” en el fichero php.ini. Aquí tenéis un ejemplo con funciones peligrosas que conviene desactivar:

disable_functions = “apache_child_terminate, apache_setenv, define_syslog_variables, escapeshellarg, escapeshellcmd, eval, exec, fp, fput, ftp_connect, ftp_exec, ftp_get, ftp_login, ftp_nb_fput, ftp_put, ftp_raw, ftp_rawlist, highlight_file, ini_alter, ini_get_all, ini_restore, inject_code, mysql_pconnect, openlog, passthru, php_uname, phpAds_remoteInfo, phpAds_XmlRpc, phpAds_xmlrpcDecode, phpAds_xmlrpcEncode, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_setuid, posix_uname, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, shell_exec, syslog, system, xmlrpc_entity_decode”

O un ejemplo más conservador:

disable_functions ="system,passthru,escapeshellarg,escapeshellcmd,proc_close,proc_open,ini_alter,popen,show_source,pcntl_exec"

deshabilitar session ID en URL

Si queremos que las URL’s de nuestros sitios PHP no muestren los ID de sesiones:

http://ejemplo.com/?PHPSESSID=4kgj577sgabvnmhjgkdiuy1956if6ska

Modificamos la directiva siguiente en el fichero php.ini:

session.use_trans_sid = off

Deshabilitar register_globals

Pese a ser una directiva antigua y que se encuentra en periodo de extinción todavía quedan desarrolladores/programadores que la utilizan. Esta función permite al atacante manipular cualquier variable global definida en la programación. Por defecto está deshabilitada en las versiones actuales de PHP, pero conviene revisarlo por si en algún momento se ha activado:

register_globals = Off

Desactivar acceso a URL remotas en funciones de manejo de ficheros

Funciones como include, fopen o file_get_contents permiten, además de hacer llamadas a ficheros locales, llamar a ficheros vía URL, esto puede provocar graves errores de seguridad invocando a scripts maliciosos que se encuentran fuera de nuestro servidor y su ejecución remota.

Para deshabilitarlo modificamos la directiva allow_url_fopen a Off en el php.ini:

allow_url_fopen = Off

Evitar el acceso a la información de PHP

Como ya sabéis con un simple script como el siguiente podemos ver vía URL toda la información de PHP, su compilación, sus módulos activados, versión, directivas de configuración, etc.

<? phpinfo() ?>

Si queremos desactivarlo, únicamente hay que modificar a Off la siguiente directiva en el fichero php.ini:

expose_php = Off

Si en algún momento necesitáis conocer alguna información, siempre podéis averiguarla desde línea de comandos: información sobre PHP desde línea de comandos

Safe Mode

safe_mode = On

Si bien por defecto safe_mode On puede significar una restricción demasiado fuerte en determinados entornos, puede ayudar mucho a incrementar la seguridad de nuestro servidor. Activar safe_mode implica que los scripts PHP únicamente pueden acceder a los ficheros que tienen como propietario el mismo que ellos. De este modo evitamos por ejemplo que tengan acceso de lectura a ficheros de sistema como /etc/passwd entre otros.

Efectivamente, esto puede ser un problema en el momento que necesitamos acceder a información generada por otros usuarios en el sistema (ficheros de otras aplicaciones. La solución es la siguiente:

safe_mode = Off
safe_mode_gid = On

Activamos safe_mode_gid en lugar de safe_mode, de modo que en lugar de revisar el usuario se revisa el grupo. Independientemente del UID del fichero, necesitaremos que estar dentro del grupo para poder acceder al ficheros. El grupo del script PHP deberá ser el mismo que el del fichero a acceder.

Otro punto a tener en cuenta con safe_mode es que no podremos ejecutar binarios. Únicamente aquellos que ubiquemos en el directorio especificado en la configuración:

safe_mode_exec_dir = /directorio

Esto se solaparía con las funciones deshabilitadas anteriormente, como por ejemplo system() o exec().

open_basedir

open_basedir = /directorio

La directiva open_basedir permite configurar que PHP pueda acceder únicamente a los ficheros de un único directorio (y sus subdirectorios). Es una buena forma de enjaular PHP si realmente sólo necesitamos que acceda a un determinado directorio.

Visualización y registro de errores

display_errors = Off
log_errors = On
error_log = /ruta/fichero/log

Con estas tres directivas, evitamos que cualquier error o warning se muestre por pantalla y hacemos que se registren directamente en un log especificado. De este modo podemos evitar que se muestre información sensible por pantalla. También podéis hacer uso de la directiva error_reporting si queréis mostrar por pantalla los errores. Con ella podéis especificar que se muestren únicamente los warning, los notice o lo que queráis.

SQL Injection

En su día se utilizaba magic_quotes para limpiar los datos de entrada de un script PHP, pero es una directiva obsoleta a partir de PHP 5.3 así que no merece la pena hablar de ella. En su defecto, si usáis servidor web Apache, os recomiendo encarecidamente configurar mod_security y habilitar sus reglas que permiten detectar y parar una gran cantidad de ataques de este tipo, y sino, puedes crear tus propias reglas.

Como último apunte, conviene siempre si es posible mantener una versión estable de PHP, libre de bugs y fallos de seguridad. Seguro además que una vez aplicados estos trucos os toca lidiar con los programadores. Se quejarán de algo seguro ;)