1 pontos por GN⁺ 2025-03-19 | 1 comentários | Compartilhar no WhatsApp
  • Trata-se de um PoC que coloca um proxy MITM baseado em pfSense entre a Apple TV e a internet, descriptografa HTTPS e depois modifica as respostas Protobuf enviadas pelo YouTube para impedir que slots de anúncios sejam registrados em dispositivos Apple
  • Os métodos existentes de bloqueio por DNS e roteamento via VPN esbarravam em limitações como TTL de DNS, divergência de IP, erros 403 e vazamento de ASN, porque os anúncios do YouTube e o vídeo principal usam o mesmo domínio e a mesma infraestrutura
  • Os testes com Squid foram abandonados por questões de desempenho e configuração; depois disso, a abordagem mudou para descriptografar o tráfego TLS com mitmproxy/mitmdump em um jail do FreeBSD e alterar diretamente respostas JSON e Protobuf
  • No YouTube web, era possível remover adPlacements, playerAdParams e URLs pagead do JSON, mas o app do YouTube no iOS carregava slots de anúncios e informações de rastreamento em respostas application/x-protobuf, exigindo lidar com a estrutura Protobuf
  • O método final faz uma varredura linear com modificação de 1 byte: procura o field tag de trás para frente perto da string /pagead/ e troca tags como 50195462 por target_field_tag - 1, obtendo desempenho adequado para processamento em tempo real sem decodificação completa

Objetivo e arquitetura inicial de rede

  • O objetivo era criar um roteador baseado em FreeBSD e pfSense para bloquear, em toda a rede, anúncios pre-roll, mid-roll e end-roll do YouTube na Apple TV e no iPhone
  • Colocar um proxy man-in-the-middle entre a Apple TV e a internet externa permitia descriptografar o tráfego HTTPS e ler os dados Protocol Buffer que o Google usa para preencher anúncios do YouTube
  • Depois de implementar o bloqueio de anúncios do YouTube por alguns meses, o autor passou a assinar o YouTube Premium e afirma que “ser capaz” e “dever fazer” são coisas diferentes
  • Os motivos para bloquear anúncios e rastreamento incluíam monitoramento de privacidade, desperdício de banda, clickbait e cryptojacking
    • Ele estimava que 25% a 40% do tráfego de rede poderia ser composto por anúncios, scripts de rastreamento, fingerprint.js, googletagmanager.js e loaders de analytics em tempo real como Hotjar
    • Também explica que JavaScript de mineração de cripto como CoinHive.js pode superaquecer o computador ou ser explorado para gerar pequenas quantias de dinheiro

Hardware do pfSense e configuração básica

  • Para proteger toda uma rede SMB, ele considerava que VM, imagem Docker e Raspberry Pi não tinham desempenho suficiente, então seria necessário hardware dedicado apenas para roteamento, descriptografia e monitoramento de pacotes
  • O hardware do roteador usado incluía um mini PC com conjunto de instruções AES-NI, RAM DDR4, SSD mSATA e um pendrive USB para gravar o pfSense
    • Um exemplo de configuração era mini PC J4125, 32GiB de RAM DDR4 e SSD mSATA de 128GiB
    • Ele considerava 128GB de armazenamento suficientes para logs, redução de desgaste do SSD, captura de pacotes e cache de borda para NPM e Docker
  • A imagem de instalação do pfSense tem cerca de 360MB e pode ser gravada em um pendrive com o Etcher AppImage
  • Após a configuração inicial, AES-NI aparecia como “Yes (inactive)” e foi ativado manualmente em System › Advanced › Miscellaneous
  • Para aproveitar os 32GiB de RAM, ele alocou bastante RAM disk em /var e /tmp, e configurou o SSD de 128GiB esperando wear-leveling, com backup do RAM disk a cada hora
  • No Dashboard, adicionou o widget S.M.A.R.T. para detectar problemas no SSD

Bloqueio por DNS, separação de rede e pfBlockerNG

  • Antes, ele usava o Pi-hole em um Raspberry Pi como bloqueador de anúncios em nível de DNS e, no pfSense, instalou o pfBlockerNG-devel para testar bloqueio de anúncios, conteúdo malicioso e geo-blocking
  • Se o serviço pfb_dnsbl não iniciar ou a aba de status mostrar [ Missing CRON task ], ele recomenda tentar apagar o arquivo vazio /var/run/booting
  • Aproveitando as 3 portas Gigabit do mini PC, ele criou redes físicas em vez de VLANs e separou do restante da rede principal dispositivos que “ligam para casa”, como Alexa e Apple TV
    • Dispositivos não confiáveis foram colocados na rede privada 172.31.1.0/24
    • A LAN confiável permaneceu em 192.168/16
    • A LAN física para IoT passava pelo adblocker, e ele queria interceptar consultas DNS hard-coded como 1.1.1.1 e 9.9.9.9 para que o YouTube não pudesse contornar o bloqueador por DNS
  • Ele configurou regras de NAT para que todos os clientes atrás do pfSense usassem o servidor local Unbound DNS
    • Considerava necessário bloquear primeiro DNS over TLS para poder interceptar consultas DNS
    • O iPhone pode exibir um Privacy Warning sobre bloqueio de tráfego DNS criptografado, mas as consultas DNS upstream continuariam criptografadas para a Cloudflare
    • A reflexão de NAT deveria ser desativada para impedir que a internet externa acessasse o servidor DNS
  • Foi criado um alias de firewall Non_WAN para redirecionar para localhost a porta 53 das consultas DNS locais em interfaces que não fossem WAN
  • Como o YouTube entrega anúncios e vídeo principal a partir do mesmo domínio, era difícil filtrar apenas anúncios com bloqueadores baseados em nome de domínio como pfBlockerNG ou Pi-hole

