2 Punkte von GN⁺ 2023-08-28 | 2 Kommentare | Auf WhatsApp teilen
  • Um YouTube-Werbung auf Apple TV und iPhone auf Netzwerkebene zu blockieren, wurden nacheinander pfSense, DNS-Blocking, VPN-Routing, Squid, MITMProxy und schließlich die Manipulation von Protobuf-Antworten getestet
  • Da YouTube-Werbung über dieselben Domains wie normale Videos ausgeliefert wird, war es mit reinem DNS-Blocking wie bei Pi-hole oder pfBlockerNG schwierig, Inhalte und Werbung zuverlässig zu trennen
  • Nach der Entschlüsselung des TLS-Traffics mit MITMProxy wurden in Web-YouTube die JSON-Werbefelder entfernt; in der iOS-App wurden die Werbestrukturen in Antworten vom Typ application/x-protobuf direkt gesucht und verändert
  • Ein vollständiges Protobuf-Decoding auf Python-Basis dauerte auf dem pfSense-Router rund 23 Sekunden, aber mit einem linearen Scan im Bereich um /pagead/ ließ sich selbst ein 1,8-MiB-Payload in Echtzeit verarbeiten
  • Diese Methode erfordert die Installation einer vertrauenswürdigen CA und ausreichend CPU-Leistung; auf mit dem Netzwerk verbundenen Apple-Geräten ließ sich Werbung zwar blockieren, letztlich wurde aber sogar eine YouTube-Premium-Zahlung in Betracht gezogen

pfSense-Router und Netzwerksegmentierung

  • Ziel war es, einen Router auf Basis von FreeBSD und pfSense zu bauen, um YouTube-Pre-Roll-, Mid-Roll- und End-Roll-Werbung auf Apple TV und iPhone im gesamten Netzwerk zu blockieren
  • Verwendet wurden ein J4125-Mini-PC mit AES-NI-Befehlssatz, DDR4-RAM, eine mSATA-SSD und ein USB-Laufwerk zur Installation von pfSense
    • Eine Beispielkonfiguration bestand aus 32 GiB RAM und einer 128-GB-mSATA-SSD
    • Die 128 GB Speicher wurden als nützlich für Logs, geringeren SSD-Verschleiß, Paketmitschnitte und Edge-Cache-Platz angesehen
  • Nach der Installation von pfSense wurde LAN 1 auf die statische IP 192.168.1.3 außerhalb des bestehenden DHCP-Bereichs gesetzt, und auf das Admin-Webportal wurde mit dem Konto admin/pfsense zugegriffen
  • Nach Prüfung der Anzeige AES-NI CPU Crypto: Yes (inactive) im pfSense-Dashboard wurde AES-NI unter System › Advanced › Miscellaneous manuell aktiviert
  • Die 32 GiB RAM wurden genutzt, um /var und /tmp großzügig als RAM-Disk zuzuweisen; außerdem wurde eine stündliche Sicherung der RAM-Disk eingerichtet

DNS-Blocking und physische Netztrennung

  • Statt des bisherigen Pi-hole wurde das pfSense-Paket pfBlockerNG-devel installiert, um Werbe- und Malware-Inhalte sowie Geo-Blocking zu konfigurieren
    • Der Speicherbedarf erhöhte sich dabei um etwa 20 MiB
    • Falls der Dienst pfb_dnsbl nicht startet oder [ Missing CRON task ] erscheint, wird empfohlen, die leere Datei /var/run/booting zu löschen
  • Die drei Gigabit-Ports des pfSense-Routers wurden statt für VLANs für eine physische Trennung der LANs verwendet
    • Geräte mit starkem „Phoning Home“ wie Alexa und Apple TV kamen in ein separates Hardware-LAN
    • Ein wichtiges LAN wurde von Smart-Geräten und WLAN-Geräten getrennt, um Geräte für Banking, Aktienhandel und Crypto Wallets zu schützen
  • Das Netz für Smart-Geräte wurde auf 172.31.1.0/24 ausgelagert, während das stärker vertrauenswürdige LAN auf 192.168/16 blieb
    • Wenn es keine Route zwischen den Netzen gibt, reduziert das aus dieser Sicht auch teilweise die Auswirkungen falsch konfigurierter iptables-Regeln
    • Auf der physischen NIC muss der DHCP-Resolver aktiviert werden, damit neue Netzwerkgeräte Adressen erhalten
  • Ein AC1200 Archer C5 wurde ausgemustert, weil AP mode fehlte, Remote-Zugriff problematisch war, die Stock-Firmware veraltet war und der Broadcom-Chipsatz nur schwache Unterstützung durch OpenWRT/DD-WRT/Tomato bot
  • Anschließend wurde ein Nighthawk R7000 als AP für Apple/Amazon/TV und als AP für das Trusted Wireless Network verwendet
    • Im Trusted Wireless Network sollte 2,4 GHz deaktiviert und nur 5 GHz genutzt werden
    • 5 GHz gilt als günstiger gegen Snooping auf mittlere Distanz, weil Wände und Beton das Signal stärker dämpfen

