2 points par GN⁺ 2023-08-28 | 2 commentaires | Partager sur WhatsApp
  • Pour bloquer les publicités YouTube sur Apple TV et iPhone au niveau du réseau, l’auteur a expérimenté pfSense, le blocage DNS, le routage VPN, Squid, MITMProxy, jusqu’à la modification des réponses Protobuf
  • Les publicités YouTube étant servies depuis les mêmes domaines que les vidéos ordinaires, il était difficile de séparer de manière fiable contenu et publicité avec le seul blocage DNS de type Pi-hole ou pfBlockerNG
  • Après avoir déchiffré le trafic TLS avec MITMProxy, il a supprimé les champs publicitaires JSON de YouTube Web et, dans l’app iOS, a directement repéré et modifié les structures publicitaires au sein des réponses application/x-protobuf
  • Le décodage Protobuf complet en Python prenait environ 23 secondes sur le routeur pfSense, mais une approche par scan linéaire autour de /pagead/ a permis de traiter en temps réel même un payload de 1,8 MiB
  • Cette méthode nécessite l’installation d’une CA de confiance et des performances CPU suffisantes ; elle a permis de bloquer les publicités sur les appareils Apple connectés au réseau, mais a finalement aussi conduit à envisager un abonnement YouTube Premium

Routeur pfSense et segmentation du réseau

  • L’objectif était de créer un routeur basé sur FreeBSD et pfSense afin de bloquer, à l’échelle du réseau, les publicités YouTube pre-roll, mid-roll et end-roll sur Apple TV et iPhone
  • Le matériel utilisé comprenait un mini-PC J4125 avec jeu d’instructions AES-NI, de la RAM DDR4, un SSD mSATA et une clé USB pour installer pfSense
    • La configuration d’exemple comprenait 32 Gio de RAM et un SSD mSATA de 128 Go
    • Les 128 Go de stockage étaient jugés utiles pour les logs, la réduction de l’usure du SSD, la capture de paquets et l’espace d’edge cache
  • Après l’installation de pfSense, le LAN 1 a été configuré avec l’IP statique 192.168.1.3, en dehors de la plage DHCP existante, puis le portail web d’administration a été ouvert avec le compte admin/pfsense
  • Après avoir vérifié l’indication AES-NI CPU Crypto: Yes (inactive) dans le tableau de bord pfSense, AES-NI a été activé manuellement dans System › Advanced › Miscellaneous
  • Pour exploiter les 32 Gio de RAM, de généreux RAM disks ont été alloués à /var et /tmp, avec une sauvegarde des RAM disks configurée toutes les heures

Blocage DNS et segmentation physique du réseau

  • À la place du Pi-hole existant, le paquet pfSense pfBlockerNG-devel a été installé pour configurer le blocage des publicités, des contenus malveillants et le blocage géographique
    • L’installation ajoutait environ 20 Mio
    • Si le service pfb_dnsbl ne démarre pas ou si [ Missing CRON task ] apparaît, il est recommandé d’essayer de supprimer le fichier vide /var/run/booting
  • Les trois ports Gigabit du routeur pfSense ont été utilisés pour une séparation physique des LAN plutôt que pour des VLAN
    • Les appareils de type phoning-home, comme Alexa et Apple TV, ont été placés sur un LAN matériel séparé
    • L’objectif était de protéger les appareils utilisés pour la banque, le trading d’actions et les crypto wallets en séparant le LAN critique des objets connectés et appareils Wi-Fi
  • Le réseau des appareils connectés a été isolé en 172.31.1.0/24, tandis que le LAN plus fiable est resté en 192.168/16
    • L’auteur estime que l’absence de route entre les réseaux réduit aussi en partie l’impact de règles iptables mal configurées
    • Il faut activer le DHCP resolver sur la NIC physique pour que les nouveaux appareils réseau puissent obtenir une adresse
  • L’AC1200 Archer C5 a été abandonné en raison de l’absence de mode AP, de problèmes d’accès à distance, d’un stock firmware ancien et du manque de prise en charge OpenWRT/DD-WRT/Tomato du chipset Broadcom
  • Par la suite, un Nighthawk R7000 a été utilisé comme AP pour Apple/Amazon/TV et comme AP pour le Trusted Wireless Network
    • Sur le Trusted Wireless Network, il a été décidé de désactiver le 2,4 GHz et de n’utiliser que le 5 GHz
    • Le 5 GHz étant plus facilement bloqué par les murs et le béton, il a été jugé avantageux pour éviter le snooping à moyenne distance

