1 pontos por GN⁺ 2024-03-06 | 1 comentários | Compartilhar no WhatsApp
  • 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

 
GN⁺ 2024-03-06
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

    • Este texto era originalmente um post interno da Texts.com, e a parte em que eu também tinha tentado exatamente a mesma abordagem algumas semanas antes, mas desisti ao atingir o limite de tempo que eu tinha definido, foi removida quando adaptei o texto para compartilhar
      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/

    • É uma abordagem prática, e quase certamente também seria possível usar outros métodos, como hookar as funções de envio/recebimento TLS com algo como Frida. Mas, se dá para fazer o bypass da fixação de certificado, há a vantagem de o pesquisador poder passar o tráfego por ferramentas existentes como Burp Suite ou mitmproxy
      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
    • A propósito, essa abordagem não funciona em programas Rust que linkam estaticamente a 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

    • Eu tentei a mesma coisa e cheguei a conseguir interceptar as requisições depois de patchar o app, mas desisti ao tentar fazer engenharia reversa do objeto compartilhado responsável pela assinatura das requisições
      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
    • Se o usuário consegue modificar o binário, é fundamentalmente difícil impor fixação de certificado
      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
    • Sim. Daria para implementar fazendo a saída da função de flag de sandbox ser simplesmente atribuída como true dentro da função consumidora, mas, neste caso, a abordagem atual também funcionou bem :)
    • É engraçado ver tanta gente que tentou fazer isso em apps móveis e foi barrada
      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

    • Menções a +Orc ou Fravia (RIP) sempre dão uma sensação de nostalgia
      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...

    • Isso só funciona no Android. Não estávamos interessados em interceptar o app Android
  • 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

    • É macOS (desktop), não iOS (mobile)
    • De qualquer forma, ao modificar o binário você precisa assiná-lo novamente, então o efeito é o mesmo
      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ção

    • Pelo que eu lembro de quando trabalhava lá, defesa contra engenharia reversa nunca foi um objetivo
      A fixação de certificado era para dificultar a adulteração por atacantes, não para dificultar que usuários fizessem isso
    • Os apps da Meta incluem um menu de depuração inteiro até nos builds de produção. A string que o autor encontrou provavelmente fazia parte desse menu
  • 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

    • Isso não é exatamente um problema de IA; está mais para fuzzing
      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
    • Ferramentas assim já existiam nos anos 90. Não era IA, era só força bruta
  • 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

    • Falando da perspectiva de quem participou dessa decisão pela primeira vez no app do Facebook: não vale a pena
      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
    • Ofuscação tem custo, e a fixação de certificado tem mais o objetivo de dificultar ataques man-in-the-middle prejudiciais ao usuário do que impedir engenharia reversa
      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
    • Em apps móveis/frontend, fazer isso não adianta
      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
    • É bem provável que seja uma questão de prioridades e custo-benefício
      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
    • Por que será que nem todos os bancos têm segurança no estilo Fort Knox?
  • 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

    • Boa pergunta. A usada no texto é o Proxyman
      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