miércoles, 25 de diciembre de 2013

Separación de privilegios en OpenSSH

Muchos servicios del sistema operativo requieren privilegios especiales para poder ejecutar sus respectivas tareas. Un error de programación en un servicio que posea tales privilegios abre la puerta para comprometer al sistema mediante la adquisición no autorizada de permisos. En el peor de los casos, un atacante remoto podría alcanzar privilegios de superusuario.
[Mediante la separación de privilegios], aun cuando existan errores en partes no privilegiadas, los mismos no pueden utilizarse para escalar privilegios. [Este procedimiento] es especialmente útil para servicios del sistema que autentican usuarios.
El principio básico de la separación de privilegios consiste en reducir la cantidad de código que se ejecuta con permisos especiales sin afectar o limitar la funcionalidad del servicio. Esto se logra al dividir una aplicación en dos o más partes: una de ellas (denominada monitor) es ejecutada con privilegios y la/s otra/s (esclavo/s) sin ellos. Esto disminuye la exposición a bugs en el código que es ejecutado con privilegios. Idealmente, la única consecuencia que resulta de un error en un servicio que esté corriendo con separación de privilegios es la denegación del servicio al atacante mismo.
La línea del archivo de configuración de sshd que habilita la separación de privilegios es la siguiente:
Con esta configuración, estos son los procesos relacionados con ssh:
La salida del comando ps -ef muestra los siguientes datos:
  1. UID (User ID)
  2. PID (Process ID)
  3. PPID (Parent Process ID)
  4. C (Tiempo de CPU utilizado recientemente por el proceso, en términos de ciclos de reloj, más información aquí)
  5. STIME (Start TIME)
  6. TTY (terminal en la que está corriendo el proceso, una ? indica que el proceso es un daemon que está ejecutándose en segundo plano)
  7. TIME (tiempo de CPU utilizado por el proceso desde que fue iniciado, en términos de minutos y segundos)
  8. CMD (argumentos de la línea de comandos que iniciaron el proceso)
En la imagen anterior vemos que el demonio ssh (PID 3287) fue iniciado por init (PID 1). A su vez, sshd inició el monitor (parte de la aplicación que posee privilegios, PID 3354), el cual luego de la autenticación hizo lo propio con la sesión de usuario remota (PID 3430) creando una pseudo-terminal para la misma.
Este ejemplo muestra dos operaciones que requieren privilegios de superusuarios en la máquina remota:
  • Antes de la autenticación, el monitor es la parte de la aplicación que chequea la contraseña provista por el usuario (o la clave pública) contra el archivo de contraseñas (/etc/shadow) o contra la clave pública del cliente previamente guardada en el servidor, según corresponda. Si la autenticación es exitosa, se crea la sesión de usuario; caso contrario, se niega el acceso.
  • Después de la autenticación, se crea una pseudo-terminal para la sesión de usuario.
De esta manera, el monitor y el esclavo corren separadamente y se reduce considerablemente la posibilidad de un ataque por escalación de privilegios.
Ahora voy a deshabilitar la separación de privilegios y reiniciar el servidor ssh, para luego verificar los procesos nuevamente:
En este caso podemos ver que tanto sshd como el monitor fueron iniciados directamente por init, y aunque se lanzó la sesión remota de usuario correctamente, no se dispone de la "interfaz" de protección entre las peticiones efectuadas y la aplicación con privilegios. De esta manera, si un atacante lograra encontrar un bug en el código, podría fácilmente explotarla para escalar privilegios sobre el sistema.
Sin embargo, se puede ver en este caso que el monitor retuvo su PID y solamente cambiaron los correspondientes al esclavo y a sshd. Al inspeccionar el estado de los procesos luego de finalizar la conexión anterior e iniciar una nueva, podemos ver lo siguiente:
Por último, vuelvo a editar el archivo /etc/ssh/sshd_config para volver a utilizar separación de privilegios (recordar que más allá de reiniciar el servidor ssh, los cambios recién serán aplicados a la próxima conexión):

Nota: La teoría de este post está basado en el paper "Preventing Privilege Escalation" por Niels Provos, Markus Friedl, y Peter Honeyman, el cual puede descargarse desde este link.

