1 puntos por GN⁺ 2025-03-19 | 1 comentarios | Compartir por WhatsApp
  • Es una PoC que coloca un proxy MITM basado en pfSense entre el Apple TV e internet, descifra HTTPS y luego modifica las respuestas Protobuf que entrega YouTube para evitar que se registren espacios publicitarios en dispositivos Apple
  • Los bloqueadores DNS tradicionales y el enrutamiento por VPN chocaban con límites como TTL de DNS, discrepancias de IP, errores 403 y fugas de ASN, porque los anuncios de YouTube y el video principal usan el mismo dominio e infraestructura
  • El experimento con Squid se abandonó por problemas de rendimiento y configuración, y luego se cambió a un enfoque con mitmproxy/mitmdump en un jail de FreeBSD para descifrar tráfico TLS y modificar directamente respuestas JSON y Protobuf
  • En YouTube web se podían eliminar adPlacements, playerAdParams y URLs de pagead del JSON, pero la app de YouTube en iOS guardaba espacios publicitarios e información de rastreo en respuestas application/x-protobuf, así que hubo que trabajar con la estructura Protobuf
  • El método final consiste en una exploración lineal y una modificación de 1 byte: buscar hacia atrás el field tag cerca de la cadena /pagead/ y cambiar etiquetas como 50195462 a target_field_tag - 1, logrando rendimiento apto para procesamiento en tiempo real sin decodificar todo

Objetivo y diseño inicial de red

  • El objetivo era construir un router basado en FreeBSD y pfSense para bloquear a nivel de toda la red los anuncios pre-roll, mid-roll y end-roll de YouTube en Apple TV y iPhone
  • Si se colocaba un proxy man-in-the-middle entre el Apple TV y el internet externo, era posible descifrar el tráfico HTTPS y leer los datos de Protocol Buffers que Google usa para llenar anuncios en YouTube
  • Tras varios meses implementando el bloqueo de anuncios en YouTube, comenzó a pagar YouTube Premium, y aclara que “poder hacerlo” y “deber hacerlo” son cosas distintas
  • Menciona como razones para bloquear anuncios y rastreo la vigilancia de datos personales, el desperdicio de ancho de banda, el clickbait y el cryptojacking
    • Considera que entre el 25% y el 40% del tráfico de red puede ser anuncios, scripts de rastreo, fingerprint.js, googletagmanager.js y cargadores de analítica en tiempo real como Hotjar
    • Explica que JavaScript de cryptominería como CoinHive.js puede sobrecalentar la computadora o explotarla para ganar pequeñas cantidades de dinero

Hardware de pfSense y configuración básica

  • Para proteger toda una red SMB, considera que VM, imágenes de Docker y Raspberry Pi no dan el rendimiento suficiente, por lo que se necesita hardware dedicado encargado solo de enrutamiento, descifrado y monitoreo de paquetes
  • El hardware de router usado fue una mini PC con conjunto de instrucciones AES-NI, RAM DDR4, SSD mSATA y una memoria USB para flashear pfSense
    • Un ejemplo de configuración es una mini PC J4125, 32 GiB de RAM DDR4 y SSD mSATA de 128 GiB
    • Considera que 128 GB de almacenamiento bastan para logs, reducir desgaste del SSD, captura de paquetes y edge cache de NPM y Docker
  • La imagen de instalación de pfSense pesa unos 360 MB y puede grabarse en una memoria USB con Etcher AppImage
  • Después de la configuración inicial, AES-NI aparecía como “Yes (inactive)”, así que se activó manualmente en System › Advanced › Miscellaneous
  • Para aprovechar los 32 GiB de RAM, asignó bastante RAM disk a /var y /tmp, y configuró el SSD de 128 GiB esperando wear-leveling y respaldos horarios del RAM disk
  • En el Dashboard agregó el widget S.M.A.R.T. para poder detectar fallas del SSD

Bloqueo DNS, segmentación de red y pfBlockerNG

  • Antes usaba Pi-hole en una Raspberry Pi como bloqueador de anuncios a nivel DNS, y en pfSense instaló pfBlockerNG-devel para probar bloqueo de anuncios, contenido malicioso y geo-blocking
  • Si el servicio pfb_dnsbl no inicia o la pestaña de estado muestra [ Missing CRON task ], recomienda intentar borrar el archivo vacío /var/run/booting
  • Aprovechó los 3 puertos Gigabit de la mini PC para crear redes físicas en vez de VLAN y aislar del network principal dispositivos que “llaman a casa”, como Alexa y Apple TV
    • Los dispositivos no confiables se colocan en la red privada 172.31.1.0/24
    • La LAN confiable se mantiene en 192.168/16
    • La LAN física para IoT pasa por el adblocker y busca interceptar consultas DNS hard-coded a 1.1.1.1 y 9.9.9.9 para que YouTube no pueda saltarse el bloqueador DNS
  • Configuró reglas NAT para que todos los clientes detrás de pfSense usen el servidor local de Unbound DNS
    • Considera que primero hay que bloquear DNS over TLS para poder interceptar consultas DNS
    • Dice que el iPhone puede mostrar una Privacy Warning por bloquear tráfico DNS cifrado, pero que las consultas DNS upstream sí van cifradas hacia Cloudflare
    • Recomienda desactivar NAT reflection para que internet externo no pueda acceder al servidor DNS
  • Creó un alias de firewall Non_WAN para redirigir al localhost el puerto 53 de consultas DNS locales en interfaces distintas de WAN
  • Como YouTube entrega los anuncios y el video principal desde el mismo dominio, era difícil filtrar solo los anuncios con bloqueadores por nombre de dominio como pfBlockerNG o Pi-hole

