2 pontos por GN⁺ 2023-08-28 | 2 comentários | Compartilhar no WhatsApp
  • Para bloquear, no nível da rede, os anúncios do YouTube na Apple TV e no iPhone, foram feitos testes que passaram por pfSense, bloqueio de DNS, roteamento por VPN, Squid, MITMProxy e chegaram até a adulteração de respostas Protobuf
  • Como os anúncios do YouTube são entregues a partir dos mesmos domínios dos vídeos comuns, era difícil separar com estabilidade conteúdo e anúncios usando apenas bloqueio de DNS como Pi-hole e pfBlockerNG
  • Após descriptografar o tráfego TLS com o MITMProxy, os campos de anúncios em JSON do YouTube web foram removidos e, no app do iOS, as estruturas de anúncio dentro de respostas application/x-protobuf foram localizadas e modificadas diretamente
  • A decodificação completa de Protobuf com Python levava cerca de 23 segundos no roteador pfSense, mas, com uma varredura linear ao redor de /pagead/, até um payload de 1.8MiB passou a poder ser processado em tempo real
  • Esse método exige a instalação de uma CA confiável e CPU com desempenho suficiente e, embora tenha permitido bloquear anúncios em dispositivos Apple conectados à rede, depois acabou levando até a considerar pagar pelo YouTube Premium

Roteador pfSense e segmentação da rede

  • O objetivo era montar um roteador baseado em FreeBSD·pfSense para bloquear, em toda a rede, os anúncios pre-roll, mid-roll e end-roll do YouTube na Apple TV e no iPhone
  • O hardware usado foi um mini PC J4125 com conjunto de instruções AES-NI, RAM DDR4, SSD mSATA e um pendrive para instalar o pfSense
    • Um exemplo de configuração era 32GiB de RAM e SSD mSATA de 128GB
    • Considerava-se que 128GB de armazenamento eram úteis para logs, redução de desgaste do SSD, captura de pacotes e espaço para edge cache
  • Após instalar o pfSense, a LAN 1 foi configurada com o IP estático 192.168.1.3, fora da faixa DHCP existente, e o acesso ao portal web administrativo foi feito com a conta admin/pfsense
  • Depois de confirmar no dashboard do pfSense a indicação AES-NI CPU Crypto: Yes (inactive), o AES-NI foi ativado manualmente em System › Advanced › Miscellaneous
  • Aproveitando os 32GiB de RAM, foi alocado bastante espaço de RAM disk para /var e /tmp, com backups do RAM disk configurados para ocorrer a cada hora

Bloqueio de DNS e separação física da rede

  • No lugar do Pi-hole existente, foi instalado o pacote do pfSense pfBlockerNG-devel para configurar bloqueio de anúncios·conteúdo malicioso e bloqueio geográfico
    • O tamanho da instalação acrescentava cerca de 20MiB
    • Caso o serviço pfb_dnsbl não iniciasse ou aparecesse [ Missing CRON task ], a recomendação era tentar apagar o arquivo vazio /var/run/booting
  • As 3 portas Gigabit do roteador pfSense foram usadas para separação física de LAN, em vez de VLAN
    • Dispositivos que “ligam para casa”, como Alexa e Apple TV, foram colocados em uma LAN de hardware separada
    • A intenção era separar a LAN importante dos dispositivos smart e dispositivos Wi‑Fi para proteger aparelhos usados para banco, negociação de ações e uso de crypto wallet
  • A rede dos dispositivos smart foi separada em 172.31.1.0/24, enquanto a LAN mais confiável foi mantida em 192.168/16
    • Considerava-se que, sem rotas entre as redes, também se reduzia parte do impacto de regras de iptables mal configuradas
    • Era necessário ativar o DHCP resolver na NIC física para que novos dispositivos da rede recebessem endereços
  • O AC1200 Archer C5 foi descartado por não ter AP mode, por problemas de acesso remoto, firmware stock antigo e falta de suporte do chipset Broadcom a OpenWRT/DD-WRT/Tomato
  • Depois, um Nighthawk R7000 foi usado como AP para Apple/Amazon/TV e como AP para a Trusted Wireless Network
    • Na Trusted Wireless Network, decidiu-se desligar o 2.4GHz e usar apenas 5GHz
    • Considerava-se que 5GHz é mais facilmente bloqueado por paredes e concreto, o que ajudaria a evitar snooping a média distância

