2 puntos por GN⁺ 2023-08-28 | 2 comentarios | Compartir por WhatsApp
  • Para bloquear a nivel de red los anuncios de YouTube en Apple TV y iPhone, se experimentó con pfSense, bloqueo DNS, enrutamiento por VPN, Squid, MITMProxy y hasta modificación de respuestas Protobuf
  • Como los anuncios de YouTube se sirven desde los mismos dominios que los videos normales, solo con bloqueo DNS como Pi-hole o pfBlockerNG era difícil separar de forma estable el contenido y la publicidad
  • Tras descifrar tráfico TLS con MITMProxy, en YouTube web se eliminaron campos publicitarios en JSON y, en la app de iOS, se localizaron y modificaron directamente estructuras de anuncios dentro de respuestas application/x-protobuf
  • La decodificación completa de Protobuf basada en Python tardaba unos 23 segundos en el router pfSense, pero con un escaneo lineal alrededor de /pagead/ fue posible procesar en tiempo real incluso payloads de 1.8MiB
  • Este método requiere instalar una CA de confianza y suficiente rendimiento de CPU; aunque fue posible bloquear anuncios en dispositivos Apple conectados a la red, después incluso se terminó considerando pagar YouTube Premium

Router pfSense y separación de red

  • El objetivo era construir un router basado en FreeBSD y pfSense para bloquear en toda la red los anuncios de YouTube pre-roll, mid-roll y end-roll en Apple TV y iPhone
  • El hardware usado fue una mini PC J4125 con conjunto de instrucciones AES-NI, RAM DDR4, SSD mSATA y una memoria USB para instalar pfSense
    • Un ejemplo de configuración era 32GiB de RAM y un SSD mSATA de 128GB
    • Se consideró útil contar con 128GB de almacenamiento para logs, reducir desgaste del SSD, captura de paquetes y espacio para edge cache
  • Tras instalar pfSense, se configuró LAN 1 con la IP estática 192.168.1.3, fuera del rango DHCP existente, y se accedió al portal web de administración con la cuenta admin/pfsense
  • Después de confirmar en el dashboard de pfSense el indicador AES-NI CPU Crypto: Yes (inactive), se activó manualmente AES-NI en System › Advanced › Miscellaneous
  • Aprovechando los 32GiB de RAM, se asignó bastante espacio de RAM disk a /var y /tmp, y se configuró un respaldo del RAM disk cada hora

Bloqueo DNS y separación física de la red

  • En lugar del Pi-hole existente, se instaló el paquete de pfSense pfBlockerNG-devel para configurar bloqueo de publicidad, contenido malicioso y bloqueo geográfico
    • La instalación añadió alrededor de 20MiB
    • Si el servicio pfb_dnsbl no inicia o aparece [ Missing CRON task ], se recomienda intentar borrar el archivo vacío /var/run/booting
  • Los 3 puertos Gigabit del router pfSense se usaron para separación física de LAN en vez de VLAN
    • Dispositivos que “llaman a casa”, como Alexa y Apple TV, se colocaron en una LAN de hardware separada
    • Se buscó separar la LAN importante de los dispositivos inteligentes y equipos Wi‑Fi para proteger dispositivos usados para banca, trading y crypto wallets
  • La red para dispositivos inteligentes se separó como 172.31.1.0/24, mientras que la LAN más confiable se mantuvo en 192.168/16
    • Se consideró que, si no hay rutas entre redes, también se reduce en parte el impacto de reglas iptables mal configuradas
    • Hay que activar el DHCP resolver en la NIC física para que los nuevos dispositivos de red reciban dirección
  • Se descartó un AC1200 Archer C5 por la falta de modo AP, problemas de acceso remoto, firmware stock antiguo y escaso soporte de OpenWRT/DD-WRT/Tomato por su chipset Broadcom
  • Después se usó un Nighthawk R7000 como AP para Apple/Amazon/TV y como AP para la Trusted Wireless Network
    • En la Trusted Wireless Network se decidió desactivar 2.4GHz y usar solo 5GHz
    • Se consideró que 5GHz, al bloquearse más fácilmente por paredes y concreto, ayuda a evitar el snooping a media distancia

