MITM – Fake Whatsapp

MITM FAKE WHATSAPP – Owne o whats de pessoas e grupos!

Como é sabido, o WhatsApp criptografa todas as mensagens, fotos, chamadas, vídeos ou qualquer outro tipo de conteúdo enviado para que apenas o destinatário possa vê-lo. Além do mais, nem o WhatsApp tem a capacidade de visualizar essas mensagens.

Categorias: , ,

Descrição

MITM FAKE WHATSAPP – Owne o whats de pessoas e grupos!

Como é sabido, o WhatsApp criptografa todas as mensagens, fotos, chamadas, vídeos ou qualquer outro tipo de conteúdo enviado para que apenas o destinatário possa vê-lo. Além do mais, nem o WhatsApp tem a capacidade de visualizar essas mensagens.

Exemplo de mensagem criptografada do WhatsApp

Figura 1: Chat criptografado do WhatsApp

Esses processos de criptografia chamaram nossa atenção e decidimos tentar reverter o algoritmo do WhatsApp para descriptografar os dados. De fato, após descriptografar a comunicação do WhatsApp, descobrimos que o WhatsApp está usando o ” protocolo protobuf2 ” para fazer isso.

Ao converter esses dados protobuf2 em Json, pudemos ver os parâmetros reais enviados e manipulá-los para verificar a segurança do WhatsApp.

O resultado de nossa pesquisa é uma extensão de burp suit e três métodos de manipulação .
Para iniciar a manipulação, no entanto, primeiro precisamos obter a chave pública e privada de nossa sessão e preenchê-la em nossa extensão de burpsuit.

Se você estiver interessado em uma explicação detalhada sobre como a criptografia realmente funciona nos bastidores, leia o parágrafo de criptografia no final desta postagem do blog.

Acessando as chaves

As chaves podem ser obtidas na fase de geração de chaves no WhatsApp Web antes que o código QR seja gerado:

Figura 2: Chave pública e privada da comunicação

Depois de pegar essas chaves, precisamos usar o parâmetro “secret”, que é enviado pelo celular para o WhatsApp Web enquanto o usuário lê o código QR:

Figura 3: A chave secreta do WebSocket

Como resultado disso, nossa extensão será semelhante à abaixo:

Figura 4: extensão do decodificador do WhatsApp Burp

Depois de clicar em “Conectar”, a extensão se conecta ao servidor local da extensão, que executará todas as tarefas necessárias para a extensão.

Manipulando o WhatsApp

Ao descriptografar a comunicação do WhatsApp, pudemos ver todos os parâmetros que são realmente enviados entre a versão móvel do WhatsApp e a versão da Web. Isso nos permitiu manipulá-los e começar a procurar problemas de segurança.

Isso resultou na capacidade de realizar uma variedade de tipos de ataque, descritos a seguir.

Ataque 1: Alterar a identidade de um remetente em um bate-papo em grupo, mesmo que não seja um membro do grupo

Nesse ataque, é possível falsificar uma mensagem de resposta para representar outro membro do grupo e até mesmo um membro inexistente do grupo, por exemplo, ‘Mickey Mouse’.

Para se passar por alguém do grupo, tudo o que o invasor precisa fazer é capturar o tráfego criptografado:

Figura 5: Comunicação WhatsApp criptografada

Depois que o tráfego é capturado, ele pode simplesmente enviá-lo para uma extensão que, então, descriptografa o tráfego:

Figura 6: Descriptografando a mensagem do WhatsApp
Usando nossa extensão

Os parâmetros interessantes a serem observados aqui são:

  • conversa – Este é o conteúdo real enviado.
  • participante – este é o participante que realmente enviou o conteúdo.
  • fromMe – Este parâmetro indica se eu enviei os dados ou outra pessoa no grupo.
  • remoteJid – Este parâmetro indica para qual grupo / contato os dados são enviados.
  • id – O ID dos dados. O mesmo ID aparecerá nos bancos de dados do telefone.

E é nesse ponto que coisas interessantes começam a acontecer …