Forçando todo o DNS a passar pelo pfSense

  • Foram adicionadas regras de NAT para que todos os clientes atrás do pfSense usassem o servidor local de Unbound DNS
    • O objetivo era impedir que apps e assistentes domésticos contornassem isso usando seus próprios servidores DNS ou DNS hardcoded
    • Para que o pfBlockerNG pudesse intervir nas requisições DNS, era preciso bloquear DNS over TLS
  • As regras de NAT inicialmente foram criadas em cada interface, exceto WAN, mas depois isso foi simplificado com o alias de firewall Non_WAN
    • Tanto em IPv4 quanto em IPv6, consultas DNS locais na porta 53 eram redirecionadas para localhost
    • Era necessário desativar NAT reflection para impedir acesso ao servidor DNS local a partir da internet externa
  • Em Services › DNS Resolver › Display Custom Options, foi adicionado server: log-queries: yes para registrar em log as requisições DNS interceptadas
  • Nos logs de DNS, apareceu uma requisição do Windows tentando acessar o Google Tag Manager, e essa requisição foi tratada por blackhole para o IP inexistente 10.10.10.1

Tentativa de contornar o direcionamento de anúncios do YouTube com VPN

  • Como os anúncios do YouTube vinham dos mesmos domínios dos vídeos comuns, era difícil selecionar apenas os anúncios com bloqueadores por domínio como pfBlockerNG ou Pi-hole
    • Considerava-se que bloquear googleadservices.com só teria utilidade depois de assistir ao vídeo do anúncio e clicar no anúncio
    • No navegador, o uBlock Origin pode interferir no JavaScript, mas acreditava-se que, no app do YouTube no iPhone, era difícil limitar anúncios sem jailbreak
  • Em vez de bloquear os anúncios diretamente, foi feito um experimento para levar o algoritmo de anúncios do YouTube a considerar o usuário um alvo menos atraente
    • A ideia era enviar o tráfego da Apple TV por uma VPN e usar um endpoint de VPN em uma região com poucos espectadores do YouTube
    • O objetivo era fazer o usuário parecer um homem de 70 anos morando na Itália
  • Foi configurado WireGuard no pfSense, importando a private key do NordLynx/WireGuard a partir de uma VM Linux
    • Ao inserir o endereço do túnel como 1.0.0.0 com subnet mask 0, a UI passava a mostrar o resultado como 0.0.0.0/0
  • Ao enviar todo o tráfego da Apple TV pela VPN, o YouTube passou a aparecer em italiano e a quantidade de anúncios diminuiu, mas surgiram problemas no Netflix e no Amazon Prime
    • Parecia que arquivos de CSS ou font estavam sendo bloqueados e que thumbnails não carregavam
    • Havia o alerta de que Netflix e Prime fazem geofencing de provedores de VPN com eficiência
  • Depois, tentou-se rotear pela VPN apenas os FQDNs relacionados ao YouTube, usando como candidatos www.youtube.com, youtube.com, googlevideo.com, accounts.google.com, googleapis.com, gstatic.com e outros
    • Após a configuração, o YouTube via o usuário como estando em Milão, enquanto Netflix e Prime Video o viam como estando no Canadá
    • Os anúncios ficaram raros e, quando apareciam, eram em italiano

Condição de corrida de DNS e problema de domínio curinga

  • Um dia depois, foi descoberta uma condição de corrida de DNS em que o alias de hostname do pfSense e o cache DNS do cliente viam conjuntos diferentes de IPs do YouTube
    • O intervalo padrão de resolução do alias de hostname do pfSense é de 300 segundos
    • O TTL de DNS do YouTube foi observado em 1.440 segundos
  • Se o IP resolvido pelo Alias Daemon para o FQDN não corresponder ao IP que a Apple TV recebeu depois, o tráfego pode não entrar no túnel VPN
    • A mitigação é fazer o pfSense ignorar o TTL do destino e manter em cache a entrada de alias por mais tempo
  • Os subdomínios variantes de googlevideo.com exigiam roteamento curinga, mas as regras de NAT e firewall operam com base em IP e não conseguem lidar diretamente com hostname curinga
  • Foi escrito um PoC usando o módulo Python do Unbound e a API REST do pfSense para capturar os IPs das respostas DNS e adicioná-los dinamicamente ao alias VPN_wildcards
    • O TTL de VPN_wildcards foi definido como 1 hora, com capacidade de 500
    • Registros A são analisados como ipaddress.IPv4Address, e registros AAAA como ipaddress.IPv6Address
  • Na verificação da manhã, o Unbound DNS Resolver estava em estado de segfault, e como cada adição de IP exigia recarregar as regras do pfSense, o pfSense ficou muito lento

