2 ポイント 投稿者 GN⁺ 2023-08-28 | 2件のコメント | WhatsAppで共有
  • Apple TVとiPhoneのYouTube広告をネットワーク層で止めるため、pfSense、DNSブロック、VPNルーティング、Squid、MITMProxyを経て、Protobufレスポンス改変まで実験した
  • YouTube広告は通常の動画と同じドメインから配信されるため、Pi-holeやpfBlockerNGのようなDNSブロックだけではコンテンツと広告を安定して分離しにくかった
  • TLSトラフィックをMITMProxyで復号した後、Web版YouTubeではJSONの広告フィールドを削除し、iOSアプリではapplication/x-protobufレスポンス内部の広告構造を直接見つけて修正した
  • Pythonベースの完全なProtobufデコードはpfSenseルーターで約23秒かかったが、/pagead/周辺を線形スキャンする方式により、1.8MiBのpayloadもリアルタイム処理が可能になった
  • この方式には信頼されたCAの導入と十分なCPU性能が必要で、ネットワークに接続されたApple機器で広告ブロックは可能だったが、その後YouTube Premiumの支払いまで検討するようになった

pfSenseルーターとネットワーク分離

  • 目標は、FreeBSD・pfSenseベースのルーターを作り、Apple TVとiPhoneのYouTube pre-roll、mid-roll、end-roll広告をネットワーク全体でブロックすることだった
  • 使用ハードウェアは、AES-NI命令セットを備えたJ4125 mini PC、DDR4 RAM、mSATA SSD、pfSenseインストール用USBドライブだった
    • 構成例は32GiB RAMと128GB mSATA SSDだった
    • 128GBのストレージは、ログ、SSD摩耗の低減、パケットキャプチャ、edge cache領域に有用だと見ていた
  • pfSenseインストール後、LAN 1を既存のDHCP範囲外の固定IP 192.168.1.3に設定し、管理Webポータルへ admin/pfsense アカウントでアクセスした
  • pfSenseダッシュボードの AES-NI CPU Crypto: Yes (inactive) 表示を確認した後、System › Advanced › MiscellaneousでAES-NIを手動で有効化した
  • 32GiB RAMを活用して /var/tmp に十分なRAM diskを割り当て、RAM-diskバックアップを毎時実行するよう設定した

DNSブロックと物理ネットワーク分離

  • 既存のPi-holeの代わりに、pfSenseパッケージのpfBlockerNG-develをインストールして広告・悪性コンテンツのブロックと地理的ブロックを構成した
    • インストール容量は約20MiB増加した
    • pfb_dnsbl サービスが起動しない、または [ Missing CRON task ] が表示される場合は、/var/run/booting の空ファイル削除を試すよう案内している
  • pfSenseルーターの3つのGigabitポートは、VLANの代わりに物理的なLAN分離に使った
    • AlexaやApple TVのようなphoning-homeデバイスを別のハードウェアLANに置いた
    • 重要なLANとスマート機器・Wi‑Fi機器を分離し、銀行、株取引、crypto wallet利用デバイスを保護しようとした
  • スマート機器用ネットワークは 172.31.1.0/24 に分離し、より信頼するLANは 192.168/16 に維持した
    • ネットワーク間にルートがなければ、誤設定されたiptablesルールの影響も一部減らせると考えた
    • 新しいネットワーク機器がアドレスを受け取れるよう、物理NICでDHCP resolverを有効にする必要があった
  • AC1200 Archer C5は、AP mode非対応、リモートアクセスの問題、古いstock firmware、Broadcom chipsetのOpenWRT/DD-WRT/Tomatoサポート不足のため廃棄した
  • その後、Nighthawk R7000をApple/Amazon/TV用APとTrusted Wireless Network用APとして使用した
    • Trusted Wireless Networkでは2.4GHzを切り、5GHzのみ使うことにした
    • 5GHzは壁やコンクリートにより遮られやすく、中距離のsnooping回避に有利だと見ていた

