BLOGUE

Interceptando e modificando respostas com o Chrome via Devtools Protocol

Miniatura F5
F5
Publicado em 17 de setembro de 2018


Na Shape, nos deparamos com muitos trechos incompletos de JavaScript. Encontramos scripts que são injetados maliciosamente nas páginas, eles podem ser enviados por um cliente para aconselhamento, ou nossas equipes de segurança podem encontrar um recurso na web que parece fazer referência específica a alguns aspectos do nosso serviço. Como parte da nossa rotina diária, mergulhamos de cabeça nos scripts para entender o que eles estão fazendo e como funcionam. Eles geralmente são minimizados, muitas vezes ofuscados e sempre exigem vários níveis de modificação antes de estarem realmente prontos para uma análise profunda.

Até recentemente, a maneira mais fácil de fazer essa análise era com configurações armazenadas em cache localmente, que permitiam a edição manual, ou usando proxies para reescrever o conteúdo dinamicamente. A solução local é a mais conveniente, mas os sites nem sempre funcionam perfeitamente em outros ambientes e isso muitas vezes leva as pessoas a uma jornada de solução de problemas apenas para se tornarem produtivas. Os proxies são extremamente flexíveis, mas geralmente são pesados e pouco portáteis – cada um tem sua própria configuração personalizada para seu ambiente e algumas pessoas estão mais familiarizadas com um proxy do que com outro. Comecei a usar o Chrome e seu protocolo devtools para acessar solicitações e respostas conforme elas acontecem e modificá-las rapidamente. Isso é portátil para qualquer plataforma que tenha o Chrome, evita uma série de problemas e se integra bem com ferramentas comuns de JavaScript. Neste post, mostrarei como interceptar e modificar JavaScript rapidamente usando o protocolo devtools do Chrome .

Usaremos o node, mas muito do conteúdo é portátil para a linguagem de sua escolha, desde que você tenha os hooks do devtools facilmente acessíveis.

Primeiramente, se você nunca explorou a criação de scripts no Chrome, Eric Bidelman escreveu um excelente Guia de Introdução ao Headless Chrome . As dicas aqui se aplicam tanto ao Chrome Headless quanto ao Chrome com interface gráfica (com uma peculiaridade que abordarei na próxima seção).

Iniciando o Chrome

Usaremos a biblioteca chrome-launcher do npm para facilitar isso.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

O chrome-launcher faz exatamente o que você acha que ele faria e você pode passar as mesmas opções de linha de comando que está acostumado a usar no terminal, sem alterações (uma ótima lista é mantida aqui ). Passaremos pelas seguintes opções:

–tamanho-da-janela=1200,800

  • Defina automaticamente o tamanho da janela para algo razoável.

–auto-open-devtools-for-tabs

  • Abra automaticamente as ferramentas de desenvolvimento porque as usamos com frequência.

–user-data-dir=/tmp/chrome-testing

  • Defina um diretório de dados de usuário constante. (Idealmente, não precisaríamos disso, mas o modo não headless no Mac OSX parece não permitir que você intercepte solicitações sem esse sinalizador. Se você conhece uma maneira melhor, por favor me avise via Twitter !)
Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Tente executar seu script para ter certeza de que você consegue abrir o Chrome. Você deverá ver algo assim:

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Usando o protocolo Chrome Devtools

Isso também é conhecido como “protocolo depurador do Chrome”, e ambos os termos parecem ser usados de forma intercambiável nos documentos do Google. Primeiro, instale o pacote chrome-remote-interface via npm, que nos fornece métodos convenientes para interagir com o protocolo devtools. Certifique-se de ter os documentos do protocolo em mãos se quiser se aprofundar mais.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Para usar o CDP, você precisa se conectar à porta do depurador e, como estamos usando a biblioteca chrome-launcher , isso é convenientemente acessível via chrome.port .

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Muitos dos domínios no protocolo precisam ser habilitados primeiro, e vamos começar com o domínio Runtime para que possamos nos conectar à API do console e entregar quaisquer chamadas de console no navegador para a linha de comando.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Agora, ao executar seu script, você obtém uma janela do Chrome totalmente funcional que também exibe todas as mensagens do console no seu terminal. Isso é incrível por si só, especialmente para fins de teste!