Experimentos de desvío por VPN y puntos de falla

  • También hizo pruebas para engañar al algoritmo publicitario de YouTube y parecer un usuario menos atractivo para anunciantes, en vez de bloquear anuncios directamente
    • Quería enrutar por VPN el tráfico de geolocalización de YouTube desde el router pfSense hacia una región con menos audiencia
    • Se fijó la meta de que YouTube identificara la cuenta como “hombre de 70 años, residente en Italia”
  • En pfSense usó WireGuard en vez de OpenVPN para un experimento base que enviara todo el tráfico del Apple TV por la VPN
    • Instaló el paquete WireGuard de FreeBSD y agregó y activó un túnel
    • Para la configuración de NordLynx, revisó la private key en una VM Linux con sudo wg showconf nordlynx y la trasladó a pfSense
  • En las pruebas, Google en la laptop aparecía en italiano, y YouTube en el Apple TV también cambió a italiano
    • Los anuncios seguían apareciendo en parte, pero según dice eran menos que antes
    • Netflix y Amazon Prime presentaron problemas, y parecía que se bloqueaban archivos CSS o fuentes, o que no cargaban las miniaturas
    • Advierte que no se debe enviar todo el tráfico del Apple TV por la VPN, y considera que Netflix y Prime detectan muy bien a los proveedores de VPN y el geofencing
  • Después configuró reglas de política de firewall para pasar por VPN solo el tráfico de YouTube del Apple TV, apuntando a www.youtube.com, youtube.com, googlevideo.com, accounts.google.com, googleapis.com y gstatic.com
    • Como resultado, YouTube veía al usuario como si estuviera en Milán, mientras que Netflix y Prime Video lo veían en Canadá
    • Los anuncios se redujeron hasta ser muy esporádicos
  • Un día después apareció una race condition de DNS
    • Por defecto, el alias de hostname de pfSense se resuelve cada 300 segundos
    • El TTL de DNS de YouTube puede ser de 1,440 segundos, es decir, 24 minutos
    • Si la IP resuelta por Alias Daemon no coincide con la IP real que recibió el cliente, la política puede no tunelizar el tráfico de YouTube
  • Algunos videos de YouTube no se reproducían y devolvían 403 Forbidden
    • Dice que YouTube incrusta la IP del usuario en cada solicitud a googlevideo.com
    • Si dominios transformados como r5---sn-hpa7kn76.googlevideo.com no pasan por el túnel, la solicitud sale con una IP incorrecta y causa el problema
    • Lo necesario sería tunelizar con comodín *.googlevideo.com, pero las reglas de NAT y firewall operan con IP, no con hostnames comodín

PoC de rastreo de IP basado en consultas DNS

  • Se ideó un enfoque de secuestro de consultas DNS de Google Video para enrutar *.googlevideo.com por la VPN
    • Consistía en rastrear periódicamente el log de consultas DNS para añadir las consultas *.googlevideo.com a la lista de alias
    • Se consideró que, si cada video usaba un dominio único y modificado, este método no funcionaría a menos que se refrescara para cada video
  • El nuevo objetivo fue vigilar las consultas DNS con Python 3 y la API REST de pfSense para capturar la IP, retener brevemente la respuesta, añadir la IP a la regla de tunelización VPN y luego liberar la respuesta DNS
  • Se instaló la API REST de pfSense y se envió una solicitud GET a https://pfsense/api/v1/firewall/alias para consultar el alias VPN_domains
  • Se exploró el módulo de Python de Unbound DNS Resolver y se logró registrar mensajes de consulta DNS
    • La versión actual de Python era 3.8
    • Se consideró que los ejemplos del módulo Python de Unbound estaban basados en Python 2.4, por lo que podrían requerir 2to3 o formateo
  • El script PoC extrae las IP de los registros A/AAAA de la respuesta DNS y las añade al alias de pfSense
    • Los registros A se procesan con ipaddress.IPv4Address(d.rr_data[j][2:]).exploded
    • Los registros AAAA se procesan con ipaddress.IPv6Address(d.rr_data[j][2:]).exploded
    • El TTL del alias se configuró en 1 hora y la capacidad en 500
  • Al día siguiente, Unbound DNS Resolver sufrió un segfault, y como había que recargar la regla de pfSense cada vez que se añadía una IP, pfSense se volvió muy lento