Forcer tout le DNS vers pfSense

  • Des règles NAT ont été ajoutées afin que tous les clients derrière pfSense utilisent le serveur Unbound DNS local
    • L’objectif était d’empêcher les apps et assistants domestiques de contourner le réseau avec leurs propres serveurs DNS ou des serveurs DNS codés en dur
    • Pour que pfBlockerNG puisse intervenir sur les requêtes DNS, il faut bloquer le DNS over TLS
  • Les règles NAT ont d’abord été créées sur chaque interface sauf WAN, puis simplifiées avec l’alias de pare-feu Non_WAN
    • En IPv4 comme en IPv6, les requêtes DNS locales sur le port 53 sont redirigées vers localhost
    • La NAT reflection doit être désactivée pour empêcher l’accès au serveur DNS local depuis Internet
  • server: log-queries: yes a été ajouté dans Services › DNS Resolver › Display Custom Options afin de journaliser les requêtes DNS interceptées
  • Les logs DNS ont montré des requêtes de Windows vers Google Tag Manager, et ces requêtes ont été blackholées vers l’IP inexistante 10.10.10.1

Tentative de contournement du ciblage publicitaire de YouTube via VPN

  • Les publicités YouTube provenant des mêmes domaines que les vidéos ordinaires, il était difficile de ne filtrer que les publicités avec des bloqueurs de domaines comme pfBlockerNG ou Pi-hole
    • Le blocage de googleadservices.com n’était jugé utile qu’après avoir vu une publicité vidéo puis cliqué sur celle-ci
    • Dans un navigateur, uBlock Origin peut intervenir dans le JavaScript, mais l’app YouTube sur iPhone était considérée comme difficile à limiter sans jailbreak
  • Plutôt que de bloquer directement les publicités, l’auteur a expérimenté une méthode visant à rendre l’utilisateur moins attractif pour l’algorithme publicitaire de YouTube
    • L’idée était d’envoyer le trafic de l’Apple TV via un VPN et d’utiliser un point de sortie VPN dans une région où YouTube compte peu de spectateurs
    • L’objectif était de faire passer l’utilisateur pour un homme de 70 ans vivant en Italy
  • WireGuard a été configuré sur pfSense, avec une private key NordLynx/WireGuard récupérée depuis une VM Linux
    • Lors de la saisie de l’adresse du tunnel, entrer 1.0.0.0 avec un subnet mask 0 fait apparaître 0.0.0.0/0 dans l’interface, selon l’auteur
  • Quand tout le trafic de l’Apple TV a été envoyé via le VPN, YouTube s’est affiché en italien et le nombre de publicités a diminué, mais des problèmes sont apparus avec Netflix et Amazon Prime
    • Cela ressemblait à un blocage de fichiers CSS ou font et à des thumbnails qui ne se chargeaient pas
    • Netflix et Prime sont signalés comme pratiquant bien le geofencing contre les fournisseurs de VPN
  • L’auteur a ensuite tenté de ne router via le VPN que les FQDN liés à YouTube, avec notamment www.youtube.com, youtube.com, googlevideo.com, accounts.google.com, googleapis.com, gstatic.com parmi les candidats
    • Après configuration, YouTube voyait l’utilisateur comme étant à Milan, tandis que Netflix et Prime Video le voyaient au Canada
    • Les publicités sont devenues rares et, lorsqu’elles apparaissaient, elles étaient en italien

Problèmes de DNS race condition et de domaines wildcard

  • Un jour plus tard, une DNS race condition a été découverte : l’alias hostname de pfSense et le cache DNS du client voyaient des ensembles d’IP YouTube différents
    • L’intervalle de résolution par défaut d’un alias hostname pfSense est de 300 secondes
    • Le TTL DNS de YouTube a été observé à 1 440 secondes
  • Si l’IP obtenue par résolution du FQDN par l’Alias Daemon ne correspond pas à celle reçue plus tard par l’Apple TV, le trafic peut manquer le tunnel VPN
    • La mesure d’atténuation consiste à faire en sorte que pfSense ignore le TTL de la cible et conserve plus longtemps l’entrée d’alias en cache
  • Les variantes de sous-domaines de googlevideo.com nécessitaient un routage wildcard, mais les règles NAT et firewall fonctionnent sur la base des IP et ne peuvent pas gérer directement les hostnames wildcard
  • Une PoC a été écrite avec le module Python d’Unbound et l’API REST de pfSense pour capturer les IP des réponses DNS et les ajouter dynamiquement à l’alias VPN_wildcards
    • Le TTL de VPN_wildcards a été fixé à 1 heure, avec une capacité de 500
    • Les enregistrements A sont parsés avec ipaddress.IPv4Address, les enregistrements AAAA avec ipaddress.IPv6Address
  • Vérification le matin : Unbound DNS Resolver était en état de segfault, et chaque ajout d’IP nécessitait un rechargement des règles pfSense, ce qui rendait pfSense très lent