Experimentos com bypass via VPN e pontos de falha

  • Ele também fez experimentos para, em vez de bloquear anúncios, enganar o algoritmo de anúncios do YouTube e parecer um usuário menos atraente para anunciantes
    • A ideia era usar o roteador pfSense para rotear, via VPN, o tráfego de rastreamento de localização do YouTube por uma região com menos espectadores
    • O objetivo era fazer com que a conta do YouTube fosse vista como “homem de 70 anos, morando na Itália”
  • No pfSense, em vez de OpenVPN, ele usou WireGuard para um teste-base que enviava todo o tráfego da Apple TV pela VPN
    • Instalou o pacote WireGuard do FreeBSD e adicionou/ativou o túnel
    • Para a configuração do NordLynx, verificou a private key em uma VM Linux com sudo wg showconf nordlynx e a transferiu para o pfSense
  • Nos testes, o Google no notebook passou a aparecer em italiano, e o YouTube da Apple TV também mudou para italiano
    • Os anúncios ainda apareciam em parte, mas em menor quantidade
    • Netflix e Amazon Prime tiveram problemas, e parecia que arquivos CSS ou fontes eram bloqueados ou que thumbnails não carregavam
    • Ele alerta para não enviar todo o tráfego da Apple TV pela VPN e considera que Netflix e Prime detectam muito bem provider de VPN e geofencing
  • Depois, para enviar apenas o tráfego do YouTube da Apple TV pela VPN, ele configurou regras de política no firewall para www.youtube.com, youtube.com, googlevideo.com, accounts.google.com, googleapis.com, gstatic.com etc.
    • Como resultado, 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 caíram para algo no nível de “few and far between”
  • Um dia depois, surgiu uma race condition de DNS
    • O alias de hostname do pfSense é resolvido por padrão a cada 300 segundos
    • O TTL de DNS do YouTube pode ser de 1.440 segundos, ou seja, 24 minutos
    • Se o IP resolvido pelo Alias Daemon diferir do IP que o cliente realmente recebeu, a política pode deixar de tunelar o tráfego do YouTube
  • Alguns vídeos do YouTube não reproduziam e retornavam 403 Forbidden
    • O YouTube supostamente embute o IP do usuário em cada requisição para googlevideo.com
    • Se um domínio modificado como r5---sn-hpa7kn76.googlevideo.com não fosse tunelado, a requisição sairia com o IP errado e causaria o problema
    • O necessário seria tunelamento com wildcard para *.googlevideo.com, mas regras de NAT e firewall operam com IP, não com hostname wildcard

PoC de rastreamento de IP com base em consultas DNS

  • Foi concebida uma abordagem de sequestro de consultas DNS do Google Video para rotear *.googlevideo.com pela VPN
    • O método consistia em rastrear periodicamente o log de consultas DNS e adicionar consultas *.googlevideo.com à lista de aliases
    • Se cada vídeo usasse um domínio único e modificado, concluiu-se que esse método não funcionaria sem atualizar a cada vídeo
  • O novo objetivo passou a ser monitorar consultas DNS com Python 3 e a API REST do pfSense para capturar IPs, segurar brevemente a resposta, adicionar o IP à regra de tunelamento VPN e então liberar a resposta DNS
  • A API REST do pfSense foi instalada, e uma requisição GET para https://pfsense/api/v1/firewall/alias foi enviada para consultar o alias VPN_domains
  • Ao explorar o módulo Python do Unbound DNS Resolver, conseguiu-se registrar mensagens de consulta DNS
    • A versão atual do Python era 3.8
    • Como os exemplos do módulo Python do Unbound eram baseados em Python 2.4, considerou-se que 2to3 ou formatação poderiam ser necessários
  • O script PoC extraía IPs de registros A/AAAA da resposta DNS e os adicionava ao alias do pfSense
    • O registro A era tratado com ipaddress.IPv4Address(d.rr_data[j][2:]).exploded
    • O registro AAAA era tratado com ipaddress.IPv6Address(d.rr_data[j][2:]).exploded
    • O TTL do alias foi definido como 1 hora, e a capacidade como 500
  • No dia seguinte, o Unbound DNS Resolver sofreu segfault e, como era preciso recarregar a regra do pfSense toda vez que um IP era adicionado, o pfSense ficou muito lento