すべてのDNSをpfSenseへ強制

  • pfSense配下のすべてのクライアントがローカルのUnbound DNSサーバーを使うようにNATルールを追加した
    • アプリやホームアシスタントが独自のDNSサーバーやハードコードされたDNSサーバーで迂回できないようにするのが目的だった
    • pfBlockerNGがDNSリクエストに介入するには、DNS over TLSをブロックする必要がある
  • NATルールは当初WANを除く各インターフェースに作成していたが、Non_WAN ファイアウォールaliasで単純化した
    • IPv4とIPv6の両方で、ポート53のローカルDNSクエリをlocalhostへリダイレクトした
    • NAT reflectionは無効化し、外部インターネットからローカルDNSサーバーへアクセスできないようにした
  • Services › DNS Resolver › Display Custom Optionsに server: log-queries: yes を追加し、横取りしたDNSリクエストをログ記録した
  • DNSログには、WindowsがGoogle Tag Managerへアクセスしようとするリクエストが見え、そのリクエストは存在しないIP 10.10.10.1 へブラックホール処理された

VPNでYouTube広告ターゲティング回避を試行

  • YouTube広告は通常の動画と同じドメインから来るため、pfBlockerNGやPi-holeのようなドメインブロッカーで広告だけを選別するのは難しかった
    • googleadservices.com のブロックは、広告動画を見た後に広告をクリックして初めて意味があると考えた
    • ブラウザではuBlock OriginがJavaScriptに介入できるが、iPhoneのYouTubeアプリはjailbreakなしでは広告制限が難しいと見ていた
  • 広告を直接ブロックする代わりに、YouTube広告アルゴリズムがユーザーを魅力の低い広告対象と見なすようにする実験を進めた
    • Apple TVトラフィックをVPNへ送り、YouTube視聴者が少ない地域のVPN終端を使うアイデアだった
    • 目標は、ユーザーをイタリア在住の70歳男性のように見せることだった
  • pfSenseにWireGuardを構成し、NordLynx/WireGuard private keyをLinux VMから取得して設定した
    • トンネルアドレス入力時に 1.0.0.0 とsubnet mask 0 を入れると、UI結果が 0.0.0.0/0 と表示されるという
  • Apple TVの全トラフィックをVPNへ送ると、YouTubeはイタリア語表示になり広告数も減ったが、NetflixとAmazon Primeで問題が発生した
    • CSSやfontファイルがブロックされ、thumbnailが読み込まれないように見えた
    • NetflixとPrimeはVPNプロバイダーに対するgeofencingが厳しいと警告している
  • その後、YouTube関連のFQDNだけをVPNへルーティングしようと、www.youtube.comyoutube.comgooglevideo.comaccounts.google.comgoogleapis.comgstatic.com などを候補にした
    • 構成後、YouTubeはユーザーをミラノにいるものとして見なし、NetflixとPrime Videoはカナダにいるものとして見た
    • 広告はまれになり、表示される広告もイタリア語の広告だった

DNSレースコンディションとワイルドカードドメインの問題

  • 1日後、pfSenseのhostname aliasとクライアントDNSキャッシュが異なるYouTube IP集合を見るDNSレースコンディションが見つかった
    • pfSense hostname aliasのデフォルトのresolve intervalは300秒
    • YouTube DNS TTLは1,440秒と観測された
  • Alias DaemonがFQDNをresolveしたIPとApple TVが後で受け取ったIPが一致しない場合、トラフィックがVPNトンネルを通らない可能性がある
    • 緩和策は、pfSenseが対象TTLを無視してalias entryをより長くキャッシュするようにすること
  • googlevideo.comの派生subdomainではワイルドカードルーティングが必要だったが、NATとfirewall ruleはIPベースで動作し、wildcard hostnameを直接扱えなかった
  • Unbound Python moduleとpfSense REST APIを使ってDNS応答IPをキャプチャし、VPN_wildcards aliasに動的に追加するPoCを書いた
    • VPN_wildcards TTLは1時間、capacityは500に設定
    • Aレコードはipaddress.IPv4Address、AAAAレコードはipaddress.IPv6Addressとしてパースする
  • 朝に確認したところ、Unbound DNS Resolverはsegfault状態で、IPを追加するたびにpfSense rule reloadが必要だったため、pfSenseは非常に遅くなった