Cambio de Squid a mitmproxy

  • El nuevo objetivo cambió a investigar e instalar un proxy de la familia Squid, crear un certificado de CA falso pero confiable y descifrar el tráfico TLS
  • En las pruebas con Squid, se evaluó si el proxy squid3 ofrecido como paquete de pfSense cumplía los requisitos
    • Se creó una carpeta exclusiva /squid_cache y se configuró el tamaño de caché en 8GiB
    • Se esperaba soporte HTTPS transparente
  • Tras configurar Squid y SquidGuard durante un día, se abandonó el intento
    • La velocidad era muy lenta
    • La configuración de ACL era engorrosa
    • Había un problema relacionado con https://http/*
    • La actualización de la lista de filtros de URL de SquidGuard tardaba muchísimo
    • La UI de Squid era insuficiente
  • Después se decidió usar mitmproxy, escrito en Python
    • Se eligió mitmproxy en lugar de SSLSplit por la extensibilidad mediante hooks de Python y por la UI
    • La versión de FreeBSD de pfSense era 12.2-Stable, compilación de 64 bits
  • En el entorno base de pfSense, los jail estaban deshabilitados, así que se instaló ezjail manualmente y se creó un jail para mitmproxy
    • El jail se creó con ezjail-admin create mitmproxy 'lo0|127.0.1.1'
    • Se configuró allow.raw_sockets=1 para el modo de proxy transparente
    • Se indicó que, si los raw sockets estaban bloqueados, podían aparecer errores como Transparent mode failure o Cannot open connection, no hostname given.
  • La ejecución del binario Linux tarball falló en FreeBSD
    • Apareció ELF interpreter /lib64/ld-linux-x86-64.so.2 not found
    • Tampoco se pudieron encontrar libdl.so.2, libz.so.1, libpthread.so.0, libc.so.6
  • Dentro del jail se ejecutó pkg install mitmproxy, y la instalación requirió 50 paquetes, 206MiB de espacio adicional y 33MiB de descarga
  • Para permitir el acceso a MITMProxy desde la LAN, se asignó la IP virtual 127.0.1.1 al localhost y se configuró temporalmente una regla NAT para redirigir [Private IPs]:8080 a 127.0.1.1:8080
  • El archivo PEM de la CA generado automáticamente por MITMProxy es ~/.mitmproxy/mitmproxy-ca-cert.pem, y este certificado de CA se instaló en el Trusted Root Store del dispositivo de prueba
  • mitmproxy usaba mucho CPU incluso en estado idle, y se consideró que la generación en tiempo real de certificados TLS por solicitud y el logging excesivo ralentizaban mucho el rendimiento
    • Se consideró que mitmdump, al omitir la UI y el logging excesivo, tenía menor carga de CPU

Eliminación de anuncios JSON en YouTube web

  • Certificate Pinning es una técnica en la que el servidor o el cliente conoce de antemano la huella digital esperada del certificado, por lo que la falsificación de certificados de MITMProxy no funciona
  • Los hosts problemáticos se pueden hacer pasar por fuera del proxy con la opción --ignore-hosts
    • Como ejemplo, se ignoraron apple.com:443 e icloud.com:443
  • Mientras se accedía a YouTube, los anuncios de la página aparecían en MITMProxy con headers no cifrados, y se evaluó la posibilidad de bloquearlos con regex simples
  • Para aplicar el script de bloqueo de anuncios de YouTube, se añadió --scripts "youtube.py" a mitmdump
  • El filtro smoke-test bloquea solicitudes de anuncios según subcadenas en la URL
    • youtube.com: /pagead/, /log_event?, /stats/ads, /stats/qoe?, /ptracking?, /generate_204, el=adunit, adformat=, /activeview?
    • google.com, google.ca: /pagead/
    • ggpht.com: .
  • Las solicitudes que se intentaban bloquear efectivamente parecían estar bloqueadas tanto en MITMProxy como en el panel Network de DevTools, pero los anuncios seguían apareciendo y a veces se saltaban solos o fallaban al reproducirse
  • Después se encontraron muchas URL relacionadas con anuncios dentro del payload JSON
  • Tras analizar la UI de YouTube y el flujo HTTP, incluyendo cookies y service workers, se afirmó que ya era posible eliminar anuncios pre-roll, post-roll y mid-video
  • En esta etapa, ya se podían eliminar anuncios desde el payload JSON de anuncios web de YouTube mediante el router