Forzar todo el DNS hacia pfSense

  • Se añadieron reglas NAT para que todos los clientes detrás de pfSense usaran el servidor local de Unbound DNS
    • El objetivo era impedir que apps y asistentes del hogar evadieran el control usando sus propios servidores DNS o DNS hardcodeados
    • Para que pfBlockerNG intervenga en las solicitudes DNS, hay que bloquear DNS over TLS
  • Las reglas NAT primero se crearon por interfaz, excluyendo WAN, pero luego se simplificaron con el alias de firewall Non_WAN
    • En IPv4 e IPv6 se redirigen las consultas DNS locales del puerto 53 hacia localhost
    • Se debe desactivar NAT reflection para que no se pueda acceder al servidor DNS local desde Internet externa
  • En Services › DNS Resolver › Display Custom Options se añadió server: log-queries: yes para registrar las solicitudes DNS interceptadas
  • En los logs DNS apareció que Windows intentaba acceder a Google Tag Manager, y esa solicitud se mandó a un blackhole con la IP inexistente 10.10.10.1

Intento de evadir el targeting de anuncios de YouTube con VPN

  • Como los anuncios de YouTube llegan desde los mismos dominios que los videos normales, era difícil filtrar solo la publicidad con bloqueadores de dominios como pfBlockerNG o Pi-hole
    • Se consideró que bloquear googleadservices.com solo sirve después de ver el video del anuncio y hacer clic en él
    • En el navegador, uBlock Origin puede intervenir en JavaScript, pero en la app de YouTube para iPhone se consideró difícil limitar anuncios sin jailbreak
  • En vez de bloquear directamente los anuncios, se hizo un experimento para lograr que el algoritmo publicitario de YouTube viera al usuario como un objetivo menos atractivo
    • La idea era enviar el tráfico del Apple TV por una VPN y usar un endpoint VPN en una región con menos espectadores de YouTube
    • El objetivo era que el usuario pareciera un hombre de 70 años viviendo en Italia
  • Se configuró WireGuard en pfSense y se importó desde una VM Linux la private key de NordLynx/WireGuard
    • Se indica que, al introducir 1.0.0.0 y subnet mask 0 para la dirección del túnel, la UI muestra el resultado como 0.0.0.0/0
  • Al enviar todo el tráfico del Apple TV por la VPN, YouTube apareció en italiano y disminuyó la cantidad de anuncios, pero surgieron problemas con Netflix y Amazon Prime
    • Parecía que se bloqueaban archivos CSS o font y que las miniaturas no cargaban
    • Se advierte que Netflix y Prime hacen bien el geofencing frente a proveedores de VPN
  • Después se intentó enrutar por la VPN solo los FQDN relacionados con YouTube, usando como candidatos www.youtube.com, youtube.com, googlevideo.com, accounts.google.com, googleapis.com, gstatic.com y otros
    • Tras la configuración, 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 volvieron poco frecuentes, y los que aparecían estaban en italiano

Condición de carrera de DNS y problema de dominios wildcard

  • Un día después se descubrió una condición de carrera de DNS en la que el alias de hostname de pfSense y la caché DNS del cliente veían conjuntos distintos de IP de YouTube
    • El intervalo de resolución predeterminado del alias de hostname de pfSense es de 300 segundos
    • Se observó que el TTL de DNS de YouTube era de 1,440 segundos
  • Si las IP que resuelve Alias Daemon para el FQDN no coinciden con las IP que recibe después el Apple TV, el tráfico puede no entrar al túnel VPN
    • La mitigación consiste en hacer que pfSense ignore el TTL de destino y mantenga en caché la entrada del alias por más tiempo
  • El subdominio variable de googlevideo.com requería enrutamiento con wildcard, pero las reglas de NAT y firewall funcionan por IP y no pueden manejar directamente hostnames wildcard
  • Se escribió una PoC usando el módulo Python de Unbound y la API REST de pfSense para capturar las IP de las respuestas DNS y agregarlas dinámicamente al alias VPN_wildcards
    • El TTL de VPN_wildcards se configuró en 1 hora y la capacidad en 500
    • Los registros A se analizan con ipaddress.IPv4Address y los registros AAAA con ipaddress.IPv6Address
  • Al revisar por la mañana, Unbound DNS Resolver estaba en estado de segfault, y como cada adición de IP requería recargar las reglas de pfSense, pfSense se volvió muy lento