SquidとMITMProxyでHTTPSを復号

  • 新たな目標はSquidに類似したプロキシを導入し、偽装されているが信頼されたCA certificateをデバイスに追加して、TLSトラフィックの復号を行うことだった
  • SquidはpfSense packageとしてインストールし、SSL Filteringのsmoke testまでは成功したが断念した
    • 性能が非常に遅かった
    • ACL設定が煩雑だった
    • https://http/*に関するissueがあった
    • SquidGuard URL filter list updateに非常に長い時間がかかった
    • Squid UIは不十分だと感じた
  • その後MITMProxyを選択した
    • Python scriptingとUIがあり、YouTube広告ブロック向けに拡張できると考えた
    • mitmproxy 7.0.4 Linux tarballは、FreeBSDではELF interpreter /lib64/ld-linux-x86-64.so.2 not foundとライブラリ不足のため実行できなかった
  • pfSenseのFreeBSD jail環境にpkg install mitmproxyでインストールした
    • インストール対象packageは50個、追加容量は206MiB、ダウンロード容量は33MiBだった
    • jail内でmitmproxyを実行するとUIが開く
  • MITMProxy実験のため、pfSenseで127.0.1.1 virtual IPをlocalhostに割り当て、NAT ruleで[Private IPs]:8080127.0.1.1:8080へ転送した
    • 検証用ノートPCのプロキシを192.168.20.1:8080に設定すると、ブラウザ要求がMITMProxy UIログに表示された
  • MITMProxyのCA PEMファイルは~/.mitmproxy/mitmproxy-ca-cert.pem
    • Python 3 Webサーバーでcert.pemを配信した
    • MITMProxyはmitm.itでも同じCA証明書を配信する
    • クリーンなノートPCとiPhoneに証明書を追加した

MITMProxy運用とcertificate pinningへの対応

  • ルーター上のmitmproxyはアイドル時でもCPU使用率が高く、リクエストごとにTLS証明書をその場で生成する処理と、UIの過剰なロギングが原因だと見られた
  • mitmdumpはUIと過剰なロギングを省くため、CPU負荷が低いと考えた
    • 実行時には--anticomp--mode regular--listen-port 8080--listen-host 127.0.1.1などを使用した
  • Certificate Pinningは、サーバーまたはクライアントが期待する証明書fingerprintを事前に知っているため、MITMProxyによる証明書偽装が通用しない手法
    • 回避策として--ignore-hostsを使い、apple.com:443icloud.com:443のようなホストがプロキシをバイパスするようにした
  • Transparent Proxy Modeで--allowed-hostsがSNIを基準により適切に動作するよう、MITMProxy 7.0.4のnext_layer.pyをパッチした
    • 従来は多くの場合、サーバーIPだけがマッチングに使われていると見ていた
    • パッチではserver.address[0]だけでなくserver.sniもhostname候補に追加した
  • パッチ後は、一部のホストを安定してインターセプトし、残りは通過させられるようになったという

Web版YouTubeのJSON広告除去

  • MITMProxyのsmoke testでは、YouTubeの広告・追跡URLを小さなスクリプトでブロックした
    • youtube.comでは/pagead//log_event?/stats/ads/stats/qoe?/ptracking?/generate_204el=adunitadformat=/activeview?などをブロック
    • google.comgoogle.caでは/pagead/をブロック
  • 初期テストでは、ブロック対象リクエストはDevToolsのNetworkパネルでも実際にブロックされていた
    • (failed)項目はスクリプトで発生する
    • 502失敗はpfBlockerNGがリクエストをblack-hole処理した結果だと見ていた
    • HTTP/2は、同じチャネルの後続リクエストが通らないよう無効化した
  • 単純なURLブロックだけでは広告は完全には消えず、YouTubeのHTML・JavaScriptとuBlock Origin filterを見ながら、広告情報がJSON応答本文に入っている可能性を追跡した
  • MITMProxyがキャプチャしたJSON応答では、playerAdsplaybackTracking構造に広告・追跡情報が確認された
    • playerAdsにはplayerLegacyDesktopWatchAdsRendererplayerAdParamsgutParams.tagshowCompanionshowInstreamuseGutなどが含まれる
    • playbackTrackingにはvideostatsPlaybackUrlptrackingUrlqoeUrlatrUrlなどが含まれる
    • youtubeRemarketingUrlwww.youtube.com/pagead/viewthroughconversion/...形式だった
  • Web版YouTubeでは、JSON payloadから広告情報を削除することで、ルーター経由でWeb広告をなくせた

