Um tutorial para engenharia reversa da API privada do seu software: hackear seu sofá

Publicados: 2022-03-11

Viajar é a minha paixão, e sou um grande fã do Couchsurfing. Couchsurfing é uma comunidade global de viajantes, onde você pode encontrar um lugar para ficar ou compartilhar sua própria casa com outros viajantes. Além disso, o Couchsurfing ajuda você a desfrutar de uma experiência de viagem genuína enquanto interage com os habitantes locais. Estou envolvido com a comunidade Couchsurfing há mais de 3 anos. Eu participei de encontros no começo, e então finalmente consegui hospedar pessoas. Que jornada incrível foi! Conheci tantas pessoas incríveis de todo o mundo e fiz muitos amigos. Toda essa experiência realmente mudou minha vida.

Eu mesmo já recebi muitos viajantes, muito mais do que já surfei. Enquanto morava em um dos principais destinos turísticos da Riviera Francesa, recebi uma enorme quantidade de pedidos de sofá (até 10 por dia na alta temporada). Como desenvolvedor de back-end freelance, notei imediatamente que o problema com o site couchsurfing.com é que ele não lida adequadamente com esses casos de “carga alta”. Não há informações sobre a disponibilidade do seu sofá - quando você recebe uma nova solicitação de sofá, não pode ter certeza se já está hospedando alguém naquele momento. Deve haver uma representação visual de suas solicitações aceitas e pendentes, para que você possa gerenciá-las melhor. Além disso, se você pudesse tornar sua disponibilidade de sofá pública, evitaria solicitações desnecessárias de sofá. Para entender melhor o que tenho em mente, dê uma olhada no calendário do Airbnb.

Muitas empresas são notórias por não ouvir seus usuários. Conhecendo a história do Couchsurfing, não podia contar com eles para implementar esse recurso tão cedo. Desde que o site se tornou uma empresa com fins lucrativos, a comunidade se deteriorou. Para entender melhor do que estou falando, sugiro a leitura desses dois artigos:

  • http://www.nithincoca.com/2013/03/27/the-rise-and-fall-of-couchsurfing/
  • http://mechanicalbrain.wordpress.com/2013/03/04/couchsurfing-a-sad-end-to-a-great-idea/

Eu sabia que muitos membros da comunidade ficariam felizes em ter essa funcionalidade. Então, decidi fazer um aplicativo para resolver esse problema. Acontece que não há API Couchsurfing pública disponível. Aqui está a resposta que recebi de sua equipe de suporte:

“Infelizmente, temos que informar que nossa API não é realmente pública e não há planos no momento de torná-la pública.”

Invadindo meu sofá

Era hora de usar algumas das minhas técnicas favoritas de engenharia reversa de software para invadir o Couchsurfing.com. Presumi que seus aplicativos móveis devem usar algum tipo de API para consultar o back-end. Então, eu tive que interceptar as solicitações HTTP provenientes de um aplicativo móvel para o back-end. Para isso, configurei um proxy na rede local e conectei meu iPhone a ele para interceptar solicitações HTTP. Dessa forma, consegui encontrar pontos de acesso de sua API privada e descobrir seu formato de carga útil JSON.

Finalmente, criei um site que serve para ajudar as pessoas a gerenciar seus pedidos de sofá e mostrar aos internautas um calendário de disponibilidade de sofá. Publiquei um link para ele nos fóruns da comunidade (que também são bastante segmentados na minha opinião, e é difícil encontrar informações lá). A recepção foi principalmente positiva, embora algumas pessoas não gostassem da ideia de que o site exigia credenciais do couchsurfing.com, o que era realmente uma questão de confiança.

O site funcionava assim: você entra no site com suas credenciais do couchsurfing.com e, após alguns cliques, obtém o código html que pode ser incorporado ao seu perfil do couchsurfing.com e pronto - você tem um calendário atualizado automaticamente em seu perfil. Abaixo está a captura de tela do calendário e aqui os artigos sobre como eu fiz:

  • https://github.com/nderkach/couchsurfing-python