Descifrado de HTTPS con Squid y MITMProxy

  • El nuevo objetivo era instalar un proxy similar a Squid y agregar al dispositivo un certificado de CA falso pero confiable para realizar descifrado de tráfico TLS
  • Squid se instaló como paquete de pfSense y hasta pasó una prueba básica de SSL Filtering, pero se abandonó
    • El rendimiento era muy lento
    • Configurar las ACL era engorroso
    • Había un problema relacionado con https://http/*
    • La actualización de la lista de filtros de URL de SquidGuard tardaba muchísimo
    • Se consideró que la interfaz de Squid era insuficiente
  • Después se eligió MITMProxy
    • Se vio que tenía scripting en Python e interfaz, y que podía ampliarse para adaptarlo al bloqueo de anuncios de YouTube
    • El tarball Linux de mitmproxy 7.0.4 no se ejecutaba en FreeBSD por ELF interpreter /lib64/ld-linux-x86-64.so.2 not found y bibliotecas faltantes
  • Se instaló con pkg install mitmproxy en el entorno jail de FreeBSD de pfSense
    • Los paquetes a instalar eran 50, el espacio adicional 206MiB y la descarga 33MiB
    • Al ejecutar mitmproxy dentro del jail, se abría la interfaz
  • Para experimentar con MITMProxy, en pfSense se asignó la IP virtual 127.0.1.1 a localhost y, con una regla NAT, se redirigió [Private IPs]:8080 a 127.0.1.1:8080
    • Al configurar el proxy de una laptop de prueba en 192.168.20.1:8080, las solicitudes del navegador aparecían en el registro de la interfaz de MITMProxy
  • El archivo PEM de la CA de MITMProxy es ~/.mitmproxy/mitmproxy-ca-cert.pem
    • Se sirvió cert.pem con un servidor web de Python 3
    • MITMProxy también entrega el mismo certificado de CA en mitm.it
    • Se agregó el certificado a una laptop limpia y a un iPhone

Operación de MITMProxy y manejo de certificate pinning

  • En el router, mitmproxy consumía mucho CPU incluso en reposo, y se consideró que la causa era la generación al vuelo de certificados TLS por solicitud y el exceso de logging de la interfaz
  • mitmdump omite la interfaz y el logging extremo, por lo que se consideró que reducía la carga de CPU
    • Al ejecutarlo se usaron opciones como --anticomp, --mode regular, --listen-port 8080 y --listen-host 127.0.1.1
  • El certificate pinning es una técnica en la que el servidor o el cliente ya conoce de antemano el fingerprint esperado del certificado, por lo que la falsificación de certificados de MITMProxy no funciona
    • Como alternativa, se usó --ignore-hosts para hacer que hosts como apple.com:443 e icloud.com:443 evitaran el proxy
  • En Transparent Proxy Mode, se parcheó next_layer.py de MITMProxy 7.0.4 para que --allowed-hosts funcionara mejor tomando como base el SNI
    • Antes, en muchos casos, parecía que solo se usaba la IP del servidor para la coincidencia
    • El parche agrega server.sni como candidato de hostname además de server.address[0]
  • Después del parche, se pudieron interceptar de forma estable algunos hosts y dejar pasar el resto

Eliminación de anuncios JSON en YouTube web

  • En la prueba básica con MITMProxy, se bloquearon URLs de anuncios y rastreo de YouTube con un script pequeño
    • En youtube.com se bloquearon /pagead/, /log_event?, /stats/ads, /stats/qoe?, /ptracking?, /generate_204, el=adunit, adformat=, /activeview? y otros
    • En google.com y google.ca se bloqueó /pagead/
  • En las pruebas iniciales, las solicitudes objetivo efectivamente aparecían bloqueadas también en el panel Network de DevTools
    • Los elementos (failed) provenían del script
    • Se consideró que los fallos 502 eran resultado de que pfBlockerNG enviaba la solicitud a black-hole
    • Se desactivó HTTP/2 para evitar que pasaran solicitudes posteriores por el mismo canal
  • El simple bloqueo por URL no hizo desaparecer por completo los anuncios, así que, revisando el HTML y JavaScript de YouTube junto con filtros de uBlock Origin, se siguió la pista de que la información publicitaria podía estar dentro del cuerpo de respuestas JSON
  • En las respuestas JSON capturadas por MITMProxy se confirmó información de anuncios y rastreo en las estructuras playerAds y playbackTracking
    • playerAds incluía playerLegacyDesktopWatchAdsRenderer, playerAdParams, gutParams.tag, showCompanion, showInstream, useGut y otros
    • playbackTracking incluía videostatsPlaybackUrl, ptrackingUrl, qoeUrl, atrUrl y otros
    • youtubeRemarketingUrl tenía la forma www.youtube.com/pagead/viewthroughconversion/...
  • En YouTube web, al eliminar la información publicitaria del payload JSON, se pudieron quitar los anuncios web a través del router