Descriptografia de HTTPS com Squid e MITMProxy

  • O novo objetivo era instalar um proxy semelhante ao Squid e adicionar ao dispositivo um certificado de CA falso, mas confiável, para realizar a descriptografia do tráfego TLS
  • O Squid foi instalado como pacote do pfSense e até o smoke test de SSL Filtering teve sucesso, mas acabou sendo abandonado
    • O desempenho era muito lento
    • A configuração de ACL era trabalhosa
    • Havia um problema relacionado a https://http/*
    • A atualização da lista de filtros de URL do SquidGuard demorava muito
    • A UI do Squid foi considerada insuficiente
  • Depois disso, foi escolhido o MITMProxy
    • Foi considerado extensível para o bloqueio de anúncios do YouTube, por ter scripting em Python e UI
    • O tarball Linux do mitmproxy 7.0.4 não rodava no FreeBSD por causa de ELF interpreter /lib64/ld-linux-x86-64.so.2 not found e bibliotecas ausentes
  • Ele foi instalado no ambiente jail FreeBSD do pfSense com pkg install mitmproxy
    • Foram 50 pacotes para instalar, 206MiB de espaço adicional e 33MiB de download
    • Ao executar mitmproxy dentro do jail, a UI abria
  • Para experimentar o MITMProxy, foi adicionado no pfSense um IP virtual 127.0.1.1 ao localhost, e uma regra de NAT encaminhava [Private IPs]:8080 para 127.0.1.1:8080
    • Quando o proxy de um notebook de teste foi configurado como 192.168.20.1:8080, as requisições do navegador apareceram no log da UI do MITMProxy
  • O arquivo PEM da CA do MITMProxy fica em ~/.mitmproxy/mitmproxy-ca-cert.pem
    • Um servidor web em Python 3 foi usado para servir cert.pem
    • O MITMProxy também fornece o mesmo certificado de CA em mitm.it
    • O certificado foi adicionado a um notebook limpo e a um iPhone

Operação do MITMProxy e resposta a certificate pinning

  • No roteador, o mitmproxy consumia muita CPU mesmo ocioso, e isso foi atribuído à geração sob demanda de certificados TLS para cada requisição e ao excesso de logs da UI
  • O mitmdump foi considerado menos pesado para a CPU por dispensar a UI e o logging extremo
    • Na execução, foram usados --anticomp, --mode regular, --listen-port 8080, --listen-host 127.0.1.1 etc.
  • Certificate Pinning é uma técnica em que o servidor ou o cliente já conhece antecipadamente a fingerprint esperada do certificado, de modo que a falsificação de certificado do MITMProxy não funciona
    • Como alternativa, foi usado --ignore-hosts para fazer hosts como apple.com:443 e icloud.com:443 ignorarem o proxy
  • No Transparent Proxy Mode, foi aplicado um patch em next_layer.py do MITMProxy 7.0.4 para que --allowed-hosts funcionasse melhor com base em SNI
    • Antes, em muitos casos, parecia que apenas o IP do servidor era usado na correspondência
    • O patch adiciona server.sni como candidato de hostname além de server.address[0]
  • Após o patch, alguns hosts puderam ser interceptados com estabilidade, enquanto os demais apenas passavam

Remoção de anúncios em JSON no YouTube web

  • No smoke test do MITMProxy, URLs de anúncios e rastreamento do YouTube foram bloqueadas com um pequeno script
    • Em youtube.com, foram bloqueados /pagead/, /log_event?, /stats/ads, /stats/qoe?, /ptracking?, /generate_204, el=adunit, adformat=, /activeview? etc.
    • Em google.com e google.ca, foi bloqueado /pagead/
  • Nos testes iniciais, as requisições marcadas para bloqueio realmente eram bloqueadas também no painel Network do DevTools
    • Itens (failed) eram gerados pelo script
    • As falhas 502 foram atribuídas ao pfBlockerNG tratando a requisição como black-hole
    • O HTTP/2 foi desativado para impedir que requisições subsequentes no mesmo canal passassem
  • O simples bloqueio de URL não eliminou completamente os anúncios, então, observando o HTML/JavaScript do YouTube e os filtros do uBlock Origin, foi investigada a possibilidade de as informações de anúncio estarem no corpo das respostas JSON
  • Nas respostas JSON capturadas pelo MITMProxy, foram confirmadas informações de anúncios e rastreamento nas estruturas playerAds e playbackTracking
    • playerAds inclui playerLegacyDesktopWatchAdsRenderer, playerAdParams, gutParams.tag, showCompanion, showInstream, useGut etc.
    • playbackTracking inclui videostatsPlaybackUrl, ptrackingUrl, qoeUrl, atrUrl etc.
    • youtubeRemarketingUrl tinha o formato www.youtube.com/pagead/viewthroughconversion/...
  • No YouTube web, foi possível eliminar os anúncios web via roteador removendo as informações de anúncio do payload JSON