Exemplo de calendário

Criei um ótimo recurso para o Couchsurfing e, naturalmente, presumi que eles apreciariam meu trabalho - talvez até me oferecessem uma posição em sua equipe de desenvolvimento. Enviei um e-mail para jobs(at)couchsurfing.com com um link para o site, meu currículo e uma referência. Uma nota de agradecimento deixada por um dos meus convidados do couchsurfing:

Nota de agradecimento.

Alguns dias depois, eles acompanharam meus esforços de engenharia reversa. Na resposta, ficou claro que a única coisa com que eles estavam preocupados era com sua própria segurança, então eles me pediram para retirar as postagens do blog que escrevi sobre a API e, eventualmente, o site. Retirei as postagens imediatamente, pois minha intenção não era violar os termos de uso e pescar credenciais de usuário, mas sim ajudar a comunidade de couchsurfing. Tive a impressão de que fui tratado como um criminoso, e a empresa se concentrou apenas no fato de meu site exigir credenciais de usuário.

Eu propus dar a eles meu aplicativo de graça. Eles podem hospedá-lo em seu ambiente e conectá-lo por meio da autenticação do Facebook. Afinal, é um ótimo recurso, e a comunidade precisava dele. Aqui está a resolução final que recebi:

“Estamos voltando ao ritmo das coisas aqui depois das férias e queríamos acompanhar.

Tivemos algumas discussões internas sobre seu aplicativo e como podemos honrar a criatividade e iniciativa que ele mostra sem comprometer potencialmente a privacidade e a segurança dos dados dos usuários do Couchsurfing quando eles inserem suas credenciais em um site de terceiros.

O calendário claramente preenche uma lacuna de recurso em nosso site, um recurso que faz parte de um projeto maior no qual estamos trabalhando agora.

Mas a questão de coletar nomes de usuário e senhas permanece. Não conseguimos encontrar uma maneira fácil de configurá-lo para que pudéssemos hospedá-lo ou dar suporte a isso do nosso lado sem permitir que você acesse esses dados ou que seu site seja visto como nosso produto de trabalho.

A API atualmente disponível será substituída em breve por uma versão que exigirá autenticação/autorização dos aplicativos que a acessam.”

Hoje, enquanto estou escrevendo este tutorial de software de engenharia reversa (um ano após os eventos), o recurso de calendário ainda não foi implementado no Couchsurfing.

Return To Innocence - Hacking My Couch, Again

Algumas semanas atrás, fui inspirado a escrever um artigo sobre as técnicas de engenharia reversa de APIs privadas. Naturalmente, decidi resumir os artigos anteriores que escrevi sobre esse tópico e adicionar mais alguns detalhes. Quando comecei a escrever o novo artigo, queria mostrar o processo de engenharia reversa com uma API atualizada e fazer outro esboço sobre hackers de API. Com base na minha experiência anterior e no fato de que o Couchsurfing anunciou recentemente um aplicativo móvel e web completamente novo http://blog.couchsurfing.com/the-future-of-couchsurfing-is-on-the-way/, eu decidiu hackear sua API novamente.

Por que estou fazendo esse processo de engenharia reversa? Bem, antes de tudo, é muito divertido fazer engenharia reversa de software em geral. O que eu particularmente gosto nele, é que não envolve apenas sua habilidade técnica, mas também sua intuição. Às vezes, a melhor maneira de descobrir as coisas é fazer um palpite - isso economizará muito tempo em comparação com a força bruta. Recentemente ouvi uma história de uma empresa que tinha que trabalhar com APIs proprietárias e pouca ou nenhuma documentação. Eles estavam lutando para descriptografar a carga útil de resposta da API em um formato desconhecido por dias, então alguém decidiu tentar ?decode=true no final do URL e eles tinham um JSON adequado. Às vezes, se você tiver sorte, tudo o que você precisa fazer é embelezar a resposta JSON.