Problema de Protobuf en YouTube para iOS

  • La app de YouTube para iOS mostraba datos muy similares en la versión Protobuf de las mismas llamadas API que la versión web
  • En Protobuf, las claves son numéricas y pueden cambiar, así que no se puede usar un enfoque como JSONPath para encontrar la sección de anuncios
  • YouTube enviaba en el payload una gran lista de anuncios que se verían después, y cuando esa lista se agotaba, poco después llegaba otra gran lista
  • En el payload Protobuf aparecían cadenas como “Telus”, “Samsung TV”, “Boxing Week” y “Buy now”
  • El protocolo de YouTube para iOS era distinto del tráfico web
    • en la versión web se podían distinguir hasta cierto punto el video del anuncio y el video deseado viendo la URL y el query parameter range
    • el protocolo de iOS no usaba ni el query parameter range ni el header Range, sino contadores como &nr=2 y &nr=3 en los chunks de video
    • para bloquear anuncios en iOS fue necesario hacer reverse engineering de la respuesta Protobuf
  • En el mensaje Protobuf decodificado se encontraron los campos has_unlimited_entitlement: False y has_premium_lite_entitlement: False, pero en vez de alternarlos se volvió a usar heurísticas
  • Decodificar unos 500 KiB de Protobuf crudo con una implementación pura en Python era muy lento
    • en una desktop con i7-6700, el resultado en Python fue de unos 2.06~2.11 segundos
    • en un router pfSense, el resultado en Python fue de unos 22.8~24.2 segundos
    • en C++ con protoc --decode_raw fue de unos 0.017~0.022 segundos en desktop y de unos 0.12~0.14 segundos en el router pfSense

Decodificación de Protobuf e intento de extraer el esquema

  • Como Python no soportaba decodificación raw de Protobuf, en vez de usar directamente libprotobuf.so de C++ se eligió comunicarse con el binario protoc de C++ mediante subprocess.Popen
  • Se hizo fuzzing de respuestas de videos publicitarios probando 200 vacíos, 404, 503, cuerpos de respuesta truncados y anulando parcialmente videos de anuncios, pero la app de iOS se ralentizaba, luego se cerraba o se quedaba congelada en la pantalla del anuncio
  • Bloquear URLs activaba el comportamiento de respuesta de la app, y los chunks de respuesta de video también incluían metadatos de sesión
  • blackboxprotobuf para Burp Suite permitía decodificar mensajes wire raw de Protobuf, inyectar contenido y volver a codificarlo para verificar el comportamiento del endpoint Protobuf
    • se recomienda usar la versión original para Burp Suite y no el fork de PyPI
    • algunos forks tienen problemas de stack overflow o recursión infinita por recursión profunda
    • usando bindings de C++ se podía transcodificar un Protobuf raw de unos 500 KiB en apenas unos segundos
  • El esquema generado no era perfecto, era grande, estaba profundamente anidado y su pretty-print era lento, pero bastaba para encontrar los detalles del anuncio
  • Se probaron PBTK, Apktool, dex2jar y Java Decompiler para intentar extraer archivos .proto o de esquema reales del APK de YouTube para Android
    • PBTK solo extrajo un archivo proto de 59 bytes
    • había clases Protobuf y getter/setter en Java, pero no se obtuvieron los verdaderos archivos de esquema, así que se abandonó ese camino

Punto de inflexión final: cambiar 1 byte del field tag de Protobuf

  • A partir del tráfico de red descifrado y el fuzzing de Protobuf, se observó que los anuncios se registraban en slots de videos específicos
    • los tipos de slot incluían pre-roll, mid-roll, end-roll, full-page y ad pods
    • si se bloqueaba la URL del anuncio, ocurría un error del tipo “un anuncio inexistente reservó el slot” y la UI entraba en pánico
  • Se consideró problemático decodificar, editar y volver a codificar sin el esquema original, porque eso producía una codificación modificada y no se podía saber si se usaba ZigZag, ni tipos numéricos como int32, int64, sint32/64 o varint, además de que el orden de los campos del objeto normalmente es no determinista
  • Se encontró una posible vía de evasión en la backward compatibility de Protobuf y en el comportamiento de UnknownFieldSet
    • cuando software antiguo lee un mensaje al que se le agregó un campo nuevo, pueden aparecer unknown fields
    • si se cambiaba la clave de un campo específico por otro valor, toda la subestructura que contenía anuncios e información de tracking podía quedar en estado unavailable
  • Como ejemplo, se propuso la idea de cambiar la field key 49399797 a 49399796 para que esa subestructura de anuncio/tracking se tratara como un unknown field
  • La field key 49399797 no se podía encontrar con una simple búsqueda hexadecimal, y había que considerar la codificación varint/tag
    • el wire type era 2, lo que significa una string o mensaje anidado delimitado por longitud
    • la secuencia de bytes del tag para la field key objetivo 49399797 era AA FF B8 BC 01
    • al hacer 395198378 >> 3 para eliminar los 3 bits del wire type se recuperaba la field key original 49399797
  • Se usó una firma clásica de URL publicitaria como /pagead/ dentro de los bytes Protobuf para acotar el rango de búsqueda del campo, y desde esa posición se retrocedía para encontrar el field tag y la field key que había que modificar
  • En un log de intercepción de ejemplo, en una respuesta application/x-protobuf de 1.87 MiB a una solicitud POST a youtubei.googleapis.com:443/youtubei/v1/browse?key=..., se encontró la key 49399797 en la posición 4465 y la key 50195462 en la posición 4477
  • En una smoke test O(n), se escaneó una sola vez un bloque de datos Protobuf de 1.8 MiB sin memoria adicional
    • el objetivo se encontró en el byte 30,593 de los 1.8 MiB
    • con unos 600 bytes de backtracking se encontró la field key que había que desnaturalizar
  • Cuando este método funcionó, ya no fue necesario bloquear URLs con *.googleadservices.com o /pagead/, y esas solicitudes dejaron de generarse desde el principio

