Desvio de pinagem SSL no aplicativo iOS



Olá, meu nome é Andrey Batutin, sou desenvolvedor iOS sênior da DataArt. Em um artigo anterior, falamos sobre como você pode detectar o tráfego de nosso aplicativo móvel usando um proxy HTTPS. Neste artigo, discutiremos como ignorar a pinagem SSL. Por precaução, recomendo a leitura do primeiro artigo, se você ainda não o leu: será necessário para entender o texto abaixo.

Na prática, na prática, a pinagem SSL é usada para que o método descrito para inspecionar e modificar o tráfego de um aplicativo móvel não seja acessível a bandidos ou a um chef curioso.

O que é Fixação SSL


No artigo anterior, instalamos o Charles Root Certificate em nosso dispositivo móvel, o que permitiu que o Charles Proxy recebesse, descriptografasse, mostrasse tráfego, criptografasse de volta e enviasse para o Dropbox.

Se eu, como desenvolvedor de um aplicativo móvel, desejar que meu tráfego seja inspecionado apenas pelo meu servidor e por mais ninguém, mesmo que esse outro tenha instalado seu certificado SSL no dispositivo, posso usar a pinagem SSL.

Sua essência se resume ao fato de que, durante o handshake SSL, o cliente verifica o certificado recebido do servidor.

Este artigo discute o método mais fácil de pinagem SSL a ser implementado usando uma lista autorizada de certificados conectados ao aplicativo (lista de permissões).

Mais informações sobre os tipos de pinagem SSL podem ser encontradas aqui .

Implementação de fixação de SSL no FoodSniffer


O código completo do projeto está aqui . Primeiro, precisamos obter dois certificados no formato DER para dois hosts:


O segundo servidor armazena o próprio JSON com uma lista de nossas compras.

Para obter os certificados no formato correto, usei o Mozila Firefox.

Abra o dropbox.com no navegador.

Clique no símbolo de cadeado na barra de endereço.





Clique em Mais informações, selecione Segurança -> Exibir certificado.



Em seguida, selecione Detalhes e encontre o certificado final na Hierarquia de Certificados.



Clique em Exportar e salve no formato DER.



Repita o mesmo procedimento para uc9b17f7c7fce374f5e5efd0a422.dl.dropboxusercontent.com.

Nota
O servidor de conteúdo do Dropbox (* .dl.dropboxusercontent.com) usa um certificado curinga. Isso significa que o certificado que você extraiu para o servidor uc9b17f7c7fce374f5e5efd0a422 será adequado para qualquer outro servidor Dropbox * .dl.dropboxusercontent.com.

Como resultado, obtive dois arquivos com certificados:

dropboxcom.crt ,
dldropboxusercontentcom.crt ,

que adicionei ao projeto do aplicativo FoodSniffer iOS.



Em seguida, adicionei a extensão para a classe FoodListAPIConsumer, na qual verifico o certificado recebido do servidor. Para fazer isso, procuro na lista de certificados permitidos, processando o delegado do Desafio de autenticação do protocolo NSURLSessionDelegate:

extension FoodListAPIConsumer { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let trust = challenge.protectionSpace.serverTrust else { completionHandler(.cancelAuthenticationChallenge, nil) return } let credential = URLCredential(trust: trust) if (validateTrustCertificateList(trust)) { completionHandler(.useCredential, credential) } else { completionHandler(.cancelAuthenticationChallenge, nil) } } func validateTrustCertificateList(_ trust:SecTrust) -> Bool{ for index in 0..<SecTrustGetCertificateCount(trust) { if let certificate = SecTrustGetCertificateAtIndex(trust, index){ let serverCertificateData = SecCertificateCopyData(certificate) as Data if ( certificates.contains(serverCertificateData) ){ return true } } } return false } } 

Na matriz de certificados , tenho representações de dados dos meus certificados permitidos.

Agora, quando o Charles Proxy estiver em execução, o aplicativo será desconectado, porque o certificado Charles não está incluído na lista de permitidos. O usuário verá o seguinte erro:



Hackers derrotados!

Mas agora há um pequeno problema - como posso, o desenvolvedor, monitorar o tráfego HTTPS do meu próprio aplicativo?

Frida


Uma opção é desativar a pinagem SSL com a estrutura de injeção dinâmica de código da estrutura Frida .

A ideia é que o método validateTrustCertificateList sempre retorne verdadeiro durante o desenvolvimento do aplicativo.

