Invasão de Drone do DJI
A DJI é líder mundial na indústria civil de tecnologia de drones e imagens aéreas.
Além dos consumidores, porém, ela também conquistou uma grande parte do mercado corporativo, com clientes provenientes de setores críticos de infraestrutura, manufatura, agricultura, construção, gerenciamento de emergências e muito mais. Com tantos clientes em todo o mundo, consumidores e empresas, os drones DJI podem obter dados e imagens de uma ampla variedade de pontos de vista e de um amplo espectro de assuntos.
Em uma investigação recente, a Check Point Research descobriu uma vulnerabilidade que, se explorada, concederia ao invasor acesso à conta DJI de um usuário sem que o usuário estivesse ciente disso. Isso poderia ter fornecido acesso a:
- Registros de voo, fotos e vídeos gerados durante voos com drones, caso um usuário da DJI os tivesse sincronizado com os servidores em nuvem da DJI. (Os registros de voo indicam a localização exata de um drone durante todo o voo, além de visualizações de fotos e vídeos feitos durante o voo.)
- Uma visualização da câmera ao vivo e um mapa durante os voos com drone, se um usuário da DJI estiver usando o programa de gerenciamento de vôo FlightHub da DJI.
- Informações associadas a uma conta de usuário DJI, incluindo informações de perfil de usuário.
A vulnerabilidade foi acessada através do DJI Forum, um fórum online que a DJI mantém para discussões sobre seus produtos. Um usuário que efetuou login no DJI Forum e clicou em um link malicioso especialmente plantado pode ter suas credenciais de login roubadas para permitir o acesso a outros ativos on-line da:
- Plataforma Web da DJI (conta, loja, fórum)
- Dados do servidor em nuvem sincronizados a partir de Aplicativos piloto GO ou GO 4
- DJI FlightHub da DJI (plataforma centralizada de gerenciamento de operações de drones)
Notificamos a DJI sobre essa vulnerabilidade em março de 2018 e a DJI respondeu com responsabilidade. A vulnerabilidade foi corrigida desde então. A DJI classificou essa vulnerabilidade como de alto risco, mas baixa probabilidade, e indicou que não há evidências de que essa vulnerabilidade tenha sido explorada por alguém que não seja a equipe de pesquisadores da Check Point.
Diagrama: Uma visão simplificada dos três fluxos de ataque em potencial.
Um vídeo de demonstração do ataque
(observe: capturas de tela das rotas de voo e imagens vistas neste vídeo ou no diagrama acima foram feitas com a ajuda da Airdata, que não estava envolvida nesta pesquisa e não foi afetada por esta vulnerabilidade.)
Análise técnica
Nossa pesquisa abaixo explica como conseguimos obter acesso a informações confidenciais de voo de drones da DJI, bem como a dados confidenciais de usuários, em todas as três plataformas: do site, aplicativo móvel e FlightHub.
Primeiro, porém, explicaremos onde reside a vulnerabilidade e como ela funciona.
A vulnerabilidade
Resumidamente, a vulnerabilidade reside no processo de identificação da DJI.
A DJI usa um cookie que o invasor pode obter para identificar um usuário e criar tokens ou tickets para acessar suas plataformas. Com o uso desse cookie, um invasor pode simplesmente seqüestrar a conta de qualquer usuário e assumir o controle total sobre qualquer um dos aplicativos móveis, conta da Web ou conta DJI FlightHub do usuário.
Iniciamos nossa pesquisa investigando o processo de login do site da empresa, pois queríamos primeiro entender como um usuário é identificado pelo back-end da mes,a e quais parâmetros ou cookies são importantes para o processo de login. Então, analisamos todo o tráfego passando pelo cliente (navegador) e pelo back-end da DJI.
Logo percebemos que a DJI usa alguns subdomínios para os serviços que oferece:
Além disso, o login entre esses domínios usa a estrutura OAuth. Como resultado, começamos a examinar o tráfego nesses subdomínios.
De nossa análise, descobrimos que uma solicitação para o URL mobile.php nos forneceu informações confidenciais sobre nosso usuário de teste que continham dados como nome de usuário, member_uid, token e muito mais.
Isso foi interessante, pois nos levou a investigar como o back-end da DJI identificava nosso usuário e se usava o mesmo ID de identificador. Então, examinamos os cookies usados lá e descobrimos que um cookie em particular, chamado 'meta-key', era o responsável pela identificação do usuário.
Figura 1: A solicitação mobile.php
Como nosso objetivo agora era obter esse cookie de 'meta-key' de qualquer maneira possível, tivemos que encontrar um subdomínio desprotegido por um atributo somente http, pois isso impediria que o JavaScript vazasse o cookie.
O subdomínio que atendia às nossas necessidades era forum.dji.com, então começamos a procurar vulnerabilidades nesta plataforma.
Descobrindo a vulnerabilidade
Após algumas pesquisas, encontramos a seguinte solicitação GET: https://forum.dji.com/forum.php?mod=ajax&action=downremoteimg&message=
Aqui vimos que o parâmetro da mensagem estava refletido na resposta. Mas havia dois obstáculos:
- havia a função "addslashes", que adiciona uma barra aos seguintes caracteres "'/
- A injeção de código malicioso do XSS ocorreu em uma função indefinida chamada"updateDownImageList ", que foi importada de algum outro código JavaScript que não possuímos no nosso contexto.
Assumimos que a resposta à nossa solicitação GET era algo parecido com o seguinte pseudo-código:
Então, para começar com a função escape, começamos com uma barra invertida e uma única citação como esta:
parent.updateDownImageList ('' ');
Então, "addslashes" também adicionaria uma barra invertida que escaparia da barra invertida e a alteraria para uma string como esta:
parent.updateDownImageList ('\' ');
Então agora tínhamos que lidar com os personagens restantes '); e a função não identificada, "updateDownImageList".
Para lidar com os caracteres restantes, adicionamos um comentário html simples como este <! -- que criou o seguinte código malicioso:
parent.updateDownImageList ('' <! - ');
Agora, tudo o que nos restava era lidar com a função não identificada. Para superar o fato de que essa função era indefinida, tivemos que defini-la nós mesmos.
Portanto, nosso código malicioso ficou assim:
\ 'alert (document.cookie); função updateDownImageList (data) {} <! -
Figura 3: Cookies que recebemos usando nosso código malicioso.
Um invasor poderia criar um código malicioso que enviaria esse cookie de ‘meta-key’ para o site dele. Esse tipo de XSS não seria bloqueado por nenhum Auditor XSS porque ele reside no próprio JavaScript e não consiste em scripts ou eventos.
Para acionar esse ataque XSS, tudo o que o invasor precisaria fazer é escrever uma postagem simples no fórum da DJI que contenha o link para o código malicioso. Afinal, a DJI limita os links ao conteúdo que reside no fórum e, dessa forma, é impossível enviar um link para um site malicioso.
Figura 4: Exemplo de site malicioso vinculado a uma postagem de um invasor em potencial no fórum da DJI.
Como nosso XSS reside no fórum, conseguimos ignorar a restrição de link. Além disso, como existem centenas de milhares de usuários que estão se comunicando no fórum da DJI, o invasor nem precisa compartilhar o link malicioso, pois isso seria feito pelos próprios usuários à medida que eles encaminham a mensagem e o link.
Depois que adquirimos a ‘meta-key’, passamos a examinar o processo de login e testamos como o back-end da DJI processa o login em cada plataforma da DJI, começando pelo site.
Site da DJI
Para obter acesso a qualquer conta de usuário no site da DJI, tudo o que precisávamos era sua "meta-key", chamada "mck" no subdomínio, account.dji.com.
Começamos criando uma conta DJI e fazendo login nela. Ao analisar o processo de login, descobrimos que ele usa o OAuth para autenticar o usuário entre subdomínios, por exemplo, entre accounts.dji.com para forum.dji.com e novamente para dji.com.
Portanto, toda vez que a DJI deseja autenticar um usuário, envia initData.do com um cookie "mck" e a resposta será um URL de retorno de chamada com um ticket.
Ao navegar para o URL, o usuário será autenticado sem credenciais.
Portanto, para invadir uma conta, precisamos:
- Fazer login como atacante no dji.com e clicar em DJI.COM, que nos redirecionará para o dji.com.
- Na solicitação initData.do, substituir o valor do cookie "mck" pelo "mck" da vítima (que obtemos da vulnerabilidade XSS).
- Continuar com o processo de login e acessar a conta da vítima.
Abaixo está a solicitação initData.do:
Figura 5: Solicitação initData.do
O aplicativo DJI
Para invadir uma conta em um aplicativo móvel DJI, tivemos que ignorar algumas atenuações implementadas no próprio aplicativo.
Tivemos que interceptar os pedidos do aplicativo para o back-end da DJI para investigar o processo de login. Mas aqui encontramos um mecanismo de SSL, que nos impedia de interceptar o tráfego de aplicativos e investigá-lo.
Por isso, tentamos descompilar o aplicativo para entender como ignorar o mecanismo de autenticação SSL. Infelizmente, a descompactação não teve êxito porque o aplicativo móvel da DJI usa a proteção SecNeo (uma empresa de segurança de aplicativos móveis).
De acordo com a SecNeo, as seguintes proteções são fornecidas:
- Código-fonte que transforma a prevenção e a proteção de dados confidenciais.
- Prevenção contra violação, reembalagem e depuração de aplicativos com recursos anti-gancho.
- Prevenção dinâmica de injeção de código e descompilação / desmontagem.
Como resultado, tentamos contornar essas limitações usando o Frida. De fato, tentamos nos conectar ao aplicativo dji.go.v4, mas sem sucesso.
Depois, verificamos por que não conseguimos nos conectar ao processo dji.go.v4 e começamos com o comando: 'frida-ps –U' para obter uma lista de todos os processos em execução no nosso dispositivo móvel.
Ao executar este comando, percebemos que havia apenas um processo dji.go.v4. No entanto, após alguns segundos, vimos outro processo dji.go.v4 aparecer.
Observando / proc / 11194 / status, pudemos ver que o novo processo gerado estava anexado ao primeiro processo e, na verdade, depurando-o. É por isso que não conseguimos depurar o processo com o Frida - ele já estava sendo depurado.
Figura 6: O processo recém-gerado anexado ao primeiro processo dji.go.v4, como visto em Frida.
Descobrimos que o primeiro processo iniciado não é o depurador, mas o aplicativo original. O depurador realmente se conectou ao aplicativo real e começou a protegê-lo. Isso significava que havia uma condição de corrida que poderíamos explorar e anexar nossos ganchos ao processo de aplicação e desanexá-lo antes que o processo do depurador fosse iniciado.
Para contornar isso, copiamos nosso certificado Burp Suit para o celular e geramos o aplicativo DJI. Isso iniciou o aplicativo no modo suspenso (antes da inicialização do depurador).
Em seguida, criamos um script Frida que usava a seguinte lógica:
- Abra nosso certificado Burp Suit e o converta no X509Certificate.
- Carregue um KeyStore e coloque o certificado dentro.
- Crie TrustManagerFactory e inicialize-o com o KeyStore que acabamos de criar que contém nosso certificado Burp Suit.
- Sobrecarregue o SSLContext e conecte o TrustManager ao nosso TrustManager.
Após a conclusão do gancho, retomamos o aplicativo suspenso e o desanexamos. Agora o depurador poderia iniciar sua proteção depois que terminássemos com todos os nossos ganchos.
Dessa forma, contornamos a proteção de SSL e o tráfego apareceu em nosso Burp Suit.
Figura 7: O tráfego como visto no Burp Suit após ignorar a proteção SSL.
Depois de ignorar o protocolo SSL, configuramos um proxy que nos permitiu interceptar o tráfego de aplicativos móveis.
Ao analisar o processo de login do aplicativo Web, descobrimos que, uma vez que o usuário insere suas credenciais, o aplicativo móvel envia uma solicitação para /apis/apprest/v1/email_login. A resposta recebida parece com isso:
{ “code”: 0,”message”:”ok”,”dados”: { ‘NICK_NAME’:”XXXXXXXXX”,”cookie_name”:”_ meta_key”,”cookie_key‘:’NTUxNjM2ZTXXXXXXXXXXXXXXNmI2NjhmYTQ5NGMz“ , "Ativo": falso, "e-mail": "XXXXXXXX@gmail.com", "token": "XXXXXXXXX2139", "validade": 15275XXXXX, "user_id": "9629807625XXXXXX", "register_phone": "", "area_code ”:” ”,” Inner_email ”: false,” subscrição ”: false,” vipLevel ”: null,” vipInfos ”: []}}
Os dois parâmetros importantes a serem observados aqui são:
- “cookie_key” - esta é a meta-key / mck com a qual já estamos familiarizados no fórum da DJI.
- “Token” - Este parâmetro pode ser obtido na solicitação mobile.php que descrevemos no início desta publicação de pesquisa.
O processo para sequestrar uma conta é:
- Primeiro, o invasor precisa de uma meta-key e um token para substituir os seus. Então ele envia a meta-key que deseja hackear para mobile.php e recebe um token correspondente.
- O invasor insere suas credenciais e uma solicitação de login é enviada.
- Depois que uma resposta da Etapa 2 é recebida, o atacante substitui o valor cookie_key pela meta-key da vítima e seu token pelo token da Etapa 1.
- O atacante agora tem acesso total à conta da vítima.
Ao explorar a vulnerabilidade da DJI, conforme descrito acima, o invasor pode assumir a conta da vítima e obter acesso a todos os seus voos gravados sincronizados, fotos com drones e muito mais.
Também realizamos mais pesquisas e descobrimos que, ao analisar os arquivos de registros de voo, podemos obter muito mais informações, como localização e ângulo de todas as fotos tiradas durante o voo do drone, a localização da casa, a última localização conhecida e muito mais.
Para ter acesso aos arquivos de registro de vôo, tudo o que o invasor precisa fazer é sincronizar os registros de voo com seu telefone e todos os registros de vôo que foram enviados manualmente para os servidores em nuvem da DJI serão salvos localmente em seu telefone. Ele pode simplesmente navegar até a pasta 'DJI/dji.go.v4/FlightRecord' e encontrar todos os arquivos do voo, enviá-los para o site e visualizar uma grande quantidade de informações sobre os dados de voo do usuário.
(Observe: as capturas de tela das rotas de voo e as imagens acima foram tiradas pela Airdata, que não esteve envolvida nesta pesquisa e não foi afetada por esta vulnerabilidade.)
O DJI-FlightHub
DJI-FlightHub é um aplicativo baseado na Web desenvolvido para fornecer aos usuários corporativos uma visão completa para o gerenciamento das operações de seus drones. De fato, ele permite que os usuários visualizem suas operações de drones em tempo real de qualquer lugar do mundo, incluindo uma visualização de mapa, uma visualização de câmera ao vivo com a capacidade de ouvir som, permitindo também que os usuários vejam a localização exata de cada um de seus drones, no intuito de coordenar missões.
O DJI-FlightHub possui um aplicativo de desktop para acessar o painel de controle de gerenciamento, um aplicativo piloto para pilotar o drone e, felizmente para nós, um portal da web para acessar o controle de gerenciamento. O portal da web pode ser encontrado no seguinte URL: www.dji-flighthub.com.
No DJI-FlightHub há um administrador, capitão e um piloto. Os administradores são responsáveis pela conta do DJI-FlightHub e acessam a plataforma DJI-FlightHub, visualizam os dados do voo e criam novos capitães ou pilotos. Os capitães também podem fazer login na plataforma DJI-FlightHub e criar novos pilotos. Os pilotos são os que pilotam o drone com os aplicativos de piloto e vinculam o drone à conta DJI-FlightHub.
Com isso em mente, se pudéssemos obter acesso à conta de administrador ou capitão, poderíamos visualizar as operações de um drone em tempo real. Para seqüestrar uma conta DJI de um administrador ou capitão, precisamos entender o processo de login do DJI-FlightHub.
Figura 8: A página de login do DJI-FlightHub.
Descobrimos que quando você clica em 'login', uma solicitação initData.do é enviada, mas na resposta um ticket não é recebido (semelhante ao que recebemos no portal account.dji.com). Em vez disso, recebemos apenas um ticket na resposta login.do quando o usuário digita suas credenciais. Como isso não é o mesmo que o account.dji.com comum que usamos para invadir uma conta através do portal da web, tivemos que pensar em outra maneira de invadir uma conta no DJI-FlightHub.
Embora soubéssemos que um ticket pode ser gerado pela solicitação initData.do, por algum motivo, esse não foi o caso no DJI-FlightHub. Analisamos a solicitação para entender o porquê e observar que no DJI-FlightHub a solicitação initData.do possui um appId do DJI-FlightHub, motivo pelo qual não recebemos um ticket na resposta. Com isso em mente, pensamos que talvez pudéssemos substituir o appId por algo que sabíamos que poderia funcionar para obter um ticket. Depois que um ticket é recebido, tudo o que precisamos fazer agora é verificar se o ticket de outro appid também funcionaria aqui.
As etapas necessárias a seguir foram:
- Enviar uma solicitação initdata.do de appId = store com o mck do Admin a ser invadido e adquirir um ticket na resposta.
- Ao fazer login no FlightHub, interceptar a solicitação, alterar o mck na solicitação login.do e na resposta alternar para o ticket correspondente recebido na solicitação inidata.do. O atacante seria redirecionado para a conta do administrador / vítima.
Além disso, o administrador não receberia nenhuma notificação de que um invasor acessou sua conta. Enquanto isso, o atacante teria acesso total para fazer login e visualizar a câmera do drone durante as operações, ao vivo, de qualquer voo em andamento no momento, ou baixar registros de voos gravados anteriormente que foram enviados para a plataforma FlightHub.