Análise do YouTube para iOS e do Protobuf

  • O app do YouTube para iOS usa dados no formato Protocol Buffer (Protobuf) em chamadas de API parecidas com as da versão web, em vez de JSON
    • No Protobuf, as chaves são numéricas e podem mudar, então é difícil encontrar a seção de anúncios com uma abordagem no estilo JSONPath
    • No payload, foram vistas strings de anúncios como “Telus”, “Samsung TV”, “Boxing Week” e “Buy now”
  • O tráfego de rede do YouTube no iOS era diferente do tráfego web
    • Na versão web, dá para estimar em certa medida os candidatos a anúncio pelos parâmetros range ou clen dos chunks de vídeo
    • O protocolo do iOS não usa o parâmetro de query range nem o cabeçalho Range, e usa contadores como &nr=2 e &nr=3
  • Ao decodificar respostas Protobuf para análise offline, foram encontrados itens como has_unlimited_entitlement: False e has_premium_lite_entitlement: False
    • Alterar esses valores pareceu uma forma de “trapacear”, então a abordagem heurística foi retomada
  • Experimentos de bloqueio de URLs de anúncios causaram loop infinito, erros de UI e crash no app do iOS
    • 200 com body vazio, 404, 503, body de resposta truncado e tratar parte do vídeo do anúncio como null faziam o app ficar lento ou quebrado até crashar
    • O endpoint de reporte de erros /error_204/ indica “dev assertion failed” e foi bloqueado
  • Os anúncios parecem ser registrados em slots dentro de um vídeo específico
    • Os tipos de slot incluem pre-roll, mid-roll, end-roll, full-page e ad pod
    • Se apenas a URL do anúncio for bloqueada, ocorre um erro do tipo “um anúncio inexistente reservou o slot”, e a UI entra em estado de pânico

Problemas de desempenho do Protobuf e o blackboxprotobuf

  • Decodificar cerca de 500 KiB de Protobuf bruto para texto legível apenas com Python era muito lento
    • Em um desktop com CPU i7-6700, levava cerca de 2,06 a 2,11 segundos
    • Em um roteador pfSense, levava cerca de 22,8 a 24,2 segundos
  • O C++ protoc --decode_raw era muito mais rápido
    • Em um desktop com CPU i7-6700, levava cerca de 0,017 a 0,022 segundo
    • Em um roteador pfSense, ficava na faixa de 0,12 a 0,14 segundo
  • Como o decoding bruto não é suportado em Python, foi escolhida uma abordagem de comunicação direta com o binário C++ protoc via subprocess.Popen
  • O blackboxprotobuf para Burp Suite consegue decodificar mensagens wire raw de Protobuf, injetar valores e depois reencodá-las
    • Recomenda-se usar a versão original do Burp Suite, e não um fork do PyPI
    • Alguns forks podem causar stack overflow por recursão profunda
    • Definir os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp" antes de importar protobuf faz com que a implementação em C++ de libprotobuf.so seja usada quando possível
  • O protobuf_to_json(data) do blackboxprotobuf pode gerar um schema .proto por tentativa de inferência, mas o resultado é muito grande, profundamente aninhado e imperfeito
    • O dump de schema em Python tinha mais de 250.000 caracteres
    • Isso foi considerado suficiente para extrair os detalhes dos anúncios

Desativando a seção de anúncios com a alteração de 1 byte

  • O formato wire do Protobuf pode mudar de encoding ao fazer decode/edit/re-encode sem o schema original
    • Entre as causas citadas estão o uso ou não de codificação ZigZag, a impossibilidade de identificar o tipo numérico e a não determinismo na ordem dos campos de objeto
  • A solução foi usar a compatibilidade retroativa do Protobuf para fazer a seção de anúncios parecer um campo desconhecido
    • Isso aproveita o comportamento de softwares antigos que ignoram campos desconhecidos ao ler fields novos
    • Ao alterar 1 byte em um ponto crítico, a seção profundamente aninhada passa a parecer pertencer a uma versão futura do schema, e o Protobuf a ignora
  • A chave do campo de destino 49399797 precisou ser encontrada por varredura de tag varint, e não por simples busca de string
    • O wire type era 2, indicando string ou mensagem aninhada com length-delimited
    • A tag de destino foi calculada como AA FF B8 BC 01
    • Ao deslocar para fora os 3 bits do wire type, a chave do campo 49399797 é restaurada
  • A busca real funcionava localizando primeiro uma assinatura de URL de anúncio como /pagead/ nos bytes brutos do Protobuf e, nas proximidades, recuando para encontrar a tag do campo-alvo e a chave do campo
    • Um alvo de interceptação de exemplo era POST youtubei.googleapis.com:443/youtubei/v1/browse?key=...
    • A resposta era 200, application/x-protobuf, 1.87m
    • No log de exemplo, 49399797 foi encontrado na posição 4465 e 50195462 na posição 4477
  • Em um smoke test O(n), a remoção de anúncios funcionou com uma única varredura de 1,8 MiB de dados Protobuf, sem memória adicional
    • O alvo foi encontrado no byte 30.593
    • Cerca de 600 bytes de backtracking foram usados para encontrar a chave do campo a ser corrompida
    • Não é mais necessário bloquear URLs contendo *.googleadservices.com ou /pagead/, porque essa requisição deixa de ser gerada desde o início

