- A equipe do Texts.com quis analisar o Meta Messenger para macOS, um app desktop independente semelhante ao seu, mas a análise MITM baseada em proxy foi bloqueada por causa do certificate pinning
- O certificate pinning da Meta faz com que o app confie apenas em certificados permitidos por ele, bloqueando a abordagem de interceptar e descriptografar requisições com uma autoridade certificadora criada pelo usuário
- A instrumentação dinâmica com Frida gerava crashes no Messenger e aumentava a complexidade de distribuição, então a equipe escolheu um patch binário menor e mais reproduzível
- A análise no Hopper mostrou que, ao alterar 4 bytes para fazer
IsUsingSandbox() retornar true, era possível seguir o caminho de código que desativa a verificação SSL ao usar um custom sandbox
- Depois de substituir o executável original pelo binário modificado e ajustar a assinatura, foi possível ver headers, corpo da resposta e informações da requisição na ferramenta de proxy
Por que o Texts.com analisou o Messenger
- Batuhan İçöz, responsável pelo projeto da plataforma Meta no Texts.com, concluiu que o app Messenger para macOS valia a análise por ser um app desktop independente próximo ao modelo da empresa
- Interceptar requisições de rede tem baixa barreira de entrada e é útil como primeiro passo para entender o comportamento de um app
- Mas a Meta aplicou certificate pinning no app para reforçar seu modelo de segurança e impedir até análises MITM feitas pelo próprio usuário contra si mesmo
O que o certificate pinning impede
- Para interceptar requisições com um cliente de proxy, é preciso configurar e confiar em uma autoridade certificadora criada pelo usuário
- Com certificados emitidos por essa autoridade certificadora, é possível interceptar e descriptografar informações das requisições
- Quando um serviço implementa certificate pinning, o app aceita apenas certificados emitidos por autoridades certificadoras específicas
- Nesse caso, certificados criados pelo usuário não são válidos, então as requisições não podem ser interceptadas
Estado antes do patch e objetivo
- Sem desativar o certificate pinning, todas as requisições retornavam “Internal Error”
- O software de proxy mostrava “SSL Handshake Failed” e o ciclo de vida da requisição não avançava até o fim
- Nesse estado, era difícil inferir o conteúdo das requisições
- O objetivo era permitir a leitura direta de requisições, respostas e headers em uma ferramenta de depuração de rede
Alternativas que falharam e a escolha final
- Um método que funcionou no passado era trocar strings de URL dentro do binário por um endpoint hospedado pela própria equipe que não implementava TLS
- Esse endpoint encaminhava requisições e respostas entre cliente e servidor
- Era mais adequado para apps menores do que para apps grandes como o Messenger
- Bibliotecas de instrumentação dinâmica como Frida também eram candidatas, mas apresentavam baixa estabilidade no Messenger
- Havia crashes frequentes ao instalar hooks
- Por causa do overhead, era difícil localizar o ponto problemático
- O ambiente e a configuração de ferramentas necessários para executar tornavam a distribuição entre colegas mais complicada
- A equipe também tentou um script de Frida mantido havia anos
- Esse script era usado com bibliotecas comuns de certificate pinning e métodos de bypass, funcionando na maioria dos apps
- A família de apps da Meta não fazia parte dessa “maioria”
- No fim, a equipe escolheu um patch binário que desativasse completamente o certificate pinning e pudesse ser facilmente repassado aos colegas
O ponto do patch encontrado com o Hopper
- Depois de baixar o Messenger e movê-lo para a pasta de aplicativos, a equipe abriu no Hopper o binário ARM compilado em
/Applications/Messenger.app/Content/MacOS/Messenger
- O Hopper permite desassemblar, decompilar, recompilar, depurar e visualizar binários compilados
- Depois de carregar o binário e as referências, buscaram termos como
certificate, ssl e pinning
- A string
"SSL pinning verification failed for host:" serviu como ponto de partida da análise
- Como binários compilados podem crashar se forem alterados em excesso, a estratégia foi aplicar a menor mudança possível
- A alteração ideal seria algo como inverter um valor booleano, reverter uma condição ou modificar poucas instruções, com impacto reduzido
Fazendo IsUsingSandbox() sempre retornar true
- O fluxo de execução foi visualizado com um grafo de fluxo de controle, e as referências conectadas foram rastreadas até cima
- A string
"Using custom sandbox -> turn off SSL verification" foi encontrada
- As referências à função que definia esse flag foram buscadas no arquivo, e a referência correspondente foi confirmada no topo do procedimento
- Em seguida, foi rastreado o ponto em que o valor de retorno era atribuído dentro da função
IsUsingSandbox()
- O registrador
w0 era movido a partir de w19 e então retornado
w19 originalmente era atribuído por uma instrução de load byte
- Se
w19 fosse definido sempre como true em vez de ser carregado, IsUsingSandbox() passaria a retornar true
- Pelo texto da string encontrada antes, ao usar um custom sandbox a verificação SSL é desativada, então essa mudança desabilita o certificate pinning
Original
ARM: ldrb w19, [sp, #0x40 + var_20]
HEX: F3 83 40 39
Rewritten
ARM: mov w19, #1
HEX: 33 00 80 52
- Essa substituição foi feita diretamente no código de bytes do aplicativo usando o modo hexadecimal
Resultado da execução e nova assinatura
- Um novo executável foi exportado com a opção “Produce New Executable” do Hopper
- Depois de remover a assinatura do executável, o binário original do Messenger foi substituído pelo novo binário
- Ao executar o Messenger novamente, a ferramenta de proxy passou a mostrar headers, corpo da resposta e outras informações da requisição
- Em um binário total de 97.477.728 bytes, bastou uma modificação de 4 bytes para viabilizar a interceptação das requisições
- Para ver uma abordagem semelhante no iOS, vale consultar o artigo de 2020 de Hassan Mostafa sobre bypass do certificate pinning do Instagram
- O texto mostra um caso em que o certificate pinning do Instagram foi desativado em um iPhone com jailbreak invertendo uma instrução de desvio condicional
- O binário compilado foi entregue a Batuhan
- Batuhan obteve e instalou um certificado de assinatura, depois assinou o aplicativo
- Em seguida, conseguiu usar esse binário no próprio sistema para ver as próprias requisições
codesign --force --deep -s CERTNAME_OR_ID /Applications/Messenger.app
1 comentários
Comentários do Hacker News
Eu estava seguindo um caminho parecido, mas desisti no momento em que teria de chegar a descompilar/modificar/recompilar
Nesse nível já é pura determinação, e fico curioso para saber quantas horas isso levou de fato. Eu defini um critério de parada e o segui à risca
No começo, passei 2 horas tentando mudar vários comandos e desisti. Depois vi um post antigo do engenheiro reverso “Hassan Mostafa” (cyclon3), em que ele tinha tido sucesso com a mesma abordagem — aplicando o Hopper Disassembler ao Instagram no iOS — e tentei de novo naquela noite, mas falhei. Também procurei e tentei alterar o mesmo comando
Então resolvi parar; algumas semanas depois, ainda um pouco incomodado, tentei de novo por impulso e, depois de encontrar a função de sandbox, terminei em cerca de 30 minutos
Parece que, usando eBPF, dá para ler os dados antes da criptografia TLS: Debugging with eBPF Part 3: Tracing SSL/TLS connections https://blog.px.dev/ebpf-openssl-tracing/
Roteando o tráfego real do app por um proxy de interceptação, o tempo necessário pode cair bastante, dependendo do objetivo. Por exemplo, se você quiser alterar automaticamente um único parâmetro de uma requisição que só ocorre depois da autenticação/configuração de sessão, é muito mais rápido deixar o app fazer todo o fluxo e mudar apenas um ponto no proxy do que escrever um novo cliente que realize todo o processo inicial ou implementar a lógica de modificação em um filtro eBPF
rustls, a biblioteca TLS em Rust mais usadaÉ uma abordagem muito inteligente. Ainda assim, acho que teria sido possível impor a fixação de certificado mesmo em modo sandbox
Na faculdade, tentei inspecionar o Snapchat via ataque man-in-the-middle, mas eles também usavam fixação de certificado, e lembro que acabei não conseguindo quebrar
Nem sequer consegui encontrar o ponto de entrada. Para um app de mídia social relativamente pequeno, a segurança já era absurdamente forte em 2015
Mesmo que usassem fixação de certificado no modo sandbox, é bem provável que houvesse outro jeito de remover a verificação do certificado fixado
Hoje não mais
Este texto me lembrou a época do +Orc. Parece que muito conhecimento que era comum na época, como encontrar desvios indesejados e substituí-los por NOP, desapareceu
Hoje há muito mais tecnologias para aprender, então faz sentido
[1]: https://en.m.wikipedia.org/wiki/Old_Red_Cracker
Ainda assim, acho que ainda há muita gente fazendo patch com NOP. A complexidade é que aumentou. Pessoas que quebram DRM ou investigam apps móveis aleatórios com editor hexadecimal e afins continuam existindo
Programas modernos são mais complexos, o que torna mais difícil começar, mas ao mesmo tempo o conhecimento necessário ficou mais acessível
Se você quer interceptar o tráfego dos apps da Meta, não precisa fazer tudo isso
https://www.facebook.com/whitehat/bugbounty-education/261571...
Fico pensando se checksums de binário em tempo de execução teriam ajudado a dificultar esse tipo de modificação
Isso não é prática padrão em apps móveis? Os SDKs de iOS ou Android oferecem algo assim? Imagino que estaria ligado ao processo oficial de release e seria imposto nas respectivas plataformas não desbloqueadas
É uma pergunta básica, mas, como a solução final foi modificar alguns bytes do binário, parecia algo bloqueável
Em plataformas não desbloqueadas, isso normalmente é feito com um certificado de desenvolvedor
As defesas contra engenharia reversa da Meta, pelo menos no Messenger, parecem bem frouxas
Mesmo sem partir para ofuscação avançada, teria sido fácil remover totalmente
IsUsingSandbox()do build de produçãoA fixação de certificado era para dificultar a adulteração por atacantes, não para dificultar que usuários fizessem isso
Quando crackeei meu primeiro app, eu achava que obviamente ia falhar, mas descobri que encontrar pontos JNE/JEZ fáceis de modificar desse jeito era mais simples do que eu imaginava
Mesmo escolhendo errado, é só voltar ao arquivo original e tentar outro ponto
Parece algo que IA poderia automatizar facilmente. Bastaria inverter JEZ/JNZ em vários pontos candidatos, executar o app e ver se aparece a tela de reclamação
Se a condição de falha estiver bem definida, no fim das contas é só reduzir os candidatos
Se uma IA conseguir quebrar algo como Denuvo em zero-shot, aí já é outra história
Fico me perguntando por que o aplicativo de uma empresa tão grande não é totalmente ofuscado e também não inclui proteções suficientes para impedir a execução de binários modificados
Um indivíduo/grupo/governo suficientemente habilidoso ou motivado acaba quebrando. Distribuir um binário de cliente tem essa natureza
Dá para gastar uma quantidade enorme de tempo e dinheiro tentando impedir isso. Antigamente, o Pinterest tentou distribuir uma linguagem própria e uma máquina virtual, e eu fui contra. Ou então basta aceitar que o código do cliente, por padrão, já está comprometido, colocar a lógica no servidor e seguir em frente
Fixação de certificado é praticamente de graça e funciona como um dispositivo do tipo “só embarca se tiver pelo menos esta chave”. Não é seguro, mas filtra várias tentativas aleatórias
Claro que também afeta a engenharia reversa, mas isso é mais um bônus
No fim, o código roda no dispositivo do usuário, e o usuário pode observar o que o código faz, então sempre é possível desofuscar. Se uma pessoa resolver e compartilhar o resultado, copiar fica muito fácil. Não quer dizer que ofuscação seja inútil, mas não é algo em que valha gastar tempo demais
Se o atacante tem acesso físico ao dispositivo, a partir desse ponto não há como impedir. O que dá para fazer é só tornar o processo mais trabalhoso, na esperança de que ele fique irritado e desista
A ofuscação quase não teria influenciado o resultado desse experimento, e a abordagem talvez tivesse mudado para usar um pouco mais de instrumentação dinâmica. A ofuscação mais eficaz que já vi foi ofuscação por VM, mas o impacto em desempenho é considerável. Ofuscação também dificulta a depuração normal
A proteção contra binários modificados é feita no nível do sistema, também pode ser implementada no nível do aplicativo e é comum. Mas essa própria funcionalidade também pode ser contornada, e também é possível modificar depois que a verificação de segurança termina usando bibliotecas de instrumentação dinâmica como Frida
Do ponto de vista da Meta, entrar num jogo de gato e rato com engenheiros reversos não parece ser a melhor opção
Fiquei curioso para saber qual foi a ferramenta de proxy usada no texto. Quando ela está em execução, todo o tráfego dos aplicativos é roteado por ela?
Desculpa se for uma pergunta idiota
No macOS, ele roteia todo o tráfego dos aplicativos por ela; e, instalando um certificado autoassinado no dispositivo e conectando-o ao proxy, também dá para fazer proxy de dispositivos iOS