Outra razão pela qual estou fazendo este tutorial é que leva anos para algumas empresas adotarem um recurso específico solicitado por seus usuários. Em vez de esperar que ela seja implementada, você pode aproveitar o poder de sua API privada e construí-la você mesmo.

Então, com a nova API do couchsurfing.com, comecei com uma abordagem semelhante e instalei o aplicativo iOS mais recente.

Primeiro, você precisa configurar um proxy em sua LAN para forjar solicitações HTTP provenientes do aplicativo para a API executando um ataque man-in-the-middle (MITM).

Para conexões não criptografadas, o ataque é bastante simples - um cliente se conecta ao proxy e você retransmite as solicitações recebidas para o servidor de destino de um lado para o outro. Você poderia modificar a carga útil, se necessário. Em uma WLAN pública, é bastante fácil fazer isso disfarçado, representando o roteador WiFi.

Para conexões criptografadas, há uma pequena diferença: todas as solicitações são criptografadas de ponta a ponta. não é possível para o invasor descriptografar a mensagem, a menos que ele de alguma forma tenha acesso à chave privada (que obviamente não é enviada durante essas interações). Dito isto, mesmo que o canal de comunicação da API seja seguro, os endpoints - especialmente o cliente - não são tão seguros.

As seguintes condições devem ser atendidas para que o SSL funcione corretamente:

  • O certificado do servidor deve ser assinado com uma autoridade de certificação (CA) confiável
  • O nome comum do servidor, no certificado, deve corresponder ao nome de domínio do servidor

Para superar a criptografia em um ataque MITM, nosso Proxy precisa atuar como uma CA (Certificate Authority) e gerar certificados em tempo real. Por exemplo, se um cliente tenta se conectar a www.google.com, o proxy cria dinamicamente um certificado para www.google.com e o assina. Agora, o cliente pensa que o proxy é de fato www.google.com

Este diagrama descreve as etapas para fazer engenharia reversa de uma API privada.

Para implementar um proxy sniffing usado para fazer engenharia reversa da API privada, usarei a ferramenta chamada mitmproxy. Você pode usar qualquer outro proxy HTTPS transparente. Charles é outro exemplo com uma boa GUI. Para fazer isso funcionar, precisamos configurar as seguintes coisas:

Configure o gateway padrão da conexão WiFi do seu telefone para ser o proxy (para que o proxy fique no meio e todos os pacotes passem) Instale o certificado do proxy no telefone (para que o cliente tenha a chave pública do proxy em seu armazenamento confiável)

Verifique a documentação do seu proxy sobre a instalação do certificado. Aqui estão as instruções para mitmproxy. E aqui está o arquivo PEM do certificado para iOS.

Para monitorar solicitações HTTP interceptadas, basta iniciar o mitmproxy e conectar-se a ele a partir do seu celular (a porta padrão é 8080).

Configurações do celular.

Abra um site em seu navegador móvel. Neste ponto, você poderá ver o tráfego no mitmproxy.

Depois de confirmar que tudo está funcionando, a engenharia de software reversa pode começar.

Depois de garantir que tudo funcione conforme o planejado, é hora de começar a explorar a API privada de sua escolha. Basicamente, neste ponto, você pode simplesmente abrir o aplicativo, brincar com ele e ter uma ideia sobre os endpoints da API e a estrutura da solicitação.

Não existe um algoritmo estrito sobre como fazer engenharia reversa de uma API de software - na maioria das vezes você confia em sua intuição e faz suposições.

Minha abordagem é replicar as chamadas de API e brincar com diferentes opções. Um bom começo é reproduzir uma solicitação que você capturou no mitmproxy e ver se funciona (pressione 'r' para reproduzir uma solicitação). O primeiro passo é descobrir quais cabeçalhos são obrigatórios. É muito conveniente brincar com cabeçalhos com mitmproxy: pressione 'e' para entrar no modo de edição, depois 'h' para modificar os cabeçalhos. Com os atalhos que eles usam, os viciados em vim se sentiriam em casa. Você também pode usar extensões de navegador como Postman para testar a API, mas elas tendem a adicionar cabeçalhos desnecessários, então sugiro usar mitmproxy ou curl.