Estrutura do addon do MITMProxy

  • O script PoC é salvo como youtube.py e executado com mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py"
    • Como pré-requisitos no FreeBSD, são indicados pkg install protobuf, pkg install py38-pip e pip install jsonpath-ng
  • O script é composto pelas classes Logger, trunc, KilledError, JSONPathReplacement, ProtobufDebugParser e YouTubeAdBlocker
  • O regex de host alvo de interceptação de YouTubeAdBlocker é \.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com
    • A string de busca de URL de anúncio em Protobuf é b"/pagead/"
    • O limite de busca é 80_000
    • A tag do campo alvo é 50195462
  • A regra de bloqueio de requisições verifica strings parciais de URL por host e encerra o flow
    • Para youtube.com, inclui pagead/, log_event?, stats/ads, stats/qoe?, ptracking?, generate_204, error_204, adformat=, activeview?, _ad_, ai?, sw.js etc.
    • Em sw.js, há um comentário dizendo que service workers são recusados
  • Em respostas JSON, vários replacements de JSONPath são aplicados
    • $.responseContext.serviceTrackingParams[*].params[?(@.key == 'yt_ad')].value é alterado para "0"
    • $..adPlacements é alterado para []
    • $..adPlacementRenderer, $..adPlacementConfig, $..playerAdParams, $..gutParams são alterados para {}
    • $..adVideoId é alterado para string vazia
    • $..showCompanion, $..showInstream, $..useGut são alterados para False
  • Em respostas Protobuf, quando o content type contém protobuf, o body é convertido em bytearray e /pagead/ é procurado nos primeiros 80.000 bytes
    • TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED) cria os bytes da tag alvo, e novos bytes são gerados para target_field_tag - 1
    • Faz-se uma busca reversa até imediatamente antes da posição de /pagead/ para encontrar os bytes da tag alvo
    • Se encontrados, esses bytes são sobrescritos pelos novos bytes e flow.response.set_content(bytes(body)) substitui o corpo da resposta
  • Pelos comentários, este PoC bloqueia 90% dos anúncios
    • Outras seções também têm outras chaves de campo, e pode haver várias seções de anúncio que precisam ser corrompidas

Escopo de aplicação e limitações

  • Essa técnica é resumida como uma técnica altamente especializada para bloquear anúncios do YouTube em dispositivos Apple ou tráfego de rastreamento de Instagram, WhatsApp e Facebook
  • Considera-se que a exigência de CPU para descriptografar/recriptografar tráfego HTTPS excede em muito o que um Raspberry Pi consegue suportar
  • A avaliação é que, após fazer jailbreak na Apple TV e adicionar o certificado raiz do pfSense, o gateway pfSense pode descriptografar o tráfego da Apple TV e bloquear anúncios inspecionando o hostname de publicidade nos headers das requisições
    • Ainda assim, isso não se aplicaria a anúncios no iPhone, e fazer jailbreak no iPhone é mais difícil, além de apps bancários poderem detectar isso e deixar de funcionar
    • O próprio jailbreak é considerado extremo demais
  • Se uma CA confiável falsa for possível, dá para descriptografar os pacotes TLS em texto puro e aplicar bloqueio por URL
    • Como exemplos de URLs bloqueadas, são citados os caminhos /pagead/viewthroughconversion/... e /pagead/conversion/... do YouTube
  • Por fim, o autor configurou um roteador de hardware do zero, dividiu a LAN em zonas confiável e não confiável, adicionou bloqueio de anúncios por DNS e um proxy MITM transparente e, então, bloqueou com bom desempenho os anúncios do YouTube em dispositivos Apple conectados à rede