Déchiffrement HTTPS avec Squid et MITMProxy

  • Le nouvel objectif était d’installer un proxy similaire à Squid et d’ajouter à l’appareil un fake-but-trusted CA certificate afin d’effectuer le déchiffrement du trafic TLS
  • Squid a été installé comme paquet pfSense et le smoke test de SSL Filtering a réussi, mais la piste a été abandonnée
    • Les performances étaient très lentes
    • La configuration des ACL était fastidieuse
    • Il y avait un issue lié à https://http/*
    • La mise à jour de la liste de filtres URL SquidGuard prenait énormément de temps
    • L’UI de Squid a été jugée insuffisante
  • MITMProxy a ensuite été choisi
    • Il dispose de Python scripting et d’une UI, et semblait extensible pour le blocage des publicités YouTube
    • Le tarball Linux de mitmproxy 7.0.4 ne s’exécutait pas sur FreeBSD, avec ELF interpreter /lib64/ld-linux-x86-64.so.2 not found et des bibliothèques manquantes
  • Installation dans l’environnement FreeBSD jail de pfSense avec pkg install mitmproxy
    • L’installation visait 50 paquets, 206 MiB d’espace supplémentaire et 33 MiB de téléchargement
    • En lançant mitmproxy depuis la jail, l’UI s’ouvre
  • Pour les expérimentations avec MITMProxy, une IP virtuelle 127.0.1.1 a été attachée au localhost dans pfSense, et une règle NAT a transféré [Private IPs]:8080 vers 127.0.1.1:8080
    • Après avoir configuré le proxy d’un ordinateur portable de test sur 192.168.20.1:8080, les requêtes du navigateur sont apparues dans les logs de l’UI de MITMProxy
  • Le fichier PEM de la CA MITMProxy est ~/.mitmproxy/mitmproxy-ca-cert.pem
    • cert.pem a été servi via un serveur web Python 3
    • MITMProxy fournit aussi le même certificat CA sur mitm.it
    • Le certificat a été ajouté à un ordinateur portable propre et à un iPhone

Exploitation de MITMProxy et gestion du certificate pinning

  • Sur le routeur, mitmproxy consommait beaucoup de CPU même au repos ; la génération à la volée de certificats TLS à chaque requête et le logging excessif de l’UI ont été considérés comme les causes
  • mitmdump a été jugé moins coûteux en CPU, car il omet l’UI et le logging extrême
    • À l’exécution, des options comme --anticomp, --mode regular, --listen-port 8080 et --listen-host 127.0.1.1 ont été utilisées
  • Le Certificate Pinning est une technique dans laquelle le serveur ou le client connaît à l’avance le fingerprint du certificat attendu, ce qui empêche la falsification de certificat par MITMProxy
    • Comme contournement, --ignore-hosts a été utilisé pour faire en sorte que des hôtes comme apple.com:443 et icloud.com:443 contournent le proxy
  • En mode Transparent Proxy, next_layer.py de MITMProxy 7.0.4 a été patché pour que --allowed-hosts fonctionne mieux sur la base du SNI
    • Auparavant, dans de nombreux cas, seule l’IP du serveur semblait utilisée pour le matching
    • Le patch ajoute server.sni comme candidat hostname, en plus de server.address[0]
  • Après le patch, certains hôtes pouvaient être interceptés de manière stable, tandis que les autres pouvaient passer

Suppression des publicités JSON sur le YouTube web

  • Lors du smoke test de MITMProxy, les URL de publicité et de tracking YouTube ont été bloquées avec un petit script
    • Sur youtube.com, /pagead/, /log_event?, /stats/ads, /stats/qoe?, /ptracking?, /generate_204, el=adunit, adformat=, /activeview?, etc. ont été bloqués
    • Sur google.com et google.ca, /pagead/ a été bloqué
  • Lors des premiers tests, les requêtes ciblées étaient effectivement bloquées, y compris dans le panneau Network de DevTools
    • Les entrées (failed) provenaient du script
    • Les échecs 502 ont été interprétés comme le résultat du black-hole des requêtes par pfBlockerNG
    • HTTP/2 a été désactivé pour éviter que les requêtes suivantes sur le même canal ne passent
  • Le simple blocage d’URL ne faisait pas complètement disparaître les publicités ; en examinant le HTML et le JavaScript de YouTube ainsi que les filtres uBlock Origin, la possibilité que les informations publicitaires se trouvent dans le corps des réponses JSON a été explorée
  • Dans les réponses JSON capturées par MITMProxy, des informations de publicité et de tracking ont été identifiées dans les structures playerAds et playbackTracking
    • playerAds contenait notamment playerLegacyDesktopWatchAdsRenderer, playerAdParams, gutParams.tag, showCompanion, showInstream et useGut
    • playbackTracking contenait notamment videostatsPlaybackUrl, ptrackingUrl, qoeUrl et atrUrl
    • youtubeRemarketingUrl était de la forme www.youtube.com/pagead/viewthroughconversion/...
  • Sur le YouTube web, il a été possible de supprimer les informations publicitaires du payload JSON et d’éliminer les publicités web via le routeur