Estructura del script add-on de MITMProxy

  • El script add-on de MITMProxy se ofrece como una prueba de concepto para bloquear anuncios de YouTube en dispositivos Apple conectados a la red
    • El nombre del archivo es youtube.py
    • Un ejemplo de ejecución es mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py"
    • Los prerrequisitos en FreeBSD son pkg install protobuf, pkg install py38-pip, pip install jsonpath-ng
  • El script incluye una función de equidad que permite el 5% de los anuncios para apoyar a los creadores de contenido
    • in_allowed_ads_window() omite el bloqueo de anuncios si la hora actual está entre el minuto 0 y el minuto 2 de cada hora
  • YouTubeAdBlocker intercepta dominios relacionados con YouTube y modifica respuestas JSON o Protobuf para eliminar la información de anuncios
    • La regex de hosts interceptados es \.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com
    • La cadena usada para detectar anuncios en Protobuf es b"/pagead/"
    • El límite de búsqueda es 80_000 bytes
    • El tag del campo objetivo es 50195462
  • La lista de bloqueo en la fase de solicitud incluye pagead/, log_event?, stats/ads, stats/qoe?, ptracking?, generate_204, error_204, adformat=, activeview?, _ad_, ai?, sw.js y otros en hosts de YouTube
  • Los reemplazos JSON para YouTube web eliminan o desactivan campos relacionados con anuncios
    • yt_ad se cambia a "0"
    • adPlacements se cambia a []
    • adPlacementRenderer, adPlacementConfig, playerAdParams, gutParams se cambian a {}
    • adVideoId se cambia a ""
    • showCompanion, showInstream, useGut se cambian a False
  • El hook load() desactiva HTTP/2 y establece anticomp=True, mode="transparent"
  • El hook running() actualiza allow_hosts para que la interceptación se aplique solo a dominios relacionados con YouTube
  • El hook response() busca /pagead/ dentro de los primeros 80,000 bytes del cuerpo de la respuesta cuando content-type contiene protobuf
    • Si lo encuentra, crea los bytes del tag objetivo con TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED)
    • Crea nuevos bytes con los bytes del tag de target_field_tag - 1
    • Busca en reversa antes de la posición de /pagead/ para encontrar el tag objetivo
    • Reemplaza los bytes en esa posición por los bytes correspondientes a target_field_tag - 1
    • Vuelve a insertar el contenido Protobuf modificado con flow.response.set_content(bytes(body))
  • Los comentarios del código indican que esta prueba de concepto ya bloquea el 90% de los anuncios, y añaden que en otras secciones también hay otras claves de campo y podría haber varias secciones de anuncios que deban desactivarse