YouTube Premium e apoio aos criadores

  • Depois de bloquear anúncios do YouTube por alguns meses, o autor passou a pagar o YouTube Premium por querer apoiar os criadores de conteúdo
    • Acrescenta a ressalva de que “só porque você pode, não significa que deve”
  • O preço do YouTube Premium é mencionado como CAD $9.99/mo passando para $11.99/mo, cerca de $13.43/mo com impostos
  • No experimento assistindo a anúncios, o autor usou um notebook limpo e navegação privada para assistir ao YouTube de forma intermitente por um dia
    • O registro mostra 10 vídeos assistidos
    • Houve exposição a 8 anúncios, dos quais apenas 2 podiam ser pulados
  • Assumindo um CPV de USD $0.15, o custo diário de anúncios seria $1.20, e a extrapolação mensal daria cerca de USD $36/mo
  • Em outro cálculo com números da Statista, anunciantes dos EUA gastaram $15.1 billion no YouTube em 2019, e residentes nos EUA assistiram 916 billion vídeos, resultando em uma média de USD $0.0165 por visualização
    • Aplicando esse cálculo, o custo diário seria de cerca de USD $0.13, e a extrapolação mensal seria de aproximadamente USD $3.96
    • Considera-se que esse valor não chega perto dos USD $10 do Premium
  • Se houver uma reivindicação via DMCA, a receita de anúncios pode ir não para o criador, mas para o reclamante, como Sony ou Viacom
    • Por isso, é possível acabar sem dar nada aos canais de que se gosta sem sequer perceber
    • O autor avalia que não é surpreendente que muitos criadores migrem para o Patreon

2 comentários

 
xguru 2023-08-29