Análisis de Protobuf en YouTube para iOS

  • La app de YouTube para iOS usa datos en formato Protocol Buffer (Protobuf) en llamadas de API parecidas a la versión web, en lugar de JSON
    • En Protobuf las claves son números y pueden cambiar, así que es difícil encontrar secciones de anuncios con un enfoque tipo JSONPath
    • En el payload se observaron cadenas publicitarias como “Telus”, “Samsung TV”, “Boxing Week” y “Buy now”
  • El tráfico de red de YouTube en iOS era distinto al tráfico web
    • En la versión web se puede estimar en cierta medida qué candidatos son anuncios mediante el range del chunk de video o el parámetro clen
    • El protocolo de iOS no usa el parámetro de consulta range ni el encabezado Range, sino contadores como &nr=2 y &nr=3
  • Al decodificar respuestas Protobuf para analizarlas offline, se encontraron campos como has_unlimited_entitlement: False y has_premium_lite_entitlement: False
    • Cambiar esos valores se sintió como “hacer trampa”, así que se volvió a un enfoque heurístico
  • Los experimentos de bloqueo de URLs de anuncios provocaron loops infinitos, errores de UI y crashes en la app de iOS
    • Un 200 con body vacío, 404, 503, un response body truncado o poner en null parte del video del anuncio hacían que la app se volviera lenta o terminara crasheando en un estado roto
    • El endpoint de reporte de errores /error_204/ indicaba “dev assertion failed”, por lo que se bloqueó
  • Los anuncios parecen registrarse en slots dentro de un video específico
    • Entre los tipos de slot están pre-roll, mid-roll, end-roll, full-page y ad pod
    • Si solo se bloquea la URL del anuncio, aparece un error del tipo “un anuncio inexistente reservó el slot” y la UI entra en estado de pánico

Problemas de rendimiento de Protobuf y blackboxprotobuf

  • Decodificar solo con Python unos 500 KiB de Protobuf crudo a texto legible por humanos era muy lento
    • En una desktop con CPU i7-6700 tomaba alrededor de 2.06 a 2.11 segundos
    • En un router pfSense tomaba entre 22.8 y 24.2 segundos
  • C++ protoc --decode_raw era mucho más rápido
    • En una desktop con CPU i7-6700 tomaba alrededor de 0.017 a 0.022 segundos
    • En un router pfSense rondaba entre 0.12 y 0.14 segundos
  • Como Python no soportaba raw decoding, se eligió comunicarse directamente con el binario C++ protoc mediante subprocess.Popen
  • blackboxprotobuf para Burp Suite puede decodificar mensajes wire raw de Protobuf, inyectar valores y luego re-encodearlos
    • Se recomienda usar la versión original de Burp Suite y no el fork de PyPI
    • Algunos forks pueden provocar stack overflow por recursión profunda
    • Si se configura os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp" antes de importar protobuf, se usa la implementación en C++ de libprotobuf.so cuando esté disponible
  • protobuf_to_json(data) de blackboxprotobuf puede generar un schema .proto estimado, pero el resultado es enorme, profundamente anidado y no es perfecto
    • El volcado del schema en Python tenía más de 250,000 caracteres
    • Se consideró suficiente para extraer detalles de anuncios

Desactivar la sección de anuncios con un cambio de 1 byte

  • El formato wire de Protobuf puede cambiar su encoding si se decodifica, edita y re-encodea sin el schema original
    • Entre las causas se mencionan la imposibilidad de saber si se usa codificación ZigZag, de identificar el tipo numérico y el orden no determinista de los campos del objeto
  • La solución fue aprovechar la compatibilidad hacia atrás de Protobuf para hacer que la sección de anuncios pareciera un campo desconocido
    • Se aprovechó el comportamiento de que el software antiguo ignora campos desconocidos al leer campos nuevos
    • Al cambiar 1 byte en un punto crítico, la sección profundamente anidada pasa a verse como si perteneciera a una futura versión del schema, y Protobuf la ignora
  • La clave del campo objetivo 49399797 no podía encontrarse con una simple búsqueda de cadenas, sino mediante escaneo de tags varint
    • El wire type era 2, lo que indica una cadena o mensaje anidado delimitado por longitud
    • El tag objetivo se calculó como AA FF B8 BC 01
    • Al desplazar los 3 bits del wire type se recupera la clave de campo 49399797
  • La exploración real consistía en buscar primero una firma de URL de anuncio como /pagead/ en los bytes raw de Protobuf, y luego retroceder cerca de esa zona para encontrar el tag y la clave del campo objetivo
    • Un ejemplo de interceptación apuntaba a POST youtubei.googleapis.com:443/youtubei/v1/browse?key=...
    • La respuesta fue 200, application/x-protobuf, 1.87m
    • En el log de ejemplo, 49399797 se encontró en la posición 4465 y 50195462 en la posición 4477
  • En una prueba O(n), la eliminación de anuncios funcionó escaneando una sola vez 1.8 MiB de datos Protobuf sin memoria adicional
    • El objetivo se encontró en el byte número 30,593
    • Con unos 600 bytes de backtracking se encontró la clave del campo que había que alterar
    • Ya no fue necesario bloquear URLs que incluyeran *.googleadservices.com o /pagead/, y esas solicitudes directamente dejaron de generarse

