Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Fakes App

Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 34

FakesApp: una vulnerabilidad en WhatsApp

7 de agosto de 2018
Investigación Por: Dikla Barda, Roman Zaikin y Oded Vanunu

A partir de principios de 2018, la aplicación de mensajería propiedad de Facebook, WhatsApp, tiene más de 1.500 millones de
usuarios con más de mil millones de grupos y 65 mil millones de mensajes enviados todos los días. Con tanta charla, el potencial de
estafas en línea, rumores y noticias falsas es enorme. Entonces, no ayuda si los actores de amenazas tienen un arma adicional en su
arsenal para aprovechar la plataforma con sus intenciones maliciosas.

Sin embargo, Check Point Research recientemente reveló nuevas vulnerabilidades en la popular aplicación de mensajería que podría
permitir a los actores amenazar interceptar y manipular los mensajes enviados tanto en conversaciones privadas como grupales, dando
a los atacantes un inmenso poder para crear y difundir desinformación de fuentes aparentemente confiables.

Nuestro equipo observó tres posibles métodos de ataque que explotan esta vulnerabilidad, todos los cuales implican tácticas de
ingeniería social para engañar a los usuarios finales. Un actor de amenaza puede:

1. Use la función 'citar' en una conversación grupal para cambiar la identidad del remitente, incluso si esa persona no es
miembro del grupo.
2. Altere el texto de la respuesta de otra persona, esencialmente poniéndole palabras en la boca.
3. Envíe un mensaje privado a otro participante del grupo que esté disfrazado como un mensaje público para todos, de modo
que cuando la persona objetivo responda, estará visible para todos los participantes de la conversación.
Tras el proceso de divulgación responsable, Check Point Research informó a WhatsApp de sus hallazgos. Desde la perspectiva de
Check Point Research, creemos que estas vulnerabilidades son de suma importancia y requieren atención.

Lea a continuación para nuestro análisis técnico completo.

Video de demostración de los ataques en acción

Análisis técnico

Como es bien sabido, WhatsApp encripta cada mensaje, imagen, llamada, video o cualquier otro tipo de contenido que envíe para que
solo el destinatario pueda verlo. Además, ni siquiera WhatsApp tiene la capacidad de ver esos mensajes.

Figura 1: Chat encriptado de WhatsApp

Estos procesos de cifrado llamaron nuestra atención y decidimos tratar de invertir el algoritmo de WhatsApp para descifrar los
datos. De hecho, después de descifrar la comunicación de WhatsApp encontramos que WhatsApp está usando el " protocolo
protobuf2 " para hacerlo.

Al convertir estos datos de protobuf2 en Json, pudimos ver los parámetros reales que se envían y manipularlos para verificar la
seguridad de WhatsApp.
El resultado de nuestra investigación es una extensión de traje de Burp y 3 métodos de manipulación . 
Sin embargo, para comenzar la manipulación, primero tenemos que obtener la clave privada y pública de nuestra sesión y completarla
en nuestra extensión de traje de bursuit.

Si está interesado en una explicación detallada sobre cómo funciona realmente el cifrado entre bastidores, lea el párrafo de
encriptación al final de esta publicación de blog.

Accediendo a las llaves

Las claves se pueden obtener desde la fase de generación de claves desde WhatsApp Web antes de que se genere el código QR:

Figura 2:  clave pública y privada de la comunicación

Después de tomar estas claves, debemos tomar el parámetro "secreto" que envía el teléfono móvil a la Web de WhatsApp mientras el
usuario escanea el código QR:
Figura 3:  La clave secreta de WebSocket

Como resultado de esto, nuestra extensión tendrá el siguiente aspecto:

Figura 4:  Extensión del Burp del decodificador de WhatsApp

Después de hacer clic en "Conectar", la extensión se conecta al servidor local de la extensión, que realizará todas las tareas requeridas
para la extensión.

