
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.
NotaO 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-toolsScript 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();
- Encontramos o ponteiro para validateTrustCertificateList no binário do aplicativo pelo nome completo da função.
- Envolvemos o ponteiro em um wrapper NativeFunction, indicando o tipo do parâmetro e o valor de saída da função.
- 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.jsVamos 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.