Estructura del addon de MITMProxy

  • El script PoC se guarda como youtube.py y se ejecuta con mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py"
    • Como prerrequisitos en FreeBSD, se indican pkg install protobuf, pkg install py38-pip y pip install jsonpath-ng
  • El script está compuesto por las clases Logger, trunc, KilledError, JSONPathReplacement, ProtobufDebugParser y YouTubeAdBlocker
  • El regex de host que YouTubeAdBlocker intercepta es \.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com
    • La cadena de búsqueda de URL de anuncios en Protobuf es b"/pagead/"
    • El límite de búsqueda es 80_000
    • La etiqueta de campo objetivo es 50195462
  • La regla de bloqueo de solicitudes revisa cadenas parciales de URL por host y mata el flow
    • Para youtube.com, incluye pagead/, log_event?, stats/ads, stats/qoe?, ptracking?, generate_204, error_204, adformat=, activeview?, _ad_, ai?, sw.js, entre otras
    • En sw.js hay un comentario que indica que se rechazan los service workers
  • En las respuestas JSON, se aplican varios reemplazos con JSONPath
    • $.responseContext.serviceTrackingParams[*].params[?(@.key == 'yt_ad')].value se cambia a "0"
    • $..adPlacements se cambia a []
    • $..adPlacementRenderer, $..adPlacementConfig, $..playerAdParams, $..gutParams se cambian a {}
    • $..adVideoId se cambia a una cadena vacía
    • $..showCompanion, $..showInstream, $..useGut se cambian a False
  • En las respuestas Protobuf, cuando el content type contiene protobuf, el body se convierte en bytearray y se busca /pagead/ dentro de los primeros 80,000 bytes
    • Se crean los bytes de la etiqueta objetivo con TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED) y nuevos bytes de target_field_tag - 1
    • Se busca hacia atrás desde justo antes de la posición de /pagead/ para encontrar los bytes de la etiqueta objetivo
    • Si se encuentra, esos bytes se sobrescriben con los nuevos bytes y el cuerpo de la respuesta se reemplaza con flow.response.set_content(bytes(body))
  • Según el comentario, este PoC bloquea el 90% de los anuncios
    • En otras secciones también hay otras claves de campo, y puede haber varias secciones de anuncios que deban alterarse

Alcance y límites

  • Esta técnica se resume 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 considera que la carga de CPU de descifrar y volver a cifrar tráfico HTTPS supera ampliamente lo que una Raspberry Pi puede manejar
  • Se considera que, tras hacer jailbreak al Apple TV y agregar el certificado raíz de pfSense, el gateway de pfSense podría descifrar el tráfico del Apple TV y bloquearlo inspeccionando los hostnames publicitarios en los headers de la solicitud
    • Aun así, seguiría sin aplicarse a los anuncios en iPhone, hacer jailbreak a un iPhone es más difícil y las apps bancarias podrían detectarlo y dejar de funcionar
    • Se concluye que el jailbreak en sí es una medida demasiado extrema
  • Si fuera posible usar una CA falsa de confianza, se podría descifrar el tráfico TLS a texto plano y aplicar bloqueo por URL
    • Se ponen como ejemplos las rutas de YouTube /pagead/viewthroughconversion/... y /pagead/conversion/...
  • Al final, se configura un router de hardware desde cero, se divide la LAN en zonas de confianza y no confianza, se agregan bloqueo de anuncios por DNS y un proxy MITM transparente, y así se bloquean con buen rendimiento los anuncios de YouTube en dispositivos Apple conectados a la red

YouTube Premium y apoyo a creadores

  • Después de bloquear anuncios de YouTube durante varios meses, comenzó a pagar YouTube Premium porque quería apoyar a los creadores de contenido
    • Añade la salvedad de que “poder hacerlo no significa que debas hacerlo”
  • El precio de YouTube Premium se menciona como CAD $9.99/mo a $11.99/mo, o aproximadamente $13.43/mo con impuestos incluidos
  • En el experimento de ver anuncios, se vio YouTube de forma intermitente durante un día en una laptop limpia y con navegación privada
    • Según el registro, se vieron 10 videos
    • Se estuvo expuesto a 8 anuncios, y solo 2 de ellos se podían omitir
  • Asumiendo un CPV de USD $0.15, el costo diario de los anuncios sería de $1.20, y extrapolado al mes sería de aproximadamente USD $36/mo
  • En otro cálculo usando cifras de Statista, en 2019 los anunciantes en EE. UU. gastaron $15.1 billion en YouTube y los residentes de EE. UU. vieron 916 billion videos, lo que da un promedio de USD $0.0165 por vista
    • Aplicando ese cálculo, el costo diario sería de aproximadamente USD $0.13, y extrapolado al mes sería de aproximadamente USD $3.96
    • Se considera que este valor no se acerca a los USD $10 de Premium
  • Si cae un reclamo DMCA, los ingresos publicitarios podrían ir no al creador, sino al reclamante, como Sony o Viacom
    • Por eso, podrías terminar sin aportar nada a tus canales favoritos sin darte cuenta
    • Se opina que no sorprende que muchos creadores se muden a Patreon