Gesamten DNS-Verkehr auf pfSense erzwingen

  • Es wurden NAT-Regeln hinzugefügt, damit alle Clients hinter pfSense den lokalen Unbound-DNS-Server verwenden
    • Ziel war, zu verhindern, dass Apps und Home Assistants über eigene oder fest einprogrammierte DNS-Server ausweichen
    • Damit pfBlockerNG DNS-Anfragen beeinflussen kann, muss DNS over TLS blockiert werden
  • Die NAT-Regeln wurden zunächst pro Interface außer WAN angelegt und später per Firewall-Alias Non_WAN vereinfacht
    • Lokale DNS-Abfragen auf Port 53 werden sowohl für IPv4 als auch für IPv6 auf localhost umgeleitet
    • NAT Reflection muss deaktiviert werden, damit aus dem externen Internet kein Zugriff auf den lokalen DNS-Server möglich ist
  • Unter Services › DNS Resolver › Display Custom Options wurde server: log-queries: yes ergänzt, um abgefangene DNS-Anfragen zu protokollieren
  • In den DNS-Logs war zu sehen, dass Windows auf Google Tag Manager zugreifen wollte; diese Anfrage wurde in ein Blackhole auf die nicht existierende IP 10.10.10.1 umgeleitet

Versuch, YouTube-Ad-Targeting per VPN zu umgehen

  • Weil YouTube-Werbung über dieselben Domains wie normale Videos kommt, war es mit Domain-Blockern wie pfBlockerNG oder Pi-hole schwierig, nur die Werbung herauszufiltern
    • Das Blockieren von googleadservices.com wurde als erst dann sinnvoll betrachtet, wenn nach dem Ansehen eines Werbevideos auf die Werbung geklickt wird
    • Im Browser kann uBlock Origin in JavaScript eingreifen, aber in der YouTube-App auf dem iPhone ist Werbebeschränkung ohne Jailbreak nur schwer möglich
  • Statt Werbung direkt zu blockieren, wurde experimentiert, den YouTube-Werbealgorithmus dazu zu bringen, den Nutzer als weniger attraktives Werbeziel einzustufen
    • Die Idee war, den Apple-TV-Traffic über ein VPN zu leiten und einen VPN-Endpunkt in einer Region mit wenigen YouTube-Zuschauern zu verwenden
    • Ziel war es, den Nutzer wie einen 70-jährigen Mann aus Italien erscheinen zu lassen
  • Auf pfSense wurde WireGuard eingerichtet; der private NordLynx-/WireGuard-Schlüssel wurde aus einer Linux-VM übernommen und konfiguriert
    • Gibt man bei der Tunneladresse 1.0.0.0 und die Subnetzmaske 0 ein, zeigt die UI das Ergebnis als 0.0.0.0/0 an
  • Als der gesamte Apple-TV-Traffic über das VPN lief, zeigte YouTube Inhalte auf Italienisch an und es gab weniger Werbung, aber Netflix und Amazon Prime machten Probleme
    • Es wirkte so, als würden CSS- oder Font-Dateien blockiert und Thumbnails nicht geladen
    • Es wird darauf hingewiesen, dass Netflix und Prime Geofencing gegenüber VPN-Anbietern gut erkennen
  • Danach wurde versucht, nur YouTube-bezogene FQDNs über das VPN zu routen; als Kandidaten dienten www.youtube.com, youtube.com, googlevideo.com, accounts.google.com, googleapis.com und gstatic.com
    • Nach der Konfiguration sah YouTube den Nutzer in Mailand, während Netflix und Prime Video ihn in Kanada verorteten
    • Werbung trat seltener auf, und die angezeigte Werbung war auf Italienisch

DNS-Race-Condition und Wildcard-Domain-Problem

  • Einen Tag später wurde eine DNS-Race-Condition entdeckt, bei der der pfSense-Hostname-Alias und der DNS-Cache des Clients unterschiedliche Mengen von YouTube-IPs sahen
    • Das Standard-Resolve-Intervall des pfSense-Hostname-Alias beträgt 300 Sekunden
    • Die YouTube-DNS-TTL wurde mit 1.440 Sekunden beobachtet
  • Wenn die vom Alias Daemon für die FQDN-Auflösung ermittelten IPs nicht mit den IPs übereinstimmen, die Apple TV später erhält, kann der Traffic den VPN-Tunnel verfehlen
    • Als Gegenmaßnahme soll pfSense die Ziel-TTL ignorieren und Alias-Einträge länger cachen
  • Für die variierenden Subdomains von googlevideo.com war Wildcard-Routing nötig, aber NAT und Firewall-Regeln arbeiten IP-basiert und können Wildcard-Hostnamen nicht direkt verarbeiten
  • Es wurde ein PoC geschrieben, der mit dem Unbound-Python-Modul und der pfSense-REST-API DNS-Antwort-IPs erfasst und sie dynamisch dem Alias VPN_wildcards hinzufügt
    • Die TTL von VPN_wildcards wurde auf 1 Stunde gesetzt, die Kapazität auf 500
    • A-Records werden als ipaddress.IPv4Address, AAAA-Records als ipaddress.IPv6Address geparst
  • Bei der Prüfung am Morgen befand sich der Unbound DNS Resolver im Segfault-Zustand, und für jedes Hinzufügen einer IP war ein Reload der pfSense-Regeln nötig, wodurch pfSense sehr langsam wurde