Obviamente, isso pode ser alcançado sem a injeção dinâmica de código, por exemplo, usando a condição #if targetEnvironment (simulator) para desativar a pinagem SSL no simulador, mas é muito simples.

Usando Frida, podemos escrever um script JavaScript (habilmente, certo?) No qual substituímos a implementação de validateTrustCertificateList por uma que sempre retorna true.
E esse script será injetado no aplicativo no estágio de execução.

Como Frida funciona no iOS, você pode ler aqui .

Instalação Frida (tirada daqui ).



instalação do sudo pip frida-tools

Script Frida


O script direto para substituir a função validateTrustCertificateList é semelhante a este:

 // Are we debugging it? DEBUG = true; function main() { // 1 var ValidateTrustCertificateList_prt = Module.findExportByName(null, "_T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF"); if (ValidateTrustCertificateList_prt == null) { console.log("[!] FoodSniffer!validateTrustCertificateList(...) not found!"); return; } // 2 var ValidateTrustCertificateList = new NativeFunction(ValidateTrustCertificateList_prt, "int", ["pointer"]); // 3 Interceptor.replace(ValidateTrustCertificateList_prt, new NativeCallback(function(trust) { if (DEBUG) console.log("[*] ValidateTrustCertificateList(...) hit!"); return 1; }, "int", ["pointer"])); console.log("[*] ValidateTrustCertificateList(...) hooked. SSL pinnig is disabled."); } // Run the script main(); 

  1. Encontramos o ponteiro para validateTrustCertificateList no binário do aplicativo pelo nome completo da função.
  2. Envolvemos o ponteiro em um wrapper NativeFunction, indicando o tipo do parâmetro e o valor de saída da função.
  3. Substitua a implementação da função validateTrustCertificateList por uma que sempre retorne 1 (ou seja, true).

O script inteiro está em {source_root} /fridascrpts/killCertPinnig.js .

Um dos problemas é como o nome completo da função _T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF foi obtido

Para isso, usei a seguinte técnica.

  • Criou um destino adicional FoodSnifferFrida no aplicativo.
  • Conectei a biblioteca FridaGadget.dylib a ela, que peguei aqui. O procedimento de conexão da biblioteca é descrito em mais detalhes aqui.
  • Lançou o aplicativo FoodSniffer no simulador.
  • Utilizou este comando para localizar o nome completo da função validateTrustCertificateList :
    frida-trace -R -f re.frida.Gadget -i "* validateTrust *"
  • Entendi na forma:

E então o usei em killCertPinnig.js .

Por que um nome tão "estranho" chegou ao fim no final e o que significam todos esses T016 e 0A15 pode ser visto aqui .

Fixação de Killing SSL


Agora, finalmente, inicie o FoodSniffer com o SSL Pinnig desativado!

Inicie o Charles Proxy.

Execute o FoodSnifferFrida de destino no projeto Xcode no simulador. Deveríamos ver apenas uma tela branca. O aplicativo aguarda que Frida se conecte a ele.


Execute o Frida para executar o script killCertPinnig.js :
frida -R -f re.frida.Gadget -l ./fridascrpts/killCertPinnig.js

Vamos aguardar a conexão com o aplicativo iOS:



Continue o aplicativo usando o comando% resume:



Agora devemos ver a lista de alimentos no aplicativo:



E JSON no Charles Proxy:



Lucro!

Conclusão


Frida é como o Wireshark para binários. Funciona em plataformas iOS, Android, Linux, Windows. Essa estrutura permite rastrear chamadas para métodos e funções - sistema e usuário. E também substitua os valores dos parâmetros, retorne valores e implementação de funções.

Ignorar a fixação de SSL em um ambiente de desenvolvimento usando Frida pode parecer um pouco exagerado . Isso me atrai porque não preciso ter lógica específica no aplicativo para depuração e desenvolvimento de aplicativos. Essa lógica confunde o código e, se implementada incorretamente, pode vazar para a versão de lançamento do assembly (macros, olá para você!).

Além disso, Frida é aplicável ao Android. O que me dá a oportunidade de facilitar a vida de toda a minha equipe e garantir um processo tranquilo de desenvolvimento de toda a linha de produtos.
Frida se posiciona como uma ferramenta de injeção de código de processo de caixa preta. Com ele, é possível, sem alterar o código direto do aplicativo iOS, adicionar o registro de chamadas de método ao tempo de execução, o que pode ser indispensável ao depurar bugs complexos e raros.

Source: https://habr.com/ru/post/pt424485/


All Articles