2 comentarios

 
xguru 2023-08-29

El texto original es larguísimo, pero aunque el proceso es interesante, el punto clave es que al final el autor igual terminó pagando YouTube Premium para usarlo.

 
GN⁺ 2023-08-28
Opiniones en Hacker News
  • En general es un hack genial, pero algunas expresiones sobre Protobuf se sienten raras.
    Lo que hicieron fue romper adrede la etiqueta de un campo en Protobuf, pero que se ignoren números de etiqueta no reconocidos no es un “defecto”, sino un diseño central para la extensibilidad.
    1.87 MiB tampoco es un tamaño tan grande, y probablemente estos mensajes no se estén transmitiendo en streaming todo el tiempo, así que la explicación de que sea una barrera de rendimiento tampoco me convence mucho.
    La codificación de Protobuf no fue diseñada para encarecer el costo de decodificación; al contrario, fue diseñada para decodificarse de manera eficiente, y aun sin el esquema .proto original se puede decodificar directamente con UnknownFieldSet.
    Un mejor enfoque habría sido usar un esquema .proto falso que incluyera solo el campo que se quiere eliminar. El método de escanear cadenas es más propenso a errores, porque la misma secuencia de bytes podría aparecer por casualidad en otros datos.
    Si cambia el orden de los campos, el resultado de recodificar puede producir bytes distintos, pero el receptor debería tratarlo como el mismo mensaje, y parece poco probable que la app de YouTube detecte un cambio en el orden de los campos.
    Visto desde la perspectiva de alguien que trabajó antes con Protobuf, parece que el autor malinterpretó esta parte.

    • Buen análisis, pero me cuesta estar del todo de acuerdo con que 1.87 MB sea poco.
      He vivido la mayor parte de mi vida en zonas rurales y, si no es mi Wi‑Fi, algo así en la práctica sí es una descarga grande. En móvil quizá haya formas de evitarlo, pero el Wi‑Fi rural todavía sufre con la estructura de la Web 2.0 y normalmente se usa a velocidades de 2G a 4G.
      En zonas urbanas, donde hay población para sostener la infraestructura, 1.87 MB ya suele ser un archivo pequeño, aunque puede haber excepciones alrededor de las 6 p. m., cuando todos los que están en una conexión de cable están haciendo streaming.
    • A modo de pequeña promoción respecto de without the C++ source proto files: hice un proyecto llamado protodump, que genera archivos fuente .proto a partir de un binario.
      Recrea las definiciones de mensajes y campos, incluidos sus nombres originales; solo hay que extraer el binario de la caja de Apple TV.
      https://github.com/arkadiyt/protodump
    • Decir “trabajé antes con Protobuf” es una enorme muestra de modestia. Para quienes no lo sepan, Kenton es quien convirtió Protobuf en lo que es hoy.
      Protobuf fue la tecnología que me presentó los IDL, y en ese momento me pareció una idea mágica. Después de haber hecho mi propio IDL bastante endeble, descubrir Protobuf me sorprendió aún más.
    • Al leer este artículo me confundí de manera parecida. Los principios de diseño de Protobuf no son ningún secreto y están todos documentados con claridad.
    • La parte más complicada al decodificar Protobuf sin esquema es que los mensajes embebidos y las cadenas usan el mismo tipo de etiqueta, pero aun así se puede manejar con bastante facilidad.
      Si no quieres traer toda la dependencia de protoc, puedes escribir tú mismo un decodificador simple de Protobuf de unos cientos de líneas: https://github.com/kubernetes/test-infra/blob/master/guberna... https://github.com/kubernetes/test-infra/blob/master/guberna...
  • Desde que descubrí The Proxomitron hace más de 20 años, he venido procesando el tráfico con un proxy de intermediario para eliminar anuncios y reescribir páginas con cosas como CSS de usuario.
    Sitios como CloudFlare tienden a clasificarme como “bot”, pero también hay formas de evitarlo, aunque no sean sencillas. Casos así también muestran por qué la atestación remota es peligrosa para la libertad del usuario.

    • Busqué The Proxomitron y parece que el desarrollo terminó en 2004; sería interesante tener un resumen de alguien que conozca bien el panorama actual.
      Parece que hay varios proyectos “sucesores”, y también me pregunto si sigue siendo solo para Windows. Estaba buscando por encima un proxy simple que permitiera insertar enlaces locales en contenido remoto.
    • El bloqueo de anuncios a nivel de red, como Privoxy o pi-hole, tiene demasiadas desventajas, por ejemplo que no puede manejar anuncios inline.
      Ahora incluso tengo desconectado el pi-hole que corría en una Pi 4. Pasé horas intentando que funcionara bien junto con varios servicios, pero al final me rendí; no valía el tiempo invertido para una red doméstica.
      Lo que realmente funciona bien son los bloqueadores de anuncios basados en navegador y los parcheadores de apps como ReVanced. A medida que aumentan mis ahorros, en los casos que esas dos cosas no resuelven —como YouTube Premium, Hulu, Netflix o Max— me inclino más por simplemente pagar servicios sin anuncios.
    • Se sabe que empleados de Cloudflare andan por aquí leyendo, así que me da curiosidad: que a alguien se lo clasifique como bot de esta manera, ¿lo consideran un falso positivo o el comportamiento esperado?
    • Hacía casi 20 años que no pensaba en Proxomitron, y me pregunto si alguien todavía lo usa.
      Nunca lo usé para el propósito mencionado aquí, pero era excelente como proxy detrás del firewall de la empresa. Antes, el firewall exigía credenciales para las conexiones externas, así que muchos programas no podían acceder a Internet.
    • Supongo que esto requiere instalar un nuevo certificado de CA en el dispositivo, ¿no?
  • Me viene a la mente Privaxy, empaquetado con Docker. Es un proxy intermediario compatible con las listas de bloqueo de uBlock Origin.
    Sorprende ver cuántos anuncios y scripts de rastreo hay en los productos inteligentes, especialmente en las TVs. Según lo que he probado hasta ahora, el tráfico innecesario superaba el 40%, y experimentar con quitar anuncios de apps de smart TV fue bastante divertido.
    https://github.com/deetungsten/webui-privaxy es un fork dockerizado de https://github.com/Barre/privaxy

    • ¿Cómo se hace para que la TV confíe en un certificado autofirmado?
    • Me alegra verlo también por aquí. Me gustó que mencionaras el problema del ping a las listas de filtros.
      Quería modificar el fork para que el frontend no usara una dirección 0.0.0.0 hardcodeada, de modo que el contenedor Docker pudiera aislarse de verdad, pero la vida se metió en medio. ¿Lo probaste en Apple TV?
    • Adguard también está trabajando en algo parecido.
      https://github.com/AdguardTeam/urlfilter
    • Me tomó demasiado tiempo entender que decir fork dockerizado significaba que la GUI se cambió por una GUI web.
  • Este artículo es una gran respuesta a la pregunta frecuente: “¿cómo debería aprender para convertirme en hacker?”
    Muestra muy bien el proceso mental y el trabajo persistente detrás de cualquier exploit.

  • Hay una parte que dice: “usemos WireGuard — tiene el conjunto de instrucciones de cifrado Intel AES-NI”, pero hasta donde sé WireGuard no usa AES.
    En general, el autor parece sobreestimar un poco los requisitos de CPU del cifrado TLS, o subestimar el rendimiento de las computadoras monoplaca modernas.
    También me resulta extraña la explicación de que se exceden ampliamente los requisitos de CPU para descifrar y volver a cifrar tráfico HTTPS en una Raspberry Pi. Me sorprendería bastante que hacer MITM de TLS en una RPi 4 fuera realmente inviable, incluso usando RSA en software puro.
    Entre los teléfonos Android que todavía se usan hay algunos con CPUs más débiles que la de una RPi 4, y también usan TLS.

    • Creo que estás subestimando los requisitos de CPU.
      Que un teléfono Android débil solo pueda procesar tráfico TLS a 50 Mb/s quizá no sea un gran problema en la práctica. Los teléfonos lentos suelen estar conectados a redes lentas.
      En cambio, si tienes internet gigabit en casa y un dispositivo débil ubicado entre todas tus computadoras e internet crea un cuello de botella de 50 Mb/s, eso sí es un gran problema.
      Los requisitos de CPU de TLS dependen muchísimo del ancho de banda objetivo. A anchos de banda más altos, descargar el trabajo a aceleradores se vuelve prácticamente obligatorio. El costo del handshake tampoco es fácil de ignorar y puede limitar la cantidad de conexiones por segundo. En un solo dispositivo rara vez es un problema, pero puede crecer cuando se trata de toda una red de dispositivos.
  • Excelente artículo. Esperaba una forma de hacer MITM en dispositivos que no permiten instalar una CA personalizada.
    Tengo un dispositivo IoT que no expone una API local y solo muestra los datos a través de la nube, y quiero capturar el tráfico entre el dispositivo y la nube.
    ¿Al final no habrá otra opción que dumpear la memoria flash, cambiar la CA y volver a cargarla?

    • Si el certificado está “hardcodeado”, a eso se le llama certificate pinning. En ese caso tendrías que reemplazar o eliminar el certificado, y mover ese mismo certificado al proxy MITM para poder descifrar el tráfico.
      Hay un buen artículo con métodos que puedes intentar para interceptar dispositivos IoT sin tocar hardware ni firmware:
      https://robertheaton.com/2019/11/21/how-to-man-in-the-middle...
    • Buscar una forma de hacer MITM en dispositivos que no permiten instalar una CA personalizada va, en última instancia, contra el propósito de TLS.
      Si es posible, dependería de fallas de implementación.
      Sinceramente, creo que incluso a varios dispositivos que hoy permiten instalar certificados de confianza propios les queda poco tiempo para seguir haciéndolo.
  • Siempre aparecen nuevos métodos para bloquear anuncios en YouTube o en cualquier plataforma, pero unos meses después cambian algo y dejan de servir.
    ¿Y si en cambio se ataca a los anunciantes? YouTube/Google parece rastrear solo los “clics”, pero ¿también rastrea las compras reales?
    En teoría, si suficientes bots falsos y usuarios reales hicieran clic en anuncios sin comprar nada, podrían quemar el presupuesto publicitario. Con el tiempo, el departamento de marketing vería que en cierta plataforma los clics están en máximos históricos, pero la tasa de conversión por clic o impresión es bajísima, y quizá terminaría retirándose de esa plataforma.

    • Viendo que Nauseum fue bloqueado de la Chrome Store, parece que sí es bastante efectivo.
  • Artículo asombroso. En cuanto vi la etapa de parche mitm, pensé que sería algo especial, y realmente lo fue.

  • Me llamó la atención el punto del índice: “Nuevo objetivo: hacer que YouTube crea que soy un hombre de 70 años que vive en Italia”.
    Hace un tiempo, no sé cómo, la segmentación de anuncios llegó a creer que yo era alguien que quería comprarle a mi pareja una pijama de seda lavable de 500 dólares.
    Los anuncios en sí eran buenísimos, pero me pregunto cuánto estarían pagando por impresión.
    Después de cambiarme a Apple TV, en general me aparecen anuncios locales con segmentación geográfica equivocada. En promedio, quizá eso sea mejor.

  • Esto no es una “falla” de Protobuf. Que al modificar bytes se decodifiquen como campos en otras posiciones funciona según lo previsto.
    Protobuf es, desde el inicio, un protocolo basado en números de campo y prefijos de longitud; parte de la suposición razonable de que los bytes no cambian durante la transmisión, y deja la integridad en manos de quien lee.
    Incluso si fuera una falla, sería una falla de la app de YouTube para iOS, no de Protobuf; y como en realidad tampoco es una falla, es difícil llamarlo “exploit”. A menos que se refieran a que, en el intercambio Protobuf de la app de YouTube para iOS, no se verifica el hash del payload devuelto.
    Después de este artículo, probablemente empiecen a verificarlo.

    • La forma en que lo expresa el autor es un poco rara. “Falla” solo aparece en el título, y el cuerpo simplemente explica cómo funciona el formato.
      No es una falla, funciona según el diseño.
      También es rara la parte de que “Google hace que decodificar, modificar y re-encodear sea computacionalmente caro sin el archivo proto fuente en C++”. Si se hace con código Python no optimizado, puede ser caro, pero escrito en C u otro lenguaje compilado, escanear un Protobuf de 1.8 MB es algo trivial, haya o no archivo proto fuente.
      No parece que hacer que un archivo Protobuf sea difícil de decodificar sin el código fuente haya sido un objetivo de diseño. Si ese era el objetivo, lo lograron bastante mal.
    • No sé cómo funcionan los campos obligatorios en Protobuf, pero para mitigar el ataque, el cliente de YouTube de Google podría tratar ese campo como campo obligatorio y rechazar el servicio si el campo no está presente o tiene el valor predeterminado.
    • La fecha del artículo es enero de 2022. Si después del post del blog querían endurecer el protocolo, es muy probable que ya lo hayan hecho.