Manipulando WhatsApp
Al descifrar la comunicación de WhatsApp, pudimos ver todos los parámetros que realmente se envían entre la versión móvil de
WhatsApp y la versión web. Esto nos permitió manipularlos y comenzar a buscar problemas de seguridad.

Esto nos permitió realizar una variedad de tipos de ataques, que se describen a continuación.

Ataque 1:  cambiar la identidad de un remitente en un chat grupal, incluso si no es miembro del grupo

En este ataque, es posible falsificar un mensaje de respuesta para suplantar a otro miembro del grupo e incluso a un miembro del
grupo no existente, por ejemplo, 'Mickey Mouse'.

Para suplantar a alguien del grupo, todo lo que el atacante necesita es capturar el tráfico cifrado:

Figura 5:  Comunicación encriptada de WhatsApp

Una vez que se captura el tráfico, simplemente puede enviarlo a una extensión que luego descifrará el tráfico:
Figura 6:  descifrar el mensaje de WhatsApp 
utilizando nuestra extensión

Los parámetros interesantes a tener en cuenta aquí son:

 conversación : este es el contenido real que se envía.


 participante : este es el participante que realmente envió el contenido.
 fromMe : este parámetro indica si envío los datos o alguien más en el grupo.
 remoteJid - Este parámetro indica a qué grupo / contacto se envían los datos.
 id : la identificación de los datos. La misma identificación aparecerá en las bases de datos del teléfono.
Y este es el punto donde las cosas interesantes comienzan a suceder ...

Por ejemplo, podemos cambiar la conversación a otra cosa. El mensaje con el contenido " ¡Genial! "Enviado por un miembro de un
grupo, por ejemplo, podría cambiarse por algo más como:" Voy a morir, en un hospital en este momento "y el parámetro del
participante también podría cambiarse a otra persona del grupo:
Figura 7:  un mensaje de respuesta falso

Tenga en cuenta que tenemos que cambiar el ID a otra cosa porque ya está enviado y aparece en la base de datos.

Para hacer que todos vean el nuevo mensaje falso, el atacante debe responder al mensaje que falsificó, citando y cambiando ese
mensaje ("Excelente") para que se envíe a todos los integrantes del grupo.

Como puede ver en la siguiente captura de pantalla, creamos un nuevo grupo donde no se enviaron mensajes anteriores, y al usar el

método de arriba, pudimos crear una respuesta falsa.

Figura 8:  La conversación original

El parámetro 'participante' también puede ser un texto o un número de teléfono de alguien que no está en el grupo, lo que provocaría
que todos en el grupo creyeran que realmente se envió desde este participante.

Por ejemplo:
Figura 9:  Cambio del contenido del mensaje 
mediante nuestra herramienta de depuración

... y el resultado se verá así:

Esto nuevamente se enviaría a todos en el grupo como antes.


Figura 10:  Respuesta a un mensaje enviado desde 
alguien fuera del grupo

Ataque 2: cambiar la respuesta de un corresponsal para  poner palabras en su boca

En este ataque, el atacante puede manipular el chat enviándole un mensaje a él mismo en nombre de la otra persona, como si hubiera
venido de ellos. Al hacerlo, sería posible incriminar a una persona o cerrar un trato fraudulento, por ejemplo.

Para falsificar los mensajes, debemos manipular el parámetro ' fromMe ' en el mensaje, que indica quién envió el mensaje en el chat
personal.

Esta vez capturaremos el mensaje saliente de WhatsApp Web antes de que se envíe a nuestro Burp Suite. Para hacerlo, podemos
poner un punto de corte en la función aesCbcEncrypt y tomar los datos del parámetro 'a':

Figura 11:  Manipulación de mensajes de OutGoing


Copiaremos estos datos en nuestra extensión Burp y seleccionaremos la dirección saliente. Al presionar "Descifrar", nuestra extensión
descifrará los datos:

