- Il s’agit d’un PoC qui place un proxy MITM basé sur pfSense entre l’Apple TV et Internet pour déchiffrer le HTTPS, puis modifie les réponses Protobuf envoyées par YouTube afin que les emplacements publicitaires ne soient pas enregistrés sur les appareils Apple.
- Les bloqueurs DNS existants et le routage VPN se heurtent à des limites comme le TTL DNS, les incohérences d’IP, les erreurs 403 ou les fuites d’ASN, car les publicités YouTube et la vidéo principale utilisent les mêmes domaines et la même infrastructure.
- Les essais avec Squid ont été abandonnés à cause de problèmes de performances et de configuration, puis l’approche a basculé vers le déchiffrement du trafic TLS avec mitmproxy/mitmdump dans une jail FreeBSD, afin de modifier directement les réponses JSON et Protobuf.
- Sur YouTube Web, il était possible de supprimer
adPlacements,playerAdParams, les URLpagead, etc. dans le JSON, mais l’app YouTube iOS plaçait les emplacements publicitaires et les informations de suivi dans des réponsesapplication/x-protobuf, ce qui imposait de manipuler la structure Protobuf. - La méthode finale consiste en un scan linéaire et une modification d’un octet : chercher à rebours le field tag près de la chaîne
/pagead/, puis remplacer un tag comme50195462partarget_field_tag - 1. Elle offre des performances adaptées au traitement en temps réel sans décodage complet.
Objectif et conception réseau initiale
- L’objectif était de créer un routeur basé sur FreeBSD et pfSense afin de bloquer, à l’échelle de tout le réseau, les publicités YouTube pre-roll, mid-roll et end-roll sur Apple TV et iPhone.
- En plaçant un proxy man-in-the-middle entre l’Apple TV et Internet, il devient possible de déchiffrer le trafic HTTPS et de lire les données Protocol Buffer utilisées par Google pour remplir les publicités YouTube.
- Après avoir mis en place le blocage des publicités YouTube pendant plusieurs mois, l’auteur a commencé à payer YouTube Premium, en précisant qu’« être capable de le faire » et « devoir le faire » sont deux choses différentes.
- Les raisons invoquées pour bloquer les publicités et le tracking étaient le suivi des données personnelles, le gaspillage de bande passante, le clickbait et le cryptojacking.
- Il estime que 25 à 40 % du trafic réseau peut être constitué de publicités, de scripts de tracking, de fingerprint.js, de googletagmanager.js et de loaders d’analytics temps réel comme Hotjar.
- Il explique que du JavaScript de minage crypto comme CoinHive.js peut faire surchauffer un ordinateur ou l’exploiter abusivement pour gagner de petites sommes.
Matériel pfSense et configuration de base
- Pour protéger tout un réseau SMB, des VM, images Docker ou Raspberry Pi n’offrent pas assez de performances ; il faut donc, selon lui, du matériel dédié chargé uniquement du routage des paquets, du déchiffrement et de la supervision.
- Le matériel routeur utilisé était un mini PC doté du jeu d’instructions AES-NI, de RAM DDR4, d’un SSD mSATA et d’une clé USB pour flasher pfSense.
- Un exemple de configuration est un mini PC J4125, 32 Gio de RAM DDR4 et un SSD mSATA de 128 Gio.
- Il juge que 128 Go de stockage suffisent pour les logs, la réduction de l’usure du SSD, les captures de paquets et le cache edge NPM/Docker.
- L’image d’installation de pfSense fait environ 360 Mo et peut être flashée sur une clé USB avec l’AppImage d’Etcher.
- Après la première configuration, AES-NI apparaissait comme “Yes (inactive)” ; il l’a donc activé manuellement dans
System › Advanced › Miscellaneous. - Pour exploiter les 32 Gio de RAM, il a largement alloué des RAM disks à
/varet/tmp, et configuré des sauvegardes horaires des RAM disks vers le SSD de 128 Gio, en comptant sur le wear-leveling. - Il a ajouté un widget S.M.A.R.T. au Dashboard afin de détecter d’éventuelles anomalies du SSD.
Blocage DNS, segmentation réseau et pfBlockerNG
- Auparavant, il utilisait Pi-hole sur Raspberry Pi comme bloqueur de publicité au niveau DNS ; avec pfSense, il a installé pfBlockerNG-devel pour tester le blocage des publicités, des contenus malveillants et le geo-blocking.
- Si le service pfb_dnsbl ne démarre pas ou si l’onglet status affiche
[ Missing CRON task ], il recommande d’essayer de supprimer le fichier vide/var/run/booting. - En utilisant les trois ports Gigabit du mini PC, il a créé de véritables réseaux physiques plutôt que des VLAN, et isolé du réseau principal les appareils qui “appellent à la maison”, comme Alexa et l’Apple TV.
- Les appareils non fiables sont placés sur le réseau privé
172.31.1.0/24. - Le LAN de confiance reste en
192.168/16. - Le LAN matériel destiné à l’IoT passe par l’adblocker, et les requêtes DNS codées en dur vers
1.1.1.1,9.9.9.9, etc. sont interceptées afin d’empêcher YouTube de contourner le bloqueur DNS.
- Les appareils non fiables sont placés sur le réseau privé
- Il a configuré des règles NAT pour que tous les clients derrière pfSense utilisent le serveur DNS Unbound local.
- Il estime qu’il faut d’abord bloquer DNS over TLS pour pouvoir intercepter les requêtes DNS.
- L’iPhone peut afficher un Privacy Warning concernant le blocage du trafic DNS chiffré, mais les requêtes DNS upstream sont, selon lui, chiffrées vers Cloudflare.
- Le NAT reflection doit être désactivé afin d’empêcher Internet d’accéder au serveur DNS.
- Il crée un alias firewall
Non_WANpour rediriger vers localhost le port 53 des requêtes DNS locales provenant des interfaces autres que le WAN. - Comme YouTube diffuse les publicités et la vidéo principale depuis les mêmes domaines, il était difficile de filtrer uniquement les publicités avec des bloqueurs par nom de domaine comme pfBlockerNG ou Pi-hole.
Expériences de contournement via VPN et points d’échec
- Il a aussi mené des expériences visant, plutôt que de bloquer les publicités, à tromper l’algorithme publicitaire de YouTube afin de paraître moins intéressant pour les annonceurs.
- Il voulait router le trafic de localisation de YouTube via un VPN vers une région comptant peu de spectateurs, à l’aide du routeur pfSense.
- Il s’était fixé comme objectif que le compte YouTube soit perçu comme appartenant à un « homme de 70 ans vivant en Italie ».
- Sur pfSense, il a utilisé WireGuard plutôt qu’OpenVPN pour réaliser une expérience de référence envoyant tout le trafic de l’Apple TV dans le VPN.
- Il a installé le paquet FreeBSD WireGuard, puis ajouté et activé un tunnel.
- Pour la configuration NordLynx, il a récupéré la private key dans une VM Linux avec
sudo wg showconf nordlynx, puis l’a transférée dans pfSense.
- Les tests ont montré que Google s’affichait en italien sur l’ordinateur portable, et que YouTube sur Apple TV passait également en italien.
- Quelques publicités apparaissaient encore, mais moins qu’auparavant.
- Netflix et Amazon Prime posaient problème, et des fichiers CSS ou de polices semblaient bloqués, ou des vignettes ne se chargeaient pas.
- Il avertit qu’il ne faut pas envoyer tout le trafic de l’Apple TV dans le VPN, et estime que Netflix et Prime détectent bien les fournisseurs de VPN et le geofencing.
- Il a ensuite configuré une règle de politique firewall ciblant
www.youtube.com,youtube.com,googlevideo.com,accounts.google.com,googleapis.com,gstatic.com, etc., afin de ne faire passer dans le VPN que le trafic YouTube de l’Apple TV.- Au final, YouTube considère l’utilisateur comme situé à Milan, tandis que Netflix et Prime Video le voient au Canada.
- Les publicités sont devenues très rares, “few and far between”.
- Au bout d’une journée, une condition de course DNS est apparue.
- Les alias de hostname pfSense sont résolus par défaut toutes les 300 secondes.
- Le TTL DNS de YouTube peut être de 1 440 secondes, soit 24 minutes.
- Si l’IP résolue par l’Alias Daemon diffère de celle réellement reçue par le client, la policy peut ne pas tunneler le trafic YouTube.
- Certaines vidéos YouTube ne se lançaient pas, avec un
403 Forbidden.- Selon lui, YouTube intègre l’IP de l’utilisateur dans chaque requête
googlevideo.com. - Si un domaine modifié comme
r5---sn-hpa7kn76.googlevideo.comn’est pas tunnelé, la requête sort depuis la mauvaise IP et pose problème. - Ce qu’il faudrait, c’est un tunneling wildcard de
*.googlevideo.com, mais les règles NAT et firewall fonctionnent avec des IP, pas avec des hostnames wildcard.
- Selon lui, YouTube intègre l’IP de l’utilisateur dans chaque requête
PoC de suivi d’IP basé sur les requêtes DNS
- Pour router
*.googlevideo.comvia le VPN, l’idée était d’utiliser une méthode de détournement des requêtes DNS Google Video- Elle consistait à suivre périodiquement le journal des requêtes DNS et à ajouter les requêtes
*.googlevideo.comà la liste d’alias - Si chaque vidéo utilise un domaine unique et modifié, cette méthode ne fonctionnerait probablement pas à moins d’actualiser à chaque vidéo
- Elle consistait à suivre périodiquement le journal des requêtes DNS et à ajouter les requêtes
- Le nouvel objectif était de surveiller les requêtes DNS avec Python 3 et l’API REST de pfSense pour capturer les IP, retenir brièvement la réponse, ajouter l’IP à la règle de tunneling VPN, puis libérer la réponse DNS
- L’API REST de pfSense a été installée, puis une requête GET vers
https://pfsense/api/v1/firewall/aliasa permis de consulter l’aliasVPN_domains - En explorant le module Python d’Unbound DNS Resolver, il a été possible de journaliser les messages de requête DNS
- La version actuelle de Python était la 3.8
- Les exemples du module Python d’Unbound étant basés sur Python 2.4, il a été estimé que
2to3ou un reformatage pourrait être nécessaire
- Le script PoC extrait les IP des enregistrements A/AAAA dans la réponse DNS et les ajoute à l’alias pfSense
- Les enregistrements A sont traités avec
ipaddress.IPv4Address(d.rr_data[j][2:]).exploded - Les enregistrements AAAA sont traités avec
ipaddress.IPv6Address(d.rr_data[j][2:]).exploded - Le TTL de l’alias est fixé à 1 heure et sa capacité à 500
- Les enregistrements A sont traités avec
- Le lendemain, Unbound DNS Resolver a subi un segfault, et comme chaque ajout d’IP imposait de recharger les règles pfSense, pfSense est devenu très lent
Passage de Squid à mitmproxy
- Le nouvel objectif a consisté à étudier et installer un proxy de la famille Squid, créer un faux certificat CA de confiance, puis déchiffrer le trafic TLS
- Lors des essais avec Squid, le proxy
squid3fourni comme package pfSense a été testé pour vérifier s’il répondait aux besoins- Un dossier dédié
/squid_cachea été créé et la taille du cache fixée à 8 GiB - Le support HTTPS transparent était attendu
- Un dossier dédié
- Après une journée de configuration de Squid et SquidGuard, l’approche a été abandonnée
- C’était très lent
- La configuration des ACL était laborieuse
- Il y avait un problème lié à
https://http/* - La mise à jour de la liste de filtrage d’URL de SquidGuard prenait énormément de temps
- L’interface utilisateur de Squid était insuffisante
- Le choix s’est ensuite porté sur mitmproxy, écrit en Python
- mitmproxy a été préféré à
SSLSplitpour l’extensibilité via des hooks Python et pour son UI - La version FreeBSD de pfSense était
12.2-Stable, en build 64 bits
- mitmproxy a été préféré à
- Dans l’environnement par défaut de pfSense, les jails étant désactivées,
ezjaila été installé manuellement et une jail dédiée àmitmproxya été créée- La jail a été créée avec
ezjail-admin create mitmproxy 'lo0|127.0.1.1' allow.raw_sockets=1a été configuré pour le mode proxy transparent- Si les raw sockets sont bloquées, des erreurs comme
Transparent mode failureouCannot open connection, no hostname given.peuvent apparaître
- La jail a été créée avec
- L’exécution du binaire Linux en tarball a échoué sous FreeBSD
- L’erreur
ELF interpreter /lib64/ld-linux-x86-64.so.2 not founds’est produite libdl.so.2,libz.so.1,libpthread.so.0etlibc.so.6étaient également introuvables
- L’erreur
- Dans la jail,
pkg install mitmproxya été exécuté ; l’installation nécessitait 50 packages, 206 MiB d’espace supplémentaire et 33 MiB de téléchargement - Pour rendre MITMProxy accessible depuis le LAN, l’IP virtuelle
127.0.1.1a été attachée à localhost, puis une règle NAT a temporairement redirigé[Private IPs]:8080vers127.0.1.1:8080 - Le fichier PEM CA généré automatiquement par MITMProxy est
~/.mitmproxy/mitmproxy-ca-cert.pem, et ce certificat CA a été installé dans le Trusted Root Store de l’appareil de test - Même au repos,
mitmproxyconsommait beaucoup de CPU ; la génération en temps réel d’un certificat TLS par requête et le logging excessif semblaient fortement ralentir l’ensemblemitmdump, qui omet l’UI et le logging excessif, semblait moins solliciter le CPU
Suppression des publicités JSON de YouTube Web
- Le certificate pinning est une technique par laquelle le serveur ou le client connaît à l’avance l’empreinte du certificat attendu, ce qui rend inefficace la falsification de certificat par MITMProxy
- Les hôtes problématiques peuvent contourner le proxy via l’option
--ignore-hosts- Par exemple,
apple.com:443eticloud.com:443ont été ignorés
- Par exemple,
- Lors de l’accès à YouTube, des publicités de page sont apparues dans MITMProxy avec des en-têtes non chiffrés, et la possibilité d’un blocage par simple regex a été examinée
- Pour appliquer le script de blocage des publicités YouTube,
--scripts "youtube.py"a été ajouté àmitmdump - Le filtre de smoke-test bloque les requêtes publicitaires en fonction de sous-chaînes dans l’URL
youtube.com:/pagead/,/log_event?,/stats/ads,/stats/qoe?,/ptracking?,/generate_204,el=adunit,adformat=,/activeview?google.com,google.ca:/pagead/ggpht.com:.
- Les requêtes ciblées semblaient bien bloquées dans MITMProxy et dans le panneau Network de DevTools, mais les publicités apparaissaient toujours, et il arrivait qu’elles se passent d’elles-mêmes ou que leur lecture échoue
- Par la suite, un grand nombre d’URL liées aux publicités ont été trouvées dans le payload JSON
- Les sections
playerAdsetplaybackTrackingy figuraient youtubeRemarketingUrlcontenaithttps://www.youtube.com/pagead/viewthroughconversion/...googleRemarketingUrlcontenaithttps://www.google.com/pagead/1p-user-list/...
- Les sections
- Après analyse de l’UI YouTube et du workflow HTTP, jusqu’aux cookies et service workers, il serait devenu possible de supprimer toutes les publicités pre-roll, post-roll et mid-roll
- À ce stade, le routeur pouvait supprimer les publicités du payload JSON des publicités Web YouTube
YouTube iOS et le problème Protobuf
- L’app iOS de YouTube expose, dans la version Protobuf du même appel d’API que la version web, des données très similaires
- Dans Protobuf, les clés sont numériques et peuvent changer, il est donc impossible d’utiliser une méthode consistant à trouver la section publicitaire avec JSONPath
- YouTube envoie dans le payload une grande liste de publicités à afficher prochainement, et une fois cette liste épuisée, une autre grande liste arrive rapidement
- Dans le payload Protobuf, on voyait des chaînes comme « Telus », « Samsung TV », « Boxing Week », « Buy now »
- Le protocole YouTube iOS était différent du trafic web
- Dans la version web, il était possible de distinguer dans une certaine mesure la vidéo publicitaire de la vidéo souhaitée en observant l’URL et le paramètre de requête
range - Le protocole iOS n’utilise ni le paramètre de requête
range, ni l’en-têteRange, et emploie à la place des compteurs comme&nr=2,&nr=3dans les chunks vidéo - Pour bloquer les publicités YouTube sur iOS, il fallait rétroconcevoir la réponse Protobuf
- Dans la version web, il était possible de distinguer dans une certaine mesure la vidéo publicitaire de la vidéo souhaitée en observant l’URL et le paramètre de requête
- Dans le message Protobuf décodé, des entrées
has_unlimited_entitlement: Falseethas_premium_lite_entitlement: Falseont été trouvées, mais au lieu de les basculer, l’approche est revenue à des heuristiques - Décoder environ 500 KiB de Protobuf brut avec l’implémentation Python pure était très lent
- Sur un ordinateur de bureau i7-6700, les résultats Python étaient d’environ 2,06 à 2,11 secondes
- Sur un routeur pfSense, les résultats Python étaient d’environ 22,8 à 24,2 secondes
- Le C++
protoc --decode_rawprenait environ 0,017 à 0,022 seconde sur l’ordinateur de bureau, et environ 0,12 à 0,14 seconde sur le routeur pfSense
Tentatives de décodage Protobuf et d’extraction du schéma
- Comme Python ne prend pas en charge le décodage Protobuf brut, l’option retenue a été de communiquer avec le binaire C++
protocviasubprocess.Popen, plutôt que d’utiliser directementlibprotobuf.soen C++ - En fuzzant les réponses vidéo publicitaires, des essais ont été faits avec des
200vides, des404, des503, des corps de réponse tronqués et la mise à null d’une partie de la vidéo publicitaire, mais l’app iOS ralentissait puis crashait, ou restait bloquée sur l’écran publicitaire - Le blocage d’URL déclenchait des comportements de repli de l’app, et les chunks de réponse vidéo contenaient aussi des métadonnées de session
- blackboxprotobuf pour Burp Suite permet de décoder un message Protobuf wire brut, d’y injecter du contenu, puis de le réencoder afin de vérifier le comportement d’un endpoint Protobuf
- Il est recommandé d’utiliser la version originale pour Burp Suite, et non le fork PyPI
- Certains forks ont des problèmes de stack overflow ou de récursion infinie à cause d’une récursion profonde
- Avec les bindings C++, environ 500 KiB de Protobuf brut peuvent être transcodés en quelques secondes
- Le schéma généré n’était pas parfait, il était volumineux et profondément imbriqué, et son pretty-print était lent, mais il suffisait pour trouver les détails des publicités
- Pour extraire les vrais fichiers
.protoou de schéma depuis l’APK Android de YouTube, PBTK, Apktool, dex2jar et Java Decompiler ont été essayés- PBTK n’a extrait qu’un fichier proto de 59 octets
- Il y avait des classes Protobuf et des getter/setter dans le Java, mais les vrais fichiers de schéma n’ont pas pu être obtenus, ce qui a conduit à abandonner cette piste
Tournant final : modification d’un octet du tag de champ Protobuf
- D’après le trafic réseau déchiffré et le fuzzing Protobuf, les publicités semblaient être enregistrées dans des slots associés à une vidéo donnée
- Les types de slots incluaient pre-roll, mid-roll, end-roll, full-page et ad pods
- Si l’URL publicitaire était bloquée, une erreur du type « une publicité inexistante a réservé un slot » survenait et provoquait une panique de l’UI
- Sans schéma original, décoder, modifier puis réencoder produit un encodage modifié ; cela pose problème, car on ne sait pas si ZigZag est utilisé ni quels types numériques sont employés, comme
int32,int64,sint32/64ouvarint, et l’ordre des champs d’objet est généralement non déterministe - Une possibilité de contournement a été trouvée dans la compatibilité ascendante de Protobuf et le comportement de UnknownFieldSet
- Lorsqu’un ancien logiciel lit un message auquel un nouveau champ a été ajouté, un champ inconnu peut apparaître
- En remplaçant la clé d’un champ donné par une autre valeur, toute la sous-structure contenant les informations publicitaires et de tracking peut devenir indisponible
- L’idée proposée consiste par exemple à remplacer la clé de champ
49399797par49399796, afin de faire passer cette sous-structure publicité/tracking pour un champ inconnu - La clé de champ
49399797ne peut pas être trouvée par une simple recherche hexadécimale ; il faut tenir compte de l’encodage varint/tag- Le wire type est
2, ce qui correspond à une chaîne ou un message imbriqué à longueur délimitée - La séquence d’octets du tag pour la clé de champ cible
49399797devientAA FF B8 BC 01 - En supprimant les 3 bits de wire type avec
395198378 >> 3, on obtient la clé de champ originale49399797
- Le wire type est
- Dans les octets Protobuf, une signature classique d’URL publicitaire comme
/pagead/a été recherchée pour délimiter la zone de recherche du champ, puis un retour en arrière depuis cette position a permis de trouver le tag de champ et la clé de champ à modifier - Dans l’exemple de journal d’interception, dans la réponse
application/x-protobufde 1,87 MiB d’une requête POSTyoutubei.googleapis.com:443/youtubei/v1/browse?key=..., la clé49399797a été trouvée à la position4465et la clé50195462à la position4477 - Lors d’un smoke test en O(n), les données Protobuf de 1,8 MiB ont été scannées une seule fois sans mémoire supplémentaire
- La cible a été trouvée au 30 593e octet sur les 1,8 MiB
- Un retour en arrière d’environ 600 octets a permis de trouver la clé de champ à dénaturer
- Une fois cette méthode fonctionnelle, il n’a plus été nécessaire de bloquer les URL contenant
*.googleadservices.comou/pagead/, et les requêtes correspondantes ont cessé de se produire dès le départ
Structure du script add-on MITMProxy
- Le script add-on MITMProxy est fourni comme proof of concept pour bloquer les publicités YouTube sur les appareils Apple en réseau
- Le nom du fichier est
youtube.py - Exemple d’exécution :
mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py" - Les prérequis FreeBSD sont
pkg install protobuf,pkg install py38-pip,pip install jsonpath-ng
- Le nom du fichier est
- Le script inclut une fonction d’équité qui autorise 5 % des publicités afin de soutenir les créateurs de contenu
in_allowed_ads_window()saute le blocage des publicités si l’heure actuelle se situe entre la minute 0 et la minute 2 de chaque heure
YouTubeAdBlockerintercepte les domaines liés à YouTube et modifie les réponses JSON ou Protobuf pour supprimer les informations publicitaires- La regex des hôtes à intercepter est
\.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com - La chaîne utilisée pour détecter les publicités Protobuf est
b"/pagead/" - La limite de recherche est de
80_000octets - Le tag du champ cible est
50195462
- La regex des hôtes à intercepter est
- La liste de blocage à l’étape de requête inclut notamment
pagead/,log_event?,stats/ads,stats/qoe?,ptracking?,generate_204,error_204,adformat=,activeview?,_ad_,ai?,sw.jssur les hôtes YouTube - Pour YouTube Web, les remplacements JSON suppriment ou désactivent les champs liés à la publicité
yt_adest remplacé par"0"adPlacementsest remplacé par[]adPlacementRenderer,adPlacementConfig,playerAdParams,gutParamssont remplacés par{}adVideoIdest remplacé par""showCompanion,showInstream,useGutsont remplacés parFalse
- Le hook
load()désactive HTTP/2 et définitanticomp=True,mode="transparent" - Le hook
running()met à jourallow_hostsafin que l’interception ne s’applique qu’aux domaines liés à YouTube - Le hook
response()cherche/pagead/dans les 80 000 premiers octets du corps de la réponse si lecontent-typecontientprotobuf- S’il le trouve, il crée les octets du tag cible avec
TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED) - Il crée de nouveaux octets de tag pour
target_field_tag - 1 - Il effectue une recherche inversée avant la position de
/pagead/pour trouver le tag cible - Il remplace les octets à cet emplacement par ceux correspondant à
target_field_tag - 1 - Il réinjecte le contenu Protobuf modifié avec
flow.response.set_content(bytes(body))
- S’il le trouve, il crée les octets du tag cible avec
- Les commentaires du code indiquent que ce PoC bloque déjà 90 % des publicités, et ajoutent qu’il existe d’autres clés de champ dans d’autres sections et qu’il peut y avoir plusieurs sections publicitaires à neutraliser
Performances, limites et utilisateurs visés
- La technique finale exploite la capacité de Protobuf à autoriser les champs inconnus pour rester compatible avec les changements de schéma, ainsi que la sensibilité aux modifications d’un seul octet de son format compact
- En modifiant 1 octet à un endroit critique pour faire passer une section profondément imbriquée pour un élément appartenant à une future version du schéma, Protobuf peut l’ignorer et supprimer les informations publicitaires
- Google renvoie une grosse réponse Protobuf qui inclut même la mise en page de l’app iOS ; l’exemple de payload fait 1,8 Mio
- Parser tout le payload nécessiterait du code natif comme C++/Swift, et le décodage en Python serait plusieurs ordres de grandeur plus lent, au point de provoquer des timeouts de connexion
- Le JSON côté Web doit être entièrement parsé, modifié puis resérialisé, tandis que la technique Protobuf ne fait qu’un scan linéaire et un backtracking rapide : elle se traite en quelques microsecondes, convient au blocage publicitaire en temps réel et ne nécessite pas de blocklist
- Toutes les URL
*.googleadservices.comet/pagead/*sur les appareils Apple proviennent du payload Protobuf ; si les données publicitaires disparaissent du payload, ces requêtes disparaissent automatiquement elles aussi - L’app YouTube ne tente pas de récupérer les URL publicitaires, ce qui donne une impression de plus grande rapidité, et le contenu est lu immédiatement car aucune publicité n’est enregistrée dans le slot vidéo
- Cette approche est présentée comme une technique très spécialisée pour bloquer les publicités YouTube sur appareils Apple ou le trafic de tracking d’Instagram, WhatsApp et Facebook
- La charge CPU nécessaire pour déchiffrer/réchiffrer le trafic HTTPS dépasserait largement les performances d’un Raspberry Pi
- Comme elle vise les propriétaires d’appareils Apple qui ne veulent pas compromettre leur OS, le public concerné est considéré comme encore plus restreint
YouTube Premium et expérimentation sur le coût publicitaire
- L’auteur estime qu’il n’est pas certain que YouTube Premium soit raisonnable à CAD $9.99/mo ou CAD $11.99/mo, soit environ CAD $13.43/mo taxes incluses
- Il a mené une expérience d’exposition publicitaire en regardant YouTube par intermittence pendant une journée sur un ordinateur portable propre, en navigation privée
- D’après l’historique, seuls 10 vidéos ont été « vues »
- Pendant le visionnage partiel de ces 10 vidéos, 8 publicités ont été affichées
- Seules 2 publicités étaient ignorables, et toutes deux ont été ignorées
- En supposant un CPV approximatif de USD $0.15, 8 publicités par jour représentent un coût annonceur de
8 x $0.15 = $1.20, soit environ USD $36/mo extrapolé sur un mois - Il présente aussi un calcul basé sur des données Statista, en divisant les dépenses publicitaires aux États-Unis par le nombre total de vues
- En 2019, les annonceurs américains ont dépensé $15.1 billion sur YouTube
- Les résidents américains auraient regardé 916 billion de vidéos
- La moyenne est de
$15.1B / 916B = USD $0.0165 per view - Dans son cas, il calcule que cela correspond à environ USD $0.13 par jour, soit environ USD $3.96 par mois de coût annonceur
- Comme il avait coupé le son au niveau matériel et détournait souvent le regard pendant l’expérience, il estime que les dépenses publicitaires engagées sur lui ont été gaspillées
- Il dit néanmoins vouloir soutenir les créateurs et tenter l’essai de 3 mois de Premium, tout en continuant à surveiller ce que Google suit à son sujet
- Il s’inquiète du fait qu’à partir du moment où une réclamation DMCA est déposée, tous les revenus publicitaires puissent aller au plaignant plutôt qu’au créateur, et ajoute qu’il n’est pas surprenant que beaucoup de créateurs migrent vers Patreon
Conclusion
- Il a configuré un routeur matériel à partir de zéro et séparé le LAN en zones trusted/untrusted
- Il a mis en place un blocage publicitaire DNS traditionnel
- Il a ajouté un proxy MITM transparent
- Au final, il affirme avoir pu bloquer efficacement les publicités YouTube sur les appareils Apple connectés au réseau
- Il conclut qu’il envisagera de payer YouTube Premium maintenant que la partie difficile est terminée, tout en ajoutant que les trackers restent fortement bloqués
1 commentaires
Avis sur Hacker News
Plutôt qu’un défaut du format Protobuf, il semble que l’auteur ait remplacé le numéro de champ par un grand numéro inutilisé.
La méthode consiste à chercher, dans les octets Protobuf, des signatures d’URL publicitaires comme
/pagead/pour délimiter la plage du champ, puis à remonter en arrière afin de trouver le tag et la clé du champ cible pour le neutraliser ; ce n’est pas vraiment une faille, mais plutôt un comportement prévu.Si l’on fait l’effort de trouver le tag, lire la longueur varint juste à côté et sauter les octets correspondants ne représente pas beaucoup de travail supplémentaire. Il faudrait certes copier le buffer ou déplacer des octets, mais le script de PoC doit déjà effectuer une copie puisque les
bytesrenvoyés par l’API mitmproxy sont immuables.Google déploierait d’abord une nouvelle application avant de modifier le protocole au point de supprimer toutes les publicités dans les anciennes versions ; un simple certificate pinning de base, ou un décodage moins tolérant en cas d’échec d’extraction des informations publicitaires, suffirait donc à bloquer immédiatement cette méthode. L’équipe YouTube considérerait probablement cela comme un défaut.
bytessont immuables, mais les objetsbytearrayne le sont pas.Un petit proxy en C++/Go pourrait faire la même chose avec bien moins de surcharge. Pour une tâche aussi bien définie, ce serait plus stable et demanderait moins d’efforts que de se battre avec mitmproxy.
Envoyer tout le trafic vers un proxy dégrade les performances, même avec de l’interception SNI. C’est pareil avec pfSense : un simple serveur Linux et quelques règles iptables suffiraient, sans avoir à lutter contre les couches d’abstraction de pfSense.
Il suffit d’écrire dans un fichier
.protouniquement les champs rétro-ingéniérés nécessaires, de générer automatiquement le code et de changer le flag. Ce serait moins coûteux qu’une implémentation Python et plus facile à mettre à jour si le proto change. Ignorer les tags de champs inconnus est une fonctionnalité importante de Protobuf, qui permet des changements de schéma compatibles sans casser les déploiements existants.L’auteur semblait déjà conscient d’une bonne partie des remarques faites en commentaire, et l’article est assez approfondi. Il a benchmarké Python et C++, et l’implémentation finale ne décode même pas Protobuf. Il a aussi essayé plusieurs solutions mitm, et pfSense n’est pas utilisé comme simple routeur de sécurité, mais pour cibler uniquement le trafic de l’Apple TV via VLAN et VPN.
Ce commentaire donne une impression trop facile et dépréciative. L’article original ne l’est pas ; si tu veux le formuler ainsi, il faudrait le démontrer toi-même pour la communauté.
Est-ce que payer YouTube Premium soutient les créateurs ? Si oui, je me demande dans quelle mesure par rapport à un soutien direct comme Patreon.
La part d’un abonnement YouTube Premium qui revient à un créateur individuel est minime, mais c’est tout de même mieux que de regarder ses vidéos avec un bloqueur de publicités.
Comme c’est basé sur le temps de visionnage et non sur les impressions publicitaires, les créateurs de contenus longs sont avantagés.
Le compte YouTube de ma petite amie n’affiche étrangement aucune publicité, quel que soit l’appareil sur lequel elle se connecte. Cela inclut l’Apple TV, elle n’a pas Premium et ne l’a jamais eu.
Je me demande quel flag interne a été activé pour désactiver les publicités.
C’est seulement à ce moment-là que j’ai compris pourquoi les gens se plaignaient.
Je n’utilise pas de bloqueur de publicités, mais dès que je suis connecté, je ne vois aucune publicité, ni sur le site web ni dans l’application mobile. Je n’ai pas Twitch Turbo, et je n’ai plus Amazon Prime. Je n’ai pas non plus les autres avantages de Turbo, donc mon compte n’est pas entièrement marqué comme Turbo.
Je ne sais pas si mon profil de compte a été accidentellement cassé quand je bricolais diverses choses dans le cadre d’un bug bounty, mais si cela permet de conserver cet avantage, je peux fournir plus d’informations.
Ce qui est étrange, c’est que je me souviens d’une fois à l’hôpital, sous médicaments et dans la douleur, où je voulais juste regarder la télé, mais les publicités Twitch étaient tellement envahissantes que j’ai presque craqué. Puis, un ou deux ans plus tard, je me suis soudain rendu compte que je n’avais pas vu de publicité depuis des années.
Il existe probablement un vieux test A/B sans publicité oublié, qui est resté en place parce que ça ne valait pas la peine de le nettoyer. J’en profite depuis des années et je regarde Twitch plus que n’importe quelle autre plateforme. Au Royaume-Uni, Twitch Turbo coûte 12 £ par mois, soit environ 15,50 $, ce qui est cher même à l’échelle mondiale ; comparé aux 12 $/12 € aux États-Unis et en Europe, c’est un prix assez désavantageux.
J’ai trouvé assez surprenant qu’en plaçant un proxy MITM entre l’Apple TV et l’Internet extérieur, on puisse déchiffrer le trafic HTTPS.
Je pensais que, normalement, ça ne devrait pas fonctionner ; puis j’ai été à nouveau surpris en apprenant qu’on pouvait ajouter une CA au magasin de certificats de l’Apple TV. C’était un article fouillé qui passe en revue toute la stack.
Par exemple, dans les universités, pour connecter un appareil au Wi‑Fi, il fallait parfois ajouter son adresse MAC à une liste d’autorisation ou installer un certificat.
Cela dit, en faisant ça, YouTube pourrait casser dans de nombreux environnements d’entreprise, donc je ne sais pas s’ils le feront vraiment. Malheureusement, ce serait tout de même très facile à bloquer.
J’ai essayé plusieurs fois de l’implémenter sur Apple TV, sans aucun succès. Il semble que YouTube ait désormais ajouté du certificate pinning dans l’app, ou quelque chose du genre. Je me demande si quelqu’un a réussi à faire fonctionner ça récemment.
[0] https://frida.re/docs/home/
J’aime toutes les tentatives de blocage à l’échelle du réseau de ces mauvais services en ligne qu’on se retrouve forcé d’utiliser.
Bloquer les pubs, c’est bien, mais j’aimerais qu’il existe davantage de moyens, plus simples, de bloquer à l’échelle du réseau le scroll infini agressif comme YouTube Shorts ou Instagram Reels.
Sur Instagram, je veux seulement voir les posts et les stories des gens que je suis, pas me faire recommander des vidéos idiotes conçues pour capter mon attention. C’est peut-être révélateur d’un manque de volonté de ma part, mais il m’arrive souvent d’en regarder quelques-unes et de perdre 15 minutes de ma vie.
Les internautes ont globalement choisi de ne pas vouloir payer, donc quelqu’un d’autre en supporte le coût. Dans l’ensemble, les internautes ne récompensent pas ceux qui ne leur montrent pas de publicité. Ils veulent du contenu, mais le veulent généralement gratuitement.
J’ai trouvé un script qui transforme la page Instagram en quelque chose de proche d’une simple balise image, pour ne voir que les photos : https://greasyfork.org/en/scripts/5014-un-instagram
Donc ce n’est pas tant un manque de volonté qu’une forme de désensibilisation que nous avons construite, et c’est assez mauvais. Les efforts et la créativité visant à faire en sorte que ce soit nous qui utilisions les plateformes, plutôt que les plateformes qui nous fassent les utiliser, méritent le respect.
J’en parle régulièrement avec eux, et ils reconnaissent aussi que c’est nocif, mais ils ont beaucoup de mal à résister. Même moi, je me fais parfois aspirer par le doomscrolling.
Là où c’est possible, j’ai configuré un filtrage des pubs avec Pi-hole, mais je ne veux pas bloquer YouTube entièrement. Cela dit, pour protéger ma famille, je vais probablement devoir l’envisager sérieusement à l’avenir.
L’ingénierie est intéressante, mais c’est un peu triste de devoir aller aussi loin pour utiliser son propre matériel ou logiciel comme si on en était propriétaire, ne serait-ce qu’un peu.
Il y a des pubs sur YouTube ? Mon navigateur les bloque si bien que je ne le savais pas.
Le vrai problème, c’est que l’expérience Apple TV est bien pire que celle d’un navigateur web classique. Apple verrouille tellement le matériel que la structure profite davantage aux revenus publicitaires de YouTube qu’au consommateur final qui l’a payé.
C’est pareil quand je navigue sur le web avec l’iPad en dehors du réseau Pi-hole de la maison. Je ne comprends pas comment les gens supportent ça au quotidien.
L’iPad est un appareil fourni par le travail, donc je ne l’utilise pas souvent à titre personnel, mais chaque utilisation me rappelle à quel point c’est pénible.
Curieusement, avant de recevoir l’iPad, je pensais qu’il ne serait utile que pour consommer du contenu ; en réalité, il est très pratique pour accéder rapidement à distance à des ressources de travail, mais pour la navigation web générale et le streaming, c’est un appareil enfermé dans un désert couvert de pubs.
Si vous voulez YouTube sans publicité, vous pouvez utiliser https://yewtu.be ou une autre instance Invidious https://docs.invidious.io/instances/.
Il existe une course aux armements entre YouTube et Invidious, et il arrive qu’Invidious ne fonctionne pas, mais l’équipe a toujours fini par trouver de nouvelles façons de contourner YouTube et de fournir les vidéos sans publicité.