Migração do Squid para o mitmproxy

  • O novo objetivo mudou para investigar e instalar um proxy da família Squid, criar um certificado de CA falsa confiável e descriptografar o tráfego TLS
  • Nos testes com Squid, foi verificado se o proxy squid3, fornecido como pacote do pfSense, atendia aos requisitos
    • Foi criada uma pasta exclusiva /squid_cache e o tamanho do cache foi configurado para 8GiB
    • Esperava-se suporte a HTTPS transparente
  • Após um dia configurando Squid e SquidGuard, a tentativa foi abandonada
    • A velocidade era muito lenta
    • 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 interface do Squid era insuficiente
  • Depois disso, decidiu-se usar o mitmproxy, escrito em Python
    • O mitmproxy foi escolhido no lugar do SSLSplit por causa da extensibilidade via hooks em Python e da interface
    • A versão do FreeBSD do pfSense era 12.2-Stable, build 64-bit
  • No ambiente padrão do pfSense, jail vinha desativado, então o ezjail foi instalado manualmente e um jail para o mitmproxy foi criado
    • O jail foi criado com ezjail-admin create mitmproxy 'lo0|127.0.1.1'
    • Para o modo de proxy transparente, foi definido allow.raw_sockets=1
    • Foi observado que, se raw sockets estivessem bloqueados, poderiam ocorrer erros como Transparent mode failure ou Cannot open connection, no hostname given.
  • A execução do binário Linux tarball falhou no FreeBSD
    • O erro ELF interpreter /lib64/ld-linux-x86-64.so.2 not found ocorreu
    • Também não foi possível encontrar libdl.so.2, libz.so.1, libpthread.so.0, libc.so.6
  • Dentro do jail, foi executado pkg install mitmproxy, e a instalação exigiu 50 pacotes, 206MiB de espaço adicional e 33MiB de download
  • Para permitir acesso ao MITMProxy pela LAN, o IP virtual 127.0.1.1 foi anexado ao localhost e uma regra NAT encaminhou temporariamente [Private IPs]:8080 para 127.0.1.1:8080
  • O arquivo PEM da CA gerada automaticamente pelo MITMProxy fica em ~/.mitmproxy/mitmproxy-ca-cert.pem, e esse certificado de CA foi instalado no Trusted Root Store do dispositivo de teste
  • O mitmproxy usava muita CPU mesmo em estado ocioso, e concluiu-se que a geração em tempo real de certificados TLS por requisição e o logging excessivo reduziam muito a velocidade
    • Considerou-se que o mitmdump impunha menos carga à CPU por dispensar a interface e o logging excessivo

Remoção de anúncios JSON do YouTube na web

  • Certificate Pinning é uma técnica em que o servidor ou cliente conhece antecipadamente a fingerprint esperada do certificado, de modo que a falsificação de certificado pelo MITMProxy não funciona
  • Hosts problemáticos podem ser configurados para contornar o proxy com a opção --ignore-hosts
    • Como exemplo, apple.com:443 e icloud.com:443 foram ignorados
  • Durante o acesso ao YouTube, anúncios de página apareceram no MITMProxy com cabeçalhos não criptografados, e foi avaliada a possibilidade de bloqueio com regex simples
  • Para aplicar o script de bloqueio de anúncios do YouTube, foi adicionado --scripts "youtube.py" ao mitmdump
  • O filtro de smoke test bloqueava requisições de anúncios com base em substrings de URL
    • youtube.com: /pagead/, /log_event?, /stats/ads, /stats/qoe?, /ptracking?, /generate_204, el=adunit, adformat=, /activeview?
    • google.com, google.ca: /pagead/
    • ggpht.com: .
  • As requisições que se pretendia bloquear de fato pareciam bloqueadas no MITMProxy e no painel Network do DevTools, mas os anúncios ainda apareciam, e às vezes eram pulados sozinhos ou falhavam na reprodução
  • Depois disso, foram encontradas várias URLs relacionadas a anúncios dentro do payload JSON
  • Após analisar a UI do YouTube e o fluxo HTTP, incluindo cookies e service workers, afirmou-se que foi possível remover anúncios pre-roll, post-roll e no meio do vídeo
  • Nesta etapa, já era possível remover anúncios do payload JSON dos anúncios do YouTube web pelo roteador

