- Es handelt sich um einen PoC, bei dem zwischen Apple TV und dem Internet ein pfSense-basierter MITM-Proxy platziert wird, HTTPS entschlüsselt und anschließend die von YouTube ausgelieferten Protobuf-Antworten verändert werden, damit auf Apple-Geräten keine Werbe-Slots registriert werden
- Bestehende DNS-Blocker und VPN-Routing stießen an Grenzen wie DNS-TTL, IP-Abweichungen, 403-Fehler und ASN-Leaks, weil YouTube-Werbung und das eigentliche Video über dieselbe Domain- und Infrastrukturstruktur laufen
- Das Squid-Experiment wurde wegen Performance- und Konfigurationsproblemen abgebrochen; danach wurde auf einen Ansatz mit mitmproxy/mitmdump in einer FreeBSD-Jail umgestellt, der TLS-Traffic entschlüsselt und JSON- sowie Protobuf-Antworten direkt verändert
- Bei Web-YouTube konnten
adPlacements,playerAdParams,pagead-URLs usw. aus JSON entfernt werden, aber die iOS-YouTube-App speicherte Werbe-Slots und Tracking-Informationen inapplication/x-protobuf-Antworten, sodass die Protobuf-Struktur behandelt werden musste - Der finale Ansatz ist ein linearer Scan mit 1-Byte-Modifikation, der in der Nähe des Strings
/pagead/rückwärts nach dem Field-Tag sucht und Tags wie50195462intarget_field_tag - 1ändert; das liefert ohne vollständiges Decoding eine für Echtzeitverarbeitung geeignete Performance
Ziel und anfängliches Netzwerkdesign
- Ziel war es, einen Router auf Basis von FreeBSD und pfSense zu bauen, um pre-roll-, mid-roll- und end-roll-YouTube-Werbung auf Apple TV und iPhone netzwerkweit zu blockieren
- Wenn man zwischen Apple TV und dem externen Internet einen Man-in-the-Middle-Proxy platziert, kann man HTTPS-Traffic entschlüsseln und die Protocol-Buffer-Daten lesen, die Google zum Befüllen von YouTube-Werbung verwendet
- Nach einigen Monaten, in denen YouTube-Werbeblocking umgesetzt wurde, wurde schließlich YouTube Premium bezahlt; dabei wird klargestellt, dass „es ist möglich“ und „es sollte getan werden“ zwei verschiedene Dinge sind
- Als Gründe für das Blockieren von Werbung und Tracking werden Datenschutz-Tracking, Bandbreitenverschwendung, Clickbait und Cryptojacking genannt
- Es wird davon ausgegangen, dass 25 bis 40 % des Netzwerk-Traffics aus Werbung, Tracking-Skripten, fingerprint.js, googletagmanager.js und Echtzeit-Analyse-Loadern wie Hotjar bestehen können
- Crypto-Mining-JavaScript wie CoinHive.js könne Computer überhitzen oder missbrauchen, um kleine Geldbeträge zu erwirtschaften
pfSense-Hardware und Grundeinstellungen
- Um ein gesamtes SMB-Netzwerk zu schützen, seien VM, Docker-Images und Raspberry Pi leistungsmäßig unzureichend; stattdessen werde dedizierte Hardware benötigt, die ausschließlich Paket-Routing, Entschlüsselung und Monitoring übernimmt
- Die verwendete Router-Hardware bestand aus einem Mini-PC mit AES-NI-Befehlssatz, DDR4-RAM, mSATA-SSD und einem USB-Laufwerk zum Flashen von pfSense
- Eine Beispielkonfiguration ist ein J4125-Mini-PC mit 32 GiB DDR4-RAM und 128 GiB mSATA-SSD
- 128 GB Speicherplatz werden als ausreichend für Logs, geringeren SSD-Verschleiß, Paketmitschnitte und NPM-/Docker-Edge-Cache angesehen
- Das pfSense-Installations-Image ist etwa 360 MB groß und kann mit der Etcher-AppImage auf ein USB-Laufwerk geflasht werden
- Nach der ersten Einrichtung wurde AES-NI als „Yes (inactive)“ angezeigt und dann unter
System › Advanced › Miscellaneousmanuell aktiviert - Um die 32 GiB RAM zu nutzen, wurden
/varund/tmpgroßzügig als RAM-Disk zugewiesen; die 128-GiB-SSD wurde mit erwartetem Wear-Leveling konfiguriert, während stündliche RAM-Disk-Backups eingerichtet wurden - Zum Dashboard wurde ein S.M.A.R.T.-Widget hinzugefügt, damit sich Auffälligkeiten der SSD erkennen lassen
DNS-Blockierung, Netzwerksegmentierung und pfBlockerNG
- Zuvor wurde Pi-hole auf einem Raspberry Pi als Werbeblocker auf DNS-Ebene verwendet; auf pfSense wurde pfBlockerNG-devel installiert, um Werbe- und Schadinhalt-Blockierung sowie Geo-Blocking zu testen
- Wenn der Dienst pfb_dnsbl nicht startet oder im Status-Tab
[ Missing CRON task ]angezeigt wird, wird empfohlen, das leere File/var/run/bootingzu löschen - Mithilfe der drei Gigabit-Ports des Mini-PCs wurde statt VLAN ein physisch getrenntes Netzwerk aufgebaut, um „phoning-home“-Geräte wie Alexa und Apple TV vom Hauptnetz zu isolieren
- Nicht vertrauenswürdige Geräte wurden im privaten Netzwerk
172.31.1.0/24platziert - Das vertrauenswürdige LAN blieb bei
192.168/16 - Das Hardware-LAN für IoT soll durch den Adblocker laufen, und fest kodierte DNS-Abfragen an
1.1.1.1oder9.9.9.9sollen abgefangen werden, damit YouTube den DNS-Blocker nicht umgehen kann
- Nicht vertrauenswürdige Geräte wurden im privaten Netzwerk
- Es wurden NAT-Regeln eingerichtet, damit alle Clients hinter pfSense den lokalen Unbound-DNS-Server verwenden
- Dafür müsse zunächst DNS over TLS blockiert werden, damit sich DNS-Abfragen umleiten lassen
- Das iPhone könne zwar eine Privacy Warning wegen blockiertem verschlüsseltem DNS-Traffic anzeigen, aber Upstream-DNS-Anfragen würden weiterhin verschlüsselt an Cloudflare gesendet
- NAT-Reflection sollte deaktiviert werden, damit das externe Internet keinen Zugriff auf den DNS-Server erhält
- Es wurde ein Firewall-Alias
Non_WANerstellt, um Port 53 lokaler DNS-Abfragen auf Interfaces außer WAN auf localhost umzuleiten - Da YouTube Werbung und Hauptvideo über dieselbe Domain ausliefert, war es schwierig, Werbung allein mit Domainnamen-Blockern wie pfBlockerNG oder Pi-hole herauszufiltern
Experimente mit VPN-Umgehung und deren Scheitern
- Zusätzlich zum Werbeblocking wurde auch mit einem Ansatz experimentiert, den YouTube-Werbealgorithmus zu täuschen, sodass der Nutzer für Werbekunden weniger attraktiv erscheint
- Dazu sollte der pfSense-Router YouTube-Standort-Tracking-Traffic über ein VPN in Regionen mit geringerer Zuschauerzahl routen
- Ziel war, vom YouTube-Konto als „70-jähriger Mann, wohnhaft in Italien“ erkannt zu werden
- Auf pfSense wurde statt OpenVPN WireGuard genutzt, um in einem Basisversuch den gesamten Traffic des Apple TV über ein VPN zu leiten
- Das FreeBSD-WireGuard-Paket wurde installiert und ein Tunnel hinzugefügt sowie aktiviert
- Für die NordLynx-Konfiguration wurde auf einer Linux-VM mit
sudo wg showconf nordlynxder private Schlüssel ausgelesen und nach pfSense übertragen
- Die Tests ergaben, dass Google auf dem Laptop auf Italienisch angezeigt wurde und auch YouTube auf dem Apple TV auf Italienisch umschaltete
- Werbung wurde weiterhin teilweise ausgeliefert, aber weniger als zuvor
- Bei Netflix und Amazon Prime traten Probleme auf; offenbar wurden CSS- oder Font-Dateien blockiert oder Thumbnails nicht geladen
- Es wird davor gewarnt, den gesamten Apple-TV-Traffic über ein VPN zu schicken, da Netflix und Prime VPN-Anbieter und Geofencing gut erkennen
- Danach wurden Firewall-Policy-Regeln erstellt, um nur den YouTube-Traffic des Apple TV über das VPN zu leiten, darunter
www.youtube.com,youtube.com,googlevideo.com,accounts.google.com,googleapis.comundgstatic.com- Das Ergebnis war, dass YouTube den Nutzer in Mailand verortete, während Netflix und Prime Video ihn in Kanada sahen
- Die Werbung sei auf ein „few and far between“-Niveau zurückgegangen
- Einen Tag später trat eine DNS-Race-Condition auf
- pfSense-Hostname-Aliase werden standardmäßig alle 300 Sekunden aufgelöst
- Die YouTube-DNS-TTL kann 1.440 Sekunden, also 24 Minuten, betragen
- Wenn sich die vom Alias-Daemon aufgelöste IP von der tatsächlich vom Client erhaltenen IP unterscheidet, kann die Policy den YouTube-Traffic möglicherweise nicht durch den Tunnel leiten
- Einige YouTube-Videos ließen sich wegen
403 Forbiddennicht abspielen- YouTube bette laut Beschreibung die IP des Nutzers in jede
googlevideo.com-Anfrage ein - Wenn abgewandelte Domains wie
r5---sn-hpa7kn76.googlevideo.comnicht durch den Tunnel geleitet werden, wird die Anfrage von der falschen IP gesendet und verursacht Probleme - Erforderlich wäre ein Wildcard-Tunneling für
*.googlevideo.com, aber NAT und Firewall-Regeln arbeiten nicht mit Wildcard-Hostnamen, sondern mit IPs
- YouTube bette laut Beschreibung die IP des Nutzers in jede
PoC zur IP-Verfolgung auf Basis von DNS-Queries
- Es wurde ein Ansatz für Google Video DNS Query Hijacking entworfen, um
*.googlevideo.comüber ein VPN zu routen- Dabei werden DNS-Query-Logs regelmäßig verfolgt und
*.googlevideo.com-Queries einer Alias-Liste hinzugefügt - Wenn jedes Video eine eigene und veränderte Domain verwendet, funktioniert diese Methode nach Einschätzung des Autors nicht, es sei denn, man aktualisiert sie für jedes Video neu
- Dabei werden DNS-Query-Logs regelmäßig verfolgt und
- Das neue Ziel war, mit Python 3 und der pfSense-REST-API DNS-Queries zu überwachen, IPs zu erfassen, die Antwort kurz zurückzuhalten, dann die IP zur VPN-Tunneling-Regel hinzuzufügen und anschließend die DNS-Antwort freizugeben
- Die pfSense-REST-API wurde installiert und per GET-Request an
https://pfsense/api/v1/firewall/aliasder AliasVPN_domainsabgefragt - Das Python-Modul des Unbound DNS Resolver wurde untersucht, und das Logging von DNS-Query-Messages gelang
- Die aktuelle Python-Version war 3.8
- Da die Beispielcodes für das Unbound-Python-Modul auf Python 2.4 basierten, wurde angenommen, dass
2to3oder zusätzliche Formatierung nötig sein könnten
- Das PoC-Skript extrahiert A/AAAA-Record-IPs aus der DNS-Antwort und fügt sie dem pfSense-Alias hinzu
- A-Records werden mit
ipaddress.IPv4Address(d.rr_data[j][2:]).explodedverarbeitet - AAAA-Records werden mit
ipaddress.IPv6Address(d.rr_data[j][2:]).explodedverarbeitet - Für den Alias wurden TTL auf 1 Stunde und die Kapazität auf 500 gesetzt
- A-Records werden mit
- Am nächsten Tag trat im Unbound DNS Resolver ein Segfault auf, und da bei jedem Hinzufügen einer IP die pfSense-Regel neu geladen werden musste, wurde pfSense sehr langsam
Wechsel von Squid zu mitmproxy
- Das neue Ziel änderte sich dahin, einen Proxy aus der Squid-Familie zu prüfen und zu installieren, ein vertrauenswürdiges gefälschtes CA-Zertifikat zu erstellen und dann den TLS-Traffic zu entschlüsseln
- In den Squid-Experimenten wurde getestet, ob der als pfSense-Paket bereitgestellte
squid3-Proxy die Anforderungen erfüllt- Es wurde ein eigener Ordner
/squid_cacheangelegt und die Cache-Größe auf 8 GiB gesetzt - Transparent HTTPS Support wurde erwartet
- Es wurde ein eigener Ordner
- Nach einem Tag Konfiguration von Squid und SquidGuard wurde das Vorhaben aufgegeben
- Die Geschwindigkeit war sehr langsam
- Die ACL-Konfiguration war umständlich
- Es gab ein Problem im Zusammenhang mit
https://http/* - Das Aktualisieren der SquidGuard-URL-Filterliste dauerte sehr lange
- Die Squid-Oberfläche war unzureichend
- Danach fiel die Entscheidung auf mitmproxy, das in Python geschrieben ist
- Wegen der Erweiterbarkeit über Python-Hooks und der UI wurde mitmproxy statt
SSLSplitgewählt - Die FreeBSD-Version von pfSense war
12.2-Stable, 64-bit build
- Wegen der Erweiterbarkeit über Python-Hooks und der UI wurde mitmproxy statt
- In der Standardumgebung von pfSense waren Jails deaktiviert, daher wurde
ezjailmanuell installiert und ein Jail fürmitmproxyerstellt- Das Jail wurde mit
ezjail-admin create mitmproxy 'lo0|127.0.1.1'angelegt - Für den transparenten Proxy-Modus wurde
allow.raw_sockets=1gesetzt - Wenn Raw Sockets blockiert sind, können Fehler wie
Transparent mode failureoderCannot open connection, no hostname given.auftreten
- Das Jail wurde mit
- Das Ausführen des Linux-Tarball-Binaries schlug unter FreeBSD fehl
- Es trat
ELF interpreter /lib64/ld-linux-x86-64.so.2 not foundauf - Auch
libdl.so.2,libz.so.1,libpthread.so.0,libc.so.6wurden nicht gefunden
- Es trat
- Innerhalb des Jails wurde
pkg install mitmproxyausgeführt; die Installation verlangte 50 Pakete, 206 MiB zusätzlichen Speicherplatz und 33 MiB Download - Damit MITMProxy im LAN erreichbar ist, wurde die virtuelle IP
127.0.1.1an localhost gebunden und per NAT-Regel[Private IPs]:8080temporär auf127.0.1.1:8080weitergeleitet - Die von MITMProxy automatisch erzeugte CA-PEM-Datei ist
~/.mitmproxy/mitmproxy-ca-cert.pem, und dieses CA-Zertifikat wurde im Trusted Root Store des Testgeräts installiert mitmproxynutzte selbst im Idle-Zustand viel CPU; als Ursache wurden die Echtzeit-Erzeugung von TLS-Zertifikaten pro Request und übermäßiges Logging vermutet, was die Geschwindigkeit deutlich verringertemitmdumplässt die UI und übermäßiges Logging weg und verursacht daher voraussichtlich weniger CPU-Last
Entfernung von YouTube-JSON-Werbung im Web
- Certificate Pinning ist ein Verfahren, bei dem Server oder Client den erwarteten Zertifikats-Fingerprint bereits kennen, sodass eine Zertifikatsfälschung durch MITMProxy nicht funktioniert
- Problematische Hosts können mit der Option
--ignore-hostsam Proxy vorbeigeleitet werden- Als Beispiele werden
apple.com:443undicloud.com:443ignoriert
- Als Beispiele werden
- Beim Zugriff auf YouTube waren Seitenanzeigen in MITMProxy zusammen mit unverschlüsselten Headern sichtbar, und es wurde geprüft, ob eine einfache Sperrung per Regex möglich wäre
- Um das YouTube-Adblock-Skript anzuwenden, wurde
mitmdumpum--scripts "youtube.py"ergänzt - Der Smoke-Test-Filter blockiert Werbeanfragen anhand von URL-Substrings
youtube.com:/pagead/,/log_event?,/stats/ads,/stats/qoe?,/ptracking?,/generate_204,el=adunit,adformat=,/activeview?google.com,google.ca:/pagead/ggpht.com:.
- Die zu blockierenden Requests schienen in MITMProxy und im DevTools-Network-Panel tatsächlich blockiert zu werden, dennoch wurden weiterhin Ads angezeigt; manchmal wurden sie auch von selbst übersprungen oder die Wiedergabe schlug fehl
- Später wurden in der JSON-Payload zahlreiche werbebezogene URLs gefunden
- Darin waren die Abschnitte
playerAdsundplaybackTrackingenthalten youtubeRemarketingUrlenthielthttps://www.youtube.com/pagead/viewthroughconversion/...googleRemarketingUrlenthielthttps://www.google.com/pagead/1p-user-list/...
- Darin waren die Abschnitte
- Nach der Analyse der YouTube-UI und des HTTP-Workflows bis hin zu Cookies und Service Workern sei es gelungen, Pre-Roll-, Post-Roll- und Mid-Video-Werbung vollständig zu entfernen
- In diesem Stadium ließ sich auf dem Router Werbung aus der JSON-Payload von YouTube-Web-Ads entfernen
iOS-YouTube und das Protobuf-Problem
- Die YouTube-iOS-App zeigte in der Protobuf-Version desselben API-Calls sehr ähnliche Daten wie die Webversion
- Bei Protobuf sind die Keys numerisch und können sich ändern, daher ließ sich die Advertisement-Sektion nicht mit einem Ansatz wie JSONPath finden
- YouTube sendet eine große Liste kommender Ads als Payload; wenn diese vollständig verbraucht ist, trifft kurz darauf die nächste große Liste ein
- Im Protobuf-Payload waren Zeichenketten wie „Telus“, „Samsung TV“, „Boxing Week“ und „Buy now“ zu sehen
- Das iOS-YouTube-Protokoll unterschied sich vom Web-Traffic
- In der Webversion ließen sich Ad-Video und gewünschtes Video bis zu einem gewissen Grad anhand der URL und des Query-Parameters
rangeunterscheiden - Das iOS-Protokoll verwendet weder den Query-Parameter
rangenoch den HeaderRange, sondern Zähler wie&nr=2und&nr=3für Video-Chunks - Um Ads auf iOS zu blockieren, musste die Protobuf-Antwort reverse-engineert werden
- In der Webversion ließen sich Ad-Video und gewünschtes Video bis zu einem gewissen Grad anhand der URL und des Query-Parameters
- In der decodierten Protobuf-Nachricht wurden die Einträge
has_unlimited_entitlement: Falseundhas_premium_lite_entitlement: Falsegefunden, statt diese umzuschalten kehrte man aber zu Heuristiken zurück - Das Decodieren von etwa 500 KiB rohem Protobuf mit einer reinen Python-Implementierung war sehr langsam
- Auf einem i7-6700-Desktop lag das Python-Ergebnis bei etwa 2,06 bis 2,11 Sekunden
- Auf einem pfSense-Router lag das Python-Ergebnis bei etwa 22,8 bis 24,2 Sekunden
- C++
protoc --decode_rawbrauchte auf dem Desktop etwa 0,017 bis 0,022 Sekunden und auf dem pfSense-Router etwa 0,12 bis 0,14 Sekunden
Versuche zur Protobuf-Decodierung und Schema-Extraktion
- Da rohes Protobuf-Decoding in Python nicht unterstützt wurde, entschied man sich statt direkter Nutzung von C++
libprotobuf.sofür einen Ansatz, der persubprocess.Popenmit der C++-Binaryprotockommuniziert - Beim Fuzzing von Ad-Video-Antworten wurden leere
200,404,503, abgeschnittene Response-Bodys und teilweise genullte Ad-Videos ausprobiert, doch die iOS-App wurde nur langsamer, stürzte dann ab oder blieb auf dem Ad-Bildschirm hängen - Das Blockieren von URLs löste Gegenreaktionen der App aus, und die Video-Response-Chunks enthielten auch Session-Metadaten
- blackboxprotobuf für Burp Suite kann rohe Protobuf-Wire-Messages decodieren, Inhalte injizieren und sie anschließend wieder encodieren, um das Verhalten von Protobuf-Endpoints zu prüfen
- Empfohlen wird die originale Burp-Suite-Version, nicht ein Fork auf PyPI
- Einige Forks haben wegen tiefer Rekursion Probleme mit Stack Overflow oder unendlicher Rekursion
- Mit C++-Bindings lassen sich etwa 500 KiB rohes Protobuf in wenigen Sekunden transkodieren
- Das erzeugte Schema war nicht perfekt, groß und tief verschachtelt, und Pretty-Print war langsam, aber zum Finden von Ad-Details ausreichend
- Um echte
.proto- oder Schema-Dateien aus der Android-YouTube-APK zu extrahieren, wurden PBTK, Apktool, dex2jar und Java Decompiler ausprobiert- PBTK extrahierte nur eine 59-Byte große Proto-Datei
- In Java gab es Protobuf-Klassen sowie Getter/Setter, aber da keine echten Schema-Dateien gefunden wurden, wurde dieser Ansatz abgebrochen
Der endgültige Durchbruch: 1-Byte-Änderung eines Protobuf-Field-Tags
- Aus entschlüsseltem Netzwerk-Traffic und Protobuf-Fuzzing ergab sich, dass Ads offenbar in Slots eines bestimmten Videos eingetragen werden
- Zu den Slot-Typen gehören Pre-Roll, Mid-Roll, End-Roll, Full-Page und Ad Pods
- Wenn Ad-URLs blockiert werden, tritt ein Fehler auf nach dem Muster „eine nicht existierende Ad hat einen Slot reserviert“, was eine UI-Panik auslöst
- Ohne das ursprüngliche Schema führt Decode-, Edit- und Re-Encode zu veränderter Encodierung; da unklar ist, ob ZigZag verwendet wird, welche Zahlentypen wie
int32,int64,sint32/64odervarintvorliegen und da die Reihenfolge von Objektfeldern meist nicht deterministisch ist, wurde das als problematisch angesehen - In der Backward-Kompatibilität von Protobuf und im Verhalten von UnknownFieldSet wurde ein möglicher Umweg gefunden
- Wenn alte Software eine Message mit neu hinzugefügten Feldern liest, können Unknown Fields entstehen
- Wird ein bestimmter Field-Key auf einen anderen Wert geändert, kann die gesamte Substruktur mit Ad- und Tracking-Informationen in einen nicht verfügbaren Zustand geraten
- Als Beispiel wurde die Idee genannt, den Field-Key
49399797in49399796zu ändern, damit diese Ad-/Tracking-Substruktur wie ein Unknown Field behandelt wird - Der Field-Key
49399797lässt sich nicht per einfacher Hex-Suche finden; dafür muss Varint-/Tag-Encoding berücksichtigt werden- Der Wire-Typ ist
2und bedeutet eine length-delimited verschachtelte Zeichenkette bzw. Message - Die Tag-Byte-Sequenz des Ziel-Field-Keys
49399797lautetAA FF B8 BC 01 - Mit
395198378 >> 3werden die 3 Bits des Wire-Typs entfernt, wodurch wieder der ursprüngliche Field-Key49399797entsteht
- Der Wire-Typ ist
- In den Protobuf-Bytes wurde nach klassischen Ad-URL-Signaturen wie
/pagead/gesucht, um den Suchbereich für das Field einzugrenzen; von dort wurde rückwärts gegangen, um das zu ändernde Field-Tag und den Field-Key zu finden - In einem Beispiel-Intercept-Log wurden in einer 1,87-MiB-
application/x-protobuf-Antwort auf einen POST-Request anyoutubei.googleapis.com:443/youtubei/v1/browse?key=...der Key49399797an Position4465und der Key50195462an Position4477gefunden - Im O(n)-Smoke-Test wurden 1,8 MiB Protobuf-Daten ohne zusätzlichen Speicher in einem einzigen Scan verarbeitet
- Das Ziel wurde im 30.593. Byte von 1,8 MiB gefunden
- Mit etwa 600 Bytes Backtracking wurde der zu denaturierende Field-Key gefunden
- Nachdem diese Methode funktionierte, mussten URLs mit
*.googleadservices.comoder/pagead/nicht mehr blockiert werden, und die entsprechenden Requests wurden gar nicht erst ausgelöst
Aufbau des MITMProxy-Add-on-Skripts
- Das MITMProxy-Add-on-Skript wird als Proof of Concept bereitgestellt, um YouTube-Werbung auf vernetzten Apple-Geräten zu blockieren
- Der Dateiname ist
youtube.py - Ein Ausführungsbeispiel ist
mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py" - Die Voraussetzungen unter FreeBSD sind
pkg install protobuf,pkg install py38-pip,pip install jsonpath-ng
- Der Dateiname ist
- Das Skript enthält eine Fairness-Funktion, die zur Unterstützung von Content-Erstellern 5 % der Werbung zulässt
in_allowed_ads_window()überspringt das Blockieren von Werbung, wenn die aktuelle Zeit zwischen Minute 0 und 2 jeder Stunde liegt
YouTubeAdBlockerfängt YouTube-bezogene Domains ab und entfernt Werbeinformationen, indem JSON- oder Protobuf-Antworten verändert werden- Der Host-Regex für das Abfangen ist
\.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com - Die Zeichenkette zur Protobuf-Werbeerkennung ist
b"/pagead/" - Das Suchlimit beträgt
80_000Bytes - Das Ziel-Feld-Tag ist
50195462
- Der Host-Regex für das Abfangen ist
- Zur Sperrliste in der Anforderungsphase gehören bei YouTube-Hosts unter anderem
pagead/,log_event?,stats/ads,stats/qoe?,ptracking?,generate_204,error_204,adformat=,activeview?,_ad_,ai?,sw.js - Die JSON-Ersetzungen für Web-YouTube entfernen oder deaktivieren werbebezogene Felder
yt_adwird zu"0"adPlacementswird zu[]adPlacementRenderer,adPlacementConfig,playerAdParams,gutParamswerden zu{}adVideoIdwird zu""showCompanion,showInstream,useGutwerden zuFalse
- Der Hook
load()deaktiviert HTTP/2 und setztanticomp=True,mode="transparent" - Der Hook
running()aktualisiertallow_hosts, sodass das Abfangen nur auf YouTube-bezogene Domains angewendet wird - Der Hook
response()sucht, wenncontent-typeprotobufenthält, in den ersten 80.000 Bytes des Antwortkörpers nach/pagead/- Wenn gefunden, erzeugt er mit
TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED)die Ziel-Tag-Bytes - Er erzeugt neue Bytes für das Tag
target_field_tag - 1 - Er führt eine Rückwärtssuche vor der Position von
/pagead/aus, um das Ziel-Tag zu finden - Er ersetzt die Bytes an dieser Position durch die Bytes, die
target_field_tag - 1entsprechen - Er setzt den modifizierten Protobuf-Inhalt mit
flow.response.set_content(bytes(body))wieder ein
- Wenn gefunden, erzeugt er mit
- Die Code-Kommentare erklären, dass dieses PoC bereits 90 % der Werbung blockiert, und ergänzen, dass es in anderen Abschnitten weitere Field-Keys geben könnte und mehrere Werbeabschnitte neutralisiert werden müssten
Leistung, Grenzen und Zielgruppe
- Die finale Technik nutzt zum einen die Eigenschaft von Protobuf, bei Schema-Änderungen aus Gründen der Abwärtskompatibilität unknown fields zuzulassen, und zum anderen die Empfindlichkeit kompakter Formate gegenüber Einzelbyte-Änderungen
- Wenn man an einer kritischen Stelle 1 Byte verändert und dadurch ein tief verschachtelter Abschnitt so aussieht, als gehöre er zu einer zukünftigen Schema-Version, kann Protobuf ihn ignorieren und die Werbeinformationen entfernen
- Google liefert große Protobuf-Antworten zurück, die sogar das Layout der iOS-App enthalten; die Beispiel-Payload ist 1,8 MiB groß
- Um die gesamte Payload zu parsen, wäre nativer Code wie C++ oder Swift nötig, und Python-Decoding sei um mehrere Größenordnungen langsamer und verursache Connection-Timeouts
- Das webbasierte JSON muss die gesamte Payload parsen, bearbeiten und erneut serialisieren, doch die Protobuf-Technik benötigt nur einen linearen Scan und schnelles Backtracking, verarbeitet also im Mikrosekundenbereich und eignet sich damit für Adblocking in Echtzeit, ohne dass eine Blocklist nötig ist
- Alle
*.googleadservices.com- und/pagead/*-URLs auf Apple-Geräten stammen aus der Protobuf-Payload; wenn die Ad-Daten aus der Payload verschwinden, verschwinden auch diese Requests automatisch - Die YouTube-App versucht dann nicht, Ad-URLs abzurufen, wirkt dadurch schneller, und der Content wird sofort abgespielt, weil keine Werbung im Video-Slot registriert wird
- Diese Methode wird als hochspezialisierte Technik zum Blockieren von YouTube-Werbung auf Apple-Geräten oder von Tracker-Traffic bei Instagram, WhatsApp und Facebook vorgestellt
- Der CPU-Bedarf zum Entschlüsseln und erneuten Verschlüsseln von HTTPS-Traffic übersteige die Leistung eines Raspberry Pi deutlich
- Da sie sich an Apple-Gerätebesitzer richtet, die ihr OS nicht kompromittieren wollen, wird die Zielgruppe als noch kleiner eingeschätzt
YouTube Premium und ein Experiment zu Werbekosten
- Ob der Preis von YouTube Premium mit CAD $9.99/mo oder CAD $11.99/mo und inklusive Steuern etwa CAD $13.43/mo angemessen ist, wird als unklar bewertet
- Mit einem sauberen Laptop und privatem Browsing wurde einen Tag lang ein Experiment zur Werbeeinblendung beim gelegentlichen Ansehen von YouTube durchgeführt
- Laut Wiedergabeverlauf wurden nur 10 Videos „angesehen“
- Beim Ansehen von Teilen dieser 10 Videos wurden 8 Werbeanzeigen eingeblendet
- Nur 2 Anzeigen waren überspringbar, und beide wurden übersprungen
- Unter der Annahme eines groben CPV von USD $0.15 entsprechen 8 Anzeigen pro Tag Werbekosten von
8 x $0.15 = $1.20; auf einen Monat hochgerechnet etwa USD $36/mo - Auf Basis von Statista-Daten wird auch eine Berechnung vorgestellt, bei der die US-Werbeausgaben durch die Gesamtzahl der Views geteilt werden
- 2019 gaben US-Werbetreibende $15.1 billion für YouTube aus
- Einwohner der USA hätten 916 billion Videos angesehen
- Der Durchschnitt beträgt
$15.1B / 916B = USD $0.0165 per view - Im Fall des Autors entspreche das etwa USD $0.13 pro Tag und etwa USD $3.96 pro Monat an Werbekosten
- Während des Werbeexperiments war die Hardware stummgeschaltet und der Blick häufig abgewendet, daher wird das für ihn ausgegebene Werbebudget als verschwendet angesehen
- Trotzdem wolle er Creator unterstützen und eine 3-monatige Premium-Testphase ausprobieren, während er weiter beobachtet, was Google über ihn trackt
- Es wird die Sorge geäußert, dass ab dem Moment eines eingereichten DMCA-Claims sämtliche Werbeeinnahmen nicht an den Creator, sondern an den Claimant gehen könnten; daher sei es nicht überraschend, dass viele Creator zu Patreon wechseln
Abschließende Zusammenfassung
- Ein Hardware-Router wurde von Grund auf eingerichtet und das LAN in vertrauenswürdige und nicht vertrauenswürdige Zonen aufgeteilt
- Traditionelles DNS-basiertes Adblocking wurde eingerichtet
- Ein transparenter MITM-Proxy wurde zusätzlich eingeführt
- Am Ende konnten YouTube-Werbeanzeigen auf vernetzten Apple-Geräten mit guter Performance blockiert werden
- Da der schwierige Teil abgeschlossen sei, werde die Bezahlung von YouTube Premium in Betracht gezogen, während Tracker weiterhin konsequent blockiert würden
1 Kommentare
Meinungen auf Hacker News
Es scheint weniger ein Fehler im Protobuf-Format zu sein, sondern eher so, als habe der Autor die Feldnummer auf eine große, ungenutzte Nummer geändert.
Die Methode besteht darin, in den Protobuf-Bytes nach Signaturen von Werbe-URLs wie
/pagead/zu suchen, daraus den Feldbereich zu bestimmen und sich von dort rückwärts zum Ziel-Feld-Tag und Feldschlüssel vorzuarbeiten, um sie unschädlich zu machen. Das ist aber kein Fehler, sondern eher beabsichtigtes Verhalten.Wenn man sich schon die Mühe macht, den Tag zu finden, ist es kein großer zusätzlicher Aufwand, direkt daneben die Varint-Länge zu lesen und die entsprechenden Bytes zu überspringen. Man müsste zwar den Buffer kopieren oder Bytes verschieben, aber auch das PoC-Skript muss ohnehin bereits kopieren, weil die von der mitmproxy-API zurückgegebenen
bytesunveränderlich sind.Google wird vor einer Protokolländerung, die dazu führen würde, dass in allen alten App-Versionen gar keine Werbung mehr erscheint, erst eine neue App ausrollen. Daher ließe sich diese Blockade schon mit einfachem Certificate Pinning oder einem weniger toleranten Decoding bei fehlgeschlagener Extraktion von Werbeinformationen sofort verhindern. Das YouTube-Team würde so etwas vermutlich als Fehler ansehen.
bytes-Objekte sind unveränderlich,bytearray-Objekte dagegen nicht.Dasselbe ließe sich auch mit einem kleinen C++/Go-Proxy mit deutlich weniger Overhead erledigen. Eine so klar definierte Aufgabe ist stabiler und weniger mühsam, als sich mit mitmproxy herumzuschlagen.
Wenn man den gesamten Traffic über den Proxy leitet, sinkt die Performance, selbst wenn man SNI-Interception nutzt. Bei pfSense ist es ähnlich: Mit einem einfachen Linux-Server und simplen iptables-Regeln lässt sich das erledigen, ohne gegen die Abstraktionsschichten von pfSense anzukämpfen.
Man schreibt nur die benötigten Teile der reverse-engineerten Proto-Felder in eine
.proto-Datei, generiert den Code automatisch und ändert ein Flag. Das ist günstiger als eine Python-Implementierung und leichter zu aktualisieren, wenn sich das Proto ändert. Unbekannte Feld-Tags zu ignorieren ist eine wichtige Eigenschaft von Protobuf und ermöglicht kompatible Schemaänderungen, ohne bestehende Deployments zu beschädigen.Der Autor scheint einen großen Teil der Punkte aus den Kommentaren bereits berücksichtigt zu haben, und der Artikel ist ziemlich gründlich. Er hat Benchmarks mit Python und C++ durchgeführt, und die endgültige Implementierung decodiert Protobuf nicht einmal. Er hat auch mehrere mitm-Lösungen ausprobiert, und pfSense nutzt er nicht als simplen Sicherheitsrouter, sondern um über VLANs und VPNs gezielt nur den Apple-TV-Traffic zu erfassen.
Dieser Kommentar wirkt sehr billig und herablassend. Der Originalbeitrag ist das nicht; wenn man so argumentiert, sollte man es der Community zuliebe selbst beweisen.
Unterstützt man Creator, wenn man für YouTube Premium bezahlt? Falls ja, würde mich interessieren, in welchem Umfang im Vergleich zu direkter Unterstützung wie Patreon.
Die Einnahmen, die ein einzelner Creator durch ein YouTube-Premium-Abo erhält, sind gering, aber es ist immerhin besser, als Videos mit Adblocker anzusehen.
Es richtet sich nicht nach Werbeeinblendungen, sondern nach Wiedergabezeit, daher sind Creator mit längeren Inhalten im Vorteil.
Der YouTube-Account meiner Freundin bekommt seltsamerweise auf keinem Gerät Werbung, wenn sie eingeloggt ist. Apple TV eingeschlossen; sie hat kein Premium und hatte auch nie Premium.
Ich frage mich, ob intern irgendein Flag gesetzt ist, das Werbung deaktiviert.
Erst dann kam der Moment, in dem ich verstand, warum sich die Leute beschweren.
Obwohl ich keinen Adblocker nutze, bekomme ich nirgends Werbung, sobald ich eingeloggt bin, weder auf der Website noch in der Mobile-App. Ich habe auch kein Twitch Turbo und kein Amazon Prime mehr. Andere Turbo-Vorteile habe ich ebenfalls nicht, also bin ich nicht vollständig als Turbo markiert.
Vielleicht habe ich vor Jahren bei Bug-Bounty-Sachen herumprobiert und dabei mein Account-Profil versehentlich kaputtgemacht; ich weiß es nicht. Wenn man mir diesen Vorteil lässt, kann ich gern nähere Informationen liefern.
Seltsam ist, dass ich mich daran erinnere, wie ich vor Jahren im Krankenhaus unter Medikamenten und Schmerzen einfach nur fernsehen wollte, die Twitch-Werbung aber so schlimm war, dass ich fast einen Nervenzusammenbruch bekam. Ein bis zwei Jahre später fiel mir dann plötzlich auf, dass ich seit Jahren keine Werbung mehr gesehen hatte.
Wahrscheinlich gibt es irgendeinen längst vergessenen werbefreien A/B-Test, der nie aufgeräumt wurde, weil es den Aufwand nicht wert war. Ich habe davon jahrelang profitiert und Twitch mehr geschaut als jede andere Plattform. Twitch Turbo kostet im Vereinigten Königreich 12 £ pro Monat, also etwa 15,50 $, und ist damit auch weltweit eher teuer; verglichen mit 12 $/12 € in den USA und Europa ist das ein ziemlich schlechter Preis.
Es war ziemlich überraschend, dass man HTTPS-Traffic entschlüsseln kann, wenn man einen Man-in-the-Middle-Proxy zwischen Apple TV und das externe Internet setzt.
Normalerweise hätte ich erwartet, dass das nicht funktionieren sollte; später war ich dann noch einmal überrascht, als ich erfuhr, dass man dem Zertifikatsspeicher von Apple TV eine CA hinzufügen kann. Das war ein gründlicher Artikel, der den gesamten Stack durchgeht.
An Universitäten musste man zum Beispiel die MAC-Adresse auf eine Allowlist setzen oder ein Zertifikat installieren, um Geräte ins WLAN zu bringen.
Ob sie das tatsächlich tun würden, weiß ich nicht, denn dadurch könnte YouTube in vielen Unternehmensumgebungen kaputtgehen. Leider wäre es aber sehr einfach zu blockieren.
Ich habe ein paar Mal versucht, das auf Apple TV umzusetzen, hatte aber überhaupt keinen Erfolg. YouTube hat inzwischen wohl Certificate Pinning in die App eingebaut oder etwas in der Art. Ich frage mich, ob das in letzter Zeit jemand zum Laufen gebracht hat.
[0] https://frida.re/docs/home/
Ich mag jeden Versuch, die miesen Online-Dienste, zu deren Nutzung man gedrängt wird, netzwerkweit zu blockieren.
Adblocking ist gut, aber ich wünschte, es gäbe mehr und einfachere Möglichkeiten, aggressive Infinite-Scroll-Angebote wie YouTube Shorts oder Instagram Reels netzwerkweit zu blockieren.
Auf Instagram möchte ich nur Posts und Stories der Leute sehen, denen ich folge, und keine dummen Videos empfohlen bekommen, die darauf ausgelegt sind, meine Aufmerksamkeit zu kapern. Vielleicht zeigt das nur mangelnde Willenskraft, aber oft schaue ich mir am Ende doch ein paar an und verliere 15 Minuten meines Lebens.
Internetnutzer haben sich im Großen und Ganzen dafür entschieden, nicht zahlen zu wollen, also übernimmt irgendjemand die Kosten. Insgesamt belohnen Internetnutzer nicht diejenigen, die keine Werbung zeigen. Sie wollen Inhalte, aber meistens kostenlos.
Ich habe ein Script gefunden, das die Instagram-Seite praktisch in etwas wie ein Image-Tag verwandelt, sodass man nur Fotos ansehen kann: https://greasyfork.org/en/scripts/5014-un-instagram
Deshalb wirkt es weniger wie fehlende Willenskraft, sondern eher wie die Abstumpfung, die wir aufgebaut haben, und das ist ziemlich schlecht. Die Mühe und Kreativität, die Plattformen wieder so umzubauen, dass wir sie benutzen statt sie uns, verdient Respekt.
Ich spreche regelmäßig mit meinen Kindern darüber, und sie stimmen auch zu, dass es schädlich ist, aber sie finden es zu schwer, dagegen anzukämpfen. Selbst ich werde manchmal ins Doomscrolling hineingezogen.
Wo es möglich ist, habe ich Werbefilterung per Pi-hole eingerichtet, aber ich möchte YouTube nicht komplett blockieren. Um meine Familie zu schützen, werde ich das künftig aber wohl ernsthaft in Erwägung ziehen.
Das Engineering ist gut, aber es ist schon etwas traurig, dass man so weit gehen muss, nur um die eigene Hardware oder Software zumindest ein Stück weit so zu nutzen, als würde man sie besitzen.
Gibt es auf YouTube Werbung? Mein Browser blockiert sie so gut, dass ich es gar nicht wusste.
Das eigentliche Problem ist, dass die Apple-TV-Erfahrung so viel schlechter ist als die in einem normalen Webbrowser. Apple sperrt die Hardware so stark ab, dass sie eher YouTubes Werbeeinnahmen zugutekommt als dem zahlenden Endkunden.
Dasselbe gilt, wenn ich außerhalb des Pi-hole-Netzwerks zu Hause mit dem iPad im Web surfe. Ich weiß nicht, wie Menschen das täglich aushalten.
Das iPad ist ein dienstlich bereitgestelltes Gerät, daher nutze ich es privat nicht oft, aber jedes Mal erinnert es mich daran, wie nervig das ist.
Seltsamerweise dachte ich, bevor ich das iPad bekam, es wäre nur für Medienkonsum nützlich. Tatsächlich ist es sehr praktisch, um schnell remote auf Arbeitsressourcen zuzugreifen, während es beim normalen Browsen und Streaming von Medien ein in Werbung versunkenes Ödland ist.
Wenn du YouTube ohne Werbung brauchst, kannst du https://yewtu.be oder eine andere Invidious-Instanz https://docs.invidious.io/instances/ verwenden.
Zwischen YouTube und Invidious gibt es ein Wettrüsten, und manchmal funktioniert Invidious nicht, aber das Team hat immer wieder neue Wege gefunden, YouTube zu umgehen und Videos ohne Werbung auszuliefern.