HTTPS-Entschlüsselung mit Squid und MITMProxy

  • Das neue Ziel war, einen Squid-ähnlichen Proxy zu installieren und dem Gerät ein gefälschtes, aber vertrauenswürdiges CA-Zertifikat hinzuzufügen, um TLS-Traffic zu entschlüsseln
  • Squid wurde als pfSense-Paket installiert und sogar ein SSL-Filtering-Smoke-Test war erfolgreich, wurde dann aber verworfen
    • Die Performance 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-UI wurde als unzureichend angesehen
  • Danach fiel die Wahl auf MITMProxy
    • Es bietet Python-Scripting und eine UI und wurde als erweiterbar für YouTube-Werbeblockierung angesehen
    • Das Linux-Tarball von mitmproxy 7.0.4 lief unter FreeBSD wegen ELF interpreter /lib64/ld-linux-x86-64.so.2 not found und fehlender Bibliotheken nicht
  • In der FreeBSD-Jail-Umgebung von pfSense wurde es mit pkg install mitmproxy installiert
    • Es waren 50 Pakete zur Installation vorgesehen, mit 206 MiB zusätzlichem Speicherbedarf und 33 MiB Download-Volumen
    • Wenn mitmproxy innerhalb der Jail ausgeführt wird, öffnet sich die UI
  • Für die MITMProxy-Experimente wurde in pfSense die virtuelle IP 127.0.1.1 an localhost gebunden und per NAT-Regel [Private IPs]:8080 an 127.0.1.1:8080 weitergeleitet
    • Nachdem auf einem Test-Laptop der Proxy auf 192.168.20.1:8080 gesetzt wurde, erschienen Browser-Anfragen im MITMProxy-UI-Log
  • Die MITMProxy-CA-PEM-Datei ist ~/.mitmproxy/mitmproxy-ca-cert.pem
    • cert.pem wurde über einen Python-3-Webserver bereitgestellt
    • MITMProxy stellt dasselbe CA-Zertifikat auch unter mitm.it bereit
    • Das Zertifikat wurde auf einem sauberen Laptop und einem iPhone hinzugefügt

Betrieb von MITMProxy und Umgang mit Certificate Pinning

  • Auf dem Router verbrauchte mitmproxy selbst im Leerlauf viel CPU; als Ursache wurden das On-the-fly-Erzeugen von TLS-Zertifikaten pro Anfrage und das übermäßige Logging der UI vermutet
  • mitmdump wurde als CPU-schonender angesehen, da es auf UI und extremes Logging verzichtet
    • Beim Start wurden unter anderem --anticomp, --mode regular, --listen-port 8080, --listen-host 127.0.1.1 verwendet
  • Certificate Pinning ist eine Technik, bei der Server oder Client den erwarteten Zertifikat-Fingerprint im Voraus kennen, sodass die Zertifikatsfälschung durch MITMProxy nicht funktioniert
    • Als Umgehung wurde --ignore-hosts verwendet, damit Hosts wie apple.com:443 und icloud.com:443 den Proxy umgehen
  • Im Transparent-Proxy-Modus wurde next_layer.py von MITMProxy 7.0.4 gepatcht, damit --allowed-hosts auf Basis von SNI besser funktioniert
    • Zuvor wurde in vielen Fällen offenbar nur die Server-IP für das Matching verwendet
    • Der Patch fügt nicht nur server.address[0], sondern auch server.sni als Kandidaten für Hostnamen hinzu
  • Nach dem Patch konnten einige Hosts zuverlässig abgefangen werden, während der Rest durchgelassen wurde

Entfernen von JSON-Werbung auf YouTube im Web

  • Im MITMProxy-Smoke-Test wurden YouTube-Werbe- und Tracking-URLs mit einem kleinen Skript blockiert
    • Auf youtube.com wurden unter anderem /pagead/, /log_event?, /stats/ads, /stats/qoe?, /ptracking?, /generate_204, el=adunit, adformat=, /activeview? blockiert
    • Auf google.com und google.ca wurde /pagead/ blockiert
  • In ersten Tests wurden die zu blockierenden Anfragen auch im DevTools-Network-Panel tatsächlich als blockiert angezeigt
    • Einträge mit (failed) stammen aus dem Skript
    • 502-Fehler wurden als Ergebnis davon angesehen, dass pfBlockerNG die Anfrage in ein Black Hole leitete
    • HTTP/2 wurde deaktiviert, damit nachfolgende Anfragen auf demselben Kanal nicht durchkommen
  • Mit einfachem URL-Blocking verschwanden die Anzeigen nicht vollständig; daher wurde anhand von YouTube-HTML, JavaScript und uBlock-Origin-Filtern untersucht, ob Werbeinformationen im JSON-Antwort-Body enthalten sind
  • In den von MITMProxy erfassten JSON-Antworten wurden Werbe- und Tracking-Informationen in den Strukturen playerAds und playbackTracking bestätigt
    • playerAds enthält playerLegacyDesktopWatchAdsRenderer, playerAdParams, gutParams.tag, showCompanion, showInstream, useGut und mehr
    • playbackTracking enthält videostatsPlaybackUrl, ptrackingUrl, qoeUrl, atrUrl und mehr
    • youtubeRemarketingUrl hatte die Form www.youtube.com/pagead/viewthroughconversion/...
  • Bei YouTube im Web konnten durch das Entfernen von Werbeinformationen aus dem JSON-Payload Web-Anzeigen über den Router beseitigt werden