Problemas de Protobuf no YouTube para iOS

  • O app do YouTube para iOS mostra dados muito parecidos na versão Protobuf da mesma chamada de API da versão web
  • No Protobuf, as chaves são números e podem mudar, então não é possível usar uma abordagem de JSONPath para localizar a seção de anúncios
  • O YouTube envia uma grande lista de anúncios futuros no payload e, quando essa lista se esgota, outra grande lista chega logo em seguida
  • No payload Protobuf, foram vistas strings como “Telus”, “Samsung TV”, “Boxing Week” e “Buy now”
  • O protocolo do YouTube no iOS era diferente do tráfego web
    • na versão web, era possível distinguir até certo ponto o vídeo de anúncio e o vídeo desejado olhando a URL e o parâmetro de query range
    • o protocolo do iOS não usa nem o parâmetro de query range nem o header Range, e usa contadores como &nr=2 e &nr=3 nos chunks de vídeo
    • para bloquear anúncios no iOS, foi necessário fazer engenharia reversa da resposta Protobuf
  • Na mensagem Protobuf decodificada, foram encontrados os campos has_unlimited_entitlement: False e has_premium_lite_entitlement: False, mas em vez de alterná-los, voltou-se ao uso de heurísticas
  • Decodificar cerca de 500 KiB de Protobuf bruto com uma implementação pura em Python era muito lento
    • em um desktop com i7-6700, o resultado em Python foi de cerca de 2,06~2,11 segundos
    • em um roteador pfSense, o resultado em Python foi de cerca de 22,8~24,2 segundos
    • o C++ protoc --decode_raw levou cerca de 0,017~0,022 segundo no desktop e cerca de 0,12~0,14 segundo no roteador pfSense

Tentativas de decodificação de Protobuf e extração de schema

  • Como o Python não oferece suporte a decodificação bruta de Protobuf, em vez de usar diretamente libprotobuf.so em C++, optou-se por se comunicar com o binário C++ protoc via subprocess.Popen
  • Ao fazer fuzzing das respostas de vídeo de anúncio, foram tentados 200, 404 e 503 vazios, body de resposta truncado e nulificação parcial do vídeo do anúncio, mas o app do iOS apenas ficava mais lento, travava ou congelava na tela do anúncio
  • Bloquear URLs disparava reações do app, e os chunks de resposta de vídeo também continham metadados de sessão
  • O blackboxprotobuf para Burp Suite permite decodificar mensagens wire Protobuf brutas, injetar conteúdo e depois reencodar para verificar o comportamento do endpoint Protobuf
    • recomenda-se usar a versão original do Burp Suite, não o fork do PyPI
    • alguns forks têm problemas de stack overflow ou recursão infinita por causa de recursão profunda
    • usando bindings em C++, é possível transcodificar cerca de 500 KiB de Protobuf bruto em alguns segundos
  • O schema gerado não era perfeito, era grande e profundamente aninhado, e o pretty-print era lento, mas foi suficiente para encontrar detalhes dos anúncios
  • Foram testados PBTK, Apktool, dex2jar e Java Decompiler para tentar extrair os arquivos .proto reais ou arquivos de schema do APK do YouTube para Android
    • o PBTK extraiu apenas um arquivo proto de 59 bytes
    • havia classes Protobuf e getter/setter em Java, mas como não foi possível obter os arquivos de schema reais, essa linha foi abandonada

Ponto de virada final: alteração de 1 byte na tag de campo Protobuf

  • A partir do tráfego de rede descriptografado e dos resultados do fuzzing de Protobuf, observou-se que os anúncios eram registrados em slots de um vídeo específico
    • os tipos de slot incluem pre-roll, mid-roll, end-roll, full-page e ad pods
    • ao bloquear a URL do anúncio, ocorria um erro como “um anúncio inexistente reservou o slot”, causando pânico na UI
  • Sem o schema original, ao decodificar, editar e reencodar, sai uma codificação modificada; isso é problemático porque não dá para saber se há uso de ZigZag, nem tipos numéricos como int32, int64, sint32/64 e varint, e a ordem dos campos do objeto geralmente também é não determinística
  • Encontrou-se uma possibilidade de contorno na compatibilidade retroativa do Protobuf e no comportamento de UnknownFieldSet
    • quando um software antigo lê uma mensagem à qual foram adicionados novos campos, podem surgir campos desconhecidos
    • ao trocar uma determinada chave de campo por outro valor, toda a subestrutura que contém anúncios e informações de rastreamento pode ficar indisponível
  • Como exemplo, propõe-se a ideia de mudar a chave de campo 49399797 para 49399796, fazendo essa subestrutura de anúncio/rastreamento parecer um campo desconhecido
  • A chave de campo 49399797 não pode ser encontrada com uma simples busca em hex, e é preciso considerar a codificação varint/tag
    • o wire type é 2, indicando string/mensagem aninhada delimitada por comprimento
    • a sequência de bytes da tag para a chave de campo alvo 49399797 é AA FF B8 BC 01
    • removendo os 3 bits do wire type com 395198378 >> 3, obtém-se a chave de campo original 49399797
  • Nos bytes do Protobuf, usa-se uma assinatura clássica de URL de anúncio como /pagead/ para delimitar a faixa de busca do campo e, a partir dessa posição, volta-se para encontrar a tag de campo e a chave de campo a serem alteradas
  • Em um log de interceptação de exemplo, na resposta application/x-protobuf de 1,87 MiB de uma requisição POST para youtubei.googleapis.com:443/youtubei/v1/browse?key=..., a chave 49399797 foi encontrada na posição 4465 e a chave 50195462 na posição 4477
  • Em um smoke test O(n), os 1,8 MiB de dados Protobuf foram varridos uma única vez sem memória adicional
    • o alvo foi encontrado no byte de número 30.593 entre 1,8 MiB
    • com cerca de 600 bytes de backtracking, foi encontrada a chave de campo a ser desnaturada
  • Quando esse método passou a funcionar, não foi mais necessário bloquear URLs contendo *.googleadservices.com ou /pagead/, e essas requisições deixaram de ocorrer desde a origem