martes, 24 de diciembre de 2013

Contenido de /proc (Parte 1)

El directorio /proc contiene varios archivos y subdirectorios que muestran información sobre el kernel, incluso los recursos del sistema que están siendo utilizados. Dichos archivos y directorios no son "reales" (el tamaño en disco que acusan es 0) sino que representan una especie de "ventana" que permite observar qué está sucediendo en el kernel en un momento dado. En otras palabras, el directorio /proc puede verse como el centro de control e información del kernel. Para más detalles, se puede consultar su man page.
Esta publicación, y las que siguen, serán llevadas a cabo sobre un servidor casero con Debian Wheezy 7.2 y kernel 3.2.0-4-686-pae:
Un listado del contenido de /proc nos muestra lo siguiente (en azul los directorios, en cyan los enlaces simbólicos, y en blanco los archivos):
Lo que primero nos llama la atención es que hay varios directorios cuyo nombre es un número (1, 10, 12, 13, 3284, etc). Los mismos reciben el nombre de directorios de proceso debido a que están relacionados con el PID de un cierto proceso y contienen información específica sobre el mismo. El usuario y el grupo que figuran como dueños de cada directorio son los mismos que el usuario y el grupo bajo los que está corriendo dicho proceso.
Cuando un proceso finaliza su ejecución, o es terminado, su directorio asociado dentro de /proc desaparece.
Por ejemplo, voy a buscar el PID que corresponde a mi sesión de usuario (conexión por ssh) desde la PC cliente que estoy usando para conectarme al servidor:
Puedo ver que dentro del directorio /proc hay un subdirectorio 3695, y el contenido del mismo es el siguiente:
Algunos de los archivos más interesantes dentro del mismo son los siguientes:
  • cmdline: contiene los argumentos de la línea de comandos que se utilizaron para iniciar el proceso.
  • cwd: es un enlace al directorio actual del proceso.
  • exe: es un enlace al ejecutable del proceso.
  • status: muestra el estado del proceso de una manera más "amigable".

Más información:

jueves, 12 de diciembre de 2013

Web form con html5, php, jQuery, archivos adjuntos y MySQL como RDBMS

Para que un formulario html permita la carga de archivos adjuntos debe tener seteado el siguiente atributo:

<form method="post" action="cargar.php" enctype="multipart/form-data">

Html5 brinda, entre muchas otras innovaciones, la posibilidad de agregar controles de contenido del tipo number que permiten fijar valores iniciales, mínimos y máximos, e intervalos de salto:



Si se necesita utilizar una lista desplegable que permita la selección de varios ítems de manera simultánea, se debe agregar el atributo multiple="multiple" e indicar que el resultado de la selección estará compuesto por un arreglo de elementos:

<select name="partes[]" multiple="multiple">


Para que el archivo cargar.php pueda procesar estos valores correctamente, se le debe indicar que separe los elementos provenientes del formulario con un separador a nuestra elección. En el caso siguiente se muestra el uso de la coma-espacio para cumplir esta función:

join(', ', $_POST['partes'])

Resultando en:
Cabeza, Cuello, Hombro

(Se puede asignar el resultado de esa función a una variable que posteriormente será la que se utilice para la consulta SQL).

Para evitar errores al ingresar fechas y horas, se puede utilizar el excelente plugin de jQuery escrito por Trent Richardson (distribuido bajo licencia GPL). El siguiente ejemplo utiliza la función datetimepicker para desplegar un calendario con fecha y hora, o con almanaque solamente, cuando un control con id datetime o class datepicker recibe foco, respectivamente (debe colocarse dentro de los tags de head):


Ejemplo de calendario con fecha y hora

Ejemplo de calendario con fecha únicamente

El formato de la fecha puede preformatearse mediante Javascript en el lado del cliente (ver ejemplo anterior) o hacerse posteriormente en el lado del servidor utilizando PHP utilizando las funciones date y strtotime (string to time):
$fecha_accion1 = date("Y-m-d H:i:s", strtotime($_POST['fecha_accion1']));