Eu fiz um script que lê o arquivo de despejo mitmproxy e gera uma string curl - https://gist.github.com/nderkach/bdb31b04fb1e69fa5346

Vamos começar com a solicitação enviada quando você está logando.

 POST https://hapi.couchsurfing.com/api/v2/sessions ← 200 application/json 

A primeira etapa neste tutorial de engenharia reversa é replicar as chamadas de API e brincar com as opções resultantes.

A primeira coisa que notei é que cada solicitação contém um cabeçalho obrigatório X-CS-Url-Signature que é sempre diferente. Eu também tentei reproduzir uma solicitação depois de um tempo para verificar se há uma verificação de carimbo de data/hora no servidor e não há nenhuma. A próxima coisa a fazer é descobrir como essa assinatura é calculada.

Nesse ponto, decidi fazer a engenharia reversa do binário e descobrir o algoritmo. Naturalmente, tendo experiência em desenvolvimento para iPhone e tendo um iPhone à minha disposição, decidi começar com o iPhone ipa (entrega de aplicativo para iPhone). Acontece que para descriptografar um, eu preciso de um telefone desbloqueado. Pare! Hora do Martelo.

Então, lembrei que eles também têm um aplicativo para Android. Eu estava um pouco hesitante em tentar essa abordagem, pois não sei nada sobre Android ou Java. Então pensei que seria uma boa chance de aprender algo novo. Acabou sendo mais fácil obter um código-fonte quase legível por humanos descompilando o bytecode java do que o código de máquina do iphone altamente otimizado.

Apk (entrega do aplicativo Android) é basicamente um arquivo zip. Você pode usar qualquer extrator de zip para descompactar seu conteúdo. Você encontrará um arquivo chamado classes.dex, que é um bytecode Dalvik. Dalvik é uma máquina virtual usada para executar bytecode Java traduzido no Android.

Para descompilar o arquivo .dex em código fonte .java, usei a ferramenta chamada dex2jar. A saída desta ferramenta é um arquivo jar, que você pode descompilar com uma variedade de ferramentas. Você pode até abrir um jar no Eclipse ou no IntelliJ IDEA e ele fará todo o trabalho para você. A maioria dessas ferramentas produz um resultado semelhante. Nós realmente não nos importamos se podemos compilá-lo de volta para executá-lo, estamos apenas usando-o para analisar o código-fonte.

Aqui está uma lista de ferramentas que eu tentei:

  • FernFlower (agora parte do IntelliJ IDEA)
  • CFR
  • JD-GUI
  • Krakatau
  • Procyon

CFR e FernFlower funcionaram melhor para mim. O JD-GUI foi incapaz de descompilar algumas partes críticas do código e foi inútil, enquanto os outros tinham a mesma qualidade. Felizmente, parece que o código do código Java não foi ofuscado, mas existem ferramentas como o ProGuard http://developer.android.com/tools/help/proguard.html para ajudá-lo a desobstruir o código.

A descompilação de Java não é realmente o escopo deste tutorial de engenharia reversa - há muito escrito sobre este tópico, então vamos supor que você descompilou e desofuscou seu código Java com sucesso.

Combinei todo o código relevante usado para calcular X-CS-Url-Signature na seguinte essência: https://gist.github.com/nderkach/d11540e9af322f1c1c74

Em primeiro lugar, procurei menções de X-CS-Url-Signature , que encontrei em RetrofitHttpClient . Uma chamada em particular parecia interessante - para o módulo EncUtils . Cavando nele, percebi que eles estão usando HMAC SHA1. HMAC é um código de autenticação de mensagem que usa uma função criptográfica (SHA1 neste caso) para calcular um hash de uma mensagem. É usado para garantir integridade (ou seja, para evitar que um intermediário modifique a solicitação) e autenticação.