Figura 12:  descifrado del mensaje saliente

Después de cambiarlo a falso y encriptarlo de nuevo, obtenemos el siguiente resultado:

Figura 13:  Cifrado del mensaje saliente

Tenemos que modificar el parámetro 'a' en nuestro navegador, y el resultado será una notificación automática con el contenido. De
esta manera, incluso es posible falsificar toda la conversación.
 

Figura 14:  Envío de mensajes a mí mismo 


en nombre de otra persona.

Toda la conversación se verá así:


 

Figura 15:  Envío de mensajes a mí mismo 


en nombre de alguien más

Ataque 3:  envíe un mensaje privado en un grupo de chat, pero cuando el destinatario responda, todo el grupo lo verá.

En este ataque, es posible enviar un mensaje en un chat grupal que solo verá una persona específica, aunque si responde a este
mensaje, todo el grupo verá su respuesta.

De esta manera, es posible manipular a un determinado miembro del grupo y "tripitarlo" para que revele información al grupo que de
otro modo no querría que ellos supieran.

Encontramos este vector de ataque mientras invertimos la aplicación móvil Android. En esta instancia, encontramos que si el atacante
manipula un mensaje simple en el grupo, como "Somos el equipo", en realidad encontraremos este mensaje en
'/data/data/com.whatsapp/databases/msgstore.db'. base de datos - como se ve a continuación.

Figura 16:  Envío de un mensaje privado en el chat grupal

Encontraremos este mensaje en la base de datos '/data/data/com.whatsapp/databases/msgstore.db'


Luego, si abrimos la conversación en un teléfono móvil usando el cliente sqlite3 y emitimos el siguiente comando:

SELECCIONE * DESDE mensajes;

Veremos los siguientes datos:

Figura 17:  Manipulación de la base de datos

Para enviar un mensaje al grupo, pero restringirlo solo a un miembro específico del grupo, tenemos que establecer su número bajo
el parámetro ' remote_resource ' .

El truco aquí es simplemente cambiar el parámetro ' key_from_me ' de 0 a 1

Una vez hecho esto, ejecutaremos el siguiente comando y actualizaremos key_from_me y los datos:

mensajes de actualización establecidos key_from_me = 1, data = "¡Todos sabemos lo que has hecho!", donde _id = 2493;

El atacante debe cerrar y volver a abrir su WhatsApp para forzar a la aplicación a enviar el nuevo mensaje. Después de hacerlo, el
resultado será el siguiente:

Tenga en cuenta que solo la víctima recibió el mensaje?

Si la víctima escribe algo como respuesta, todos en el grupo recibirán su respuesta, pero si responde el mensaje, solo verá el
contenido contestado y todos los demás verán el mensaje original ...
 

Explicación del cifrado de WhatsApp

Código fuente:  https://github.com/romanzaikin/BurpExtension-WhatsApp-Decryption-CheckPoint

Comencemos con WhatsApp Web. Antes de generar el código QR, WhatsApp Web genera una clave pública y privada que se utiliza
para el cifrado y descifrado.
Figura 23:  Clave pública y privada de la conversación

Llamemos a nuestra clave privada ' priv_key_list' y a nuestra clave pública ' pub_key_list ' .

Estas claves se crearon utilizando curve25519_donna utilizando 32 bytes aleatorios.


Figura 24:  Curva de proceso de cifrado25519

Para descifrar los datos, comenzaremos a crear un código de descifrado. Esto tomará la clave privada de WhatsApp Web en lugar de
los bytes aleatorios porque necesitamos las mismas claves para descifrar los datos:

self.conn_data [ "private_key" ] = curve25519.Private ( "" .join ([chr (x) para x en priv_key_list])) 


self.conn_data [ "public_key" ] = self.conn_data [ "private_key" ] .get_public ( )

assert (self.conn_data [ "public_key" ] .serialize () == "" .join ([chr (x) para x en pub_key_list]))