Interceptando solicitações

Primeiro, precisamos registrar o que queremos interceptar enviando uma lista de RequestPatterns para setRequestInterception . Você pode interceptar no estágio “Request” ou no estágio “HeadersReceived” e, para realmente modificar uma resposta, precisaremos esperar por “HeadersReceived”. O tipo de recurso mapeia os tipos que você normalmente veria no painel de rede do devtools.

Não se esqueça de habilitar o domínio de rede como você fez com o Runtime acima, adicionando Network.enable() ao mesmo array.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Registrar o manipulador de eventos é relativamente simples e cada solicitação interceptada vem com um ​interceptionId que pode ser usado para consultar informações sobre a solicitação ou, eventualmente, emitir uma continuação. Aqui estamos apenas intervindo e registrando cada solicitação que interceptamos no terminal.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Modificando solicitações

Para modificar solicitações, precisaremos instalar algumas bibliotecas auxiliares que codificarão e decodificarão strings base64. Há muitas bibliotecas disponíveis; fique à vontade para escolher a sua. Usaremos atob e btoa .

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

A API para lidar com respostas é um pouco estranha. Para manipular respostas, você precisa incluir toda a sua lógica de resposta na interceptação da solicitação (em vez de simplesmente interceptar uma resposta, por exemplo) e então você tem que consultar o corpo pelo ID de interceptação. Isso ocorre porque o corpo pode não estar disponível no momento em que seu manipulador é chamado e isso permite que você espere explicitamente por exatamente o que está procurando. O corpo também pode vir codificado em base64, então você vai querer verificar e decodificá-lo antes de passá-lo adiante cegamente.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Neste ponto, você está livre para se aventurar no JavaScript. Seu código coloca você no meio de uma resposta, permitindo que você acesse o JavaScript completo que foi solicitado e envie de volta sua resposta modificada. Incrível! Vamos apenas ajustar o JS acrescentando um console.log no final dele para que nosso terminal receba uma mensagem quando nosso código modificado for executado no navegador.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Não podemos simplesmente repassar um corpo modificado porque o conteúdo pode entrar em conflito com os cabeçalhos que foram enviados com o recurso original. Como você está testando e ajustando ativamente, provavelmente desejará começar com o básico antes de se preocupar muito com qualquer outra informação de cabeçalho que precise transmitir. Você pode acessar os cabeçalhos de resposta por meio de ​responseHeaders passados ​​ao manipulador de eventos, se necessário, mas por enquanto criaremos nosso próprio conjunto mínimo em uma matriz para facilitar a manipulação e edição posterior.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

O envio da nova resposta requer a criação de uma resposta HTTP completa, codificada em base64 (incluindo a linha de status HTTP) e o envio por meio de uma propriedade rawResponse no objeto passado para continueInterceptedRequest .

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Agora, quando você executar seu script e navegar pela internet, verá algo parecido com o seguinte em seu terminal, pois seu script intercepta o JavaScript e também o JavaScript modificado é executado no navegador e os ​console.log() aparecem através do gancho que criamos no início do tutorial.

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

O código de trabalho completo para o exemplo básico está aqui:

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Para onde ir a partir daqui

Você pode começar imprimindo o código-fonte, o que é sempre uma maneira útil de começar a fazer engenharia reversa de algo. Sim, claro, você pode fazer isso na maioria dos navegadores modernos, mas você vai querer controlar cada etapa da modificação para manter a consistência entre os navegadores e versões de navegadores e para poder conectar os pontos enquanto analisa a fonte. Quando estou investigando código estrangeiro e ofuscado, gosto de renomear variáveis e funções conforme começo a entender sua finalidade. Modificar JavaScript com segurança não é um exercício trivial e isso é assunto para um post de blog, mas por enquanto você pode usar algo como ​unminify para desfazer técnicas comuns de minificação e ofuscação.

Você pode instalar o unminify via npm e encapsular seu novo corpo JavaScript com uma chamada para ​unminify para vê-lo em ação:

Interceptando e modificando respostas com o Chrome por meio do protocolo Devtools

Falaremos mais sobre as transformações no próximo post. Se você tiver alguma dúvida, comentário ou outros truques legais, entre em contato comigo pelo Twitter!