Precisamos de duas coisas para calcular o X-CS-Url-Signature : a chave privada e a mensagem codificada (provavelmente alguma variação da carga útil e URL da solicitação HTTP).

 final String a2 = EncUtils.a(EncUtils.a(a, s)); final ArrayList<Header> list = new ArrayList<Header>(request.getHeaders()); list.add(new Header("X-CS-Url-Signature", a2));

No código a é uma mensagem e s é a chave que é usada para calcular o cabeçalho a2 (a chamada dupla para EncUtils apenas calcula um resumo hexadecimal HMAC SHA1).

Encontrar a chave não foi um problema - ela foi armazenada em texto simples em ApiModule e foi usada para inicializar o segundo parâmetro de RetrofitHttpClient.

 RetrofitHttpClient a(OkHttpClient okHttpClient) { return new RetrofitHttpClient(okHttpClient, "v3#!R3v44y3ZsJykkb$E@CG#XreXeGCh"); }

Se observarmos a chamada para EncUtils , podemos ver que a string literal acima é usada literalmente como uma chave para calcular o HMAC, exceto no caso em que this.b é definido. No último caso, this.b está sendo anexado com um ponto a ele.

 String s; if (this.b == null) { s = this.a; } else { s = this.a + "." + this.b; }

Agora, apenas olhando para o código, não ficou claro para mim onde e como this.b é inicializado (a única coisa que consegui descobrir é que ele é chamado em um método com uma assinatura this.a(String b) , mas não consegui encontrar uma chamada para ele em nenhum lugar do código).

 public void a(final String b) { this.b = b; }

Eu encorajo você a descompilá-lo e descobrir você mesmo :)