Analyse de YouTube iOS et de Protobuf

  • L’application YouTube iOS utilise, pour des appels d’API similaires à ceux de la version web, des données au format Protocol Buffer (Protobuf) au lieu de JSON
    • Dans Protobuf, les clés sont numériques et peuvent changer, ce qui rend difficile la recherche des sections publicitaires avec une approche de type JSONPath
    • Dans le payload, des chaînes publicitaires comme « Telus », « Samsung TV », « Boxing Week » et « Buy now » ont été observées
  • Le trafic réseau de YouTube iOS était différent du trafic web
    • Dans la version web, les paramètres range ou clen des chunks vidéo permettent dans une certaine mesure d’identifier des candidats publicitaires
    • Le protocole iOS n’utilise pas de paramètre de requête range ni d’en-tête Range, mais des compteurs comme &nr=2 et &nr=3
  • En décodant les réponses Protobuf pour les analyser hors ligne, des champs comme has_unlimited_entitlement: False et has_premium_lite_entitlement: False ont été trouvés
    • Modifier ces valeurs donnant l’impression de « tricher », l’auteur est revenu à une approche heuristique
  • Les essais de blocage des URL publicitaires ont provoqué des boucles infinies, des erreurs d’interface et des crashs dans l’application iOS
    • Des réponses 200 avec body vide, 404, 503, des corps de réponse tronqués ou le remplacement partiel de vidéos publicitaires par null ralentissaient l’app ou la laissaient dans un état cassé avant de la faire crasher
    • L’endpoint de signalement d’erreurs /error_204/ indiquait « dev assertion failed » et était bloqué
  • Les publicités semblent être enregistrées dans des slots au sein d’une vidéo donnée
    • Les types de slots incluent pre-roll, mid-roll, end-roll, full-page et ad pod
    • Si l’on bloque seulement l’URL publicitaire, une erreur du type « une publicité inexistante a réservé un slot » se produit et l’interface passe en état de panic

Problèmes de performance de Protobuf et blackboxprotobuf

  • Décoder environ 500 Kio de Protobuf brut en texte lisible par un humain uniquement avec Python était très lent
    • Sur un PC de bureau avec CPU i7-6700, cela prenait environ 2,06 à 2,11 secondes
    • Sur un routeur pfSense, environ 22,8 à 24,2 secondes
  • Le protoc --decode_raw en C++ était beaucoup plus rapide
    • Sur un PC de bureau avec CPU i7-6700, environ 0,017 à 0,022 seconde
    • Sur un routeur pfSense, environ 0,12 à 0,14 seconde
  • Comme le raw decoding n’est pas pris en charge en Python, l’approche retenue a été de communiquer directement avec le binaire C++ protoc via subprocess.Popen
  • blackboxprotobuf pour Burp Suite peut décoder un message Protobuf wire brut, y injecter des valeurs, puis le réencoder
    • Il est recommandé d’utiliser la version Burp Suite d’origine, et non le fork PyPI
    • Certains forks peuvent provoquer un stack overflow à cause d’une récursion profonde
    • Définir os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp" avant l’import de protobuf permet d’utiliser l’implémentation C++ libprotobuf.so lorsque c’est possible
  • protobuf_to_json(data) de blackboxprotobuf peut produire un schéma .proto estimé au mieux, mais le résultat est très volumineux, profondément imbriqué et imparfait
    • Le dump de schéma Python dépassait environ 250 000 caractères
    • Cela a été jugé suffisant pour extraire les détails publicitaires

Neutraliser une section publicitaire avec une modification d’un octet

  • Avec le format Protobuf Wire, décoder, éditer puis réencoder sans le schéma d’origine peut modifier l’encodage
    • Les causes avancées sont l’utilisation éventuelle du ZigZag encoding, l’impossibilité de déterminer le type numérique et le caractère non déterministe de l’ordre des champs d’un objet
  • La solution a consisté à exploiter la compatibilité ascendante de Protobuf pour faire passer la section publicitaire pour un champ inconnu
    • Elle s’appuie sur le comportement par lequel un ancien logiciel ignore les champs inconnus lorsqu’il lit un nouveau champ
    • En modifiant un octet à un emplacement critique, une section profondément imbriquée peut sembler appartenir à une future version du schéma, ce qui conduit Protobuf à l’ignorer
  • La clé de champ cible 49399797 devait être trouvée non pas par simple recherche de chaîne, mais par balayage des tags varint
    • Le wire type est 2, ce qui correspond à une chaîne ou un message imbriqué à longueur délimitée
    • Le tag cible est calculé comme AA FF B8 BC 01
    • En décalant les 3 bits du wire type, on récupère la clé de champ 49399797
  • La recherche effective consiste à trouver d’abord, dans les octets Protobuf bruts, une signature d’URL publicitaire comme /pagead/, puis à remonter à proximité pour trouver le tag du champ cible et la clé de champ
    • L’exemple d’interception cible POST youtubei.googleapis.com:443/youtubei/v1/browse?key=...
    • La réponse était 200, application/x-protobuf, 1.87m
    • Dans l’exemple de logs, 49399797 est trouvé à la position 4465, et 50195462 à la position 4477
  • Lors d’un smoke test en O(n), 1,8 Mio de données Protobuf ont été balayés une seule fois, sans mémoire supplémentaire, et la suppression des publicités a fonctionné
    • La cible a été trouvée au 30 593e octet
    • Environ 600 octets de backtracking ont permis de trouver la clé de champ à altérer
    • Il n’est plus nécessaire de bloquer les URL contenant *.googleadservices.com ou /pagead/, et la requête correspondante n’est tout simplement plus créée