Estrutura do script add-on do MITMProxy

  • O script add-on do MITMProxy é fornecido como uma prova de conceito para bloquear anúncios do YouTube em dispositivos Apple conectados à rede
    • O nome do arquivo é youtube.py
    • Exemplo de execução: mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py"
    • Os pré-requisitos no FreeBSD são pkg install protobuf, pkg install py38-pip, pip install jsonpath-ng
  • O script inclui uma função de fairness que permite 5% dos anúncios para apoiar criadores de conteúdo
    • in_allowed_ads_window() ignora o bloqueio de anúncios quando o horário atual está entre 0 e 2 minutos de cada hora
  • YouTubeAdBlocker intercepta domínios relacionados ao YouTube e modifica respostas JSON ou Protobuf para remover informações de anúncios
    • A regex de host interceptado é \.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com
    • A string usada para detectar anúncios em Protobuf é b"/pagead/"
    • O limite de busca é 80_000 bytes
    • A tag de campo alvo é 50195462
  • A lista de bloqueio na etapa de requisição inclui pagead/, log_event?, stats/ads, stats/qoe?, ptracking?, generate_204, error_204, adformat=, activeview?, _ad_, ai?, sw.js e outros em hosts do YouTube
  • A substituição de JSON para o YouTube na web remove ou desativa campos relacionados a anúncios
    • yt_ad é alterado para "0"
    • adPlacements é alterado para []
    • adPlacementRenderer, adPlacementConfig, playerAdParams, gutParams são alterados para {}
    • adVideoId é alterado para ""
    • showCompanion, showInstream, useGut são alterados para False
  • O hook load() desativa HTTP/2 e define anticomp=True, mode="transparent"
  • O hook running() atualiza allow_hosts para que a interceptação se aplique apenas a domínios relacionados ao YouTube
  • O hook response() procura /pagead/ nos primeiros 80.000 bytes do corpo da resposta quando content-type contém protobuf
    • Se encontrar, cria os bytes da tag alvo com TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED)
    • Cria novos bytes de tag para target_field_tag - 1
    • Faz uma busca reversa antes da posição de /pagead/ para localizar a tag alvo
    • Substitui os bytes nessa posição pelos bytes correspondentes a target_field_tag - 1
    • Reinsere o conteúdo Protobuf modificado com flow.response.set_content(bytes(body))
  • Os comentários no código afirmam que esse PoC já bloqueia 90% dos anúncios e acrescentam que outras seções também podem ter outras chaves de campo e várias seções de anúncios a serem neutralizadas

