Archivo

Posts Tagged ‘openvpn VPN’

Monitorizar conexiones y desconexiones contra un servidor OpenVPN con Python.

agosto 10, 2019 Deja un comentario

Hola a todos. He vuelto. 🙂

Las próximas semanas tendré algo de tiempo libre y he decidido dedicarlo a este espacio, escribiré algunos artículos sobre seguridad que tengo pendientes desde hace mucho tiempo y que ahora, me dispongo a compartir con todos vosotros. Vamos a por uno sencillo primero!

Una de las características más interesantes que tiene OpenVPN es precisamente la posibilidad de ejecutar scripts ante determinados eventos, por ejemplo, es posible indicar en el fichero de configuración que se ejecute un programa determinado cuando un usuario se conecta o desconecta. Dicho programa puede ser un script escrito en Python, por ejemplo, que se encargará de recopilar la información de la conexión que ha establecido el cliente. Esto puede tener varios usos, entre los que se incluyen la monitorización de las conexiones de los usuarios, forzar a que los clientes se conecten únicamente desde ubicaciones concretas, limitar el número de conexiones con el mismo certificado o OVPN, etc. En resumen, permite un buen nivel de control sobre los usuarios y las conexiones realizadas contra el servidor. Ahora, esto cómo se hace?

Configuración de OpenVPN con “script-security”

La directiva de configuración “script-security” es precisamente la que permite la ejecución de scripts y otros programas externos desde el proceso en ejecución de OpenVPN. Sin embargo, está opción no está exenta de riesgos y problemas de seguridad potenciales, tal como se puede leer en la documentación oficial de OpenVPN:

–script-security level [method]