Luego, después de crear el código QR, luego de escanearlo con un teléfono, podemos enviar la siguiente información a la Web de
Whatsapp a través de un websocket:
Figura 25:  La clave secreta de WebSocket

El parámetro más importante aquí es el secreto que luego pasa a setSharedSecret. Esto dividirá el secreto en partes múltiples y
configurará todas las funciones criptográficas que necesitamos para descifrar el tráfico de WhatsApp.

Primero, podemos ver que hay una traducción de String 'e' en Array y algunas divisiones que dividen el secreto en dos partes: 'n', que
son los primeros 32 bytes y 'a', que son los caracteres del 64 ° byte hasta el final de la 't'.

Figura 26:  Obtener el SharedSecret

Y si profundizamos en la función '' E.SharedSecret '' , podemos ver que usa dos parámetros, los primeros 32 bytes y la clave
privada de la generación QR:
Figura 27:  Obtener el SharedSecret

Después de esto, podemos actualizar nuestro código python y agregar la siguiente línea:

self.conn_data [ "shared_secret" ] = self.conn_data [ "private_key" ] .get_shared_key (curve25519.Public (self.conn_data


[ "secret" ] [: 32]), clave lambda : key)

A continuación tenemos el gastado que es de 80 bytes:

Figura 28:  Ampliación de SharedSecret

Al sumergirse podemos ver que la función usa la función HKDF. Así que encontramos la función 'pyhkdf' y la usamos en nuestro
código para gastar la clave de la misma manera que lo hizo WhatsApp:

shared_expended = self.conn_data [ "shared_secret_ex" ] = HKDF (self.conn_data [ "shared_secret" ], 80)

A continuación tenemos la función de validación de hmac que toma los datos gastados como parámetro 'e' y los divide en 3
parámetros:

 i - los primeros 32 bytes de shared_expended


 r - 32 bytes desde el byte 32
 o - 16 bytes desde el byte 64
También está el parámetro, 's', que es una concatenación del parámetro 'n' y 'a', de la función anterior a la cual forma parte de nuestro
secreto.

Figura 29:  HmacSha256

Entonces se llamará a la función HmacSha256 con el parámetro 'r' y firmará los datos con el parámetro 's', después de eso 'n'
recibiremos el verificador de hmac que se comparará con 'r', que es un corte de 't' de 32 bytes a 64 bytes, y 't' es nuestro secreto en el
formato de matriz, como se vio anteriormente.
Figura 30: Comprobación de la validez de los mensajes

En Python se verá así:

check_hmac = HmacSha256 (shared_expended [32:64], self.conn_data [ "secret" ] [: 32] + self.conn_data [ "secret" ]


[64:]) if check_hmac! = self.conn_data [ "secret" ] [ 32:64]: elevar ValueError ( “hmac desajuste de error” ) 

La última función relacionada con el cifrado en este bloque es 'aesCbcDecrypt' que usa el parámetro 's' que es una concatenación
entre los datos del byte 64 hasta el final de los compartidos expendidos y los datos del byte 64 del secreto, y 'i' que son los primeros
32bytes de gastos compartidos.

Figura 31: Obtener la clave AES y la clave MAC

El resultado es la clave descifrada que usaremos más tarde. Entonces, si traducimos el código, se verá así:

keysDecrypted = AESDecrypt (shared_expended [: 32], shared_expended [64:] + self.conn_data [ "secret" ] [64:]) Después del
descifrado, tendremos la nueva 't' que es los primeros 32 bytes, que es la clave de cifrado y los siguientes 32 bytes, que es la clave
mac:

self.conn_data [ "key" ] [ "aes_key" ] = keysDecrypted [: 32] 


self.conn_data [ "key" ] [ "mac_key" ] = keysDecrypted [32:64]

El código completo se verá así:

self.conn_data [ "private_key" ] = curve25519.Private ( "" .join ([chr (x) para x en priv_key_list])) 


self.conn_data [ "public_key" ] = self.conn_data [ "private_key" ] .get_public ( )

assert (self.conn_data [ "public_key" ] .serialize () == "" .join ([chr (x) para x en pub_key_list]))

self.conn_data [ "secret" ] = base64.b64decode (ref_dict [ "secret" ]) 


self.conn_data [ "shared_secret" ] = self.conn_data [ "private_key" ] .get_shared_key (curve25519.Public (self.conn_data
[ "secret " ] [: 32]), clave lambda : tecla)

shared_expended = self.conn_data [ "shared_secret_ex" ] = HKDF (self.conn_data [ "shared_secret" ], 80)


check_hmac = HmacSha256 (shared_expended [32:64], self.conn_data [ "secret" ] [: 32] + self.conn_data [ "secret" ] [64:])

if check_hmac! = self.conn_data [ "secret" ] [32:64]: 


raise ValueError ( "Error hmac mismatch" )

keysDecrypted = AESDecrypt (shared_expended [: 32], shared_expended [64:] + self.conn_data [ "secret" ] [64:])

self.conn_data [ "key" ] [ "aes_key" ] = keysDecrypted [: 32] 


self.conn_data [ "key" ] [ "mac_key" ] = keysDecrypted [32:64]

Entonces, después de tener el código que puede regenerar todos los parámetros de encriptación necesarios, podemos continuar con el
proceso de descifrado.

Para hacer eso, comenzamos con la captura de un mensaje:

Figura 32: El mensaje entrante encriptado

Como puede ver, el mensaje se divide en dos partes: la etiqueta y los datos. Usaremos la siguiente función para descifrar el mensaje:

def decrypt_incoming_message (self, message): 


message = base64.b64decode (mensaje) 
message_parts = message.split ( "," , 1) 
self.message_tag = message_parts [0] 
content = message_parts [1]

check_hmac = hmac_sha256 (self.conn_data [ "mac_key" ], content [32:]) 


if check_hmac! = content [: 32]: 
raise ValueError ( "Error hmac mismatch" )

self.decrypted_content = AESDecrypt (self.conn_data [ "aes_key" ], content [32:]) 


self.decrypted_seralized_content = whastsapp_read (self.decrypted_content, True)

return self.decrypted_seralized_content

Como puede ver, recibimos los datos en formato base64 para copiar los datos Unicode fácilmente, En Burp podemos codificar los
datos a base64 simplemente presionando ctrl + b y pasándolos a la función decrypt_incomping_message. Esta función divide la
etiqueta del contenido y comprueba si nuestra clave puede descifrar los datos comparando el hmac_sha256 (self.conn_data
[" mac_key "], el contenido [32:]) con el contenido [: 32].
Si todo encaja, podemos continuar con el paso de descifrado de AES que utiliza nuestra clave aes y el contenido de 32bytes.

Este contenido contiene primero el IV, que es del tamaño del tamaño del bloque aes, y luego los datos reales:

self.decrypted_content = AESDecrypt (self.conn_data [ "aes_key" ], content [32:])

El resultado de esta función será un protobuf, que se ve así:

Figura 33: El mensaje descifrado con Protobuf

Para traducirlo a json usaremos la función ' whatsapp_read ' .

Explicación del cifrado de WhatsApp (descifrar el mensaje entrante):

Para descifrar un mensaje, primero tenemos que entender cómo funciona el protocolo de WhatsApp, así que comenzamos depurando
la función e.decrypt :

Figura 34:  Función ReadNode


Esta función desencadenará readNode que tiene el siguiente código:

Figura 35:  Función ReadNode

Traducimos todo el código a python para representar la misma función que se ve así:
 

Este código primero lee un byte de la secuencia y lo mueve a char_data.  A continuación, intenta leer el tamaño de la lista de la
secuencia entrante mediante la función read_list_size.