iOS YouTubeとProtobufの分析

  • iOS版YouTubeアプリは、Web版に近いAPI呼び出しでJSONの代わりに**Protocol Buffer(Protobuf)**形式のデータを使用している
    • Protobufではキーが数値で変わり得るため、JSONPath方式で広告セクションを見つけるのが難しい
    • payload内で「Telus」「Samsung TV」「Boxing Week」「Buy now」のような広告文字列を確認した
  • iOSのYouTubeネットワークトラフィックはWebトラフィックと異なっていた
    • Web版では動画チャンクのrangeclenパラメータから広告候補をある程度推定できる
    • iOSプロトコルはrangeクエリパラメータやRangeヘッダーを使わず、&nr=2&nr=3のようなカウンターを使う
  • Protobufレスポンスをデコードしてオフライン分析する中で、has_unlimited_entitlement: Falsehas_premium_lite_entitlement: Falseのような項目を発見した
    • この値を変える方法は「チート」のように感じられたため、再びヒューリスティックなアプローチに戻った
  • 広告URLのブロック実験は、iOSアプリで無限ループ、UIエラー、クラッシュを引き起こした
    • 空のbodyの200404503、切り詰めたresponse body、ad videoの一部null化は、アプリを遅くしたり壊れた状態でクラッシュさせたりした
    • /error_204/ error-reporting endpointは「dev assertion failed」を示しており、これをブロックした
  • 広告は特定のvideo内のslotに登録されると見られる
    • slotの種類としてpre-roll、mid-roll、end-roll、full-page、ad podがある
    • ad URLだけをブロックすると、「存在しない広告がslotを予約した」といった種類のエラーが発生し、UIがパニック状態に陥る

Protobufの性能問題とblackboxprotobuf

  • Pythonだけで約500KiBのraw Protobufを人間が読めるテキストにデコードするのは非常に遅かった
    • i7-6700 CPUのデスクトップでは約2.06〜2.11秒だった
    • pfSenseルーターでは約22.8〜24.2秒だった
  • C++のprotoc --decode_rawははるかに高速だった
    • i7-6700 CPUのデスクトップでは約0.017〜0.022秒だった
    • pfSenseルーターでは約0.12〜0.14秒程度だった
  • Pythonではraw decodingがサポートされていないため、C++のprotocバイナリとsubprocess.Popenで直接通信する方式を選んだ
  • Burp Suite向けのblackboxprotobufは、raw Protobuf wire messageをdecodeし、値を注入してからre-encodeできる
    • PyPI forkではなく、元のBurp Suite版を使うよう勧めている
    • 一部のforkはdeep recursionによってstack overflowを引き起こす可能性がある
    • os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp"protobufのimport前に設定すると、可能な場合はC++のlibprotobuf.so実装を使う
  • blackboxprotobufprotobuf_to_json(data)はbest-guessの.proto schemaを作れるが、結果は非常に大きく深くネストされており、完全ではない
    • Pythonのschema dumpは約250,000文字以上だった
    • 広告の詳細情報を取り出すには十分だと見ている

1バイト変更で広告セクションを無効化

  • Protobuf Wire Formatは、元のschemaなしでdecode/edit/re-encodeするとencodingが変わることがある
    • ZigZag encodingの使用有無、数値型の判別不能、object field順序の非決定性が原因として挙げられる
  • 解決策は、Protobufのbackward compatibilityを利用して広告sectionを未知のfieldのように見せることだった
    • old softwareが新しいfieldを読むとき、未知のfieldを無視する動作を利用する
    • 重要な箇所の1バイトを変えて、深くネストされたsectionがfuture schema versionに属するように見せると、Protobufはそれを無視する
  • target field key 49399797は単純な文字列検索ではなく、varint tag scanningで見つける必要があった
    • wire typeは2で、length-delimited nested string/messageを意味する
    • target tagはAA FF B8 BC 01と計算される
    • wire typeの3ビットをシフトアウトするとfield key 49399797が復元される
  • 実際の探索は、raw Protobuf bytes内で/pagead/のような広告URL signatureを先に探し、その近辺で後方に移動してtarget field tagとfield keyを見つける方式である
    • 例のintercept対象はPOST youtubei.googleapis.com:443/youtubei/v1/browse?key=...
    • レスポンスは200application/x-protobuf1.87mだった
    • 例のログでは49399797はposition 446550195462はposition 4477で見つかった
  • O(n)のsmoke testでは、1.8MiBのProtobuf dataを追加メモリなしで1回スキャンするだけで広告除去が動作した
    • targetは30,593バイト目で見つかった
    • 約600バイトのbacktrackingで改変すべきfield keyを見つけた
    • もはや*.googleadservices.com/pagead/を含むURLをブロックする必要はなく、そのリクエスト自体がそもそも生成されない