Por exemplo, podemos mudar a conversa para outra coisa. A mensagem com o conteúdo “ Ótimo! “Enviado por um membro de um grupo, por exemplo, pode ser alterado para outra coisa como:” Eu vou morrer agora em um hospital “e o parâmetro participante também pode ser alterado para outra pessoa do grupo:

Figura 7: Uma mensagem de resposta falsa

Observe que precisamos alterar o ID para outra coisa, porque ele já foi enviado e aparece no banco de dados.

Para fazer com que todos vejam a nova mensagem falsificada, o atacante precisa responder à mensagem que ele falsificou, citando e alterando essa mensagem (“Ótimo”) para que seja enviada a todos no grupo.

Como você pode ver na captura de tela abaixo, criamos um novo grupo para o qual nenhuma mensagem anterior foi enviada e, usando o método acima, conseguimos criar uma resposta falsa.

Figura 8: A conversa original

O parâmetro ‘participante’ também pode ser um texto ou um número de telefone de alguém que não está no grupo, o que faria com que todos no grupo acreditassem que ele realmente foi enviado desse participante.

Por exemplo:

Figura 9: Alterando o conteúdo da mensagem
Usando nossa ferramenta de depuração

… E o resultado ficará assim:

Isso seria novamente enviado a todos no grupo como antes.

Figura 10: Responder a uma mensagem enviada de
Alguém fora do grupo

Ataque 2: Mudando a resposta de um correspondente para colocar palavras na boca

Nesse ataque, o atacante é capaz de manipular o bate-papo enviando uma mensagem de volta para si mesmo em nome da outra pessoa, como se tivesse vindo deles. Ao fazer isso, seria possível incriminar uma pessoa ou fechar um negócio fraudulento, por exemplo.

Para falsificar as mensagens, precisamos manipular o parâmetro fromMe na mensagem, que indica quem enviou a mensagem no bate-papo pessoal.

Desta vez, capturaremos a mensagem de saída do WhatsApp Web antes mesmo de ser enviada ao nosso Burp Suite. Para fazer isso, podemos colocar um ponto de interrupção na função aesCbcEncrypt e pegar os dados do parâmetro ‘a’:

Figura 11: Manipulação de mensagem OutGoing

Em seguida, copiaremos esses dados em nossa extensão Burp e selecionaremos a direção de saída. Ao pressionar “Descriptografar”, nossa extensão descriptografará os dados:

Figura 12: Descriptografia da mensagem de saída

Depois de alterá-lo para false e criptografá-lo novamente, obtemos o resultado abaixo:

Figura 13: Criptografia da mensagem de saída

Temos que modificar o parâmetro ‘a’ em nosso navegador, e o resultado será uma notificação por push com o conteúdo. Dessa forma, é ainda possível falsificar todo o bate-papo.

Figura 14: Enviando mensagens para mim mesmo
em nome de outra pessoa.

A conversa inteira ficará assim:

Figura 15: Enviando mensagens para mim mesmo
em nome de outra pessoa

Ataque 3: envie uma mensagem particular em um grupo de bate-papo, mas quando o destinatário responder, o grupo inteiro o verá.

Nesse ataque, é possível enviar uma mensagem em um bate-papo em grupo que apenas uma pessoa específica verá, embora, se ela responder a essa mensagem, o grupo inteiro verá sua resposta.

Dessa maneira, é possível manipular um determinado membro do grupo e ‘desmotivá-lo’ para que ele revele informações ao grupo que, de outra forma, eles não gostariam que eles soubessem.

Encontramos esse vetor de ataque enquanto revertíamos o aplicativo móvel Android. Nesse caso, descobrimos que, se o invasor manipular uma mensagem simples no grupo, como “Somos a equipe”, encontraremos essa mensagem em ‘/data/data/com.whatsapp/databases/msgstore.db’ banco de dados – como visto abaixo.

Figura 16: Enviando uma mensagem privada no bate-papo em grupo

Esta mensagem será encontrada no banco de dados ‘/data/data/com.whatsapp/databases/msgstore.db’

Em seguida, se abrirmos a conversa em um telefone celular usando o cliente sqlite3 e emitir o seguinte comando:

SELECT * FROM mensagens;

Veremos os seguintes dados:

Figura 17: Manipulação do banco de dados

Para enviar uma mensagem ao grupo, mas restringi-la apenas a um membro específico do grupo, precisamos definir o número dele no parâmetro remote_resource .

O truque aqui é simplesmente alterar o parâmetro key_from_me de 0 para 1

Feito isso, executaremos o seguinte comando e atualizaremos o key_from_me e os dados:

atualizar conjunto de mensagens key_from_me = 1, data = ”Todos sabemos o que você fez!” onde _id = 2493;

O invasor precisa fechar e reabrir o WhatsApp para forçar o aplicativo a enviar a nova mensagem. Depois de fazer isso, o resultado será o seguinte:

Observe que apenas a vítima recebeu a mensagem?

Se a vítima escrever algo como resposta, todos no grupo receberão sua resposta, mas se ele responder apenas à mensagem, verá o conteúdo respondido e todos os outros verão a mensagem original… !!

Explicação sobre criptografia do WhatsApp

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

Vamos começar com o WhatsApp Web. Antes de gerar o código QR, o WhatsApp Web gera uma chave pública e privada usada para criptografia e descriptografia.

Figura 23: Chave pública e privada da conversa

Vamos chamar nossa chave privada ‘ priv_key_list’ e nossa chave pública ‘ pub_key_list.

Essas chaves foram criadas usando curve25519_donna usando 32 bytes aleatórios.

Figura 24: Curva do processo de criptografia25519

Para descriptografar os dados, começaremos a criar um código de descriptografia. Isso pegará a chave privada do WhatsApp Web em vez dos bytes aleatórios, porque precisamos ter as mesmas chaves para descriptografar os dados:

self.conn_data [ “private_key” ] = curve25519.Private ( “” .join ([chr (x) para x em 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 em pub_key_list])))

Depois que o código QR é criado, depois de digitalizá-lo com um telefone, podemos enviar as seguintes informações para o WhatsApp Web por um soquete da Web:

Figura 25: A chave secreta do WebSocket

O parâmetro mais importante aqui é o segredo, que passa para setSharedSecret. Isso dividirá o segredo em partes multiplicadas e configurará todas as funções criptográficas necessárias para descriptografar o tráfego do WhatsApp.

Primeiro, podemos ver que há uma tradução da String ‘e’ para Array e algumas fatias que dividem o segredo em duas partes: ‘n’, que são os primeiros 32 bytes e ‘a’, que são os caracteres do 64º byte até o final do ‘t’.

Figura 26: Obtendo o SharedSecret

E se mergulharmos na função ” E.SharedSecret ” , podemos ver que ela usa dois parâmetros, que foram os primeiros 32 bytes e a chave privada da geração QR:

Figura 27: Obtendo o SharedSecret

Depois disso, podemos atualizar nosso código python e adicionar a seguinte linha:

self.conn_data [ “shared_secret” ] = self.conn_data [ “private_key” ] .get_shared_key (curve25519.Public (self.conn_data [ “secret” ] [: 32]), chave lambda : key)

Em seguida, temos o gasto, que é de 80 bytes:

Figura 28: Estendendo o SharedSecret

Ao mergulhar, podemos ver que a função usa a função HKDF. Então, encontramos a função ‘pyhkdf’ e a usamos em nosso código para gastar a chave da mesma maneira que o WhatsApp:

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

Em seguida, temos a função de validação hmac, que pega os dados gastos como parâmetro ‘e’ e os divide em 3 parâmetros:

  • i – os primeiros 32 bytes de shared_expended
  • r – 32 bytes a partir dos 32 bytes
  • o – 16 bytes do byte de 64 bytes

Há também o parâmetro ‘s’, que é uma concatenação do parâmetro ‘n’ e ‘a’, da função anterior à qual faz parte do nosso segredo.

Figura 29: HmacSha256

Em seguida, a função HmacSha256 será chamada com o parâmetro ‘r’ e assinará os dados com o parâmetro ‘s’; depois disso, ‘n’ receberemos o verificador hmac que será comparado com ‘r’, que é uma fatia de ‘t’ de 32 bytes a 64 bytes e ‘t’ é o nosso segredo no formato de matriz, como visto anteriormente.

Figura 30: Verificando a validade das mensagens

Em python, será assim:

check_hmac = HmacSha256 (gastos_compartilhados [32:64], self.conn_data [ “secret” ] [: 32] + self.conn_data [ “secret” ] [64:])   se check_hmac! = self.conn_data [ “secret” ] [32:64]:
raise ValueError ( “Erro de incompatibilidade hmac” )

A última função relacionada à criptografia neste bloco é ‘aesCbcDecrypt’, que usa o parâmetro ‘s’, que é uma concatenação entre os dados do byte 64 até o final do gasto compartilhado e os dados do byte 64 do segredo e ‘i’ que são os primeiros 32 bytes de gasto compartilhado.

Figura 31: Obtendo a chave AES e a tecla MAC

O resultado é a chave descriptografada que usaremos mais tarde. Portanto, se traduzirmos o código, ele ficará assim:

keysDecrypted = AESDecrypt (shared_expended [: 32], shared_expended [64:] + self.conn_data [ “secret” ] [64:]) Após a descriptografia, teremos o novo ‘t’, que é o primeiro 32 bytes, que é o chave de criptografia e os próximos 32 bytes, que é a chave do mac:

self.conn_data [ “key” ] [ “aes_key” ] = keysDecrypted [: 32]
self.conn_data [ “key” ] [ “mac_key” ] = keysDecrypted [32:64]

O código inteiro ficará assim:

self.conn_data [ “private_key” ] = curve25519.Private ( “” .join ([chr (x) para x em 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 em 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]), chave lambda : key)

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

check_hmac = HmacSha256 (gastos_compartilhados [32:64], self.conn_data [ “secret” ] [: 32] + self.conn_data [ “secret” ] [64:])

se check_hmac! = self.conn_data [ “secret” ] [32:64]:
raise ValueError ( “Erro de incompatibilidade hmac” )

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]

Portanto, depois de termos o código que pode gerar novamente todos os parâmetros de criptografia necessários, podemos continuar com o processo de descriptografia.

Para fazer isso, começamos com a captura de uma mensagem:

Figura 32: A mensagem de entrada criptografada

Como você pode ver, a mensagem é dividida em duas partes: a tag e os dados. Usaremos a seguinte função para descriptografar a mensagem:

def decrypt_incoming_message (self, mensagem):
message = base64.b64decode (mensagem)
message_parts = message.split ( “,” , 1)
self.message_tag = message_parts [0]
content = message_parts [1]

check_hmac = hmac_sha256 (self.conn_data [ “mac_key” ]], conteúdo [32:])
if check_hmac! = content [: 32]:
raise ValueError ( “Erro de incompatibilidade hmac” )

self.decrypted_content = AESDecrypt (self.conn_data [ “aes_key” ]], conteúdo [32:])
self.decrypted_seralized_content = whastsapp_read (self.decrypted_content, True)

retornar self.decrypted_seralized_content

Como você pode ver, recebemos os dados no formato base64 para copiar os dados Unicode facilmente. No Burp, podemos codificar os dados para base64 simplesmente pressionando ctrl + be passá-los para a função decrypt_incomping_message. Esta função divide a tag do conteúdo e verifica se nossa chave pode descriptografar os dados comparando o hmac_sha256 (self.conn_data [” mac_key “], conteúdo [32:]) com o conteúdo [: 32].

Se tudo der certo, podemos continuar com a etapa de descriptografia do AES, que usa nossa chave aes e o conteúdo do 32 bytes.

Este conteúdo contém primeiro o IV, que é do tamanho de um bloco de aes e, em seguida, os dados reais:

self.decrypted_content = AESDecrypt (self.conn_data [ “aes_key” ]], conteúdo [32:])

A saída desta função será um protobuf, que se parece com isso:

Figura 33: A mensagem descriptografada com Protobuf

Para traduzi-lo para json, usaremos a função whatsapp_read .

Criptografia do WhatsApp explicada (descriptografar mensagem de entrada):

Para descriptografar uma mensagem, primeiro precisamos entender como o protocolo WhatsApp funciona e começamos depurando a função e.decrypt :

Figura 34: Função ReadNode

Esta função acionará o readNode que possui o seguinte código:

Figura 35: Função ReadNode

Traduzimos todo o código para python para representar a mesma função que se parece com isso:

Esse código primeiro lê um byte do fluxo e o move para char_data. Em seguida, ele tenta ler o tamanho da lista do fluxo recebido usando a função read_list_size.

Então, temos outro byte que chamaremos de token_byte que será passado para read_string e terá a seguinte aparência:

Figura 36: Função ReadString

Esse código usa getToken e passa nosso parâmetro como uma posição na matriz de tokens:

Figura 37: Função getToken

Este é o primeiro item que o whatsapp envia na comunicação, depois traduzimos todas as funções na função readString e continuamos com a depuração:

Em seguida, você pode ver a função ‘readAttributes’ na função readNode:

Figura 38: função readAttribues

Essa função continua a ler mais bytes do fluxo e a analisá-los através da mesma lista de tokens que vimos antes quando analisamos o token de “ação”, que se parecerá com isto:

Portanto, o segundo parâmetro que o WhatsApp envia é a ação real para o messenger, onde podemos ver que o WhatsApp enviou {add: ”replay”}, o que significa que uma nova mensagem chegou.

Basicamente, continuaremos com o código até chegarmos ao final do readNode, que nos dará as três partes da mensagem que foi enviada:

  1. algum sinal
  2. alguns atributos de token
  3. a mensagem protobuf codificada

Portanto, até esse ponto, obtivemos a primeira e a segunda parte facilmente reescrevendo todas as funções para python, o que é muito direto.

Figura 39: Matriz descriptografada

Em seguida, temos que lidar com o terceiro parâmetro, que é o protobuf, e descriptografá-lo.

Para obter o protobuf, podemos olhar para o esquema de protobuf implementado pelo Whatsapp e apenas copiá-lo em um arquivo .proto limpo, que pode ser obtido aqui:

Figura 40: protobuf

Os índices também podem ser copiados do esquema protobuf do Whatsapp e compilados no arquivo protobuf do python usando:

Em seguida, podemos traduzir o protobuf para json facilmente usando as funções python geradas pelo protobuf…

… E o resultado ficará assim:

Figura 41: Dados descriptografados

Depois de implementá-lo em nossas extensões, conseguimos descriptografar a comunicação:

Figura 42: Usando nossa extensão para descriptografar os dados

Criptografia do WhatsApp explicada (Criptografar mensagem recebida)

O processo de criptografia é praticamente o mesmo que a criptografia, mas na ordem oposta, portanto, desta vez, reverteremos a função writeNode :

Figura 43: função writeNode

O que é implementado assim:

Figura 44: função writeNode

Como você pode ver neste momento, já temos os atributos token e token que temos que traduzir para sua posição nas listas de token e, em seguida, apenas reimplementamos toda a função da mesma maneira que fizemos no readNode:

O código é muito direto; primeiro, verificamos se o nó que obtivemos tem comprimento de três. Em seguida, multiplicamos o número de atributos do token por dois e passamos para writeListStart, que escreverá o início do caractere de lista e o tamanho da lista (a mesma coisa que vimos no readNode ):

Depois de termos a lista inicial, entraremos no writeString que executa a mesma coisa que o readString , como você pode ver “action” traduzido para dez, que é a posição “action” no índice de tokens e assim por diante:

Figura 45: função writeToken

Traduzimos o código e todas as funções, que se parecem com as abaixo:

então, o código entra em writeAttributes, que traduz os atributos e, a partir de então, em writeChildren, que traduz os dados reais.

Figura 46: função writeChildren

Traduzimos esta função que se parece com a abaixo:

Dessa forma, construímos os dados de volta, para que nosso código que descriptografe e criptografe as mensagens fique assim:

Para simplificar o processo de criptografia, alteramos a função writeChildren real e adicionamos outro tipo de instância para simplificar a criptografia:

O resultado é a criptografia e descriptografia dos dados recebidos.
Para descriptografar os dados enviados, consulte o código do nosso github:

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