Structure de l’addon MITMProxy

  • Le script PoC est enregistré sous youtube.py et exécuté avec mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py"
    • Comme prérequis FreeBSD, il indique pkg install protobuf, pkg install py38-pip et pip install jsonpath-ng
  • Le script se compose des classes Logger, trunc, KilledError, JSONPathReplacement, ProtobufDebugParser et YouTubeAdBlocker
  • La regex des hôtes interceptés par YouTubeAdBlocker est \.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com
    • La chaîne de recherche d’URL publicitaire Protobuf est b"/pagead/"
    • La limite de recherche est 80_000
    • Le tag du champ cible est 50195462
  • Les règles de blocage des requêtes tuent le flow en vérifiant, selon l’hôte, des chaînes partielles dans l’URL
    • Pour youtube.com, cela inclut notamment pagead/, log_event?, stats/ads, stats/qoe?, ptracking?, generate_204, error_204, adformat=, activeview?, _ad_, ai?, sw.js
    • Un commentaire sur sw.js indique que les service workers sont refusés
  • Sur les réponses JSON, plusieurs remplacements JSONPath sont appliqués
    • $.responseContext.serviceTrackingParams[*].params[?(@.key == 'yt_ad')].value est remplacé par "0"
    • $..adPlacements est remplacé par []
    • $..adPlacementRenderer, $..adPlacementConfig, $..playerAdParams, $..gutParams sont remplacés par {}
    • $..adVideoId est remplacé par une chaîne vide
    • $..showCompanion, $..showInstream, $..useGut sont remplacés par False
  • Dans les réponses Protobuf, lorsque le content type contient protobuf, le corps est converti en bytearray et /pagead/ est recherché dans les 80 000 premiers octets
    • Les octets du tag cible sont générés avec TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED), puis de nouveaux octets pour target_field_tag - 1 sont créés
    • Une recherche inverse est effectuée jusqu’à juste avant la position de /pagead/ pour trouver les octets du tag cible
    • S’ils sont trouvés, ces octets sont écrasés par les nouveaux octets et le corps de la réponse est remplacé via flow.response.set_content(bytes(body))
  • D’après les commentaires, cette PoC bloque 90 % des publicités
    • D’autres sections contiennent aussi d’autres clés de champ, et il peut y avoir plusieurs sections publicitaires à altérer

Portée et limites

  • Cette technique est présentée comme hautement spécialisée pour bloquer les publicités YouTube sur les appareils Apple, ou le trafic de trackers comme Instagram, WhatsApp et Facebook
  • La charge CPU nécessaire pour déchiffrer/réchiffrer le trafic HTTPS est jugée largement au-delà de ce qu’un Raspberry Pi peut gérer
  • Après jailbreak de l’Apple TV, l’ajout du certificat racine pfSense permettrait à la passerelle pfSense de déchiffrer le trafic de l’Apple TV et de bloquer les requêtes en inspectant le hostname publicitaire dans les en-têtes
    • En revanche, cela ne s’appliquerait toujours pas aux publicités sur iPhone ; le jailbreak d’un iPhone est plus difficile, et les applications bancaires peuvent le détecter et refuser de fonctionner
    • Le jailbreak lui-même est considéré comme trop extrême
  • Si une fausse CA de confiance est possible, les paquets TLS peuvent être déchiffrés en clair et un blocage par URL peut être appliqué
    • Les exemples d’URL bloquées cités sont les chemins YouTube /pagead/viewthroughconversion/... et /pagead/conversion/...
  • Au final, il configure un routeur matériel depuis zéro, divise le LAN entre zone de confiance et zone non fiable, ajoute un blocage DNS des publicités et un proxy MITM transparent, puis bloque efficacement les publicités YouTube sur les appareils Apple connectés au réseau

YouTube Premium et soutien aux créateurs

  • Après avoir bloqué les publicités YouTube pendant quelques mois, il commence à payer YouTube Premium pour soutenir les créateurs de contenu
    • Il ajoute la réserve suivante : « ce n’est pas parce qu’on peut le faire qu’on doit le faire »
  • Le prix de YouTube Premium est mentionné comme étant passé de CAD $9.99/mo à $11.99/mo, soit environ $13.43/mo taxes incluses
  • Dans une expérience de visionnage de publicités, il utilise un ordinateur portable propre et une navigation privée pour regarder YouTube par intermittence pendant une journée
    • D’après l’historique, 10 vidéos ont été regardées
    • Il a été exposé à 8 publicités, dont seulement 2 étaient passables
  • En supposant un CPV de USD $0.15, le coût publicitaire quotidien est de $1.20, soit environ USD $36/mo par extrapolation mensuelle
  • Un autre calcul basé sur des chiffres de Statista indique qu’en 2019, les annonceurs américains ont dépensé $15.1 billion sur YouTube, tandis que les résidents américains ont regardé 916 billion vidéos, soit une moyenne de USD $0.0165 par vue
    • Avec ce calcul, le coût quotidien est d’environ USD $0.13, soit environ USD $3.96 par mois par extrapolation
    • Il estime que cette valeur n’est pas proche des USD $10 de Premium
  • Lorsqu’une réclamation DMCA est déposée, les revenus publicitaires peuvent revenir au plaignant, comme Sony ou Viacom, plutôt qu’au créateur
    • À cause de cela, il peut arriver que l’on ne donne rien, sans le savoir, à une chaîne que l’on apprécie
    • Il juge qu’il n’est donc pas surprenant que de nombreux créateurs migrent vers Patreon