MITMProxyアドオンの構造

  • PoCスクリプトはyoutube.pyとして保存し、mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py"で実行する
    • FreeBSDの前提条件としてpkg install protobufpkg install py38-pippip install jsonpath-ngを提示している
  • スクリプトはLoggertruncKilledErrorJSONPathReplacementProtobufDebugParserYouTubeAdBlockerクラスで構成される
  • YouTubeAdBlockerのintercept対象host regexは\.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.comである
    • Protobuf広告URLのsearch stringはb"/pagead/"
    • search limitは80_000
    • target field tagは50195462である
  • リクエスト遮断ruleはhostごとのpartial URL文字列を確認してflowをkillする
    • youtube.com対象にはpagead/log_event?stats/adsstats/qoe?ptracking?generate_204error_204adformat=activeview?_ad_ai?sw.jsなどが含まれる
    • sw.jsにはservice workerを拒否するというコメントがある
  • JSONレスポンスでは複数のJSONPath replacementを適用する
    • $.responseContext.serviceTrackingParams[*].params[?(@.key == 'yt_ad')].value"0"に変更する
    • $..adPlacements[]に変更する
    • $..adPlacementRenderer$..adPlacementConfig$..playerAdParams$..gutParams{}に変更する
    • $..adVideoIdは空文字列に変更する
    • $..showCompanion$..showInstream$..useGutFalseに変更する
  • Protobufレスポンスではcontent typeにprotobufが入っているとき、bodyをbytearrayにし、/pagead/を先頭80,000バイト以内で探す
    • TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED)でtarget tag bytesを作り、target_field_tag - 1の新しいbytesを作る
    • /pagead/位置の直前まで逆方向検索してtarget tag bytesを探す
    • 見つかったら該当byteを新しいbytesで上書きし、flow.response.set_content(bytes(body))でレスポンス本文を置き換える
  • コメントによると、このPoCは広告の90%をブロックするとされている
    • 別のsectionにも別のfield keyがあり、改変すべきad sectionが複数ある可能性がある

適用範囲と限界

  • この手法は、AppleデバイスのYouTube広告やInstagram、WhatsApp、Facebookのようなtracker trafficをブロックするための高度に特化した技法として整理されている
  • HTTPS trafficをdecrypt/re-encryptするCPU要求は、Raspberry Piで処理できる水準を大きく超えると見ている
  • Apple TVをjailbreakした後にpfSenseのroot certificateを追加すれば、pfSense gatewayがApple TVトラフィックを復号し、request headerの広告hostnameを検査してブロックできると見ている
    • ただしiPhone広告には依然として適用できず、iPhoneのjailbreakはさらに難しく、banking appがこれを検知して動作しなくなる可能性がある
    • jailbreak自体があまりに極端だと判断している
  • fake trusted CAが可能であれば、TLS packetを平文に復号してURL blockingを適用できる
    • 例として、YouTubeの/pagead/viewthroughconversion/...および/pagead/conversion/...パスを遮断URLに挙げている
  • 最終的には、ハードウェアルーターを一から設定し、LANを信頼ゾーンと非信頼ゾーンに分け、DNS広告ブロックと透過的MITMプロキシを追加したうえで、ネットワークに接続されたAppleデバイス上のYouTube広告を高い性能でブロックしている

YouTube Premiumとクリエイター支援

  • 数か月にわたってYouTube広告をブロックした後、コンテンツ制作者を支援したいと考え、YouTube Premiumの支払いを始めた
    • 「できるからといって、やるべきとは限らない」という留保を付けている
  • YouTube Premiumの価格は、CAD $9.99/moから$11.99/mo、税込みで約$13.43/moと述べられている
  • 広告視聴実験では、クリーンなノートPCとプライベートブラウジングで1日のあいだ断続的にYouTubeを視聴した
    • 記録上、視聴した動画は10本だった
    • 広告は8本表示され、そのうちスキップ可能な広告は2本しかなかった
  • CPVをUSD $0.15と仮定すると、1日の広告コストは$1.20、1か月に外挿すると約USD $36/moになる
  • Statistaの数値を使った別の計算では、2019年に米国の広告主がYouTubeに$15.1 billionを支出し、米国居住者が916 billion本の動画を視聴したため、平均視聴あたりUSD $0.0165になる
    • この計算を適用すると、1日のコストは約USD $0.13、1か月に外挿すると約USD $3.96である
    • この値はPremiumのUSD $10前後には近くないと見ている
  • DMCAクレームが付くと、広告収益は制作者ではなくSonyやViacomのようなクレーム申立人に入る可能性がある
    • このため、好きなチャンネルに自分が気づかないうちに何も渡せていない可能性がある
    • 多くの制作者がPatreonへ移るのも不思議ではないと評価している