Descobrir que a mensagem foi bem direta - no código, você pode ver que é uma concatenação do caminho do URL, ou seja, /api/v2/sessions e uma string com carga JSON (se houver).

 final byte[] b = this.b(request.getUrl()); byte[] a; if (request.getBody() != null && request.getBody() instanceof JsonTypedOutput) { System.out.println("body"); // this.a(x, y) concatenates byte arrays a = this.a(b, ((JsonTypedOutput)request.getBody()).a); } else { a = b; }

Apenas olhando para o código, era difícil descobrir o algoritmo exato para o cálculo do HMAC. Então, decidi reconstruir o aplicativo com símbolos de depuração para descobrir exatamente como o aplicativo funciona. Eu usei uma ferramenta chamada apktool https://code.google.com/p/android-apktool/ para desmontar o bytecode Dalvik usando smali https://code.google.com/p/smali/. Eu segui o guia em https://code.google.com/p/android-apktool/wiki/SmaliDebugging

Depois de criar o apk, você precisa assiná-lo e instalá-lo no seu dispositivo. Como eu não tinha um dispositivo Android, usei o emulador que vem com o Android SDK. Com um pouco de alimentação com colher, aqui está como você faz isso:

 jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android <path_to_your_built_apk> androiddebugkey jarsigner -verify -verbose -certs <path_to_your_built_apk> zipalign -v 4 <path_to_your_built_apk> <path_to_your_output_signed_apk>

Eu usei um emulador Android embutido que vem com o SDK e uma imagem virtual Atom x86 com HAXM habilitado para garantir que funcione sem problemas.

 tools/emulator -avd mydroid -no-boot-anim -cpu-delay 0

Aqui está um bom guia sobre como configurar uma imagem virtual: http://jolicode.com/blog/speed-up-your-android-emulator

Certifique-se de ver a linha HAX está funcionando e o emulador é executado no modo virt rápido na inicialização do emulador para garantir que o HAXM esteja ativado.

Então, instalei o apk no emulador e executei o aplicativo. Seguindo o guia apktool, aproveitei o depurador remoto IntelliJ IDEA para conectar ao emulador e definir alguns pontos de interrupção de linha:

Algumas técnicas de engenharia reversa envolvem executar o aplicativo e apenas ver o que acontece.

Brincando um pouco com o aplicativo, consegui descobrir que a chave privada usada para inicializar RetrofitHttpClient é usada para calcular o HMAC de uma assinatura de solicitação de login. Na resposta ao POST de login você recebe um ID de usuário e accessToken ( X-Access-Token ). O token de acesso é usado para autorizar todas as solicitações a seguir. O HMAC para todas as solicitações de pós-login é construído da mesma forma que a solicitação de login, exceto que a chave é composta anexando .<user_id> à chave privada original.

Isso mostra o processo de autorização necessário para fazer engenharia reversa dessa API privada.

Depois de autorizado, o aplicativo envia a seguinte solicitação:

 POST https://hapi.couchsurfing.com/api/v2/users/1003669205/registerDevice ← 200 application/json

Como pude deduzir empiricamente, essa solicitação é opcional para autenticação. Pontos de bônus se você descobrir para que é usado!

Uma vez autenticado, você pode enviar uma solicitação para buscar seu perfil de usuário (ou de qualquer outra pessoa), assim:

 GET https://hapi.couchsurfing.com/api/v2/users/1003669205 ← 200 application/json 

Neste processo de engenharia reversa, você pode buscar o perfil de usuário de qualquer pessoa.

Não entrei muito em detalhes, mas notei que um perfil é atualizado com uma solicitação PUT. Apenas por diversão, tentei atualizar outro perfil com a mesma solicitação - não foi autorizado, então aparentemente os fundamentos de segurança estão implementados.

Eu escrevi um script Python simples para fazer login usando suas credenciais do couchsurfing.com e obter seu perfil de usuário: https://gist.github.com/nderkach/899281d7e6dd0d497533. Aqui está o wrapper Python para a API: https://github.com/nderkach/couchsurfing-python com um pacote disponível no repositório pypi (pip install couchsurfing).

Próximos passos

Não tenho certeza do que exatamente vou fazer com a API desta vez. O código HTML em perfis de usuário não é mais permitido, então terei que apresentar uma abordagem diferente para o antigo problema. Continuarei desenvolvendo e aprimorando o wrapper da API python, se houver demanda, e assumindo que o couchsurfing.com não causará muitos problemas. Não explorei muito a API e apenas testei algumas vulnerabilidades básicas. Parece seguro o suficiente, mas seria interessante descobrir se você pode acessar os dados que não estão disponíveis no site. De qualquer forma, agora você pode usar minha engenharia de software reversa para criar um cliente alternativo para Windows Phone, Pebble ou seu sofá inteligente.

Conclusão com uma pergunta

Há uma discussão que gostaria de abrir - por que não publicar sua API e torná-la pública? Mesmo se eu não conseguisse hackear a API, ainda seria possível raspar o site. Seria mais lento e mais difícil de manter, mas certamente eles prefeririam que os consumidores usassem uma API em vez de um web scraper. A disponibilidade das APIs permitiria que desenvolvedores terceirizados melhorassem o produto da empresa e construíssem um serviço de valor agregado em torno dele. Pode-se argumentar que seria mais caro manter a API pública do que a privada; mas, novamente, as vantagens de seus serviços de construção de comunidade em cima de seu produto superariam os custos de manutenção da API.

É possível impedir completamente o uso de uma API privada por clientes de terceiros? Acho que não. Usar a fixação de SSL impediria a detecção de solicitações de API usando uma técnica de proxy transparente simples, conforme descrito anteriormente. No final, mesmo se você ofuscar o binário, um hacker motivado com alguns recursos e tempo sempre será capaz de fazer engenharia reversa do binário do aplicativo e obter a chave privada/certificado. Acho que a suposição de que o endpoint do cliente é seguro é inerentemente errada. Um cliente de API é um ponto fraco.

Ao manter uma API privada, uma empresa está basicamente transmitindo uma mensagem de desconfiança para seus usuários. Certamente, você pode tentar proteger ainda mais sua API privada. No entanto, você não preferiria implementar uma segurança básica para a API para evitar o uso mal-intencionado; e, em vez disso, concentrar seus recursos na melhoria do software para fornecer uma melhor experiência ao usuário?

Couchsurfing, por favor, com açúcar em cima, abra a API.