Luego tenemos otro byte que llamaremos token_byte que se pasará a read_string y se verá así:


Figura 36: Función ReadString

Este código usa  getToken y pasa nuestro parámetro como una posición en la matriz token:
Figura 37: Función getToken

Este es el primer elemento que WhatsApp envía en la comunicación, luego traducimos todas las funciones en la
función readString y continuamos con la depuración:
 

A continuación puede ver la función 'readAttributes' en la función readNode:

Figura 38: función readAttribues

Esta función simplemente sigue leyendo más bytes de la secuencia y los analiza a través de la misma lista de símbolos que vimos
antes cuando procesamos el token de "acción", que se verá así:
Entonces, el segundo parámetro que envía WhatsApp es la acción real al messenger donde podemos ver que WhatsApp envió {add:
"replay"} lo que significa que llegó un nuevo mensaje.

Básicamente, continuaremos con el código hasta que lleguemos al final de readNode, que nos dará las tres partes del mensaje que se
envió:

1. alguna ficha
2. algunos atributos de tokens
3. el mensaje protobuf codificado
Entonces, hasta este punto obtuvimos la primera y la segunda parte fácilmente reescribiendo todas las funciones a Python, lo cual es
muy directo.

Figura 39: matriz descifrada

A continuación tenemos que lidiar con el tercer parámetro que es el protobuf y descifrarlo.

Para obtener el protobuf podemos ver el esquema de protobuf implementado por Whatsapp y simplemente copiarlo en un archivo
.proto limpio que se puede obtener desde aquí:
Figura 40: protobuf

Los índices también se pueden copiar desde el esquema de protobuf de Whatsapp y compilarse en el archivo de protobuf de Python
utilizando:

Entonces podemos traducir el protobuf a json fácilmente mediante el uso de las funciones de python generadas por el protobuf ...
... y el resultado se verá así:

Figura 41: Datos descifrados

Después de implementar eso dentro de nuestras extensiones, pudimos descifrar la comunicación:


Figura 42: Uso de nuestra extensión para descifrar los datos

Cifrado de WhatsApp explicado (cifrar mensaje entrante)

El proceso de cifrado es prácticamente el mismo que el cifrado, pero en el orden opuesto, por lo que esta vez invertiremos
la función writeNode :

Figura 43: función writeNode

Que se implementa así:


Figura 44: función writeNode

Como puede ver esta vez, ya tenemos el token y los atributos del token que tenemos que traducir a su posición en las listas de tokens,
y luego simplemente volver a implementar toda la función de la misma manera que lo hicimos en readNode:

El código es muy directo; primero comprobamos si el nodo que tenemos tiene una longitud de tres. Luego multiplicamos el número
de atributos de token por dos y lo pasamos a writeListStart, que escribirá el inicio del carácter de lista y luego el tamaño de la lista
(lo mismo que vimos en readNode ):

Después de tener la lista de inicio, entraremos en writeString que realiza lo mismo que readString , ya que puede ver "acción"
traducida a diez, que es la posición de "acción" en el índice de tokens, y así sucesivamente:
Figura 45: función writeToken

Traducimos el código y todas las funciones, que se parecen a las siguientes:

luego el código entra en writeAttributes que traducirá los atributos y de ahí en writeChildren que traducirá los datos reales.
Figura 46: función writeChildren

Traducimos esta función que se ve así:

De esta forma, creamos los datos, de modo que nuestro código que descifra y encripta los mensajes se verá así:
 

Para simplificar el proceso de cifrado, cambiamos la función real writeChildren y añadimos otro tipo de instancia para simplificar el
cifrado:

El resultado es el cifrado y el descifrado de los datos entrantes. 


Para descifrar los datos salientes, consulte el código de nuestro github:

https://github.com/romanzaikin/BurpExtension-WhatsApp-Decryption-CheckPoint

También podría gustarte