O texto original é muito longo, mas o processo é interessante; o ponto principal é que, no fim, o autor acabou simplesmente assinando o YouTube Premium e usando assim.

 
GN⁺ 2023-08-28
Opiniões no Hacker News
  • No geral, é um hack bacana, mas algumas afirmações sobre Protobuf soam estranhas
    O que foi feito foi corromper de propósito a tag de um determinado campo no Protobuf, mas ignorar números de tag desconhecidos não é uma “falha”; é um princípio central de design para extensibilidade
    1,87 MiB também não é um tamanho tão grande, e provavelmente essas mensagens não ficam sendo transmitidas em streaming continuamente, então a explicação de que isso seria uma barreira de desempenho também não convence muito
    A codificação do Protobuf não foi projetada para tornar a decodificação cara; pelo contrário, foi projetada para ser decodificada com eficiência, e mesmo sem o esquema .proto original é possível decodificar diretamente com UnknownFieldSet
    Um método melhor talvez fosse usar um esquema .proto falso contendo apenas o campo que se quer remover. A abordagem de varrer strings é mais propensa a erros, porque a mesma sequência de bytes pode aparecer por acaso em outros dados
    Se a ordem dos campos mudar, os bytes resultantes da recodificação podem ser diferentes, mas o receptor deve tratar aquilo como a mesma mensagem, e parece pouco provável que o app do YouTube detecte uma mudança na ordem dos campos
    Do ponto de vista de alguém que já trabalhou com Protobuf, parece que o autor entendeu mal essa parte

    • É uma boa análise, mas é um pouco difícil concordar que 1,87 MB seja pequeno
      Vivi a maior parte da vida em áreas rurais e, quando não estou no meu Wi‑Fi, isso ainda é um download grande na prática. No celular talvez existam formas de contornar, mas o Wi‑Fi rural ainda sofre com a arquitetura da Web 2.0 e muitas vezes é usado em velocidades de 2G a 4G
      Em áreas urbanas, onde há população suficiente para sustentar a infraestrutura, 1,87 MB em geral virou um arquivo pequeno, mas por volta das 18h, quando todo mundo ligado ao cabo está fazendo streaming, isso pode ser uma exceção
    • Fazendo uma pequena divulgação sobre without the C++ source proto files: criei um projeto chamado protodump, que gera arquivos .proto de origem a partir de um binário
      Ele recria as definições de mensagens e campos, incluindo os nomes originais; basta extrair o binário da caixa Apple TV
      https://github.com/arkadiyt/protodump
    • “Já trabalhou com Protobuf” é uma forma extremamente contida de dizer isso. Para quem não sabe, Kenton é a pessoa que fez o Protobuf se tornar o que é hoje
      Protobuf foi a tecnologia que me apresentou a IDLs e, na época, parecia uma ideia mágica. Fiquei ainda mais impressionado depois de criar minha própria IDL mal-ajambrada e então descobrir o Protobuf
    • Fiquei confuso de um jeito parecido ao ler este texto. Os princípios de design do Protobuf não são segredo, e estão todos claramente documentados
    • A parte mais complicada ao decodificar Protobuf sem esquema é que mensagens embutidas e strings usam o mesmo tipo de tag, mas ainda assim isso é bem fácil de lidar
      Se você não quiser puxar a dependência inteira do protoc, pode escrever por conta própria um decodificador Protobuf simples, de algumas centenas de linhas: https://github.com/kubernetes/test-infra/blob/master/guberna... https://github.com/kubernetes/test-infra/blob/master/guberna...
  • Desde que descobri The Proxomitron, mais de 20 anos atrás, venho passando o tráfego por um proxy man-in-the-middle para remover anúncios e reescrever páginas com coisas como CSS do usuário
    Lugares como a CloudFlare tendem a me classificar como “bot”, mas também há formas de contornar isso, embora não sejam simples. Casos assim também mostram por que a atestação remota é perigosa para a liberdade do usuário

    • Fui procurar The Proxomitron e parece que o desenvolvimento terminou em 2004; seria interessante ter um resumo de alguém que conheça bem o cenário atual
      Parece haver vários projetos “sucessores”, e também fico curioso se ele é exclusivo do Windows. Eu estava procurando, sem muita intensidade, um proxy simples capaz de inserir links locais em conteúdo remoto
    • O bloqueio de anúncios em nível de rede, como Privoxy ou pi-hole, tem desvantagens demais, como não lidar com anúncios inline
      No momento, até desliguei o pi-hole que rodava no Pi 4. Passei horas tentando fazê-lo funcionar corretamente junto com vários serviços, mas acabei desistindo; não valia o tempo para uma rede doméstica
      O que realmente funciona bem são bloqueadores de anúncios baseados no navegador e patchers de apps como o ReVanced. Conforme minhas economias aumentam, fico mais inclinado a simplesmente pagar por serviços sem anúncios, como YouTube Premium, Hulu, Netflix e Max, nos casos em que esses dois não resolvem
    • Como se sabe que funcionários da Cloudflare ficam escondidos por aqui lendo, fiquei curioso. Ser classificado como bot desse jeito é visto como falso positivo ou como funcionamento intencional?
    • Eu não pensava no Proxomitron havia quase 20 anos; fico curioso se alguém ainda o usa
      Nunca o usei para o caso mencionado aqui, mas ele era ótimo como proxy atrás do firewall da empresa. Antigamente, o firewall exigia credenciais de login para conexões externas, então muitos programas não conseguiam acessar a internet
    • Imagino que seja uma abordagem em que é preciso instalar um novo certificado de CA no dispositivo, certo?
  • Lembrei do Privaxy, empacotado em Docker. É um proxy intermediário compatível com as listas de bloqueio do UBlock Origin.
    É surpreendente ver a quantidade de anúncios e scripts de rastreamento em produtos inteligentes, especialmente TVs. Pelo que testei até agora, o tráfego desnecessário passou de 40%, e foi bem divertido experimentar remover anúncios de apps de smart TV.
    https://github.com/deetungsten/webui-privaxy é um fork dockerizado de https://github.com/Barre/privaxy

    • Como fazer a TV confiar em um certificado autoassinado?
    • Que bom ver isso por aqui também. Gostei que você levantou o problema do ping das listas de filtros.
      Eu queria alterar o fork para que o frontend não usasse o endereço 0.0.0.0 hardcoded, permitindo isolar de verdade o contêiner Docker, mas a vida aconteceu. Você chegou a testar na Apple TV?
    • A Adguard também está trabalhando em algo parecido.
      https://github.com/AdguardTeam/urlfilter
    • Levei tempo demais para entender que “fork dockerizado” queria dizer que a GUI virou uma GUI web
  • Este texto é uma ótima resposta à pergunta recorrente: “como devo aprender para virar hacker?”
    Ele mostra muito bem o processo de raciocínio e o trabalho persistente por trás de qualquer exploit.

  • Há uma parte que diz “vamos usar WireGuard — há o conjunto de instruções de criptografia Intel AES-NI”, mas, até onde sei, WireGuard não usa AES.
    No geral, o autor parece superestimar um pouco os requisitos de CPU da criptografia TLS, ou subestimar o desempenho de computadores de placa única modernos.
    Também acho estranha a explicação de que os requisitos de CPU para descriptografar e recriptografar tráfego HTTPS em um Raspberry Pi seriam muito excedidos. Eu ficaria bem surpreso se MITM de TLS fosse realmente inviável em um RPi 4, mesmo usando RSA puramente em software.
    Entre os celulares Android ainda em uso, há aparelhos com CPUs mais fracas que a do RPi 4, e eles também usam TLS.

    • Acho que você está subestimando os requisitos de CPU.
      Se um celular Android fraco só consegue processar tráfego TLS a 50 Mb/s, isso pode não ser um grande problema no uso real. Celulares lentos normalmente estão conectados a redes lentas.
      Por outro lado, se você tem internet gigabit em casa e um dispositivo fraco entre todos os seus computadores e a internet cria um gargalo de 50 Mb/s, aí isso vira um problemão.
      Os requisitos de CPU do TLS dependem muito da largura de banda alvo. Em larguras de banda mais altas, descarregar para aceleradores acaba se tornando praticamente obrigatório. O custo do handshake também não é desprezível e pode limitar o número de conexões por segundo. Em um único dispositivo isso raramente é um problema, mas pode ficar maior em uma rede inteira de dispositivos.
  • Texto excelente. Eu esperava um método para fazer MITM em dispositivos que não permitem instalar uma CA personalizada.
    Tenho um dispositivo IoT que não expõe uma API local e só mostra dados pela nuvem, e queria capturar o tráfego entre o dispositivo e a nuvem.
    No fim, será que não há outro jeito além de despejar a memória flash, trocar a CA e gravar de volta?

    • Se o certificado foi “hardcoded”, isso se chama pinning de certificado. Nesse caso, para descriptografar o tráfego, você precisa substituir ou remover o certificado e levar o mesmo certificado para o proxy MITM.
      Há um bom texto com abordagens para tentar interceptar dispositivos IoT sem mexer em hardware ou firmware:
      https://robertheaton.com/2019/11/21/how-to-man-in-the-middle...
    • Procurar uma forma de fazer MITM em dispositivos que não permitem instalar uma CA personalizada é, no fundo, ir contra o objetivo do TLS.
      Se for possível, você estará dependendo de falhas de implementação.
      Sinceramente, acho que mesmo alguns dispositivos que hoje permitem instalar certificados confiáveis próprios não continuarão assim por muito tempo.
  • Sempre surgem novos métodos para bloquear anúncios no YouTube ou em qualquer plataforma, mas alguns meses depois eles mudam e aquilo deixa de funcionar.
    Em vez disso, que tal atacar os anunciantes? YouTube/Google parece rastrear apenas “cliques”, mas será que acompanha compras reais?
    Em tese, se uma quantidade suficiente de bots falsos e usuários reais clicasse em anúncios sem comprar nada, daria para queimar o orçamento de publicidade. Com o tempo, o departamento de marketing veria que, em certa plataforma, o número de cliques está no maior nível histórico, mas a taxa de conversão em relação a cliques ou impressões é baixíssima, e poderia acabar saindo dela.

    • Vendo que o Nauseum foi bloqueado na Chrome Store, parece que ele é bem eficaz.
  • Texto impressionante. Assim que vi a etapa de patch de MITM, imaginei que seria algo especial, e realmente foi.

  • O item do sumário “Novo objetivo: fazer o YouTube acreditar que sou um homem de 70 anos que mora na Itália” me chamou a atenção.
    Um tempo atrás, não sei como, o direcionamento de anúncios passou a acreditar que eu era alguém querendo comprar um pijama de seda lavável de 500 dólares para a minha namorada.
    Os anúncios em si eram ótimos, mas fico curioso para saber quanto estavam pagando por impressão.
    Depois que mudei para a Apple TV, geralmente recebo anúncios locais com segmentação regional errada. Em média, talvez isso até seja melhor.

  • Isso não é uma “falha” do Protobuf. O fato de, ao modificar bytes, eles serem decodificados como campos em outras posições é algo que funciona conforme o esperado
    O Protobuf, desde o início, é um protocolo baseado em números de campo e prefixos de comprimento, parte da suposição razoável de que os bytes não mudam durante a transmissão e deixa a integridade a cargo de quem lê
    Mesmo que fosse uma falha, seria uma falha do app do YouTube para iOS, não do Protobuf; e, na prática, nem é uma falha, então é difícil chamar de “exploit”. A menos que estejam falando do fato de que, na troca Protobuf do app do YouTube para iOS, o hash do payload retornado não é verificado
    Depois deste texto, provavelmente eles passem a verificar

    • A formulação do autor é meio estranha. “Falha” só aparece no título; o corpo apenas explica como o formato funciona
      Não é uma falha, é algo que funciona conforme o design
      A parte que diz que “o Google torna computacionalmente caro decodificar, modificar e recodificar sem os arquivos proto de código-fonte em C++” também é estranha. Com código Python não otimizado, pode ser caro, mas em C ou em outra linguagem compilada, varrer um Protobuf de 1,8 MB é algo trivial, com ou sem os arquivos-fonte proto
      Não parece que dificultar a decodificação de arquivos Protobuf sem os fontes tenha sido um objetivo de design. Se esse era o objetivo, foi executado de forma bem ruim
    • Não sei como campos obrigatórios funcionam no Protobuf, mas, para mitigar o ataque, o cliente do YouTube do Google poderia tratar esse campo como campo obrigatório e fazer o serviço ser recusado se o campo não existir ou tiver o valor padrão
    • A data do texto é janeiro de 2022. Se eles quisessem reforçar o protocolo depois do post no blog, é bem provável que já tenham feito isso