This directive offers policy-level control over OpenVPN’s usage of external programs and scripts. Lower level values are more restrictive, higher values are more permissive. Settings for level:0 — Strictly no calling of external programs.
1 — (Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
2 — Allow calling of built-in executables and user-defined scripts.
3 —Allow passwords to be passed to scripts via environmental variables (potentially unsafe).The method parameter indicates how OpenVPN should call external commands and scripts. Settings for method:

execve — (default) Use execve() function on Unix family OSes and CreateProcess() on Windows.
system —Use system() function (deprecated and less safe since the external program command line is subject to shell expansion).

The –script-security option was introduced in OpenVPN 2.1_rc9. For configuration file compatibility with previous OpenVPN versions, use: –script-security 3 system”

Evidentemente, si se utiliza ésta opción en el fichero de configuración o como argumento a la hora de levantar el servicio hay que tener en cuenta, como mínimo lo siguiente:

1. Si se establece el método “2”, sería recomendable eliminar los permisos de lectura y escritura en los scripts que se ejecutarán por medio de OpenVPN, ya que si un atacante consigue acceso local y dichos scripts permiten su edición por parte de usuarios con permisos bajos, existe la posibilidad de que elevar privilegios en el sistema.

2. Evitar usar el método “3” ya que se pueden producir fugas de información sensible a la hora de establecer credenciales en variables de entorno.

Es una opción de configuración con la que hay que tener cuidado, sin embargo cuando se siguen los criterios anteriores es posible utilizarla sin riesgo. Esta opción únicamente le indica a OpenVPN que desde el proceso se podrán lanzar scripts o comandos externos, sin embargo con esto no es suficiente para indicar cuáles serán dichos scripts y cómo se deben ejecutar. Para ello existen otras opciones de configuración en las que se especifican precisamente estos detalles, tales como “up”, “client-connect” o “client-disconnect”. Dichas opciones se pueden incluir en el fichero de configuración utilizado para levantar el proceso de OpenVPN, por ejemplo:

script-security 2
client-connect "/usr/bin/python /home/adastra/connection.py"
client-disconnect "/usr/bin/python /home/adastra/disconnection.py"

Tal como el nombre de la opción indica, estos scripts se van a ejecutar en el momento en el que un cliente se conecte o desconecte, lo que nos permite hacer cosas simples pero interesantes, como por ejemplo registrar en una base de datos los detalles relacionados con las conexiones de los usuarios (fecha, ubicación, duración de la conexión, etc.). Se trata de información que se almacena en el momento en el que el evento ocurre en forma de variables de entorno, por lo tanto, lo que se debe de hacer en estos scripts es simplemente leer el valor de dichas variables de entorno antes de que dejen de estar disponibles. El siguiente script en Python nos permite hacer precisamente esto, con muy pocas líneas y prácticamente ningún esfuerzo.

connection.py:

import posix,time;
with open('/tmp/conn.out','w') as fd:
    fd.write(posix.environ['trusted_ip'])
    fd.write(posix.environ['common_name'])

Con esto simplemente se escriben los valores de las variales “trusted_ip” y “common_name” en un fichero de texto en el directorio “/tmp”. Evidentemente existen otras variables de entorno que se pueden consultar, como por ejemplo la IP dentro de la VPN que se le ha asignado al cliente (ifconfig_pool_remote_ip).

No obstante, lo más normal es que esta información se almacene en una base de datos, para demostrar esto, el siguiente script se encargará del registro de las conexiones en una base de datos PostgreSQL, por lo tanto será necesario utilizar una librería como “psycopg2”.

import psycopg2
import posix
common_name = posix.environ['common_name']
trusted_ip = posix.environ['trusted_ip']


class Database():
	def __init__(self, dbName="database_openvpn", dbUser="postgres", dbServer="127.0.0.1", dbPort=5432, dbPass="postgres"):
		self.connection = psycopg2.connect("dbname='%s' user='%s' host='%s' port='%s'  password='%s'" %(dbName, dbUser, dbServer, dbPort, dbPass))
		self.cursor = self.connection.cursor()

	def execute(self, createInstruction, params=None):
		self.cursor.execute(createInstruction, params)
		self.connection.commit()
		id = self.cursor.fetchone()[0]
		return id

	def create(self, createInstruction, params=None):
		self.cursor.execute(createInstruction, params)
		self.connection.commit()

	def select(self, selectInstruction, params=None):
		self.cursor.execute(selectInstruction,params)
		return self.cursor.fetchall()

	def close(self):
		self.cursor.close()
		self.connection.close()

if __name__ == "__main__":
	db = None
	try:
		db = Database()
		db.create("CREATE TABLE IF NOT EXISTS user(id serial primary key, common_name varchar NOT NULL);")	
		db.create("CREATE TABLE IF NOT EXISTS addresses(id SERIAL PRIMARY KEY, address varchar(255) NOT NULL,user_id integer REFERENCES user(id));")
		db.create("CREATE TABLE IF NOT EXISTS connections(id SERIAL PRIMARY KEY, date_connection TIMESTAMP NOT NULL, date_disconnection TIMESTAMP, address_id integer REFERENCES addresses(id));")

		from datetime import datetime
		fechaActual = datetime.now()
		timeStampActual = psycopg2.Timestamp(fechaActual.year, fechaActual.month, fechaActual.day, fechaActual.hour, fechaActual.minute, fechaActual.second)
		with open('/tmp/trazas-connect','w') as fd:
			fd.write("Conexion de cliente %s desde %s \n" %(common_name, trusted_ip))
			users = db.select("select id from user where common_name = %s", (common_name,))
			userid = 0
			if len(users) < 0:
				fd.write("\nUsuario registrado previamente en la base de datos")
				userid = users[0][0]
				fd.write("\nIdentificador de usuario: %s " %(str(userid)))

			else:
				fd.write("\nUsuario NO registrado previamente en la base de datos")
				userid = db.execute("insert into user(common_name) values(%s) RETURNING id", (common_name,))
				fd.write("\nUsuario insertado en BD con identificador: %s" %(str(userid)))
				addressid = db.execute("insert into addresses(address, user_id) values(%s, %s) RETURNING id", (trusted_ip, userid,))
				fd.write("\nDirección insertada en BD con identificador: %s" %(str(addressid)))
				connid = db.execute("insert into connections(date_connection, address_id) values(%s, %s) RETURNING id", (timeStampActual, addressid,))
				fd.write("\nConexion insertada en BD con identificador: %s" %(str(connid)))
				fd.write("\nProceso de registro finalizado correctamente.")
				fd.write("\n-----------------------------------------\n\n")

	finally:
		if db is not None:
			db.close()

Este sería el script de registro, en donde se almacenan las conexiones de los usuarios y un par de detalles básicos. En este programa se verifica si el usuario se encuentra almacenado en base de datos y si está, no se procede a almacenar nada en la base de datos. El script en realidad es bastante simple, sin embargo tiene bastantes líneas de código, las cuales como se puede apreciar, en su mayoría sirven para depurar. El motivo de esto es que a día de hoy (o mejor, hasta donde llegan mis conocimientos en OpenVPN) no hay manera de depurar los programas externos que se lanzan desde el proceso de OpenVPN y si por lo que sea, dichos programas, como por ejemplo este script, lanza una excepción no controlada, el servidor rechazará la conexión con el cliente, este es otro de los motivos por los que hay que tener mucho cuidado a la hora de utilizar esta característica. Por ejemplo, suponiendo que la base de datos “database_openvpn” no se encuentre creada o que directamente el servicio de PostgreSQL se encuentre detenido, en tales casos el script lanzará una excepción y si no se controla adecuadamente, supondrá que ningún cliente se va a poder conectar a la VPN.
Ahora bien, si la opción “client-connect” permite indicar un script que se lanzará justo en el momento en el que un usuario se conecta, la opción “client-disconnect” hace lo mismo pero al reves, cuando el cliente envia la señal de cierre de conexión. Siguiendo la lógica del script anterior, el script de desconexión se encargará de realizar una operación DELETE/UPDATE sobre cada una de las tablas relacionadas. Este sería el contenido de dicho script:

import posix
import psycopg2

common_name = posix.environ['common_name']
trusted_ip  = posix.environ['trusted_ip']

class Database():
    def __init__(self, dbName="database_openvpn", dbUser="postgres", dbServer="127.0.0.1", dbPort=5432, dbPass="postgres"):
        self.connection = psycopg2.connect("dbname='%s' user='%s' host='%s' port='%s' password='%s'" %(dbName, dbUser, dbServer, dbPort, dbPass))
        self.cursor = self.connection.cursor()
        
    def execute(self, createInstruction, params=None):
        self.cursor.execute(createInstruction, params)
        self.connection.commit()

    def select(self, selectInstruction, params=None):
        self.cursor.execute(selectInstruction,params)
        return self.cursor.fetchall()

    def close(self):
        self.cursor.close()
        self.connection.close()
    
        
if __name__ == "__main__":
	db = None
	try:
		db = Database()
		from datetime import datetime
		fechaActual = datetime.now()
		timeStampActual = psycopg2.Timestamp(fechaActual.year, fechaActual.month, fechaActual.day, fechaActual.hour, fechaActual.minute, fechaActual.second)
		with open('/tmp/trazas-disconnect','w') as fd:			
			conn = db.select("select c.id from user u, addresses a, connections c where (u.id = a.user_id and c.address_id = a.id) and u.common_name = %s and a.address = %s and c.date_disconnection IS NULL", (common_name, trusted_ip,) )
			connid = 0
			fd.write("\nProceso de desconexion iniciado para el usuario %s " %(common_name))
			if len(conn) > 0:
				connid = conn[0][0]
				fd.write("\nIdentificador de conexion a actualizar %s " %(str(connid)))
				db.execute("UPDATE connections SET date_disconnection = %s where id = %s", (timeStampActual, connid,))
			fd.write("\nProceso de desconexion del usuario %s finalizado correctamente" %(common_name))
			fd.write("\n-----------------------------------------------\n\n")
	except:
		fd.write("\nSe ha producido una excepción\n")
	finally:
		if db is not None:
			db.close()

Con esto ya se tendría un sistema para la gestión de conexiones en VPN, algo que serviría para monitorizar la actividad del servidor y las conexiones/desconexiones que realizan los usuarios.
Eres libre de utilizar estos scripts y modificarlos a tu gusto para realizar pruebas o incluso para implementarlos en tu propio servicio de OpenVPN

Un saludo y Happy Hack.

Adastra.

Ocultación de tráfico con Obsfproxy y OpenVPN

enero 17, 2017 Deja un comentario

OpenVPN es una muy buena alternativa a la hora navegar de forma privada y segura, crear una VPN es algo relativamente fácil de configurar, dependiendo evidentemente de tus necesidades y de las complejidades que pueda tener tu entorno/configuración, no obstante, en algunas ocasiones no se puede utilizar de forma directa debido a restricciones y medidas de censura que se aplican en ciertos lugares del mundo, por ejemplo, si te encuentras en China continental te vas a encontrar con que tu cliente de OpenVPN no se podrá conectar con el servidor debido a los bloqueos impuestos por “el gran firewall” chino, las medidas de censura instauradas por éste y otros gobiernos impiden que se puedan completar las conexiones utilizando un canal cifrado. En este caso concreto, para conseguir el filtrado de dichas conexiones, los ISPs junto con los “entes censores” implementan técnicas para el análisis de tráfico y detección de patrones “sospechosos”, dichas técnicas son conocidas como DPI (Deep Packet Inspection) permitiendoles saber que aunque el tráfico se encuentre cifrado, utiliza algún tipo de protocolo concreto, el cual pueden encontrarse restringido o no y evidentemente, partiendo de dicha información, se toma la decisión de permitir o bloquear el tráfico de forma automática. Este problema ya lo han enfrentado soluciones de anonimato como TOR y tal como os comentaba en otro articulo, la mejor forma a día de hoy de “saltarse” esas restricciones consiste en utilizar implementaciones de “Pluggable Transports” los cuales se encargan de alterar el tráfico generado desde el cliente y enmascararlo de tal forma que de cara al ISP o censor se trata de paquetes de datos que no cumplen con ninguno de los patrones de bloqueo y en apariencia, tiene el mismo comportamiento de una petición habitual, como una búsqueda en Baidu (http://www.baidu.com/) o Sogou (http://www.sogou.com/). Existen múltiples implementaciones de Pluggable Transports y aunque las más robustas y extendidas han sido desarrolladas por el equipo de TorProject, existen otras implementaciones que buscan la interoperabilidad de los “Pluggable Transports” en soluciones de privacidad y anonimato distintas a TOR, como es el caso de uProxy, Lantern o Psiphon.
Para utilizar cualquier Pluggable Transport es importante instalar los componentes adecuados tanto en el cliente como en el destino (servidor). Dichos elementos se encargan de enmascarar y desenmascarar los paquetes de datos enviados y recibidos, de tal forma que desde el cliente se aplica el Pluggable Transport adecuado, el cual se encargará de aplicar las modificaciones correspondientes sobre los paquetes de datos y posteriormente se envían al destino. En el lado del cliente funciona como un proxy local, el cual intercepta las peticiones y las procesa antes de salir hacia el gateway de la red. En el lado del destino o servidor, funciona exactamente igual pero en sentido inverso, es decir, se reciben los paquetes de datos por parte de un listener o servicio que “desenmascarará” las peticiones y recompondrá el mensaje enviado originalmente por el cliente. Un mecanismo sencillo y muy robusto que es altamente resistente a la censura.
Una vez comprendido el funcionamiento de los Pluggable Transports y ver que realmente no es demasiado complejo, podemos intentar llevarlo a la practica utilizando alguna implementación de PT disponible, en este caso, se hará con una red VPN implementada con OpenVPN y la implementación de PT Obfsproxy, la cual ha sido desarrollada por el equipo de TorProject y funciona bastante bien.

 

Configuración en el lado del cliente.

En primer lugar, el cliente de la red VPN debe configurarse de tal forma que las conexiones no se lleven a cabo de forma directa contra el servidor OpenVPN, en su lugar, la conexión se realizará contra el proceso de Obfsproxy en el lado del servidor y éste a su vez, se encargará de reenviar los paquetes hacia el servidor OpenVPN correspondiente. Para hacer esto, basta simplemente con cambiar el parámetro “remote” en el fichero de configuración del cliente o directamente desde consola.

remote <IPSERVIDOR_OBFSPROXY> 21194

A continuación, se debe instalar Obfsproxy en el sistema del cliente y luego, levantar el proceso que se encargará de ofuscar el tráfico enviado desde la máquina del cliente. Dicho proceso, como se ha mencionado anteriormente, funcionará simplemente como un proxy local que se encargará de tratar los paquetes y enviarlos al servidor Obfsproxy indicado.
Para instalar obfsproxy sobre un sistema basado en Debian, basta simplemente con ejecutar el comando “apt-get install obfsproxy”, aunque también se puede instalar directamente con pip ejecutando el comando “pip install obfsproxy”.
Después de instalar, el siguiente comando permitirá iniciar el proceso en el lado del cliente en el puerto “10194”

 

obfsproxy –log-file=obfsproxy.log –log-min-severity=info obfs3 –shared-secret=<32 caracteres> socks 127.0.0.1:10194

 

Como se puede apreciar, se especifica un fichero de logs, nivel de log, el PT a utilizar con obfsproxy que en este caso será “obfs3” y finalmente, la opción “–shared-secret” corresponde a una cadena de texto que debe ser la misma en el cliente y servidor, además es recomendable que dicha cadena de texto tenga por lo menos 32 caracteres para generar una clave de 256bits. El parámetro “socks” le indica a obfsproxy que debe levantar un servidor proxy socks en el puerto 10194 en la máquina local, el cual deberá ser utilizado por el cliente de OpenVPN. Ahora bien, antes de continuar, se recomienda generar la clave compartida utilizando OpenSSL, tan sencillo como ejecutar el siguiente comando:

openssl rand -base64 32

Recordar que la salida del comando anterior debe ser incluida en el parámetro “–shared-secret” y además, se trata de un valor que debe ser igual en el proceso obfsproxy en el lado del cliente y en el lado del servidor.

El último paso para que la configuración de OpenVPN+Obfsproxy quede completada en el lado del cliente, consiste nuevamente en modificar el fichero de configuración del cliente openvpn e incluir las siguientes instrucciones “socks-proxy-retry” y “socks-proxy”. En el fichero de configuración estas instrucciones quedarían así:

socks-proxy-retry
socks-proxy 127.0.0.1 10194

El puerto “10194” evidentemente tiene corresponder con el valor indicado en obfsproxy cuando se ha iniciado el proceso cliente.

Configuración en el lado del servidor.

En el lado del servidor OpenVPN hay que seguir unos pasos muy similares a los que se han llevado a cabo en el lado del cliente, solamente que en este caso, es incluso más sencillo ya que en primer lugar, únicamente hay que editar el fichero de configuración “server.conf” y asegurarse de que el puerto indicado en el parámetro “port” coincide con con el que se va a utilizar posteriormente en el proceso servidor de Obfsproxy. En este caso, se asume que dicho puerto será el 1194 (valor por defecto en un servidor OpenVPN).
A continuación, se debe iniciar el proceso servidor de Obfsproxy utilizando el siguiente comando.

obfsproxy –log-file=obfsproxy.log –log-min-severity=info obfs3 –dest=127.0.0.1:1194 –shared-secret=<32 caracteres> server 0.0.0.0:21194

 

Como se puede apreciar, el comando ejecutado en el lado del servidor es muy similar al que se ha lanzado desde el lado del cliente, con la diferencia de que se ha indicado el interruptor “–dest” que permite apuntar al puerto donde se encuentra ejecutándose OpenVPN, lo cual a efectos prácticos, significa que todo el tráfico que obfsproxy consigue desenmascarar será reenviado al puerto especificado, es decir, al puerto en donde se encuentra ejecutándose el servidor OpenVPN. Por otro lado, tal como se ha indicado anteriormente, es necesario que el valor de “–shared-secret” sea el mismo tanto para el cliente como para el servidor. Se trata de la clave utilizada para descifrar los paquetes (cifrado end-to-end). Por último, se indica también que Obfsproxy se ejecutará en modo servidor en el puerto “21194” debido al valor “server” indicado por parámetro.

Ahora que todo está preparado, se debe arrancar el servidor Openvpn y si todo va bien, se puede arrancar el cliente Openvpn con el fichero de configuración correspondiente, esperar a que la conexión con el servidor se establezca correctamente y ver los logs. También resulta muy interesante capturar el tráfico tanto en cliente y servidor para ver qué es lo que hace obfsproxy y cómo altera los paquetes de datos.
Como has podido ver, ha sido fácil de configurar y los resultados son muy interesantes. Intenta probarlo en tus servidores y si estás interesado, aporta a la red de Tor levantando Bridges con OBFS 🙂

Un saludo y happy hack!
Adastra.

Intentando evadir mecanismos y restricciones de Seguridad – Uso de redes virtuales privadas – OpenVPN – Parte XII

marzo 7, 2012 1 comentario

OpenVPN es una aplicación sumamente interesante en el campo de las redes VPN que aprovecha al máximo todas las características de cifrado, certificación y autenticación que soporta OpenSSL para crear túneles sobre redes IP utilizando únicamente un puerto TCP/UDP, se trata de una aplicación bastante robusta y estable que se ha consolidado como una de las mejores aplicaciones en el campo de VPN en el software libre (su licencia es GNU/GPL).

Al igual que cualquier túnel sobre IP utilizando una VPN, una de las principales ventajas es precisamente la posibilidad de “saltar” las restricciones que pueden estar establecidas entre una máquina determinada y la red a la cual se encuentra conectada, a continuación se indica como es el proceso de instalación y como utilizar OpenVPN

Leer más…

A %d blogueros les gusta esto: