概要
- AndroidのAlways-on VPNと「Block connections without VPN」設定の信頼性に関する脆弱性報告
- 標準API経由でVPN外ネットワークへパケットが漏れる問題
- 3つの端末・複数OEMでの動作検証による再現性の確認
- 脆弱性はAndroid 12以降の大多数の端末に影響
- 攻撃者は通常アプリ権限のみで情報漏洩が可能
Android Always-on VPNとNAT-T Keepaliveによるロックダウン回避脆弱性
- Always-on VPN および「 Block connections without VPN」設定は、対象アプリのトラフィックがVPN経路以外で外部に漏れないというユーザーへの期待を形成
- 通常アプリが NAT-T socket-keepalive API を利用することで、 UDP/4500パケット がVPN経路を経由せず物理ルーターに到達する現象
- Pixel 8 Pro (Android 16)、 Samsung SM-F966B、 Nothing A059 など複数端末で実際にパケット漏洩を確認
- Wi-Fiスロット 経由で物理IPv4ゲートウェイへのアクティブなコールバックを観測
- VPN Leak Guard による24時間以上の継続的なリークも記録
技術的背景と脆弱性の原因
- IpSecManager.UdpEncapsulationSocket と ConnectivityManager.createSocketKeepalive(...) の組み合わせにより、NAT-Tマッピング維持を実現
- 内部的には startNattKeepaliveWithFd(...) 経由でリソースIDとfdを受け渡し
- VPNロックダウン状態 でも、呼び出し元UIDのVPNポリシーを検証せずにWi-Fiオフロード経路へパケットを送信
- パケット形状は固定 だが、宛先アドレスは攻撃者がAPI経由で指定可能
- これにより、 実IPアドレス とタイミング情報が外部に漏洩
影響範囲と攻撃モデル
- Android 12以降 の91%以上の端末が影響対象
- 攻撃者は 通常アプリ権限 (INTERNET, ACCESS_NETWORK_STATE)のみで攻撃可能
- ルート権限や特権パーミッション(PACKET_KEEPALIVE_OFFLOAD)は不要
- ユーザーが「Always-on VPN」+「Block connections without VPN」を有効にしていても情報漏洩が発生
脆弱性の再現手順
- IpSecManager.openUdpEncapsulationSocket() でUDPエンカプソレーションソケットを生成
- ConnectivityManager.createSocketKeepalive(...) でKeepalive要求を発行
- 10秒間隔などで UDP/4500パケット が物理ネットワークから送信される
- 宛先IPアドレス は攻撃者が自由に指定可能
セキュリティ上の問題点
- VPNロックダウン の本来の目的(トラフィックの隔離・秘匿)が破綻
- UIDごとのVPN適用状態 を確認せずにパケットオフロードを許可
- パブリックAPI 経由での攻撃が容易
- アプリのアンインストールや強制停止、ネットワーク切断、再起動など様々な状態でもパケット送信が継続
まとめと対策
- Androidの NAT-T Keepalive API 経由でのパケット漏洩は、 Always-on VPN +「 Block connections without VPN」設定でも完全には防げない
- VPNロックダウンの信頼性 を担保するには、APIレベルでの UID認証やVPNポリシー適用チェック の強化が必要
- 本脆弱性は Android 12以降のほぼ全端末 に影響するため、OSアップデートやパッチ適用が広範囲で求められる
参考: Mullvad公式ブログ記事