2 commentaires

 
xguru 2023-08-29

Le billet original est extrêmement long ; le processus est intéressant, mais le point à retenir, c’est que l’auteur a finalement fini par payer YouTube Premium et l’utiliser.

 
GN⁺ 2023-08-28
Avis sur Hacker News
  • Dans l’ensemble, c’est un hack impressionnant, mais certaines formulations à propos de Protobuf paraissent étranges.
    Il s’agit de casser volontairement le tag d’un champ dans Protobuf ; ignorer les numéros de tags non reconnus n’est pas un « défaut », mais un élément central de la conception pour l’extensibilité.
    1,87 Mio n’est pas non plus une taille énorme, et ce genre de message ne doit sans doute pas être diffusé en streaming en continu ; l’explication selon laquelle ce serait un obstacle de performance n’est donc pas très convaincante.
    L’encodage Protobuf n’a pas été conçu pour rendre le décodage coûteux, mais au contraire pour être décodé efficacement ; même sans le schéma .proto d’origine, on peut le décoder directement avec UnknownFieldSet.
    Une meilleure méthode aurait sans doute consisté à utiliser un faux schéma .proto ne contenant que le champ à supprimer. L’approche par scan de chaînes est plus sujette aux erreurs, car la même séquence d’octets peut apparaître par hasard dans d’autres données.
    Si l’ordre des champs change, les octets obtenus après réencodage peuvent être différents, mais le destinataire devrait traiter cela comme le même message, et il paraît peu probable que l’app YouTube détecte un changement d’ordre des champs.
    Pour quelqu’un qui a travaillé sur Protobuf par le passé, l’auteur semble avoir mal compris cette partie.

    • Bonne analyse, mais j’ai un peu de mal à dire que 1,87 Mo est petit.
      J’ai vécu la majeure partie de ma vie dans des zones rurales, et quand ce n’est pas mon Wi-Fi, même ça représente en pratique un gros téléchargement. Sur mobile, il existe peut-être des contournements, mais le Wi-Fi rural a encore du mal avec l’architecture du Web 2.0 et s’utilise souvent à des débits de 2 à 4G.
      Dans les zones urbaines, où il y a assez de population pour soutenir l’infrastructure, 1,87 Mo est généralement devenu un petit fichier, mais il peut y avoir des exceptions vers 18 h, quand tout le monde sur la ligne câble se met à streamer.
    • Petite promo à propos de without the C++ source proto files : j’ai créé un projet appelé protodump, qui génère les fichiers source .proto à partir de binaires.
      Il recrée les définitions des messages et des champs, y compris leurs noms d’origine ; il suffit d’extraire le binaire de la box Apple TV.
      https://github.com/arkadiyt/protodump
    • Dire « j’ai travaillé sur Protobuf par le passé » est un énorme euphémisme. Pour ceux qui ne le savent pas, Kenton est la personne qui a donné à Protobuf sa forme actuelle.
      Protobuf a été la technologie qui m’a fait découvrir les IDL, et à l’époque l’idée me paraissait magique. J’en ai été d’autant plus surpris après avoir bricolé moi-même un IDL bancal puis découvert Protobuf.
    • J’ai ressenti une confusion similaire en lisant cet article. Les principes de conception de Protobuf ne sont pas secrets, et tout est clairement documenté.
    • Quand on décode Protobuf sans schéma, la partie la plus délicate est que les messages imbriqués et les chaînes utilisent le même type de tag, mais cela reste assez facile à gérer.
      Si l’on ne veut pas tirer toute la dépendance protoc, on peut écrire soi-même un petit décodeur Protobuf de quelques centaines de lignes : https://github.com/kubernetes/test-infra/blob/master/guberna... https://github.com/kubernetes/test-infra/blob/master/guberna...
  • Depuis que j’ai découvert The Proxomitron il y a plus de vingt ans, je fais passer le trafic par un proxy homme-du-milieu pour supprimer les publicités et réécrire les pages avec, par exemple, du CSS utilisateur.
    Des services comme CloudFlare ont tendance à me classer comme « bot », mais il existe aussi des moyens de contourner cela, même si ce n’est pas trivial. Ce genre de cas montre aussi pourquoi l’attestation distante est dangereuse pour la liberté des utilisateurs.

    • En regardant The Proxomitron, il semble que son développement se soit arrêté en 2004 ; un résumé au fait de la situation actuelle serait intéressant.
      Il semble exister plusieurs projets « successeurs », et je me demande aussi si c’est uniquement pour Windows. Je cherchais vaguement un proxy simple capable d’insérer des liens locaux dans du contenu distant.
    • Le blocage de publicités au niveau réseau comme Privoxy ou pi-hole a trop d’inconvénients, notamment l’impossibilité de traiter les publicités inline.
      Pour l’instant, j’ai même débranché le pi-hole qui tournait sur mon Pi 4. J’ai passé des heures à essayer de le faire fonctionner correctement avec plusieurs services, puis j’ai fini par abandonner ; cela ne valait pas le temps investi pour un réseau domestique.
      Ce qui fonctionne vraiment, ce sont les bloqueurs de publicité dans le navigateur et les patchers d’applications comme ReVanced. À mesure que mes économies augmentent, je penche davantage vers le fait de simplement payer des services sans pub — YouTube Premium, Hulu, Netflix, Max — quand ces deux solutions ne suffisent pas.
    • On sait que des employés de Cloudflare traînent ici, donc je suis curieux : être classé comme bot de cette façon est-il considéré comme un faux positif, ou comme un fonctionnement conforme aux attentes ?
    • Je n’avais quasiment pas pensé à Proxomitron depuis près de 20 ans ; je me demande s’il est encore utilisé.
      Je ne l’ai jamais utilisé pour l’usage évoqué ici, mais il était excellent comme proxy derrière le pare-feu de l’entreprise. À l’époque, le pare-feu exigeait des identifiants de connexion pour les connexions externes, ce qui empêchait beaucoup de programmes d’accéder à Internet.
    • Il faut probablement installer un nouveau certificat d’AC sur l’appareil, non ?
  • Privaxy empaqueté avec Docker vient à l’esprit. C’est un proxy d’interception compatible avec les listes de blocage d’UBlock Origin.
    Quand on voit la quantité de publicités et de scripts de tracking présents sur les produits connectés, surtout les TV, c’est assez surprenant. D’après mes tests jusqu’ici, plus de 40 % du trafic était inutile, et l’expérience consistant à retirer les pubs des apps de smart TV était plutôt amusante.
    https://github.com/deetungsten/webui-privaxy est un fork dockerisé de https://github.com/Barre/privaxy

    • Comment faire pour que la TV fasse confiance à un certificat autosigné ?
    • Content de le voir mentionné ici aussi. C’était bien de soulever le problème du ping des listes de filtres.
      J’avais voulu modifier le fork pour éviter que le frontend utilise l’adresse 0.0.0.0 codée en dur, afin de pouvoir vraiment isoler le conteneur Docker, mais la vie s’en est mêlée. Tu as essayé sur Apple TV ?
    • Adguard travaille aussi sur quelque chose de similaire.
      https://github.com/AdguardTeam/urlfilter
    • Il m’a fallu beaucoup trop longtemps pour comprendre que « fork dockerisé » voulait dire que l’interface graphique avait été remplacée par une interface web.
  • Cet article est une excellente réponse à la question récurrente : « comment apprendre à devenir hacker ? »
    Il montre bien le cheminement de pensée et le travail acharné qui entrent dans n’importe quel exploit.

  • Il y a un passage disant « utilisons WireGuard — Intel dispose du jeu d’instructions de chiffrement AES-NI », mais à ma connaissance WireGuard n’utilise pas AES.
    Globalement, l’auteur semble surestimer un peu les besoins CPU du chiffrement TLS, ou sous-estimer les performances des ordinateurs monocartes modernes.
    L’explication selon laquelle les besoins CPU pour déchiffrer puis rechiffrer du trafic HTTPS sur un Raspberry Pi seraient largement dépassés me laisse aussi perplexe. Je serais assez surpris qu’un traitement TLS en interception soit réellement impossible sur un RPi 4, même avec du RSA purement logiciel.
    Parmi les téléphones Android encore utilisés, certains ont un CPU moins puissant qu’un RPi 4, et ils utilisent pourtant TLS.

    • Tu sembles sous-estimer les besoins CPU.
      Si un téléphone Android peu puissant ne peut traiter que 50 Mb/s de trafic TLS, ce n’est pas forcément un gros problème en usage réel. Les téléphones lents sont généralement connectés à des réseaux lents.
      En revanche, si tu as une connexion gigabit à la maison et qu’un appareil peu puissant placé entre tous tes ordinateurs et Internet crée un goulot d’étranglement à 50 Mb/s, ça devient un vrai problème.
      Les besoins CPU de TLS dépendent énormément de la bande passante visée. À des débits plus élevés, l’offload vers un accélérateur devient parfois pratiquement indispensable. Le coût du handshake n’est pas négligeable non plus et peut limiter le nombre de connexions par seconde. C’est rarement un problème sur un seul appareil, mais cela peut prendre de l’ampleur à l’échelle de tout un réseau d’appareils.
  • Super article. J’espérais une méthode pour faire de l’interception sur des appareils qui n’autorisent pas l’installation d’une CA personnalisée.
    J’ai un appareil IoT qui n’expose pas d’API locale et n’affiche ses données que via le cloud ; j’aimerais capturer le trafic entre l’appareil et le cloud.
    Au final, est-ce qu’il n’y a pas d’autre solution que de dumper la mémoire flash, remplacer la CA, puis reflasher ?

    • Si le certificat est « codé en dur », on appelle ça du certificate pinning. Dans ce cas, il faut remplacer ou supprimer le certificat, puis transférer ce même certificat vers le proxy d’interception pour pouvoir déchiffrer le trafic.
      Il y a un bon article qui donne des pistes pour intercepter des appareils IoT sans toucher au matériel ni au firmware :
      https://robertheaton.com/2019/11/21/how-to-man-in-the-middle...
    • Chercher un moyen d’intercepter un appareil qui n’autorise pas l’installation d’une CA personnalisée, c’est au fond aller à l’encontre du but de TLS.
      Si c’est possible, cela revient à s’appuyer sur une faille d’implémentation.
      Honnêtement, je pense que même pour les quelques appareils qui permettent aujourd’hui d’installer des certificats de confiance personnalisés, ce temps est compté.
  • De nouvelles méthodes pour bloquer les pubs sur YouTube ou n’importe quelle plateforme apparaissent toujours, mais quelques mois plus tard elles changent et deviennent inutiles.
    Et si, à la place, on attaquait les annonceurs ? YouTube/Google semble ne suivre que les « clics », mais suivent-ils aussi les achats réels ?
    En théorie, si suffisamment de faux bots et de vrais utilisateurs cliquent sur les publicités sans rien acheter, on peut brûler les budgets publicitaires. Avec le temps, les équipes marketing verront que, sur une plateforme donnée, les clics atteignent des records mais que le taux de conversion par rapport aux clics ou aux impressions est très faible, et elles finiront peut-être par quitter cette plateforme.

    • Vu que Nauseum a été bloqué sur le Chrome Store, ça semble plutôt efficace.
  • Article étonnant. Dès que j’ai vu l’étape de patch mitm, je me suis dit que ce serait quelque chose de spécial, et c’était bien le cas.

  • Le passage de la table des matières « Nouvel objectif : faire croire à YouTube que je suis un homme de 70 ans vivant en Italie » m’a marqué.
    Il y a quelque temps, je ne sais pas comment, le ciblage publicitaire s’est mis à croire que j’étais quelqu’un qui voulait acheter un pyjama en soie lavable à 500 dollars pour ma petite amie.
    Les pubs elles-mêmes étaient excellentes, mais je me demande combien ils payaient par impression.
    Depuis que je suis passé à l’Apple TV, j’ai surtout des pubs locales avec un ciblage géographique erroné. En moyenne, c’est peut-être mieux comme ça.

  • Ce n’est pas une « faille » de Protobuf. Le fait que, lorsqu’on modifie des octets, ils soient décodés comme un champ situé ailleurs correspond à un fonctionnement prévu
    Protobuf est à l’origine un protocole fondé sur des numéros de champ et des préfixes de longueur, qui part de l’hypothèse raisonnable que les octets ne changent pas pendant le transport, et laisse au lecteur la responsabilité de l’intégrité
    Même si c’était une faille, ce serait celle de l’app YouTube pour iOS, pas de Protobuf ; et comme en réalité ce n’est même pas une faille, il est difficile de parler d’« exploit ». À la rigueur, on pourrait dire que les échanges Protobuf de l’app YouTube iOS ne vérifient pas le hash du payload renvoyé
    Après cet article, ils vont probablement se mettre à le vérifier

    • La formulation de l’auteur est un peu étrange. Le mot « faille » n’apparaît que dans le titre, tandis que le corps explique simplement comment fonctionne le format
      Ce n’est pas une faille, c’est un comportement conforme à la conception
      Le passage disant que « Google rend le décodage, la modification et le réencodage coûteux en calcul sans les fichiers proto source C++ » est lui aussi étrange. Avec du code Python non optimisé, ce serait coûteux, mais en C ou dans un autre langage compilé, parcourir un Protobuf de 1,8 Mo est trivial, qu’on ait ou non les fichiers source proto
      Il est peu probable que l’objectif de conception ait été de rendre les fichiers Protobuf difficiles à décoder sans les sources. Si c’était l’objectif, il aurait été plutôt mal atteint
    • Je ne sais pas comment les champs obligatoires fonctionnent dans Protobuf, mais pour atténuer l’attaque, le client YouTube de Google pourrait traiter ce champ comme un champ obligatoire et refuser le service si le champ est absent ou contient la valeur par défaut
    • L’article date de janvier 2022. S’ils avaient voulu renforcer le protocole après ce billet de blog, il est très probable qu’ils l’aient déjà fait