Analyse von iOS-YouTube und Protobuf

  • Die iOS-YouTube-App verwendet bei API-Aufrufen ähnlich wie die Web-Version Daten im Format Protocol Buffer (Protobuf) statt JSON
    • In Protobuf sind die Schlüssel numerisch und können sich ändern, daher ist es schwierig, den Werbeabschnitt mit einem JSONPath-Ansatz zu finden
    • Im Payload waren Werbestrings wie „Telus“, „Samsung TV“, „Boxing Week“ und „Buy now“ zu sehen
  • Der Netzwerkverkehr von iOS-YouTube unterschied sich vom Web-Traffic
    • In der Web-Version lassen sich Werbekandidaten teilweise über den range- oder clen-Parameter der Video-Chunks abschätzen
    • Das iOS-Protokoll verwendet weder den Query-Parameter range noch den Header Range, sondern Zähler wie &nr=2 und &nr=3
  • Beim Dekodieren von Protobuf-Antworten zur Offline-Analyse wurden Einträge wie has_unlimited_entitlement: False und has_premium_lite_entitlement: False gefunden
    • Das Ändern dieser Werte fühlte sich wie „Cheating“ an, daher wurde wieder zu einem heuristischen Ansatz zurückgekehrt
  • Experimente zum Blockieren von Werbe-URLs führten in der iOS-App zu Endlosschleifen, UI-Fehlern und Abstürzen
    • 200 mit leerem Body, 404, 503, abgeschnittene Response-Bodys oder das Nullsetzen von Teilen des Ad-Videos machten die App langsam oder ließen sie in einem defekten Zustand abstürzen
    • Der Error-Reporting-Endpunkt /error_204/ zeigte „dev assertion failed“ an und wurde blockiert
  • Werbung scheint in Slots innerhalb eines bestimmten Videos registriert zu werden
    • Zu den Slot-Typen gehören Pre-Roll, Mid-Roll, End-Roll, Full-Page und Ad Pod
    • Wenn nur die Ad-URL blockiert wird, entstehen Fehler nach dem Muster „eine nicht existierende Werbung hat einen Slot reserviert“, worauf die UI in einen Panic-Zustand gerät

Protobuf-Performance-Probleme und blackboxprotobuf

  • Das Dekodieren von rund 500 KiB rohem Protobuf in menschenlesbaren Text nur mit Python war sehr langsam
    • Auf einem Desktop mit i7-6700-CPU dauerte es etwa 2,06 bis 2,11 Sekunden
    • Auf einem pfSense-Router dauerte es etwa 22,8 bis 24,2 Sekunden
  • C++ protoc --decode_raw war deutlich schneller
    • Auf einem Desktop mit i7-6700-CPU dauerte es etwa 0,017 bis 0,022 Sekunden
    • Auf einem pfSense-Router lag es bei etwa 0,12 bis 0,14 Sekunden
  • Da Raw-Decoding in Python nicht unterstützt wurde, fiel die Wahl auf direkte Kommunikation mit dem C++-Binary protoc über subprocess.Popen
  • blackboxprotobuf für Burp Suite kann rohe Protobuf-Wire-Messages dekodieren, Werte injizieren und danach wieder re-encodieren
    • Es wird empfohlen, nicht den PyPI-Fork, sondern die originale Burp-Suite-Version zu verwenden
    • Einige Forks können durch tiefe Rekursion einen Stack Overflow auslösen
    • Wenn os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp" vor dem Import von protobuf gesetzt wird, kommt nach Möglichkeit die C++-Implementierung libprotobuf.so zum Einsatz
  • protobuf_to_json(data) von blackboxprotobuf kann ein .proto-Schema per Best Guess erzeugen, aber das Ergebnis ist sehr groß, tief verschachtelt und nicht perfekt
    • Der Python-Schema-Dump hatte ungefähr mehr als 250.000 Zeichen
    • Zum Extrahieren von Werbedetails wurde es jedoch als ausreichend angesehen

Werbeabschnitt mit 1-Byte-Änderung deaktivieren

  • Beim Dekodieren, Bearbeiten und Re-Encodieren des Protobuf Wire Format ohne ursprüngliches Schema kann sich die Kodierung ändern
    • Als Gründe werden die mögliche Verwendung von ZigZag-Encoding, die fehlende Erkennbarkeit numerischer Typen und die nicht deterministische Reihenfolge von Objektfeldern genannt
  • Die Lösung bestand darin, die Backward Compatibility von Protobuf zu nutzen, um den Werbeabschnitt wie ein unbekanntes Feld aussehen zu lassen
    • Dabei wurde das Verhalten genutzt, dass alte Software unbekannte Felder ignoriert, wenn sie neue Felder liest
    • Wenn man an einer kritischen Stelle 1 Byte ändert, sodass ein tief verschachtelter Abschnitt aussieht, als gehöre er zu einer zukünftigen Schema-Version, ignoriert Protobuf ihn
  • Der Ziel-Feldschlüssel 49399797 musste nicht per einfacher Stringsuche, sondern per Varint-Tag-Scanning gefunden werden
    • Der Wire Type war 2 und steht für length-delimited nested string/message
    • Das Ziel-Tag wurde als AA FF B8 BC 01 berechnet
    • Wenn die 3 Bits des Wire Type herausgeschoben werden, erhält man wieder den Feldschlüssel 49399797
  • Die tatsächliche Suche lief so ab, dass zunächst im rohen Protobuf-Byte-Stream nach einer Werbe-URL-Signatur wie /pagead/ gesucht wurde und man sich dann in der Nähe rückwärts zum Ziel-Feld-Tag und Feldschlüssel vorarbeitete
    • Beispielziel beim Intercept war POST youtubei.googleapis.com:443/youtubei/v1/browse?key=...
    • Die Antwort war 200, application/x-protobuf, 1.87m
    • Im Beispiel-Log wurde 49399797 an Position 4465 und 50195462 an Position 4477 gefunden
  • In einem O(n)-Smoke-Test funktionierte die Werbeentfernung, indem 1,8 MiB Protobuf-Daten einmal ohne zusätzlichen Speicher gescannt wurden
    • Das Ziel wurde beim 30.593. Byte gefunden
    • Durch etwa 600 Byte Backtracking wurde der zu verfälschende Feldschlüssel gefunden
    • Es ist nicht mehr nötig, URLs mit *.googleadservices.com oder /pagead/ zu blockieren, weil diese Anfrage gar nicht erst erzeugt wird