Desempenho, limitações e público-alvo

  • A técnica final aproveita o fato de o Protobuf permitir campos desconhecidos para manter compatibilidade retroativa em mudanças de schema, além da sensibilidade a edição de um único byte do formato compacto
  • Ao alterar 1 byte em um ponto crítico para que uma seção profundamente aninhada pareça pertencer a uma versão futura do schema, o Protobuf a ignora e assim pode remover as informações de anúncios
  • O Google retorna uma grande resposta Protobuf que inclui até o layout do app iOS, e o payload de exemplo tem 1.8MiB
  • Para fazer parse do payload inteiro seria necessário código nativo como C++/Swift, e a decodificação em Python seria várias ordens de magnitude mais lenta, causando timeout de conexão
  • O JSON baseado na web exige parse, edição e reserialização do payload inteiro, mas a técnica com Protobuf faz apenas uma varredura linear e um backtrack rápido, então processa em microssegundos, sendo adequada para bloqueio de anúncios em tempo real e sem precisar de blocklist
  • Todas as URLs *.googleadservices.com e /pagead/* em dispositivos Apple vêm do payload Protobuf, então, quando os dados de anúncios desaparecem do payload, essas requisições também desaparecem automaticamente
  • O app do YouTube não tenta buscar as URLs de anúncios, então parece mais rápido, e como o anúncio não é registrado no slot de vídeo, o conteúdo começa a reproduzir imediatamente
  • Esse método é apresentado como uma técnica altamente especializada para bloquear anúncios do YouTube em dispositivos Apple ou tráfego de rastreamento do Instagram, WhatsApp e Facebook
  • Diz-se que a carga de CPU para descriptografar e recriptografar tráfego HTTPS supera em muito a capacidade de um Raspberry Pi
  • Como o alvo são donos de dispositivos Apple que não querem comprometer o sistema operacional, considera-se que o público é ainda mais restrito

YouTube Premium e experimento sobre o custo dos anúncios

  • O autor considera incerto se o preço do YouTube Premium, de CAD $9.99/mês ou CAD $11.99/mês e cerca de CAD $13.43/mês com impostos, é razoável
  • Foi feito um experimento de exposição a anúncios assistindo YouTube de forma intermitente durante um dia com um notebook limpo e navegação privada
    • No histórico de exibição, havia apenas 10 vídeos “assistidos”
    • Durante a visualização de partes desses 10 vídeos, houve exposição a 8 anúncios
    • Apenas 2 anúncios eram puláveis, e ambos foram pulados
  • Assumindo um CPV aproximado de USD $0.15, 8 anúncios por dia representam um custo para os anunciantes de 8 x $0.15 = $1.20; extrapolando para um mês, isso dá cerca de USD $36/mês
  • Também é apresentado um cálculo com base em dados da Statista, dividindo o gasto com publicidade nos EUA pelo total de visualizações
    • Em 2019, anunciantes dos EUA gastaram $15.1 billion no YouTube
    • Residentes dos EUA teriam assistido a 916 billion vídeos
    • A média é $15.1B / 916B = USD $0.0165 per view
    • No caso do autor, isso equivaleria a cerca de USD $0.13 por dia e cerca de USD $3.96 por mês em custo para anunciantes
  • Durante o experimento com anúncios, o autor deixou o hardware no mudo e desviou o olhar com frequência, então considera que o dinheiro gasto com anúncios exibidos para ele foi desperdiçado
  • Ainda assim, diz querer apoiar os criadores e que vai testar o trial de 3 meses do Premium, enquanto continua monitorando o que o Google rastreia sobre ele
  • Ele também manifesta preocupação de que, a partir do momento em que uma reivindicação DMCA é registrada, toda a receita de anúncios possa ir para o reclamante em vez do criador, acrescentando que não surpreende que muitos criadores migrem para o Patreon

Resumo final

  • Configurou um roteador de hardware desde o início e separou a LAN em zonas confiáveis/não confiáveis
  • Configurou o bloqueio tradicional de anúncios por DNS
  • Adicionou um proxy MITM transparente
  • Por fim, conseguiu bloquear anúncios do YouTube com bom desempenho em dispositivos Apple conectados à rede
  • Escreve que, como a parte difícil acabou, vai considerar assinar o YouTube Premium, embora os rastreadores continuem sendo fortemente bloqueados

1 comentários

 
GN⁺ 2025-03-19
Opiniões no Hacker News
  • Mais do que uma falha no formato Protobuf, parece que o autor mudou o número do campo para um número alto não usado
    O método consiste em procurar, nos bytes Protobuf, assinaturas de URLs de anúncios como /pagead/ para delimitar o intervalo do campo e, dali, voltar para encontrar a tag do campo-alvo e a chave do campo para neutralizá-lo; isso parece mais um comportamento intencional do que uma falha
    Se você vai fazer o esforço de encontrar a tag, ler o comprimento varint logo ao lado e pular aqueles bytes também não é um grande trabalho extra. Seria preciso copiar o buffer ou deslocar bytes, mas o script PoC também já precisa copiar, porque os bytes retornados pela API do mitmproxy são imutáveis

    • No nível do protocolo, ele se comporta como esperado, mas a brecha parece estar no fato de o Google não gerar erro quando aparece um campo desconhecido na estrutura de dados de anúncios e tratar como se não houvesse anúncios
      Antes de mudar o protocolo a ponto de fazer anúncios deixarem de aparecer em todas as versões antigas do app, o Google vai distribuir um app novo primeiro; então apenas uma fixação de certificado básica ou uma decodificação menos tolerante a falhas na extração de informações de anúncios já bastaria para bloquear esse método imediatamente. A equipe do YouTube provavelmente veria isso como uma falha
    • Objetos bytes são imutáveis, mas objetos bytearray não são
  • Um pequeno proxy em C++/Go também conseguiria fazer a mesma coisa com muito menos overhead. Para uma tarefa tão bem definida, é mais estável e dá menos trabalho do que brigar com o mitmproxy
    Enviar todo o tráfego pelo proxy degrada o desempenho, mesmo usando interceptação de SNI. O pfSense também; com um servidor Linux simples e algumas regras simples de iptables, dá para resolver sem lutar contra as camadas de abstração do pfSense
    Basta escrever em um arquivo .proto só o necessário dos campos proto obtidos por engenharia reversa, gerar o código automaticamente e mudar a flag. É mais barato que uma implementação em Python e mais fácil de atualizar se o proto mudar. Ignorar tags de campos desconhecidos é um recurso importante do Protobuf e permite mudanças de schema compatíveis sem quebrar implantações existentes

    • Talvez seja até melhor deixar a experiência do YouTube deliberadamente mais lenta e as trocas de vídeo mais demoradas. Acho que isso reduziria bastante o vício em Shorts, em especial
    • Eu esperaria um post de blog compartilhando em detalhes como fazer isso
    • Seria bom se você mesmo escrevesse um guia sobre onde está a ineficiência e como mitigá-la com software mais simples
      O autor parece já ter percebido boa parte do que está nos comentários, e o texto é bem minucioso. Ele fez benchmarks em Python e C++, e a implementação final nem sequer decodifica Protobuf. Também testou várias soluções mitm, e o pfSense é usado não como um simples roteador de segurança, mas para mirar apenas o tráfego da Apple TV com VLAN e VPN
      Este comentário soa barato e depreciativo demais. O post original não é assim; se vai falar desse jeito, deveria provar por conta própria em benefício da comunidade
    • Tenho curiosidade se alguém recomenda um proxy leve que rode no macOS e também possa atender outros dispositivos da casa
  • Se eu pagar pelo YouTube Premium, estou apoiando os criadores? Se sim, queria saber quanto, em comparação com um apoio direto como Patreon

    • Provavelmente não é muito comparado ao Patreon, mas também é difícil esperar que alguém assine o Patreon de todos os YouTubers que assiste
      A receita que uma assinatura do YouTube Premium gera para um criador individual deve ser pequena, mas ainda é melhor do que assistir aos vídeos com bloqueador de anúncios
    • Pelo que se sabe, os criadores recebem uma fatia maior por visualizações via YouTube Premium do que por visualizações com anúncios comuns. Isso porque, se o anúncio é pulado, não há receita. Ainda assim, há limites, pois há poucos usuários Premium
    • Informações recentes são raras, mas, quando foi lançado como Youtube Red, em geral pagava muito mais por visualização do que a receita de anúncios
    • É mais do que anúncios e menos do que Patreon
      Como o critério é tempo de exibição, e não impressões de anúncios, criadores que fazem conteúdo de formato longo levam vantagem
  • A conta do YouTube da minha namorada, estranhamente, não mostra anúncios em nenhum dispositivo em que ela faça login. Inclui Apple TV; ela não tem Premium e nunca teve
    Fico curioso se alguma flag interna foi definida e desativou os anúncios

    • Se você me mandar por DM o nome de usuário e o e-mail da conta, posso verificar e corrigir isso
    • Sua namorada parece estar, na prática, no grupo de controle dos anúncios. Isso pode ser usado para entender que efeito os anúncios têm sobre os usuários, comparando com o comportamento de quem vê anúncios
    • Pode ser que ela esteja em um holdback experiment. É comum colocar parte dos usuários em um grupo de retenção para ver que impacto uma funcionalidade, como veiculação de anúncios, tem nas métricas; eu também fazia esse tipo de experimento quando trabalhava no Google
    • Há muito tempo, uma assinatura do Google Music desativava anúncios no YouTube. Mesmo depois que o serviço foi encerrado ou a assinatura cancelada, os anúncios do YouTube não voltaram por mais de seis meses
      Só então veio aquele momento de entender por que as pessoas reclamavam
    • Estou tendo a mesma experiência no Twitch
      Mesmo sem usar bloqueador de anúncios, quando faço login não aparecem anúncios em lugar nenhum, nem no site nem no app móvel. Não tenho Twitch Turbo e também não tenho mais Amazon Prime. Também não tenho outros benefícios do Turbo, então não é como se minha conta estivesse totalmente marcada como Turbo
      Talvez, quando eu fazia bug bounty e mexia em várias coisas, o perfil da conta tenha sido corrompido por acaso; não sei. Mas, se mantiverem esse benefício, posso até fornecer mais detalhes
      O estranho é que eu me lembro de uma vez, no hospital, dopado de remédios e com dor, só querendo ver TV, e fiquei quase em colapso porque os anúncios do Twitch eram insuportáveis. Então, um ou dois anos depois, percebi de repente que não via anúncios havia anos
      Provavelmente existe um teste A/B sem anúncios esquecido há muito tempo, que ficou porque não vale a pena limpar. Isso me beneficiou por anos, e assisto ao Twitch mais do que qualquer outra plataforma. O Twitch Turbo no Reino Unido custa £12 por mês, cerca de US$ 15,50, o que é caro até em escala global; comparado aos US$ 12/€12 dos EUA e da Europa, é um preço bem desvantajoso
  • Foi bem surpreendente perceber que, ao colocar um proxy man-in-the-middle entre a Apple TV e a internet externa, dá para descriptografar o tráfego HTTPS
    Eu achava que isso normalmente não deveria funcionar, e depois fiquei surpreso de novo ao descobrir que era possível adicionar uma CA ao repositório de certificados da Apple TV. Foi um texto minucioso, cobrindo a pilha inteira

    • Especulando sobre por que a Apple permite adicionar certificados, é bem provável que seja para atender a requisitos de TI e gerenciamento de dispositivos em ambientes corporativos ou educacionais, onde a Apple TV é usada como uma caixa de AirPlay
      Por exemplo, em universidades, para conectar um dispositivo ao Wi-Fi era preciso colocar o endereço MAC em uma lista de permissões ou instalar um certificado
    • O Google poderia bloquear facilmente esse método no app do YouTube apenas verificando a CA que assinou o certificado SSL
      Só que, se fizesse isso, o YouTube poderia quebrar em muitos ambientes corporativos, então não sei se realmente fariam. Ainda assim, infelizmente, seria muito fácil bloquear
    • Eu não esperava que fosse possível adicionar uma CA à Apple TV. Acho que eu não sabia porque nunca tentei acessar pela Apple TV um recurso sem uma cadeia de certificados válida
    • A maioria dos dispositivos permite adicionar CAs, mas hoje em dia quase todos os apps usam certificate pinning e ignoram o repositório de certificados do sistema. É muito surpreendente que o YouTube não faça isso
    • Ironicamente, a Android TV, pelo menos na versão 7.x, não permite isso. Descobri da forma difícil tentando contornar um certificado Let's Encrypt não confiável
  • Tentei implementar isso algumas vezes na Apple TV, mas nunca consegui. Parece que agora o YouTube colocou certificate pinning no app, ou algo do tipo. Fico curioso se alguém conseguiu fazer isso funcionar recentemente

    • Se você estiver disposto a investir tempo, pode fuçar no Frida [0]. Certificados fixados também não são um problema
      [0] https://frida.re/docs/home/
  • Gosto de qualquer tentativa de bloquear na rede inteira serviços online ruins que somos meio forçados a usar
    Bloquear anúncios é ótimo, mas eu gostaria que houvesse formas mais fáceis e mais amplas de bloquear na rede inteira aqueles scrolls infinitos agressivos, como YouTube Shorts ou Instagram Reels
    No Instagram, eu só quero ver posts e stories das pessoas que sigo, sem receber recomendações de vídeos idiotas projetados para capturar minha atenção. Talvez isso revele falta de força de vontade, mas com frequência acabo vendo alguns e perco 15 minutos da minha vida

    • Você não é forçado a usar. Pode não usar, e também pode pagar
      Usuários da internet, em geral, escolheram o lado de não querer pagar, então alguém acaba arcando com o custo. No geral, usuários da internet não recompensam quem não mostra anúncios. Querem conteúdo, mas geralmente querem de graça
    • Dá para apagar o app, usar a página web e usar um navegador que permita userscripts
      Encontrei um script que transforma a página do Instagram em algo parecido com uma simples tag de imagem, permitindo ver só as fotos: https://greasyfork.org/en/scripts/5014-un-instagram
    • Vejo essas táticas como uma exploração da nossa curiosidade natural e da estética ao redor dela
      Então, mais do que falta de força de vontade, parece algo mais próximo de uma dessensibilização que fomos acumulando, e isso é bem ruim. O esforço e a criatividade para fazer com que nós voltemos a usar as plataformas, em vez de as plataformas nos fazerem usá-las, merecem respeito
    • Como pai/mãe, isso ressoa especialmente. É difícil ver as crianças caindo no algoritmo
      Converso regularmente com meus filhos, e eles também concordam que é prejudicial, mas acham difícil demais resistir. Até eu às vezes sou puxado para o doomscrolling
      Configurei filtragem de anúncios com Pi-hole onde é possível, mas não quero bloquear o YouTube inteiro. Ainda assim, para proteger a família, acho que vou considerar isso seriamente daqui para frente
    • Para bloquear o scroll infinito do Instagram, este app funcionou muito bem: https://www.distractionfreeapps.com/index.html
  • A engenharia é boa, mas é um pouco triste ter que ir tão longe só para usar seu próprio hardware ou software como se você o possuísse em alguma medida

    • Neste caso, você é dono do dispositivo. Mas não parece haver base para afirmar que é dono do YouTube ou do conteúdo dele também
    • Colocando o APK do NewPipe em uma Android box de 30 dólares, isso já era possível há quase 10 anos
  • O YouTube tem anúncios? O navegador bloqueia tão bem que eu nem sabia
    O verdadeiro problema é que a experiência na Apple TV é muito pior do que a de um navegador web comum. A Apple tranca tanto o hardware que a estrutura acaba beneficiando mais a receita de anúncios do YouTube do que o consumidor final que pagou pelo aparelho

    • No Linux, Windows e Android, não vejo anúncios de jeito nenhum. Quando tento assistir ao YouTube no iPad de vez em quando, fico surpreso com a frequência e o quão irritantes os anúncios são
      O mesmo vale quando navego na web pelo iPad fora da rede com Pi-hole de casa. Não sei como as pessoas suportam isso todos os dias
      O iPad é um dispositivo fornecido pelo trabalho, então não o uso muito para fins pessoais, mas toda vez que uso, lembro como isso é irritante
      Curiosamente, antes de receber o iPad, eu achava que ele só seria útil para consumo de conteúdo; na prática, ele é muito conveniente para acessar rapidamente recursos de trabalho remotamente, mas, para navegação web comum e mídia em streaming, virou um aparelho preso em um deserto coberto de anúncios
  • Se você precisa de YouTube sem anúncios, pode usar https://yewtu.be ou outra instância do Invidious: https://docs.invidious.io/instances/
    Há uma corrida armamentista entre YouTube e Invidious, e às vezes o Invidious não funciona, mas a equipe sempre encontrou novas formas de contornar o YouTube e entregar vídeos sem anúncios

    • Há um motivo para o título incluir “on AppleTV”. Clientes ou front-ends alternativos não rodam lá
    • Em lugares como Roku TV, isso não funciona porque não há navegador