2件のコメント

 
xguru 2023-08-29

元記事はかなり長いのですが、手順は興味深いものの、結局のところ筆者自身も最終的には YouTube Premium を契約して使っている、というのがポイントです。

 
GN⁺ 2023-08-28
Hacker Newsのコメント
  • 全体としては見事なハックだが、Protobufについてのいくつかの表現には違和感がある
    Protobufであるフィールドのタグを意図的に壊しているわけだが、認識できないタグ番号を無視するのは「欠陥」ではなく、拡張性のための中核的な設計である
    1.87MiBもそれほど大きいサイズではなく、こうしたメッセージが継続的にストリーミングされるわけでもないだろうから、性能上の障壁という説明にもあまり納得できない
    Protobufのエンコードはデコードコストを高くするために設計されたものではなく、むしろ効率よくデコードできるように設計されており、元の.protoスキーマがなくてもUnknownFieldSetで直接デコードできる
    よりよい方法は、削除したいフィールド1つだけを含む偽の.protoスキーマを使うことだったように思う。文字列スキャン方式は、同じバイト列がたまたま別のデータに現れる可能性があり、よりエラーを起こしやすい
    フィールド順序が変わると再エンコード後のバイト列が変わることはあり得るが、受信側は同じメッセージとして処理すべきであり、YouTubeアプリがフィールド順序の変更を検知する可能性は低そうだ
    以前Protobufに携わっていた立場から見ると、著者はこの部分を誤解しているように思える

    • よい分析だが、1.87MBが小さいという点には少し同意しにくい
      人生の大半を田舎で暮らしてきたので、自分のWi-Fiでなければこの程度でも実際には大きなダウンロードだ。モバイルでは回避策があるのかもしれないが、田舎のWi-FiはいまだにWeb 2.0の構造に苦戦しており、たいてい2〜4G相当の速度で使うことが多い
      都市圏のようにインフラを支える人口がいる場所では1.87MBは概して小さなファイルになったのだろうが、ケーブル回線につながった人たちが皆ストリーミングする午後6時ごろは例外かもしれない
    • without the C++ source proto filesについて少し宣伝すると、バイナリからソース.protoファイルを生成するprotodumpというプロジェクトを作った
      メッセージとフィールド定義を元の名前まで含めて再生成してくれるので、Apple TVボックスからバイナリだけ取り出せばよい
      https://github.com/arkadiyt/protodump
    • 「以前Protobufに携わっていた」というのは、ものすごく控えめな表現だ。知らない人のために言うと、KentonはProtobufを今日の形にした人物だ
      Protobufは自分にIDLを初めて触れさせてくれた技術で、当時は魔法のようなアイデアに見えた。自分でお粗末なIDLを作ったあとにProtobufを見つけたので、なおさら驚いた
    • この記事を読みながら同じように混乱した。Protobufの設計原則は秘密でも何でもなく、すべて明確に文書化されている
    • Protobufをスキーマなしでデコードするときに最も厄介なのは、埋め込みメッセージと文字列が同じタグ型を使う点だが、それでもかなり簡単に処理できる
      protoc全体の依存関係を取り込みたくないなら、数百行程度の簡単なProtobufデコーダを自分で書ける: https://github.com/kubernetes/test-infra/blob/master/guberna... https://github.com/kubernetes/test-infra/blob/master/guberna...
  • 20年以上前にThe Proxomitronを見つけて以来、広告を取り除き、ユーザーCSSのようなものでページを書き換えるために、トラフィックを中間者プロキシで処理してきた
    CloudFlareのようなところでは私を「ボット」と分類しがちだが、それも簡単ではないにせよ回避策はある。こうした事例は、リモート認証がユーザーの自由にとってなぜ危険なのかも示している

    • The Proxomitronを調べてみると、開発は2004年に終わったようだが、現在の状況をよく知る要約があれば興味深い
      いくつか「後継」プロジェクトがあるようだし、Windows専用なのかも気になる。リモートコンテンツにローカルリンクを挿入できるシンプルなプロキシを軽く探していた
    • Privoxyやpi-holeのようなネットワークレベルの広告ブロックには、インライン広告を処理できないなど欠点が多すぎる
      今はPi 4で動かしていたpi-holeも外してある。複数のサービスとうまく動作させようと何時間も費やしたが、結局あきらめたし、家庭用ネットワークにかける時間に見合わなかった
      実際にうまくいくのは、ブラウザベースの広告ブロッカーとReVancedのようなアプリパッチャーだ。貯蓄が増えるにつれ、YouTube Premium、Hulu、Netflix、Maxのようにその2つで解決できない場合は、単に広告なしサービスの料金を払う方向により傾くようになった
    • Cloudflareの社員がここをこっそり見ていると知られているので気になる。こういう形でボットに分類されるのは誤検知と見ているのか、それとも意図どおりの動作と見ているのか?
    • Proxomitronのことはほぼ20年間考えもしなかったが、今でも使っている人がいるのか気になる
      ここで言われている用途では使ったことはないが、会社のファイアウォールの背後でプロキシとして使うには素晴らしかった。昔はファイアウォールが外部接続にログイン情報を要求していたため、多くのプログラムがインターネットに接続できなかった
    • おそらくデバイスに新しいCA証明書をインストールする方式だと思うが、合っている?
  • Docker でパッケージ化された Privaxy が思い浮かぶ。UBlock Origin のブロックリストと互換性のある中間者プロキシ。
    スマート製品、とくにテレビで広告やトラッキングスクリプトがどれほど多いかを見ると驚かされる。これまでテストした限りでは不要なトラフィックが 40% を超えており、スマート TV アプリから広告を剥がす実験はかなり面白かった。
    https://github.com/deetungsten/webui-privaxyhttps://github.com/Barre/privaxy の Docker 化されたフォーク。

    • TV に 自己署名証明書を信頼させるにはどうすればいいのか?
    • ここでも見かけてうれしい。フィルタリストの ping 問題に触れてくれてよかった。
      Docker コンテナを本当に隔離できるよう、フロントエンドでハードコードされた 0.0.0.0 アドレスを使わないようにフォークを変更しようとしていたが、生活に割り込まれてしまった。Apple TV で試してみた?
    • Adguard も似たものに取り組んでいる。
      https://github.com/AdguardTeam/urlfilter
    • Docker 化されたフォークというのが、GUI が Web GUI に変わったという意味だと理解するのにかなり時間がかかった。
  • この記事は、よくある「ハッカーになるにはどう学べばいいのか?」という質問への優れた答えになっている。
    どんなエクスプロイトにも含まれる思考プロセスと粘り強い作業をよく示している。

  • 「WireGuard を使おう — Intel AES-NI 暗号化命令セットがある」という部分があるが、私の理解では WireGuard は AES を使わない
    全体的に、著者は TLS 暗号化の CPU 要件をやや過大評価しているか、現代のシングルボードコンピュータの性能を過小評価しているように見える。
    Raspberry Pi で HTTPS トラフィックを復号して再暗号化するための CPU 要件を大きく超えている、という説明にも首をかしげる。RPi 4 で TLS の中間者処理が実際に不可能だとしたらかなり驚くし、純粋なソフトウェア RSA を使う場合でも同じだ。
    いまだに使われている Android スマートフォンの中には RPi 4 より弱い CPU を持つものもあり、それらも TLS を使っている。

    • CPU 要件を過小評価しているように思う。
      弱い Android スマートフォンが TLS トラフィックを 50Mb/s しか処理できなくても、実使用では大きな問題ではないかもしれない。遅いスマートフォンはたいてい遅いネットワークにつながっているからだ。
      一方で、自宅にギガビットインターネットがあるのに、すべてのコンピュータとインターネットの間に置かれた非力な装置のせいで 50Mb/s のボトルネックが生じるなら大問題になる。
      TLS の CPU 要件は目標帯域幅に非常に大きく左右される。より高い帯域幅では、アクセラレータへオフロードすることが事実上必須になる場合もある。ハンドシェイクのコストも無視しにくく、秒間接続数を制限しうる。単一の機器では問題になることはまれだが、機器ネットワーク全体ではより大きくなりうる。
  • 素晴らしい記事。カスタム CA のインストールを許可しない機器を中間者処理する方法を期待していた。
    ローカル API を公開せず、クラウド経由でしかデータを見せない IoT 機器があり、機器とクラウドの間のトラフィックをキャプチャしたい。
    結局、フラッシュメモリをダンプして CA を差し替えたあと書き戻す以外に方法はないのだろうか?

    • 証明書を「ハードコード」しているなら、それは証明書ピンニングと呼ばれる。そうであれば、証明書を交換または削除し、同じ証明書を中間者プロキシへ移す必要がある。そうして初めてトラフィックを復号できる。
      ハードウェアやファームウェアをいじらずに IoT 機器を横取りする方法を試せる、よい記事がある。
      https://robertheaton.com/2019/11/21/how-to-man-in-the-middle...
    • カスタム CA のインストールを許可しない機器を中間者処理する方法を探すというのは、結局 TLS の目的に逆らうことだ。
      可能だとすれば、実装上の欠陥に依存しているということになる。
      正直なところ、今は独自の信頼済み証明書をインストールできるいくつかの機器も、今後はそう長くは続かないと思う。
  • YouTube であれどのプラットフォームであれ、広告をブロックする新しい方法は常に出てくるが、数か月後には変更されて役に立たなくなる。
    代わりに広告主を攻撃するのはどうだろう? YouTube/Google は「クリック」だけを追跡しているように見えるが、実際の購入まで追跡しているのだろうか?
    理論上、十分な数の偽ボットと実ユーザーが広告をクリックしつつ何も買わなければ、広告予算を燃やし尽くせる。時間が経てばマーケティング部門は、特定のプラットフォームでクリック数は過去最高なのに、クリックやインプレッションに対するコンバージョン率が非常に低いことに気づき、最終的にはそのプラットフォームから離れるかもしれない。

    • Nauseum が Chrome ストアでブロックされたのを見ると、かなり効果があるようだ。
  • 驚くべき記事。mitm パッチの段階が見えた瞬間、何か特別な記事だろうと思ったが、本当にそうだった。

  • 目次の「新しい目標:YouTube に、私がイタリア在住の 70 歳男性だと信じさせよう」が印象的だった。
    以前、どういうわけか広告ターゲティングが、私を恋人のために500ドルの洗えるシルクのパジャマを買おうとしている人だと信じ込ませたことがある。
    広告自体は最高だったが、インプレッションごとにいくら払っていたのか気になる。
    Apple TV に替えてからは、たいてい地域ターゲティングの外れたローカル広告が出る。平均的には、そのほうがましな気もする。

  • これは Protobuf の「欠陥」ではありません。バイトを変更したときに別の位置のフィールドとしてデコードされるのは、意図どおりの動作です。
    Protobuf はそもそもフィールド番号と長さプレフィックスに基づくプロトコルであり、転送中にバイトが変わらないという合理的な前提を置き、完全性は読み取る側に委ねています。
    仮に欠陥だとしても、それは Protobuf ではなく iOS 向け YouTube アプリの欠陥でしょうし、実際には欠陥でもないので「エクスプロイト」と呼ぶのも難しいです。YouTube iOS アプリの Protobuf のやり取りで、返されるペイロードのハッシュを検証していない点を指しているなら別ですが。
    この記事の後では、おそらく検証するようになると思います。

    • 著者の表現が少しおかしいです。「欠陥」はタイトルにしか出てこず、本文は単に形式がどのように動作するかを説明しているだけです。
      欠陥ではなく、設計どおりに動作しているのです。
      「Google が C++ のソース proto ファイルなしでは、デコード、修正、再エンコードを計算量的に高コストにしている」という部分もおかしいです。最適化されていない Python コードでやれば高コストでしょうが、C や他のコンパイル言語で書けば、proto ソースファイルの有無にかかわらず 1.8MB の Protobuf をスキャンするのは些細なことです。
      Protobuf ファイルをソースなしでデコードしにくくすることが設計目標だったとは思えません。もしそれが目標だったのなら、かなりお粗末に達成したことになります。
    • Protobuf で必須フィールドがどのように動作するのかは分かりませんが、攻撃を緩和するには、Google の YouTube クライアントがそのフィールドを必須フィールドとして扱い、フィールドが存在しないかデフォルト値ならサービスを拒否するようにできます。
    • 記事の日付は 2022年1月です。ブログ記事の後にプロトコルを強化したかったのなら、すでにそうしている可能性が高いです。