Rendimiento, límites y usuarios objetivo

  • La técnica final aprovecha que Protobuf permite campos desconocidos para mantener compatibilidad hacia atrás ante cambios de esquema y la sensibilidad a ediciones de un solo byte de su formato compacto
  • Si se cambia 1 byte en un punto crítico para que una sección profundamente anidada parezca pertenecer a una versión futura del esquema, Protobuf la ignorará y podrá eliminar la información de anuncios
  • Google devuelve una gran respuesta Protobuf que incluso incluye el layout de la app de iOS, y el payload de ejemplo es de 1.8 MiB
  • Para parsear todo el payload haría falta código nativo como C++ o Swift, y se dice que la decodificación en Python es varios órdenes de magnitud más lenta, provocando timeouts de conexión
  • El JSON basado en web requiere parsear, editar y volver a serializar todo el payload, pero la técnica con Protobuf solo necesita un escaneo lineal y un retroceso rápido, así que se procesa en microsegundos, lo que la hace apta para bloqueo de anuncios en tiempo real y sin necesidad de una blocklist
  • Todas las URL *.googleadservices.com y /pagead/* en dispositivos Apple provienen del payload Protobuf, así que si desaparecen los datos de anuncios del payload, esas solicitudes también desaparecen automáticamente
  • La app de YouTube no intenta obtener las URL de anuncios, por lo que se siente más rápida y el contenido se reproduce de inmediato porque el anuncio no se registra en el slot de video
  • Este enfoque se presenta como una técnica altamente especializada para bloquear anuncios de YouTube en dispositivos Apple o tráfico de rastreo de Instagram, WhatsApp y Facebook
  • Se afirma que la carga de CPU de descifrar y volver a cifrar tráfico HTTPS supera ampliamente el rendimiento de una Raspberry Pi
  • Como está dirigido a dueños de dispositivos Apple que no quieren comprometer su sistema operativo, se considera que el público objetivo es aún más limitado

YouTube Premium y experimento sobre el costo de los anuncios

  • Se considera incierto si YouTube Premium tiene un precio razonable cuando cuesta CAD $9.99/mes o CAD $11.99/mes, y aproximadamente CAD $13.43/mes con impuestos
  • Se realizó un experimento de exposición a anuncios viendo YouTube de forma intermitente durante un día con una laptop limpia y navegación privada
    • En el historial de reproducción solo había 10 videos “vistos”
    • Mientras veía partes de esos 10 videos, estuvo expuesto a 8 anuncios
    • Solo 2 anuncios se podían omitir, y ambos fueron omitidos
  • Suponiendo un CPV aproximado de USD $0.15, 8 anuncios al día equivalen a un costo para los anunciantes de 8 x $0.15 = $1.20, y extrapolado a un mes da unos USD $36/mes
  • También se presenta un cálculo basado en datos de Statista dividiendo el gasto publicitario en EE. UU. entre el total de vistas
    • En 2019, los anunciantes de EE. UU. gastaron $15.1 billion en YouTube
    • Se dice que los residentes de EE. UU. vieron 916 billion de videos
    • El promedio es $15.1B / 916B = USD $0.0165 per view
    • En su caso, calcula que eso equivale a unos USD $0.13 por día y unos USD $3.96 al mes en costo para los anunciantes
  • Durante el experimento de anuncios tuvo el hardware en silencio y apartó la vista con frecuencia, por lo que considera que el gasto publicitario dirigido a él fue desperdiciado
  • Aun así, dice que quiere apoyar a los creadores y que probará la prueba de 3 meses de Premium mientras sigue monitoreando qué rastrea Google sobre él
  • Le preocupa que desde el momento en que se presenta un reclamo DMCA, todos los ingresos publicitarios puedan ir al reclamante y no al creador, y añade que no sorprende que muchos creadores se muden a Patreon

Resumen final

  • Configuró un router de hardware desde cero y separó la LAN en zonas de confianza y no confiables
  • Configuró el bloqueo de anuncios tradicional por DNS
  • Añadió un proxy MITM transparente
  • Finalmente, dice que logró bloquear anuncios de YouTube con buen rendimiento en dispositivos Apple conectados a la red
  • Escribe que, como la parte difícil ya terminó, considerará pagar YouTube Premium, aunque añade que los rastreadores siguen bloqueados con fuerza

1 comentarios

 
GN⁺ 2025-03-19
Opiniones en Hacker News
  • Más que una falla del formato Protobuf, parece que el autor cambió el número de campo por un número grande sin usar
    El método consiste en buscar firmas de URL de anuncios como /pagead/ en los bytes de Protobuf para delimitar el rango del campo y, desde ahí, retroceder para encontrar la etiqueta del campo objetivo y la clave del campo para neutralizarlo. Pero esto se acerca más a un comportamiento previsto que a una falla
    Si uno ya se toma el trabajo de encontrar la etiqueta, tampoco es mucho trabajo adicional leer la longitud varint que está justo al lado y saltarse esos bytes. Habría que copiar el búfer o desplazar bytes, pero como los bytes que devuelve la API de mitmproxy son inmutables, el script PoC ya tiene que hacer copias

    • A nivel de protocolo funciona como se espera, pero la debilidad parece estar en que, cuando aparece un campo desconocido en la estructura de datos de anuncios, Google no arroja un error y lo trata como si no hubiera anuncios
      Antes de cambiar el protocolo hasta el punto de que los anuncios dejen de aparecer por completo en versiones antiguas de la app, Google seguramente distribuiría primero una app nueva; así que con fijación básica de certificados o con una decodificación menos tolerante ante fallas al extraer información de anuncios podrían bloquear este método de inmediato. Si fuera el equipo de YouTube, probablemente vería esto como una falla
    • Los objetos bytes son inmutables, pero los objetos bytearray no lo son
  • Con un pequeño proxy en C++/Go también se podría hacer lo mismo con mucha menos sobrecarga. Para una tarea tan bien definida, sería más estable y requeriría menos esfuerzo que pelearse con mitmproxy
    Si envías todo el tráfico por el proxy, el rendimiento cae incluso usando interceptación de SNI. Con pfSense pasa lo mismo: con un servidor Linux simple y unas reglas sencillas de iptables se puede resolver sin pelear contra las capas de abstracción de pfSense
    Basta con escribir en un archivo .proto solo los campos del proto obtenidos por ingeniería inversa que hagan falta, generar el código automáticamente y cambiar el flag. Es más barato que la implementación en Python y más fácil de actualizar aunque cambie el proto. Ignorar etiquetas de campos desconocidas es una función importante de Protobuf, y permite cambios de esquema compatibles sin romper despliegues existentes

    • Quizá sea mejor hacer que la experiencia de YouTube sea deliberadamente más lenta y que cambiar de video tarde más. En especial, parece que reduciría bastante lo adictivo de Shorts
    • Espero una entrada de blog que comparta en detalle cómo hacerlo
    • Sería bueno que escribieras tú mismo una guía sobre dónde están las ineficiencias y cómo mitigarlas con software más simple
      El autor parece haber estado al tanto de gran parte de los comentarios, y el artículo es bastante minucioso. Hizo benchmarks en Python y C++, y la implementación final ni siquiera decodifica Protobuf. También probó varias soluciones mitm, y usa pfSense no como un simple router de seguridad, sino para apuntar solo al tráfico del Apple TV mediante VLAN y VPN
      Este comentario se siente demasiado barato y despectivo. El artículo original no es así, así que si vas a decir eso deberías demostrarlo tú mismo para la comunidad
    • Me pregunto si hay alguna recomendación de proxy liviano que corra en macOS y también pueda servir a otros dispositivos de la casa
  • Si pago YouTube Premium, ¿eso apoya a los creadores? Si es así, me pregunto cuánto en comparación con un apoyo directo como Patreon

    • Probablemente no sea mucho comparado con Patreon, pero tampoco es realista esperar que alguien se suscriba al Patreon de todos los YouTubers que ve
      Lo que una suscripción a YouTube Premium le genera a un creador individual será mínimo, pero sigue siendo mejor que ver videos con bloqueador de anuncios
    • Se sabe que los creadores reciben una porción mayor por visualizaciones de YouTube Premium que por visualizaciones normales con anuncios. Porque si saltas los anuncios, no hay ingresos. Aun así, como hay pocos usuarios Premium, sigue habiendo límites
    • La información reciente escasea, pero cuando se lanzó originalmente como YouTube Red, por lo general pagaba bastante más por visualización que los ingresos por anuncios
    • Más que los anuncios y menos que Patreon
      Como se basa en el tiempo de reproducción y no en impresiones de anuncios, favorece más a los creadores de contenido de formato largo
  • La cuenta de YouTube de mi novia, curiosamente, no muestra anuncios sin importar en qué dispositivo inicie sesión. Incluye Apple TV, no es Premium y nunca lo fue
    Me pregunto si internamente tiene algún flag activado que deshabilita los anuncios

    • Si me mandas por DM el nombre de usuario y el correo de la cuenta, puedo revisarlo y arreglarlo
    • Parece que tu novia, en la práctica, quedó en el grupo de control de los anuncios. Podría servir para comparar su comportamiento con el de quienes ven anuncios y entender qué efecto tienen los anuncios en los usuarios
    • También podría estar en un experimento de retención (holdback experiment). Es común dejar a algunos usuarios en un grupo de retención para ver qué efecto tiene en las métricas una función como la ejecución de anuncios, y cuando trabajaba en Google también hicimos experimentos así
    • Hace mucho tiempo, tener una suscripción a Google Music desactivaba los anuncios de YouTube. Incluso después de que el servicio se cerró o cancelé la suscripción, los anuncios de YouTube no volvieron durante más de seis meses
      Recién entonces llegó el momento en que entendí por qué la gente se quejaba
    • Me pasa lo mismo en Twitch
      Aunque no uso bloqueador de anuncios, con solo iniciar sesión no veo anuncios ni en el sitio web ni en la app móvil. No tengo Twitch Turbo y ya no tengo Amazon Prime. Tampoco tengo otros beneficios de Turbo, así que no es que esté marcado completamente como Turbo
      No sé si, cuando antes hacía bug bounty y toqueteaba varias cosas, el perfil de mi cuenta se rompió por casualidad, pero si me dejan conservar este beneficio, puedo dar más información
      Lo raro es que recuerdo que una vez, en el hospital, drogado con medicamentos y con dolor, solo quería ver TV, pero los anuncios de Twitch eran tan insoportables que casi tuve un colapso. Luego, uno o dos años después, de pronto me di cuenta de que no había visto anuncios en años
      Probablemente exista algún test A/B sin anuncios olvidado hace tiempo y quedó ahí porque no vale la pena limpiarlo. Gracias a eso me beneficié durante años y veo Twitch más que cualquier otra plataforma. Twitch Turbo en el Reino Unido cuesta £12 al mes, unos $15.50, así que está entre los caros a nivel mundial; comparado con los $12/€12 de EE. UU. y Europa, es un precio bastante desfavorable
  • Fue bastante sorprendente que, si se pone un proxy man-in-the-middle entre el Apple TV e Internet externo, se pueda descifrar el tráfico HTTPS.
    Normalmente pensaría que no debería funcionar, y después me volvió a sorprender saber que se puede agregar una CA al almacén de certificados del Apple TV. Fue un artículo muy minucioso que recorre todo el stack.

    • Si tuviera que adivinar por qué Apple permite agregar certificados, probablemente sea para cumplir con requisitos de TI y administración de dispositivos en entornos empresariales o educativos donde se usa el Apple TV como caja de AirPlay.
      Por ejemplo, en universidades había que poner la dirección MAC en una lista de permitidos o instalar un certificado para conectar un dispositivo al Wi-Fi.
    • Google podría bloquear fácilmente este método con solo verificar en la app de YouTube qué CA firmó el certificado SSL.
      Pero si hiciera eso, YouTube podría romperse en muchos entornos corporativos, así que no sé si realmente lo harían. Aun así, lamentablemente sería muy fácil bloquearlo.
    • No esperaba que se pudiera agregar una CA al Apple TV. Creo que no lo sabía porque nunca había intentado acceder desde el Apple TV a recursos sin una cadena de certificados válida.
    • La mayoría de los dispositivos permiten agregar CA, pero hoy casi todas las apps usan certificate pinning e ignoran el almacén de certificados del sistema. Es muy sorprendente que YouTube no lo haga.
    • Irónicamente, Android TV no permite esto, al menos en la versión 7.x. Lo descubrí por las malas cuando intentaba saltarme un certificado de Let's Encrypt no confiable.
  • Intenté implementarlo varias veces en Apple TV, pero no tuve ningún éxito. Parece que YouTube ya incorporó certificate pinning en la app o algo por el estilo. Me pregunto si alguien logró hacerlo funcionar recientemente.

    • Si estás dispuesto a dedicarle tiempo, puedes investigar Frida [0]. Los certificados fijados tampoco son un problema.
      [0] https://frida.re/docs/home/
  • Me gusta cualquier intento de bloquear en toda la red esos pésimos servicios en línea que nos vemos obligados a usar.
    Bloquear anuncios está bien, pero ojalá hubiera formas más fáciles y generalizadas de bloquear en toda la red cosas como YouTube Shorts o Instagram Reels, con su scroll infinito agresivo.
    En Instagram solo quiero ver las publicaciones e historias de la gente que sigo, no que me recomienden videos tontos diseñados para robarme la atención. Tal vez esto revele falta de fuerza de voluntad, pero a menudo termino viendo algunos y pierdo 15 minutos de mi vida.

    • No estás obligado a usarlos. Puedes no usarlos, o puedes pagar.
      En general, los usuarios de Internet eligieron no querer pagar, así que alguien termina asumiendo el costo. En conjunto, los usuarios de Internet no recompensan a quienes no muestran anuncios. Quieren contenido, pero por lo general lo quieren gratis.
    • Puedes borrar la app y usar la página web, con un navegador que permita userscripts.
      Encontré un script que convierte la página de Instagram en algo más parecido a una etiqueta de imagen para que solo puedas ver fotos: https://greasyfork.org/en/scripts/5014-un-instagram
    • Creo que estas tácticas explotan nuestra curiosidad natural y la estética que la rodea.
      Por eso, más que falta de fuerza de voluntad, me parece una especie de insensibilización que hemos acumulado, y eso es bastante malo. Son respetables el esfuerzo y la creatividad para volver a que nosotros usemos las plataformas, en vez de que las plataformas nos usen a nosotros.
    • Como padre, empatizo especialmente con esto. Es difícil ver a los niños caer en el algoritmo.
      Hablo con mis hijos regularmente, y ellos también están de acuerdo en que es dañino, pero les resulta demasiado difícil resistirse. Incluso yo a veces me veo arrastrado al doomscrolling.
      Donde puedo, configuré filtrado de anuncios con Pi-hole, pero no quiero bloquear YouTube por completo. Aun así, para proteger a mi familia, creo que en adelante tendré que considerarlo seriamente.
    • Esta app me funcionó muy bien para bloquear el scroll infinito de Instagram: https://www.distractionfreeapps.com/index.html
  • La ingeniería es buena, pero da un poco de tristeza que haya que llegar tan lejos para poder usar tu propio hardware o software como si fuera tuyo, aunque sea en cierta medida.

    • En este caso, el dispositivo sí es tuyo. Pero no parece haber base para afirmar que también eres dueño de YouTube o de su contenido.
    • Con una caja Android de 30 dólares y el APK de NewPipe se podía hacer esto desde hace casi 10 años.
  • ¿YouTube tiene anuncios? No lo sabía porque el navegador los bloquea demasiado bien.
    El verdadero problema es que la experiencia en Apple TV es mucho peor que la de un navegador web común. Apple tiene el hardware tan cerrado que termina favoreciendo más los ingresos publicitarios de YouTube que al consumidor final que pagó por el dispositivo.

    • En Linux, Windows y Android no veo anuncios en absoluto. Cuando de vez en cuando intento ver YouTube en el iPad, me sorprende lo frecuentes y molestos que son los anuncios.
      Lo mismo pasa cuando navego la web desde el iPad fuera de la red Pi-hole de mi casa. No sé cómo la gente aguanta esto todos los días.
      El iPad es un dispositivo proporcionado por el trabajo, así que no lo uso mucho para cosas personales, pero cada vez que lo uso me recuerda lo molesto que es.
      Curiosamente, antes de recibir el iPad pensaba que solo sería útil para consumir contenido, pero en la práctica resultó muy cómodo para acceder rápidamente de forma remota a recursos de trabajo, mientras que para navegación web general y streaming quedó atrapado en un páramo cubierto de anuncios.
  • Si necesitas YouTube sin anuncios, puedes usar https://yewtu.be u otra instancia de Invidious https://docs.invidious.io/instances/.
    Entre YouTube e Invidious hay una carrera armamentista, y a veces Invidious deja de funcionar, pero el equipo siempre ha encontrado nuevas formas de esquivar a YouTube y entregar videos sin anuncios.

    • Hay una razón por la que el título dice “on AppleTV”. Los clientes o frontends alternativos no funcionan ahí.
    • En lugares como Roku TV no hay navegador, así que este método no sirve.