Struktur des MITMProxy-Add-ons

  • Das PoC-Skript wird als youtube.py gespeichert und mit mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py" ausgeführt
    • Als Voraussetzungen unter FreeBSD werden pkg install protobuf, pkg install py38-pip und pip install jsonpath-ng genannt
  • Das Skript besteht aus den Klassen Logger, trunc, KilledError, JSONPathReplacement, ProtobufDebugParser und YouTubeAdBlocker
  • Das Host-Regex für die Interception in YouTubeAdBlocker lautet \.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com
    • Der Protobuf-Suchstring für Werbe-URLs ist b"/pagead/"
    • Das Suchlimit beträgt 80_000
    • Das Ziel-Feld-Tag ist 50195462
  • Die Regel zum Blockieren von Requests prüft je Host partielle URL-Strings und killt dann den Flow
    • Für youtube.com umfasst das unter anderem pagead/, log_event?, stats/ads, stats/qoe?, ptracking?, generate_204, error_204, adformat=, activeview?, _ad_, ai?, sw.js
    • Zu sw.js gibt es den Kommentar, dass Service Workers abgelehnt werden
  • Bei JSON-Antworten werden mehrere JSONPath-Replacements angewendet
    • $.responseContext.serviceTrackingParams[*].params[?(@.key == 'yt_ad')].value wird zu "0" geändert
    • $..adPlacements wird zu [] geändert
    • $..adPlacementRenderer, $..adPlacementConfig, $..playerAdParams, $..gutParams werden zu {} geändert
    • $..adVideoId wird zu einem leeren String geändert
    • $..showCompanion, $..showInstream, $..useGut werden zu False geändert
  • Bei Protobuf-Antworten wird, wenn der Content-Type protobuf enthält, der Body zu einem bytearray gemacht und innerhalb der ersten 80.000 Byte nach /pagead/ gesucht
    • Mit TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED) werden die Ziel-Tag-Bytes erzeugt, außerdem werden neue Bytes für target_field_tag - 1 erstellt
    • Direkt vor der Position von /pagead/ wird rückwärts nach den Ziel-Tag-Bytes gesucht
    • Wenn sie gefunden werden, werden diese Bytes mit den neuen Bytes überschrieben und mit flow.response.set_content(bytes(body)) der Response-Body ersetzt
  • Laut Kommentar blockiert dieses PoC 90 % der Werbung
    • In anderen Sections gibt es weitere Field Keys, und es kann mehrere Ad-Sections geben, die manipuliert werden müssen

Geltungsbereich und Grenzen

  • Diese Technik wird als hochspezialisierte Methode zum Blockieren von YouTube-Werbung auf Apple-Geräten oder von Tracker-Traffic bei Instagram, WhatsApp und Facebook beschrieben
  • Der CPU-Bedarf für das Entschlüsseln und erneute Verschlüsseln von HTTPS-Traffic wird als deutlich über dem angesehen, was ein Raspberry Pi bewältigen kann
  • Nach einem Jailbreak des Apple TV könnte man ein pfSense-Root-Zertifikat hinzufügen, sodass das pfSense-Gateway den Apple-TV-Traffic entschlüsselt und auf Werbe-Hostnames in den Request-Headern prüft, um ihn zu blockieren
    • Auf iPhone-Werbung wäre das weiterhin nicht anwendbar, ein iPhone-Jailbreak ist schwieriger, und Banking-Apps könnten ihn erkennen und dann nicht mehr funktionieren
    • Der Jailbreak selbst wird als zu extrem eingeschätzt
  • Wenn eine gefälschte vertrauenswürdige CA möglich ist, können TLS-Pakete im Klartext entschlüsselt und URL-Blocking-Regeln angewendet werden
    • Als Beispiel für zu blockierende URLs werden bei YouTube die Pfade /pagead/viewthroughconversion/... und /pagead/conversion/... genannt
  • Abschließend wird beschrieben, dass ein Hardware-Router von Grund auf eingerichtet, das LAN in vertrauenswürdige und nicht vertrauenswürdige Zonen aufgeteilt, DNS-Ad-Blocking und ein transparenter MITM-Proxy ergänzt und damit YouTube-Werbung auf mit dem Netzwerk verbundenen Apple-Geräten mit guter Performance blockiert wird

YouTube Premium und Unterstützung für Creator

  • Nach einigen Monaten des Blockierens von YouTube-Werbung begann der Autor, für YouTube Premium zu zahlen, um Content Creator zu unterstützen
    • Verbunden mit dem Hinweis: „Nur weil man etwas tun kann, heißt das nicht, dass man es tun sollte“
  • Der Preis von YouTube Premium wird mit CAD $9.99/mo bis $11.99/mo, inklusive Steuern etwa $13.43/mo, angegeben
  • Für ein Experiment zum Ansehen von Werbung wurde YouTube einen Tag lang auf einem sauberen Laptop und im privaten Browsermodus sporadisch genutzt
    • Laut Verlauf wurden 10 Videos angesehen
    • Es wurden 8 Werbeanzeigen eingeblendet, von denen nur 2 überspringbar waren
  • Bei einem angenommenen CPV von USD $0.15 ergeben sich Werbekosten von $1.20 pro Tag, hochgerechnet etwa USD $36/mo
  • In einer anderen Berechnung mithilfe von Statista-Zahlen gaben US-Werbetreibende 2019 $15.1 billion für YouTube aus, während Einwohner der USA 916 billion Videos ansahen, was im Schnitt USD $0.0165 pro View ergibt
    • Nach dieser Rechnung liegen die Kosten pro Tag bei etwa USD $0.13, hochgerechnet etwa USD $3.96 pro Monat
    • Dieser Wert wird nicht als in der Nähe von Premium zu USD $10 angesehen
  • Bei DMCA-Claims können Werbeeinnahmen nicht an den Creator, sondern an Anspruchsteller wie Sony oder Viacom gehen
    • Dadurch kann es passieren, dass man seinen Lieblingskanälen unbemerkt gar nichts gibt
    • Deshalb sei es nicht überraschend, dass viele Creator zu Patreon wechseln

2 Kommentare

 
xguru 2023-08-29

Der Originalbeitrag ist ziemlich lang. Der Ablauf ist zwar interessant, aber der eigentliche Punkt ist, dass der Autor am Ende doch einfach YouTube Premium bezahlt und nutzt.

 
GN⁺ 2023-08-28
Meinungen auf Hacker News
  • Insgesamt ein cooler Hack, aber einige Aussagen zu Protobuf wirken seltsam.
    Hier wurde absichtlich das Tag eines Felds in Protobuf beschädigt; unbekannte Tag-Nummern zu ignorieren ist kein „Mangel“, sondern ein zentraler Designaspekt für Erweiterbarkeit.
    1,87 MiB ist auch keine besonders große Größe, und solche Nachrichten werden vermutlich nicht fortlaufend gestreamt; daher überzeugt mich die Erklärung als Performance-Hürde ebenfalls nicht so recht.
    Protobuf-Encoding wurde nicht so entworfen, dass das Decoding teuer wird, sondern im Gegenteil so, dass es effizient decodiert werden kann; auch ohne das ursprüngliche .proto-Schema lässt es sich direkt über UnknownFieldSet decodieren.
    Die bessere Methode wäre wohl gewesen, ein gefälschtes .proto-Schema zu verwenden, das nur das eine zu entfernende Feld enthält. Ein String-Scan ist fehleranfälliger, weil dieselbe Bytefolge zufällig in anderen Daten vorkommen kann.
    Wenn sich die Feldreihenfolge ändert, können sich die Bytes des neu encodierten Ergebnisses zwar unterscheiden, aber der Empfänger muss es als dieselbe Nachricht behandeln; es wirkt unwahrscheinlich, dass die YouTube-App eine geänderte Feldreihenfolge erkennt.
    Aus Sicht von jemandem, der früher an Protobuf gearbeitet hat, scheint der Autor diesen Teil missverstanden zu haben.

    • Eine gute Analyse, aber der Aussage, dass 1,87 MB klein sei, kann ich nur bedingt zustimmen.
      Ich habe den größten Teil meines Lebens in ländlichen Gegenden verbracht, und wenn es nicht mein eigenes WLAN ist, ist selbst so etwas in der Praxis ein großer Download. Auf Mobilfunk gibt es vielleicht Workarounds, aber ländliches WLAN hat noch immer mit Web-2.0-Strukturen zu kämpfen und wird oft mit Geschwindigkeiten im Bereich von 2G bis 4G genutzt.
      In Ballungsräumen, wo genügend Menschen vorhanden sind, um die Infrastruktur zu tragen, ist 1,87 MB meist zu einer kleinen Datei geworden – aber gegen 18 Uhr, wenn alle an den Kabelanschlüssen streamen, kann das eine Ausnahme sein.
    • Kleine Eigenwerbung zu without the C++ source proto files: Ich habe ein Projekt namens protodump gebaut, das aus Binaries die Quell-.proto-Dateien erzeugt.
      Es rekonstruiert Nachrichten- und Felddefinitionen einschließlich ihrer ursprünglichen Namen; man muss nur das Binary von der Apple-TV-Box extrahieren.
      https://github.com/arkadiyt/protodump
    • „Früher an Protobuf gearbeitet“ ist eine enorme Untertreibung. Für alle, die es nicht wissen: Kenton ist die Person, die Protobuf zu dem gemacht hat, was es heute ist.
      Protobuf war die Technologie, die mich erstmals mit IDL in Kontakt brachte, und damals wirkte die Idee wie Magie. Nachdem ich selbst eine unbeholfene IDL gebaut und dann Protobuf entdeckt hatte, war ich noch beeindruckter.
    • Beim Lesen des Artikels war ich ähnlich verwirrt. Die Designprinzipien von Protobuf sind kein Geheimnis und vollständig klar dokumentiert.
    • Der kniffligste Teil beim Decoding von Protobuf ohne Schema ist, dass eingebettete Nachrichten und Strings denselben Tag-Typ verwenden; trotzdem lässt sich das ziemlich einfach handhaben.
      Wenn man nicht die gesamte protoc-Abhängigkeit hereinziehen will, kann man einen einfachen Protobuf-Decoder mit ein paar hundert Zeilen selbst schreiben: https://github.com/kubernetes/test-infra/blob/master/guberna... https://github.com/kubernetes/test-infra/blob/master/guberna...
  • Seit ich vor über 20 Jahren The Proxomitron entdeckt habe, leite ich Traffic über einen Man-in-the-Middle-Proxy, um Werbung zu entfernen und Seiten mit Dingen wie eigenem CSS umzuschreiben.
    Anbieter wie CloudFlare neigen dazu, mich als „Bot“ einzustufen, aber auch dafür gibt es Umgehungen, wenn auch keine ganz einfachen. Solche Fälle zeigen auch, warum Remote Attestation gefährlich für die Freiheit der Nutzer ist.

    • Ich habe The Proxomitron nachgeschlagen; die Entwicklung scheint 2004 geendet zu haben. Eine Zusammenfassung von jemandem, der die aktuelle Lage gut kennt, wäre interessant.
      Es scheint mehrere „Nachfolge“-Projekte zu geben, und ich frage mich auch, ob es nur Windows ist. Ich hatte lose nach einem einfachen Proxy gesucht, der lokale Links in Remote-Inhalte einfügen kann.
    • Adblocking auf Netzwerkebene wie Privoxy oder pi-hole hat zu viele Nachteile, etwa dass Inline-Werbung nicht behandelt werden kann.
      Inzwischen habe ich auch das pi-hole, das auf einem Pi 4 lief, ausgesteckt. Ich habe Stunden damit verbracht, es mit mehreren Diensten zuverlässig zum Laufen zu bringen, aber am Ende aufgegeben; für ein Heimnetz war es die Zeit nicht wert.
      Was tatsächlich gut funktioniert, sind browserbasierte Adblocker und App-Patcher wie ReVanced. Mit wachsenden Ersparnissen tendiere ich außerdem stärker dazu, bei Fällen, die sich damit nicht lösen lassen – YouTube Premium, Hulu, Netflix, Max –, einfach für werbefreie Dienste zu bezahlen.
    • Es ist bekannt, dass Cloudflare-Mitarbeiter hier mitlesen, daher würde mich interessieren: Wird eine solche Einstufung als Bot als False Positive betrachtet oder als beabsichtigtes Verhalten?
    • An Proxomitron habe ich fast 20 Jahre lang nicht mehr gedacht; ich frage mich, ob es noch genutzt wird.
      Für den hier genannten Zweck habe ich es nie eingesetzt, aber als Proxy hinter einer Firmen-Firewall war es großartig. Früher verlangte die Firewall für externe Verbindungen Login-Daten, sodass viele Programme nicht ins Internet kamen.
    • Vermutlich läuft das so, dass man ein neues CA-Zertifikat auf dem Gerät installieren muss, oder?
  • Mir fällt Privaxy ein, als Docker-Paket. Ein Man-in-the-Middle-Proxy, der mit den Blocklisten von uBlock Origin kompatibel ist.
    Es ist erstaunlich, wie viel Werbung und Tracking-Skripte es auf smarten Produkten gibt, insbesondere auf TVs. Nach meinen bisherigen Tests machte unnötiger Traffic über 40 % aus, und das Experimentieren damit, Werbung aus Smart-TV-Apps herauszulösen, war ziemlich interessant.
    https://github.com/deetungsten/webui-privaxy ist ein dockerisierter Fork von https://github.com/Barre/privaxy

    • Wie bringt man den TV dazu, einem selbstsignierten Zertifikat zu vertrauen?
    • Schön, das auch hier zu sehen. Gut, dass du das Problem mit dem Filterlisten-Ping angesprochen hast.
      Ich wollte den Fork so ändern, dass im Frontend keine hartcodierte Adresse 0.0.0.0 mehr verwendet wird, damit man den Docker-Container wirklich isolieren kann, aber dann kam das Leben dazwischen. Hast du es auf dem Apple TV ausprobiert?
    • Adguard arbeitet ebenfalls an etwas Ähnlichem.
      https://github.com/AdguardTeam/urlfilter
    • Es hat viel zu lange gedauert, bis ich verstanden habe, dass „dockerisierter Fork“ hier bedeutet, dass die GUI durch eine Web-GUI ersetzt wurde.
  • Dieser Artikel ist eine hervorragende Antwort auf die häufige Frage: „Wie lernt man, Hacker zu werden?“
    Er zeigt sehr gut den Denkprozess und die beharrliche Arbeit, die in jedem Exploit steckt.

  • Da steht sinngemäß: „Nimm WireGuard — es gibt den Intel-AES-NI-Befehlssatz für Verschlüsselung“, aber soweit ich weiß, verwendet WireGuard kein AES.
    Insgesamt scheint der Autor die CPU-Anforderungen von TLS-Verschlüsselung etwas zu überschätzen oder die Leistung moderner Single-Board-Computer zu unterschätzen.
    Auch die Erklärung, dass die CPU-Anforderungen zum Entschlüsseln und erneuten Verschlüsseln von HTTPS-Traffic auf einem Raspberry Pi deutlich überschritten würden, finde ich fragwürdig. Wenn TLS-Man-in-the-Middle auf einem RPi 4 tatsächlich unmöglich wäre, würde mich das ziemlich überraschen, selbst bei rein softwarebasierter RSA-Nutzung.
    Unter den noch genutzten Android-Smartphones gibt es Geräte mit schwächeren CPUs als beim RPi 4, und auch sie verwenden TLS.

    • Ich glaube, du unterschätzt die CPU-Anforderungen.
      Wenn ein schwaches Android-Smartphone TLS-Traffic nur mit 50 Mb/s verarbeiten kann, ist das im Alltag vielleicht kein großes Problem. Langsame Smartphones hängen schließlich meist an langsamen Netzen.
      Wenn man zu Hause dagegen Gigabit-Internet hat und ein schwaches Gerät zwischen allen Computern und dem Internet einen Flaschenhals bei 50 Mb/s erzeugt, ist das ein großes Problem.
      Die CPU-Anforderungen von TLS hängen sehr stark von der angestrebten Bandbreite ab. Bei höheren Bandbreiten wird Offloading auf Beschleuniger praktisch unverzichtbar. Auch die Kosten des Handshakes sind nicht zu vernachlässigen und können die Zahl der Verbindungen pro Sekunde begrenzen. Auf einem einzelnen Gerät ist das selten ein Problem, in einem ganzen Gerätenetzwerk kann es aber stärker ins Gewicht fallen.
  • Großartiger Artikel. Ich hatte gehofft, eine Methode zu sehen, Geräte per Man-in-the-Middle zu bearbeiten, die die Installation einer eigenen CA nicht erlauben.
    Ich habe ein IoT-Gerät, das keine lokale API bereitstellt und Daten nur über die Cloud anzeigt, und ich möchte den Traffic zwischen Gerät und Cloud mitschneiden.
    Bleibt am Ende wirklich nur, den Flash-Speicher zu dumpen, die CA auszutauschen und das Ganze wieder aufzuspielen?

    • Wenn das Zertifikat „hartcodiert“ ist, nennt man das Certificate Pinning. In dem Fall musst du das Zertifikat ersetzen oder entfernen und dasselbe Zertifikat in den Man-in-the-Middle-Proxy übertragen, um den Traffic entschlüsseln zu können.
      Es gibt einen guten Artikel dazu, wie man versuchen kann, IoT-Geräte ohne Hardware- oder Firmware-Arbeiten abzufangen:
      https://robertheaton.com/2019/11/21/how-to-man-in-the-middle...
    • Eine Möglichkeit zu suchen, Geräte per Man-in-the-Middle zu bearbeiten, die keine eigene CA installieren lassen, läuft letztlich dem Zweck von TLS zuwider.
      Wenn es möglich ist, beruht es auf einem Implementierungsfehler.
      Ehrlich gesagt glaube ich, dass selbst einige Geräte, auf denen man heute noch eigene vertrauenswürdige Zertifikate installieren kann, dafür nicht mehr lange offen sein werden.
  • Es kommen immer wieder neue Methoden auf, Werbung auf YouTube oder anderen Plattformen zu blockieren, aber ein paar Monate später werden sie geändert und sind nutzlos.
    Wie wäre es stattdessen, die Werbetreibenden anzugreifen? YouTube/Google scheint nur „Klicks“ zu verfolgen — aber verfolgen sie auch tatsächliche Käufe?
    Theoretisch könnten genügend Fake-Bots und echte Nutzer auf Anzeigen klicken, aber nichts kaufen, und so das Werbebudget verbrennen. Mit der Zeit würde die Marketingabteilung sehen, dass die Klickzahlen auf einer bestimmten Plattform Rekordwerte erreichen, die Conversion-Rate im Verhältnis zu Klicks oder Impressions aber sehr niedrig ist, und sich schließlich von dieser Plattform zurückziehen.

    • Wenn man bedenkt, dass Nauseum im Chrome Store gesperrt wurde, scheint es ziemlich wirksam zu sein.
  • Erstaunlicher Artikel. Sobald ich den Schritt mit dem mitm-Patch gesehen habe, dachte ich, dass das etwas Besonderes werden würde, und genau das war es auch.

  • Der Eintrag im Inhaltsverzeichnis „Neues Ziel: YouTube glauben lassen, ich sei ein 70-jähriger Mann, der in Italien lebt“ ist mir hängen geblieben.
    Vor einiger Zeit hat das Ad-Targeting es irgendwie geschafft zu glauben, ich sei jemand, der für seine Freundin einen waschbaren Seidenpyjama für 500 Dollar kaufen will.
    Die Werbung selbst war großartig, aber ich frage mich, wie viel sie pro Impression bezahlt haben.
    Seit dem Wechsel auf Apple TV bekomme ich meistens lokale Werbung, bei der das regionale Targeting falsch liegt. Im Durchschnitt ist das vielleicht sogar besser.

  • Das ist kein „Fehler“ in Protobuf. Dass es beim Ändern von Bytes als Feld an einer anderen Position dekodiert wird, ist Verhalten wie vorgesehen.
    Protobuf ist von Anfang an ein Protokoll auf Basis von Feldnummern und Längenpräfixen, trifft die vernünftige Annahme, dass Bytes während der Übertragung nicht verändert werden, und überlässt die Integrität der lesenden Seite.
    Selbst wenn es ein Fehler wäre, läge er nicht bei Protobuf, sondern in der YouTube-App für iOS; tatsächlich ist es aber nicht einmal ein Fehler, daher ist es auch schwer, das als „Exploit“ zu bezeichnen. Es sei denn, gemeint ist, dass beim Protobuf-Austausch der YouTube-iOS-App der Hash der zurückgegebenen Payload nicht geprüft wird.
    Nach diesem Artikel wird das vermutlich künftig geprüft werden.

    • Die Formulierung des Autors ist etwas seltsam. „Fehler“ kommt nur im Titel vor; der Text erklärt lediglich, wie das Format funktioniert.
      Es ist kein Fehler, sondern funktioniert wie entworfen.
      Auch die Aussage, „Google mache das Dekodieren, Modifizieren und erneute Enkodieren ohne C++-Source-Proto-Datei rechnerisch teuer“, ist seltsam. Mit nicht optimiertem Python-Code mag es teuer sein, aber in C oder einer anderen kompilierten Sprache ist das Scannen eines 1,8-MB-Protobufs trivial – unabhängig davon, ob man die Proto-Quelldatei hat oder nicht.
      Es dürfte kaum ein Designziel gewesen sein, Protobuf-Dateien ohne Source schwer dekodierbar zu machen. Wenn das das Ziel war, wurde es ziemlich schlecht umgesetzt.
    • Ich weiß nicht, wie Required Fields in Protobuf funktionieren, aber zur Abschwächung des Angriffs könnte Googles YouTube-Client dieses Feld als Pflichtfeld behandeln und den Dienst verweigern, wenn das Feld fehlt oder den Standardwert hat.
    • Der Artikel ist vom Januar 2022. Wenn man das Protokoll nach dem Blogpost härten wollte, ist das wahrscheinlich längst passiert.