
2026/05/17 12:59
インターネット上で、誰でもあなたのドアベルを鳴らすことができます。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
概要:
テンムに Shenzhen Ruilang Technology が Naxclow バックエンドのブランドで 12 ドルという価格で販売しているスマートドアベル X3 は、ネットワークおよびデバイスの完全な侵害を可能にする致命的なセキュリティ脆弱性を有しています。CERT/CC との連携報告は 2026 年 5 月 6 日に提出されました。Naxclow 側による内部調査を行った後、研究者たちはこれらシステム的な問題は、デバイスに OTA アップデート用パーティションが存在せず、認証情報をローテートしないことに起因すると認めています。物理的には、ボード上の露出されたデバッグヘッダーはブート時において Wi-Fi キーを平文で漏洩させるとともに、ハードコード化された署名用塩と ID フォーマットにより攻撃者がコントロールプレーンのリクエストを偽造し、事実上 Entire fleet を列挙することを可能にします。最も重要なのは、恒久的なリレーパスワードがサーバーサイドでローテートせずに保存されている点であり、これにより静かな権限移転が可能となります。さらに、暗号化されていないピア対ピアメディアおよび漏洩した長寿命アカウントトークンは、ライブなりすまし攻撃を容易にします。これらの攻撃では、ハッカーが通話中に偽のビデオを配信し、所有者の携帯電話に鳴らすように強制することが可能となります。これらの脆弱性は、フィールドユニットへのパッチ適用や通知なしでの完全な権限侵害が可能となるため、プライバシーとホームセキュリティの根本的な崩壊をもたらします。
本文
アップデート情報 #2026-05-06
上記の調査結果について、CERT/CC の VINCE チームと連携ケースを立ち上げ、CVE の付与手続きを進めています。
2026-05-07
本投稿から一日後の 2026 年 5 月 7 日に、Naxclow から連絡を受け、報告内容を認識した旨の確認と、内部での調査プロセスを開始するとのお返事が来ました。
【公開日の翌日付】Naxclow からの返信(抜粋)
最近、人気を集める中国の市場「Temu」でスマートドアベルを購入しました。数年間にわたり世界的な注目を集めているこのプラットフォームが販売する廉価な接続型ハードウェアの実際のセキュリティレベルについて知りたいと思い、調査を行いました。
この機器は「Smart Doorbell X3」という名前で出荷され、「X Smart Home」というモバイルアプリ経由でペアリングを行います。機能としてはカメラ、マイク、双方向オーディオ、Sub-GHz 室内受信機が搭載されています。静かに多くの家の玄関に設置されているようなタイプの機器です。
数週末のうちに使用した一台での調査により、以下のことが可能であることを確認しました。
- 所有者のアカウントから、これらのドアベルを静かに窃取できること
- 双方向通話中にデバイスになりすまし、所有者のスマホ画面には攻撃者が選択した映像を表示し続けることができること
- ドアベル本体をネジで開封し、その裏に存在するデバッグポートを通じて家庭内の WiFi パスワードを抽出できること
正面から見る価格はわずか 12 ドルです。しかし裏側(本体内)にはネットワーク全体への侵害経路が存在します。最初の侵害手法は、プラットフォーム上の無料アカウントと攻撃者の電話が必要で、ドアベルからの実際の通話をすべて攻撃者の電話へ誘導し直すというもの。一方、二番目の侵害手法には何の条件も不要で、あらゆる映像を送信し続けることで所有者の電話に新規の通話を表示させることができます。どちらの場合でも、実際のドアベルはオンラインのままであり、そのことを知ることは一切ありません。つまり、インターネット上の誰でもあなたのドアベルを鳴らせるように 12 ドルを支払うことになっています。
この調査結果は、Temu の商品リストにある特定の台機器ではなく、バックエンドのプラットフォーム層に存在するものです。ドアベルは「Naxclow」というブランド名で運営されるバックエンドと通信しており、その元請けは広州を拠点とする「Guangzhou Qiangui IoT Technology Co., Ltd。」です。同ハードウェアは複数のリセラーブランドによるネーミング変更(シークレットリング)状態で出荷され、Naxclow 下でそれぞれ独立したサブドメインを持つ消費者向けアプリファミリーが展開されています。「V720」はその一例で(すでに公的にリバースエンジニアリングされているため参照:intx82/a9-v720)、同様のアプリ「ix cam」も発見されました。私はこれら個別にテストしていません。両者の Web フロントエンドは「X Smart Home」と同じ Vue スケルトンを使用しており、V720 とドアベル間のプロトコルオーバーラップについても既存の公開研究で網羅されています。共有された SPA コードベースおよびプロトコルの重複から、各ブランドのホストネームの下でバックエンドコードが同一のものであると推測されます。これは単一機器の問題ではなく、プラットフォーム全体の構造に関する話です。
このブログ記事は、センシティブな情報を一部取り除いた責任ある開示の一環です。Naxclow と連絡を取る情報は入手しにくいものでした。彼らのウェブサイトにはコンタクトページが存在せず、最終的にそのページの一つからメールアドレスを発見し、同ドメインで一般的なエイリアス名をブルートフォースすることで網を広げる作業を行いました。ほとんどはバウンド(受け付けられず)しました。2026 年 4 月 29 日、配信されたアドレス宛てに報告を送付すると同時に、X Smart Home アプリ内のフィードバックフォーム経由でも提出しました。現在執筆時点では返信はまだ来ていません。通知を受けた約一周後に本記事を公開しており、センシティブな詳細は一部伏せ字を付けています。
問題のリストは長くなるので、お好みのスナックや飲み物を手に取り、落ち着いて読んでください。
作用範囲と倫理
- 以下のテストはすべて私が所有するデバイス上で実施し、私の X Smart Home アカウント(2 つ使用)を行いました。トラフィックは Naxclow の本番バックエンドに触れていますが、これはあくまで自分の認証情報に基づくものです。他人のアカウント、デバイス、またはトラフィックに一切関与することはありませなかった。
- 実際のエンドポイントパス、正確なパラメータ名、ハッシュ計算用の固定 salts リテラル(完全な署名実装)、および動作する PoC コードはこの投稿からは除外します。ここではレシピを提供するのではなく、手法と失敗モードに焦点を当てています。
デバイスの構成要素
以下の 3 つの部品で構成されます:
- ドアベル本体:カメラ、押しボタン、マイク、スピーカー、WiFi、Sub-GHz 送信機
- Sub-GHz を受信し、ボタンが押された際にトーンを発する小型室内受信機
- リアルタイムビュー、双方向オーディオ、イベント履歴機能のためのモバイルコンパニオンアプリ
ドアベルと Sub-GHz 室内受信機を開封する前の状態。ラベルに書かれている情報はボックスの情報よりも興味深いです。ハードウェアの OEM メーカーは「Naxclow」や商品のリストに記載されている何らかの販売ブランドではなく、「Shenzhen Ruilang Technology Co., Ltd」です。FCC ID は 2A5LK-X3PRO で、2024 年 3 月に 2.4 GHz WiFi と 433.91 MHz Sub-GHz の認証を取得しました。Ruilang の受諾者コード「2A5LK」には少なくとも 7 つの WiFi カメラに関するファイルがあります。その一つに intx82 がリバースエンジニアリングした V720 ミニカメラとして知られる A9 モデルが含まれています。同じ工場が製造し、異なるモデルコードを持つ WiFi カメラの一隊を所有しており、すべて初期設定から Naxclow のバックエンドに向けられています。ラベルに記載されている EU および英国の輸入記録責任者は Whaleco で、これは Temu がそれぞれの管轄領域における企業部門です。
本体を開封するとチップ数が極めて少ないことがわかります。MCU 1 つでほぼ全ての処理を行います:Beken BK7252N という安価な WiFi オーディオ複合チップです。基板の前面に JTAG と UART ヘッダーが配置されています。UART の配線はデバッグヘッダーとして非常に工夫されており、TX および RX が小さなペアのパッドとして組み合わされ、GND は基板のエッジ側にあり、VCC パッドはありません。起動時の TX をプローブしたところ、シリアル通信のような電圧変動が見られ、その後は調査がスムーズに進みました。
UART セットアップは 115200 8N1 です。 Sub-GHz リンクは Princeton エンコーディングの 24 ビット OOK(オン・オフキース)信号で、433.91 MHz の周波数で使用され、同周波数の OOK を発する機器による再送も可能です。WiFi は RT-Thread 3.1.0 と Beken SDK を使用しています。これらはいずれも特殊な技術ではありませんが、すべて情報を容易に漏洩させる設計になっています。
ボタンを押すと同時に以下の 3 つの動作が発生します:
- Sub-GHz パルスが室内受信機へ送信され、室内でトーンが鳴ります。
- ドアベルは HTTP リクエストを Naxclow のバックエンドへ送り、所有者のスマホに通知がプッシュされます。
- 所有者が「受話」をタップすると、デバイスとアプリの間で双方向オーディオおよびビデオ用のピアツーピア(P2P)通話が確立されます。
Sub-GHz リンクは Flipper Zero などを使って屋外から再送可能です。一度ボタンを押したパケットをキャプチャし、それを別の端末から再送信すると、室内受信機が実際のボタンが押されたかのように鳴動しました。これ以上追う必要はありません:よくある話であり、私の狙いとは異なるためです。私は WiFi 側に焦点を当てて調査を行いました:ボタン押下信号がどのようにしてスマホに到達するか、そして通話がどのように開始されるかを検証しました。
Phase 1: 通信の監視(Reading the Wire)
- 実験室での中間者攻撃 (MITM) の長期セッションで Wireshark を使用し、ボタン押下から通話回答までの单一のキャプチャを行いました。
- 最初に通信路上を走るのはアラートです。ドアベルはプレーンな HTTP でバックエンドにアクセスし、通知をプッシュした後、通話レグが開始されます。
- リクエスト はマルチパートアップロード形式です。目立つフィールドとして:デバイスの ID、各リクエストごとに生成されるランダム文字列、そしてパラメータ変化した際に変わる固定長の 16 進数のような署名トークンがあります。さらに JPG イメージも添付されており、これは所有者が通知画面で確認するスナップショットです。
- レスポンス では興味深い事実が見られます。P2P 通話に必要なホストとデバイスごとの認証情報を含む
キーが含まれています。これらの認証情報は静的であり、デバイスの ID に紐付けられており、工場出荷時リセットや異なるアカウントへの再バインディング(再登録)後も維持されます。毎回同じ値が返ってきます。conf - キャプチャを比較すると、ランダム文字列とトークン値は各リクエストで異なります。これらはデバイス上で取得する事前呼び出しはなく、つまりローカルに生成されていることがわかります。トークンは暗号学的署名のような形状をしており、不規則に変化する長さの 16 進数です。ランダムフィールドも同様に変化しており、これはトークンの署名対象に追加のエントロピーを加えていることを示唆しています。
- リクエストを再送(リプレイ)しようとしたところ成功しました:バックエンドは新しいイベント ID を生成し、通知を送信しました。パラメータの基本要素(ランダムフィールドなど)を改ざんするとリクエストが失敗し、トークンがそれら全体をカバーしていることが確認できました。一方、JPG ボディだけを置換してもアラートは通り抜け、所有者のスマホには新しい画像が表示されます。これは、画像自体が署名の一部ではないことを意味しています。
- アラートの返信後、デバイスは
ブロック内のホストとポートに TCP 接続を開きます。ここから以降のプロトコルはバイナリ形式で、手製の STUN プロトコルに似ています。フレームングをリバースエンジニアリングする作業を行いました。各メッセージは固定のヘッダー(20 バイト)と JSON ボディから構成されています。ヘッダーには長さフィールド、タイプ識別子(データ送信かハートビートか)、セッション ID が含まれます。ボディにはコマンドコードとそのパラメータが含まれます。フレームングを理解した後、残りの通信プロセスは自然に理解できました。conf - デバイスとサーバー間の完全なシグナリングセッション:認証から通話設定、アプリによるステータス 0(終了)のハングアップまでの過程。カスタムバイナリヘッダーと JSON ボディを使用し、常にプレーンテキストの認証情報を含みます。
- 最初のメッセージは認証です。デバイスは自身の ID、
から得たリレーパスワード、同じレスポンスに渡されたドメインをサーバーへ送信します。リクエストに含まれるドメインから、バックエンドが単一ドメイン選択子で複数のデバイスファミリーをサポートしており、各デバイスごとにインスタンスを分割しているわけではないことがわかります。conf - 登録の ACK 後、接続はアイドル状態になります。アラートは既に送信されており、デバイスは所有者によるスマホへの回答を待っています。
- モバイルアプリは独自の接続でも同様の認証フローを実行します。アカウント ID と永続的なトークンを使ってリレーに登録し、ターゲットデバイスへの通話リクエストを送信します。これがデバイスの接続を再度アクティブにします。リレーは同じフレームング形式でメッセージをデバイスへ中継し、通話相手のクライアント(モバイルアプリ)のアカウント ID、マッチするアカウント側のトークン、クライアントの公開およびプライベート IP アドレス、またクライアントがプローブした NAT マッピングされたポートを渡します。これは典型的な STUN 风格的な再会合(rendezvous)で、リレーの役割は認証済み登録 2 つをピアツーピア通話へ橋渡しすることです。
- 数回の設定パラメータ交換の後、双方が UDP を介した NAT ホールパンチングに必要な情報を揃え、通話がそのチャンネル上でピアツーピアで始まります。
- ホールパンチ後の P2P レグ:両者のトークンを含むセッション設定、ステータス 200 の ACK その後、デバイスとアプリ間の生 JPEG フレームのストリーミング。
- P2P レグの最初のメッセージは小規模な JSON エンベロープで、
およびcliToken
を同時に含む:デバイスのトークンとアカウント側のトークンを一つのパケット内で。ピア側がステータス 200 で返信します。その時点以降のチャンネルでは主に生データ(バイナリ)のみ流れ:JFIF マジックを含む JPEG フレームと、同じストリーム内のオーディオサンプル。このレグは暗号化されていません。devToken - これはパッシブな調査でした:トラフィックを読み、プロトコルをマッピングしたものです。それ以上の操作を行うにはリクエストを改ざる必要があり、その署名機能はデバイス上にあるためです。つまり、ファームウェアのアクセスが必要となります。
- 最初のメッセージは認証です。デバイスは自身の ID、
Phase 2: UART を通じた情報の漏洩(Unasked Information)
調査ケースは既に開かれており、UART パッドも特定されています。容易なアクセスのために TX および RX のパッドに細いワイヤーを溶着しました。PCBite プロブの方がクリーンなオプションでしたが、手元にありませんでした。基板前面の TX および RX ピッチに小さいワイヤーを溶着し、それらを Flipper Zero(UART ブリッジモード)へ接続してホスト上のターミナルエミュレーターで使用しました。ドアベルはイベント間の深いスリープ状態にあります:ボタン押下で目覚め、フルアプリケーションループを実行した後、再びスリープに戻ります。以下の全情報は、この一つのサイクルからのものであり、ベルボタンを押した瞬間から始まります。
最初に通信路上に現れるのはブートバナーです。ファームウェアバージョン、未隠蔽のレジスタダンプ、RT-Thread の著作権表示、そして初期 OTA(Over-The-Air)イニシャライズの失敗を示します:
BK7252N_1.0.14 REG:cpsr spsr r13 r14 SVC:0x000000D3 0xA4AAB8CC 0x22CA0058 IRQ:0x000000D2 0x00000010 0x227AA88D 0x48C9A634 ... [I/FAL] Fal(V0.4.0)success [E/OTA] (rt_ota_init:105) Initialize failed! The download partition(download) not found. [E/OTA] (rt_ota_init:115) RT-Thread OTA package(V0.2.8-beken-1133282d-20220604) initialize failed(-2). go os_addr(0x10000).......... \ | / - RT - Thread Operating System / | \ 3.1.0 build Jun 30 2025 2006 - 2018 Copyright by rt-thread team
OS がブートを完了する前ですでに 3 つの発見事項があります。本番ファームウェアは実行ごとにデバッグモードのレジスタダンプを出力します。OTA モジュールはダウンロードパーティションが見つからないため初期化が失敗し、デバイスには OTA アップデート経路がありません。ビルドは RT-Thread 3.1.0(2025 年 6 月 30 日作成)で Beken SDK 3.0.76 を使用しています。
WiFi のアソシエーションが完了すると、デバイスは SSID、PSK、および四重ハンドシェイク中に導出するペアワイスキーとグループキーを出力します:
_wifi_easyjoin: ssid:<my SSID> bssid:00:00:00:00:00:00 key = <my PSK> ... WPA: TK <32 hex chars> ... WPA: GTK <32 hex chars>
このデバイスの UART アクセス権を持つ者が、家庭ネットワークの名前、パスワード、およびアクティブなセッションキーを持ち去ることができます。ドアベルの UART へのアクセスは物理的に容易です:デバイスはお家の前に掛かるように設計されているため、ドライバーと数分の静かな時間だけでアクセスできます。これにより、単一デバイスの侵害がネットワーク全体の侵害となります。
デバイスが IP を取得すると最初の HTTP コールを行います。これは Phase 1 で見たアラートリクエストであり、ファームウェアはレスポンスを直接出力します。
conf キーの値もここに表示され、ラベル付けが 2 つ間違っています:
[SOC_connectSockerDevice -237] Debug :CONNECT IP=<backend ip> PORT = 80 ... [cjson_api_for_device_config_pic_stun:1054] server port <port> server pwd <stun ip> server host <static device password> domain <redacted>.naxclow.com
「server pwd」行は実際にはリレーホストの IP です。「server host」行はデバイスの静的リレーパスワードです。値自体は正しく、ラベルだけが異なります。いずれにせよ、これらはすべて通信路上にあります。
その後 STUN プロトコルが立ち上がります。デバイスがリレーとの完全な TCP 交換を UART で JSON 形式(双方向)で中継します:
[start_stun_talk:78] CONNECT IP=<stun ip> PORT = <port> ... [rtc_serviceRegisDevice -267] Debug :reg: {"code": 100, "uid" : "1e2023XXXXXX", "token": "<static password>", "domain": "<redacted>.naxclow.com"} [rtc_serviceRegisDevice -283] Debug :reg: {"code":101,"status":200} [rtc_serviceRegisDevice -290] The server success to register the device
最初のフレームは Phase 1 で説明したものと同じです:デバイスは UID、静的リレーパスワード、ドメイン選択子を使用してリレーに認証します。ファームウェアは起動時の各ウェイクアップでこれを UART にそのまま出力しており、何の検索性もないままです。
登録 ACK の直後に通話レグが開始されます。同じコンソールでサーバーから返ってきたピア描像とホールパンチの結果が表示されます:
[rtc_rtthService:1018] recv = ({"code":11,"cliTarget":"<account id>","cliToken":"<account token>","cliIp":"<caller public ip>","cliPort":1948,... [obj_serverInforExchange -387] Debug : ({"code":12,"status":200,"devIp":"<our public ip>","devPort":1954,"devNatIp":"<our private ip>","devNatPort":10006,...}) [obj_prePeneTest -419] cliip(<caller public ip>) cliport(1948) natip(<caller private ip>) natport(58849) [obj_prePeneTest -419] pierce through success
アカウント ID、アカウント側のトークン、公開 IP、プライベート IP、NAT マッピングされたポートはすべて未隠蔽です。ドアベルは常にバインドされた所有者としか通信しないため、上記の値は所有者のものになります。UART に接続されたドアベルは、バインドされたアカウントだけでなくデバイス自身のアイデンティティも暴露します。
通話終了は同じチャンネル上に表示されます:
[rtc_rtthService:1018] recv = ({"code":53,"target":"<account id>","status":0})
上記のすべてはパッシブな観察から得られたものです。プロンプトへの入力は一切ありませんでした。デバイスはシリアルケーブルを搭載したヘッダーを持つ誰もがアクセスできることから、ファームウェアバージョン、OTA ステータス、家庭ネットワーク認証情報、そしてそのプロトコルを駆動するために必要なすべての認証情報をブロードキャストしています。
Phase 3: シェルからのファームウェア取得
Enter キーを押すとデバイスが
msh /> で応答しました。これは RT-Thread のシェルであり、完全にインタラクティブです。help コマンドでメニューのように長いリストを表示します:
msh />help RT-Thread shell commands: ... device_code - device_code write_device_code - write_device_code dont_sleep - dont_sleep pm_level - pm_level 1 ... printenv - Print all envrionment variables. setenv - Set an envrionment variable. saveenv - Save all envrionment variables to flash. ... ping - ping network host ls - List information about the FILEs. cat - Concatenate FILE(s) fal - FAL (Flash Abstraction Layer) operate. wifi - wifi command ...
いくつかは明らかな攻撃目標です。
device_code はデバイスアイデンティティのゲッターとして機能し、出力内容も寛大です:
msh />device_code [get_my_mac_devicecode:40] 1e2023XXXXXX 1 my device name 1e2023XXXXXX [get_my_mac_devicecode:40] 1e2023YYYYYY 1 my device mac 1e2023YYYYYY sercret <hardcoded salt> batch 1e2023 [Flash]EasyFlash V3.0.4 already initialize. doorbell m7 X9 dymic chip_five all local start wakeup:17 b 37 g 0 x 30326531 cam 808465202 [Flash]EasyFlash V3.0.4 already initialize. confirm status 1
ここで注意すべき点があります。"device name"は Phase 1 で通信路上で見た MAC スタイルの UID(
1e2023XXXXXX フォーマット)です。"device mac" は別のハードウェア MAC に似ており、同じ 1e2023 バッチプレフィックスを共有しています。そして "sercret" 行ですが、スペルミスのため「secret」とすべきところを「sercret」と誤記していますが、ファームウェアが秘密として扱っている文字列を与えます。はい、「sercret」です。追加の「r」付き。デバッグコマンドのラベルミスで、プロダクションハードウェアからプレーンテキストの認証情報を出力しています。何も注釈なしです。
その文字列をファームウェアはどのように処理するかはまだ不明ですが、ラベルされた秘密が暗号化せずにデバッグコマンドに出力されていることは重要な情報です。
help メニューで device_code の直上にありそうな write_device_code コマンドはまさにその名の通り:フラッシュ上のアイデンティティを書き換えるランタイム経路であり、認証は不要です。
シェルには EasyFlash 環境を読み取る
printenv、ファームウェアがマウントしたファイルシステムパーティションに対する cd, ls, cat、そして setenv / saveenv(フラッシュへの書き戻し)もあります。これらは直接のファームウェア抽出にはなりませんでした。それを行ったのは Flash アブストラクション層である fal コマンドでした。このコマンドには probe, read, write, erase のサブコマンドがあります。
fal probe に引数なしで実行するとパーティションテーブルが出力されます:
msh />fal probe [I/FAL] ==================== FAL partition table ==================== [I/FAL] | name | flash_dev | offset | length | [I/FAL] ------------------------------------------------------------- [I/FAL] | bootloader | beken_onchip_crc | 0x00000000 | 0x0000f000 | [I/FAL] | app | beken_onchip_crc | 0x00125000 | 0x00114000 | [I/FAL] | filesystem | beken_onchip | 0x001fd000 | 0x00002000 | [I/FAL] | param1 | beken_onchip | 0x001ff000 | 0x00001000 | [I/FAL] =============================================================
オンチップの 2MB フラッシュには 5 つのパーティションがあります:ブートローダー、アプリ、ファイルシステム、param1、netinfo。ダウンロードスロットは存在せず、これは
rt_ota_init が起動時に文句を言った理由です。チップの 2MB は頭から尾まで使われており、リフラッシュせずにダウンロードスロットを作る余地がありません。
パーティションを選択するには
fal probe <name> を使用します。その後 fal read <offset> <size> で選択されたパーティション内のバイト数をラベル付きヘキサデシマールビューとしてコンソールへダンプできます:
msh />fal probe app Probed a flash partition | app | flash_dev: beken_onchip_crc | offset: 65536 | len: 1130496 |. msh />fal read 0x.0 x.40 Read data success. Start from OxOxxxOOOO, size is 64. The data is: Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D OE OF [00000000] 0E 00 00 EA 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 ................ ...
UART 越しの限定された読み取り primitive で、興味深いものを保持する各パーティション(アプリコードが
0x10000(約 1.13MB)、ブートローダーが 0x00000(60KB)、その間のファイルシステム)を指し示します。これにより残りのダンプ作業は機械的に行えます。
私は Jupyter ノートブックで小さな Python ドライバを書きました。選択されたパーティションのアドレス範囲を 16 KB のチャンク単位で反復処理し、各チャンクに対して UART を通じて
fal read を呼び出し、ヘキサダンプを解析してバイナリファイルにバイト数を出力しました。これが比較的簡単な部分です。
問題はスリープ動作でした。アクティブに使用されていても、短いアイドル期間の後には深いスリープ状態に戻り、115200 バードの
fal read ではそれを追い越すのが遅すぎます。シェルは dont_sleep および pm_level というコマンドを提供していますが(どちらも適切な primitive のように聞こえますが)、試みたあらゆる組み合わせでデバイスを読み出し中のスリープ状態から覚醒させることができませんでした。そのため、ダンププログラム側でスリープを処理します:読み込み中に変にスリープに入ったことを検出し、一時停止し、物理ボタンの押下を促し、コマンドが戻ってくるのを待ち、再度パーティションプローブを行い、最後の成功したオフセットから再開します。
この投稿の「Show, Don't Tell(見せるだけで説明する)が始まる部分」です。午後 2 時でした。Jupyter ノートブックが回転し、机の上のドアベルがあり、私は定期的に指をリングボタンに押し付けてスリープウィンドウの間ファームウェアダンプを維持していました。部屋向こうから猫が私を見つけて、これが「仕事」だと言えるかどうかを尋ねているように見えました。数時間と数百回のボタン押し後には、私が気にするすべてのパーティションの完全なダンプができました。
私はチップを故障させる必要も、ボードから引き剥すこともありませんでした。デバイスが搭載したヘッダーを通じてファームウェアを渡してくれました。シェルに尋ねたからです。ダンプノートブックは私の所に残っています。
5 つのセル:接続と
msh /> プロンプトの待機、パーティションテーブルの読み取り、対象パーティションのプローブ、スリープ回復付きチャンクダンプの実行、そしてハッシュ計算と検証です。
検証セルではダンプされたファイルのハッシュを計算し、
fal probe で報告したバイト数と比較しました。両者が一致したので、ディスク上のバイト数はデバイスが送信したものです。
ブートローダーは無防備ではありません。ローダ内の文字列はブート時に実行されるイメージ検証論法を参照しており、「アプリ検証失敗!ファームウェアの回復が必要」といったメッセージが表示されます。つまりロード時にイメージをチェックしますが、これが暗号学的か CRC かは別途調査が必要です(まだ行っていません)。
ブートバナーでの OTA 失敗は、その周囲の文字列を読むとさらに興味深いものです。ランタイムエラーは
rt_ota_init: Initialize failed! The download partition(download) not found. で、バイナリ内の文字列から OTA モジュール自体が静的に存在しており、OTA Write: [%s] %d%% などのフォーマッターや Can not upgrade bootloader partition! のようなガードが存在することがわかります。コードは新しいイメージを書くことはできます。しかしこのデバイス上では「download」という名前のパーティションスロットが見つからないため、OTA パスは始まらず失敗します。
X Smart Home アプリがデバイスの「ファームウェアバージョン」として表示するのは
1e2023 です。これはデバイス自体から得られる MAC プレフィックスとバッチタグであり、実際のバージョン文字列ではありません。これにダウンロードパーティションの欠如を組み合わせて考えると、このプラットフォームには OTA パイプラインが存在しないのが最も合理的な解釈です。アプリ上の「最新ファームウェアを実行中」というメッセージは技術的には真です:フィールドにおける既存ハードウェアへのファームウェアアップデートは一切届きません。
その後に長い Ghidra セッションが残っていました。通信路上で見たバックエンドエンドポイントはすべてバイナリ内のプレーンな文字列として存在しましたが、それらを包囲するコードの解像には時間がかかりました。以下に示されるすべてのフェージングはこのセッションから生まれました。
Phase 4: 署名が機能していない瞬間(The Aha Moment Where the Signature Was Not a Signature)
全ての署名付きリクエストは同じルーチンを通ります。Phase 1 の
token パラメータを生成します。形状、定数、パラメータ名は汎用的です:
- リクエストに入力されるパラメータ(ローカルで生成されたランダム文字列を含む)を集める。
- さらに一項を追加:
、ここで S はファームウェアがsecret=<S>
シェル出力の「sercret」隣に出力するアルファベット数字文字列です。同モデルのすべてのデバイスでは同じ値となります。device_code - キー順に昇順でソートします。
- 連結して
の文字列にします。key=val&key=val&... - その文字列を SHA1 ハッシュ計算します。
- ダイジェストを固定長に短縮し、通信路上として
で送信します。秘密の項目はリクエスト自体には現れません。サーバー側でも同じ定数を含む baked-in 値を持ち、受信時に再追加して再計算します。token=<...>
いくつかの問題点があります。 「secret」というラベルは実際には秘密ではありません。最初に見たときは、デバイスの固有値だと考えましたが、デバイスごとのシェルコマンドの隣にあるラベル付きの秘密がそれを保持するのに適した場所だと考えられたからです。二番目のユニットのファームウェアにも全く同じ文字列がビルドされていました。すべてのファームウェアイメージに同様に含まれており、このモデルの各デバイス間で同一です。一台のデバイスを引き出してフラッシュをダンプし、一度値を取り戻せば、プラットフォーム上のあらゆるデバイスのリクエストを署名できるようになります。またはダンプ altogether を省略し、UART に丁寧に頼む:Phase 3 の
device_code シェルコマンドはすでに「sercret」隣にその値を表示します。サーバー側にはクライアントが持っていないデバイス固有の鍵は存在せず、プロトコルもデバイスが回答すべきチャレンジを持っていません。V720 sibling アプリとの通信路上のアイデンティティから、プラットフォームファミリー全体で同様の構造が使われていると強く示唆されます。
各署名付きリクエストに付随するランダムパラメータは非ス(nonce)のように見えますが、そうではありません。デバイスはローカルに生成します(大文字アルファベット 6 文字の文字列)し、ハッシュ計算対象のパラメータに入れます。サーバー側はこれを追跡しません。検証時、サーバーはクライアントが送ったパラメータをそのまま受け取り(クライアントが主張した
random の値も含め)、secret=<S> を再追加してハッシュ計算し、比較します。ハッシュが一致すれば承認します。サーバーは過去に見た値の記録を持たず、プロトコル全体でリプレイ保護機能が存在しません。ランダムな値は装飾的なものです。これを定数に固定するか、増分させたり、6 回すべて 'A' で埋め込んだりしても、サーバーはすべてのバリエーションを同等と扱います。
これらを組み合わせると:静的なサフィックスを持つ SHA1 は認証ではありません。サーバー側にはクライアントが持っていない鍵が存在しません。これは偽装されたインテグリティハッシングです。pcap を読むカジュアルな検査者を防ぐ程度のものです。
この関数を得た後、それを再現する小型の Python クライアントを構築しました。これを共有することはありません。塩分とアルゴリズムさえ分かれば、プラットフォーム上の任意の署名付きエンドポイントに対してリクエストを発行し、任意のパラメータを設定し、正しく計算された署名を追加すれば、サーバーは受け入れます。この時点から、今回の投稿内のすべての攻撃はこのクライアントを通じて実行されます。
これが最初の転換点です。これ以降、「ドアベル」と「私の Python クライアントを持っている人」はプラットフォームにとって置き換わ可能な存在になります。
Phase 5: トークンが回転しない(The Tokens Never Rotate)
Phase 1 では通話設定の 2 つの興味深いフィールド
devToken(デバイスのリレーパスワード)と cliToken(アカウントのリレーパスワード)を確認しました。両方は各通話で通信路上にプレーンテキストで渡されます。命名からはセッショントークンとして一時的なものと推測できましたが、それは違います。回転がないか確認しましたが、見つけたものはありません:
- 工場出荷時リセットでも
はバイト単位のまま保存されます。devToken - デバイスを別のアカウントに再バインディングしても、またも同じ値が維持されます。
- ファームウェアには新しいパスワードをバックエンドから取得するコード経路がありません。
トークンはサーバー側で保管され、デバイスの ID にキー付けされています。ファームウェアは各サイクルでアラートレスポンスの
conf ブロックを介してこのトークンをフェッチするため、工場出荷時リセットや再バインディングでは公式なコピーには到達しません。最初にサーバー上のレコードを作成するプロセスからは外部的に不透明ですが、それが一旦存在すれば、所有者が行う何らか的操作もそれを動かし出すことはありません。アカウント側のトークンも同様で:サインアップ時の一回での登録、その後ずっとアカウントに固定されます。
devToken だけでリレーとしてそのドアベルに登録できます。cliToken であればそのユーザーのアプリとして登録できます。どちらの値もセッションにバインドされず、リプレイ保護されておらず、短命ではありません。
cliToken の暴露ウィンドウは「所有者が通話に応答した」よりも広いです。モバイルアプリはプッシュ通知が届いた瞬間からリレーに登録します(所有者がタップして「回答」するタイミングではありません)。したがって、アプリが受取る各アラートでアカウント側のトークンは通信路上に現れ、受け取ったか無視したかに関わらず拾い上げられます。ターゲットの電話を鳴らせる人(Phase 7 で説明)は、それに伴いそのアプリの登録もコントロールできます。
Phase 9 では、通信路上にいなくてもデバイスのトークンを取得する方法が示されます。
Phase 6: デバイスは予測可能な識別子を持つ(Devices Have Predictable Identifiers)
デバイス ID は
1e2023XXXXXX という MAC 様の形式を持っています:
はローカル管理された MAC プレフィックスです。1e
はファームウェアにハードコーディングされたバッチタグです。2023- 末尾の 6 桁のヘキサ文字は増分カウンターです。
Phase 3 の
device_code シェル出力でバッチ 1e2023 が直接表示されるため、このファームウェアを実行するすべてのユニットは同じプレフィックスを広告します。枚挙に値するのは末尾の 6 つのヘキサ文字であり、カウンターは順次の順序を持っています。ID は私が観察できた限り、塩化やアカウントごとのスコーピングを行っていません。
また、新しい製造ランのために新しいデバイスコードを発行するためのバックエンドエンドポイントも存在します。バッチプレフィックスをパラメータとして受け取り、そのバッチの下で新しい順次 ID を返します。これは署名付き(Phase 4)で、署名パラメータの間にアカウント識別子がありますが、発行者そのものは新しい ID をそのアカウントにバインドしません。デバイスは呼び出し側の一覧に表示されるまでには、別々の「確認してバインドする」チェーンを実行する必要があります(Phase 8)。署名された POST リクエストを任意の名前付けされたプレフィックスで実行すると、そのバッチの次の利用可能な ID が返ります。それが返す数字は、私が一枚も枚挙を行っていない状態で、現在のハイ・ウォーターマーク(カウンター上限)です。
Phase 7: アラートはデバイス ID と何も要しない(Alerts Take a Device ID and Nothing Else)
「ドアベルが押された」というアラートを任意の所有者のスマホに送るには、単一の署名付き POST リクエストとターゲットのデバイス ID を持つだけです。エンドポイントは署名付きボディさえ届けば受け付け、リクエスト自体の認証は署名のみです(Phase 1 ではリクエストのリプレイ可能性と添付イメージが未変更で転送されることも示されました)。Phase 4 は署名を破り、Phase 6 は ID を破りました。これらを組み合わせると単一のプリミティブが生まれます:インターネット上の誰でも、プラットフォーム上の任意のドアベルを鳴らして所有者のスマホに通知を送ることができます(添付する任意の画像を使用)。
ここであなたが初めてこのprimitiveを自分の電話でテストした際の JPG ボディの中身を推測してみてください。あなたは正解しました。X Smart Home アプリはそれを「Smart Doorbell X3」の下でレンダリングし、元気に「着信中」というオーバーレイを表示しました。アプリの観点からは、これが玄関に来ている人でした。
エンドポイントはデバイスから何も要求しません。バックエンドには実際のドアベルと、有効な ID を持つ署名付き改ざんリクエストを区別する手段がありません。ファームウェアは複数のアラートエンドポイントを暴露しています。これらはスナップショットを運ぶ方法の違い(マルチパート JPEG、イメージなしのレガシー GET パス、ベース64 インライン)でしかありません。すべてが同じハッシュ構成を受け付けます。単一のエンドポイントをパッチしても、バグは別の場所へ移動するだけになります。
Phase 8: 静かな乗っ取り(Silent Takeover)
X Smart Home のオンボーディングフローでは順次に 2 つの HTTP エンドポイントを使用します。最初のエンドポイントはデバイスをバックエンド上で「バインド待ち」状態にします。2 つ目のエンドポイントがデバイスを特定のアカウントにバインドします。両方ともファームウェア内のプレーンな文字列リテラルです。両方とも Phase 4 のハードコーディングされた署名塩を使用します。バインドステップはアカウント ID を新しい所有者として受け取るため、攻撃者はプラットフォーム上の無料アカウントがあれば十分であり、それ以上のものは必要ありません。
オンボーディングフローでは、デバイスが工場出荷時リセットの直後にバインドを持っていない状態でこれらのエンドポイントにアクセスすると想定されています。しかしバックエンドはこの前提を強制しません。最初のエンドポイントへの署名付き POST は、プラットフォーム上の任意のデバイスのバインディング状態をリセットします。2 つ目のエンドポイントへの署名付き POST で攻撃者のアカウント ID を新しい所有者として指定すると、バインドが完了し、元の所有者は静かにドロップされます。
バインド後の挙動(2 つの自社のデバイスと 2 つのテストアカウントで実時間観測):
- バインドが完了した瞬間、デバイスは元の所有者のアプリから消えます。
- WiFi の接続は維持され、デバイス側での工場出荷時リセットも発生しません。
- デバイスは何か起きたことを示すシグナルを出しません。
- 所有者にとってはデバイスは正常に見えますが、アプリの一覧にはもう含まれていません。
チェーンは以下の通りです:
- フリート内のデバイス ID を枚挙するか、既知のバッチの下で新しいものを発行する(Phase 6)。
- 各 ID について、自分のアカウント ID を新しい所有者として「確認してからバインドする」シーケンスを発射する。
- 他人の家の玄関からすべてのドアベルイベント、すべての通話、すべてのスナップショットを受信する。
回復は悲惨です。所有者による復旧経路はハードウェアでの工場出荷時リセットと、アプリを通じた再オンボーディングです。これでデバイスは彼らのアカウントに再バインドされます。しかし、同じチェーンでデバイスが奪われた直後にまた攻撃者が「確認してバインドする」シーケンスを発射し、デバイスを再び取得できてしまいます。ハードウェアでの工場出荷時リセットでも、バックエンドが所有権を判定する方法は何も変わりません。
Phase 9: 私がドアベルになった瞬間(The Aha Moment Where I Became the Doorbell)
2 つ目の転換点です。Phase 8 では攻撃者がデバイスを自分のアカウントに横へ移動できます。Phase 9 では、自分が一切触れたことのないデバイスのアクティブな通話の中に座り込むことができます。
Phase 5 では通話設定トークンが回転しないことを示しましたが、これを通信路上から持ち上げるには経路内にいるかバックエンドログを持つ必要があります。ファームウェア自体はもっとクリーンな primitive を指し示しています。
Phase 1 ではアラートレスポンスの
conf ブロックにシグナリング設定(リレーホスト、ポート、デバイスの UID、永続的なリレーパスワード)が含まれていることが分かりました。この方法で認証情報を抜き取ることはできますが、アラートを送信すると所有者の電話が鳴るためノイズの多い経路です。プラットフォームはさらに別の署名済みエンドポイントを暴露しており、アラートを配達することなく同じ設定を返します。これはデバイス ID と署名付きトークンを必要とし、ホスト、ポート、UID、パスワードを返しますが、所有者のアップに一切影響を与えません。呼び出し側がデバイスそのものか、現在の所有者か、特定の人物であるかをチェックするものではありません。
つまり、改ざんされた署名付きリクエストとターゲットのデバイス ID があれば、プラットフォームからそのデバイスの永続的なリレーパスワードを直接受け取れます。単一の POST、単一のレスポンスです。所有者のアップは一切目覚めません。
パスワードが手に入ればなりすましは機械的です:
- 検索で返されたリレーに接続を開きます。
- プラットフォームの登録ヘッダーと登録 JSON を送信し、ターゲットのデバイス ID を UID に、漏洩したパスワードをトークンとして設定します。これ以後、リレーは私のソケットを実際のデバイスとして扱います。
- ターゲットアカウントに改ざんされた「ドアベルが押された」アラートを送信します。相手の電話が鳴ります。
- 所有者が「回答」をタップするのを待ちます。このタップはデバイス側に通話を確立しません。代わりにリレーにアプリが準備できたことを伝え、アプリの接続詳細を渡します。リレーはそれが登録されたデバイス(私)に対してそれらを渡します。実際のドアベルは一切ページされません。
- その後はクライアントが実際のデバイスがやることを行います:所有者のアップに対して UDP でダイヤルし、NAT 発見とホールパンチを行い、攻撃者が選択した MJPEG ビデオをストリーミングします。ピアツーピアレグは常にデバイスからアプリへであり、逆方向ではありません。
ステップ 3 は攻撃者にとって有益な副作用があります(Phase 5 で指摘)。所有者のアプリはプッシュ通知が到着する瞬間にリレーに登録しますが、「回答」をタップするタイミングではありません。所有者が無視したなりすまし通話で、電話が鳴るだけで回収されない状態でも、所有者の
cliToken は通信路上に乗ります。
同じ攻撃のハードウェア専用バージョンもあります。Phase 3 で UART シェルの
write_device_code コマンドを発見しました。これは認証を必要とせずにデバイスのオンフラッシュアイデンティティを書き換えます。私の持っているドアベルに UART アクセスがあれば、ターゲットのデバイス ID を自分のユニットに書き込み、ボタンを押すと標準ファームウェアが残りを処理します。それはターゲットとして署名付きアラートを送り、conf ブロックでターゲットのパスワードを受け取り、リレー上でターゲットとして登録されます。攻撃者は何も書き込みません。ファームウェアはすでにプロトコルを流暢に話すので、借り物のアイデンティティだけで動作します。プラットフォームの観点からは、私のハードウェアが彼らのドアベルになります。
所有者の電話では通常の玄関からの通常通話が表示されます。彼らが見る映像は私が送り続けるもので:静止画、録画クリップ、フレームループ、準備されたスプーフです。ビデオチャンネルはデバイスからアプリへの一方通行なので、電話が行うことは攻撃者が送るものを制限しません。オーディオレグは双方向で:所有者は自分の電話を通じて通常の通り話し聞き、攻撃者もなりすましクライアントを通じて話し聞きます。リレーは両方のエンドポイント間のオーディオを運搬し、攻撃者のクライアントを実際のドアベルとして扱います。実際のドアベルはこのすべてを見ません。電話やデバイスに、カメラが別の場所にあることを示すシグナルはありません。
これは Phase 8 と意味のある違いがあります。乗っ取りはデバイスを恒久的に再割り当てし、所有者は通知を受けなくなります。一方なりすましではデバイスはその場所に留まります。所有者は引き続き通知を受けます。ただし通話に応答したとき、実際には玄関のドアではありません。
動作しているなりすましクライアントを持っていますが、公開する予定はありません。
恥の壁(Wall of Shame)
チェーンを組み合わせた結果です。HTTP ライブラリを持っている攻撃者が実際にできることと、各プリミティブを起動するための前置条件は以下の通りです:
- TAKEOVER (乗っ取り): プラットフォーム上の任意のドアベルを静かに窃取します。プラットフォーム上のどのアカウントからも 2 つの署名付き POST を実行します。最初のポストはターゲットデバイスのバインディング状態をリセットし、2 つ目のポストは攻撃者に割り当てます。元の所有者のアップはバインドが完了した瞬間にデバイスをドロップします。デバイス自体はオンラインのままであり、何も起こったことを知りません。順次の ID と改ざん可能な署名を組み合わせて、これはフリート全体に影響します。
- WIFI THEFT (WiFi 窃取): デバッグポートを通じて家庭のネットワーク認証情報を引き上げます。ドアベルの起動時に UART アクセスから家庭の SSID、PSK、および WPA ペアワイスキーとグループキーがデバッグコンソールに出力されます。ドアベルは家の外に取り付けられており、要件はドライバーと数分の静かな時間だけで済みます。1 つのフットホールドで LAN 全体への入点が得られます。
- CALL HIJACKING (通話乗っ取り): 他の人の通話中にドアベルになりすまし、ライブで通話をします。単一の署名付きリクエストでターゲットデバイスの永続的なリレーパスワードを取得します。リレー上でそのドアベルとして登録し、改ざんされたアラートで相手の電話を鳴らし、攻撃者が選択した映像と双方向オーディオを持つことでデバイス代わりに通話に応答します。実際のドアベルはオンラインのままです。所有者は結局対向のカメラが別の場所にあることに気づきません。
発見結果(Findings, by severity)
一つの場所に集約された完全なリスト。重さは私の評価に基づくものです。
- [Critical] 「確認してからバインドする」チェーンによる静かな乗っ取り。プラットフォーム上の任意のアカウントから 2 つの署名付き POST:一つはバインディング状態をリセットし、二つ目はデバイスを攻撃者のアカウントに割り当てます。元の所有者のアップはバインドが完了した瞬間にデバイスをドロップします。デバイス自体はオンラインのまま、何の変化もありません。これに順次 ID と改ざん可能な署名を組み合わせて、これはフリート全体に影響します。
- [Critical] シグナリング設定エンドポイントを通じたワンショット認証情報引き上げ。ターゲットのデバイス ID を含む任意の署名付きリクエストで、レスポンスボディからそのデバイスの永続的なリレーパスワードを直接取得できます。攻撃者は通信路上にいなくてもバックエンドログを読まなくてもよさいます。これが Phase 9 が単一ステップ攻撃になっている理由です。
- [Critical] ライブデバイスなりすまし。漏洩したデバイスごとのリレーパスワードと改ざんされた署名付きアラートにより、リレー上で他人のドアベルとして登録し、相手の電話を鳴らし、攻撃者が選択した映像と双方向オーディオを持つことでデバイス代わりに通話に応答できます。元のデバイスはオンラインのまま、所有者は引き続き通知を受けます。ただし応答すると実際の玄関ではなく別の場所から来ることに気づきます。
- [Critical] デバイスごとの永続的パスワードが回転しない。パスワードはサーバー側に格納され、デバイスの ID にキー付けされています。工場出荷時リセット、異なるアカウントへの再バインディング、時間の経過を問わずバイト単位で同一です。一度攻撃者が引き上げれば(上記の単一署名付きリクエスト)、ハードウェアを捨てるだけでは不十分で、サーバーがパスワードを手渡すのを止めるまで有効です。再オンボーディングでデバイスを再バインドしても、攻撃者が持つ認証情報は引き続き機能します。
- [High] 通話設定中の永続的プレーンテキスト認証情報。通話設定プロトコルは各通話でピアとバックエンドの間にアカウント ID、公開およびプライベート IP、NAT マッピング、そして永続的なデバイスごとかつアカウントごとのリレーパスワードを暗号化せずに転送します。これらはセッショントークンではなく、回転しない長い生パスワードです。モバイルアプリはプッシュ通知到着時にリレーに登録するため、アカウント側のパスワードはアプリが受取る各アラートで通信路上に乗ります(拾うか無視するかに関わらず)。
- [High] 暗号化されていない P2P 通話メディア。ドアベルからアプリへのビデオストリームと双方向オーディオはピアツーピア経路でプレーンテキスト形式です。ピアのどちらかとの間、またはホールパンチ後の両者間にあるパス上の誰でも、ライブフィードを通信路上から引き上げることができます。
- [High] UART ログを通じて WiFi 認証情報の漏洩。WiFi アソシエーション中のファームウェア起動時、家庭ネットワークの SSID、PSK、および導出された WPA ペアワイスキーとグループキーがデバッグコンソールに出力されます。家の外に設置されているドアベルの UART アクセスはドライバーと数分の静かな時間で済みます。単一デバイスのフットホールドで LAN 全体の侵害となります。
- [High] 順次のデバイス識別子。12 桁のヘキサデシマルデバイス ID は
に増分カウンターが付いたものです。フリート全体を枚挙可能です。1e2023 - [High] 認証不要な新デバイスコード発行。既知のバッチプレフィックスとアカウント識別子を持つ署名付きリクエストで、そのバッチの下で新しい順次デバイス ID が返ります。 Mint(発行)自体は新規 ID を呼び出し側アカウントにバインドせず、別々の確認・バインディングチェーンが必要です。明らかな悪用のほかに、 namespace を任意で拡大するだけでなく、すべての Mint で誰かが要求した際、そのバッチカウンターのハイ・ウォーターマークが明らかになります。これは標的化された枚挙を容易にします。
- [High] 認証不要のアラート配信。アラートエンドポイントはデバイス ID、イメージ、署名付きトークンを必要とします。これらは入力全体であり、リクエスト自体は署名以外に何の認証も持っていないです。インターネット上の誰でもリクエストを署名できれば、攻撃者が提供した画像で任意の所有者の電話を鳴らせます。
- [Medium] ハードコーディングされた署名塩。制御平面のリクエスト上の「署名」はアルファベット順のパラメータと、ファームウェアにビルドされた固定文字列を結合して短縮 SHA1 で計算されます。一度取り戻せば、あらゆるデバイスのリクエストを改ざんできます。V720 sibling アプリとの通信路上のアイデンティティから、この構成はプラットフォームファミリー全体に跨る可能性が高いです。
- [Medium] 複数のアラートエンドポイント、同じ認証スキーム。マルチパート JPEG、イメージなしのレガシー GET 風、ベース64 インラインなどすべてが同じハッシュ形式を受け付けます。単一エンドポイントをパッチしても何の効果もありません。
- [Medium] TLS サーバー上でのプレーン HTTP 制御平面。すでに TLS を終結させる nginx が、同じホストネーム上で有効な証明書を持つ HTTPS ポート 443 で受け付け、同様の API エンドポイントを同一に提供します。ファームウェアは必ずプレーン HTTP を使用するよう設計されています。これはサーバーの制限ではなく設定上の選択です。
- [Medium] プロダクションハードウェアでのverbose UART デバッグシェル。搭載されたファームウェアはラベル付き UART ヘッダーでインタラクティブコンソールに落ち、ヘルプ、ステータス、メモリコマンドがシリアルアダプターを持つ誰でも利用可能です。このシェルは起動ごとにフル STUN プロトコル交換、デバイスの永続リレーパスワード、バインドされた所有者のアカウント ID をコンソールへ鏡映します。
- [Medium] デバッグコマンドによる任意のメモリ読み取り。高度なツールなしで全ファームウェアイメージをダンプするのに十分です。他のファームウェア側発見事項のブートストロープとなります。
- [Low] 搭載ファームウェアでの OTA の故障。このビルドでは OTA パーティションが存在せず、デバイスが空からのアップデートを引き上げることができません。ベンダーによる修復は将来のユニットで製造時のリフラッシュとして出荷する必要があり、既存のフィールドハードウェアへの経路はありません。
- [Low / informational] ブートローダにロード時のイメージ検証機能がある。ブートローダ内の文字列は「App verify failed!」のような検証論法を参照しています。これが暗号学的か CRC かは今回のパスで決定されていません。将来の研究者が修正ファームウェアを注入しようとする際に知っておく価値があります。
インパクト(Impact)
単一所有者にとっては、2 つの異なる意味で悪影響があります。乗っ取りによりインターネット上の誰でも静かにあなたのドアベルを自分のアカウントに移動させることができます。それ以降から、家の玄関からのすべてのイベントが相手の電話に通知されます。なりすましにより、あなたが応答した通話の途中で相手側に座り込み、カメラフィードを攻撃者が送るものに入れ替え、偽の訪問者として生会話を持続できます。一方はデバイスを盗む行為、他方は個別の会話を盗む行為です。両方ともデバイス上のインジケーターはありません:デバイスはライトアップし続け、アプリは「online」と表示し続け、あなたは発見する(静かになる=乗っ取り、あるいは玄関にいない=なりすまし)。
回復は見た目ほど容易ではありません。工場出荷時リセットでデバイスを元の所有者に再バインドできますが、デバイスごとのパスワードはサーバー側に格納され、デバイス ID にキー付けされており、リセットや再バインディングでも回転しません。一度攻撃者が引き上げれば、そのレコードがサーバーに残っている限りリレー上でそのデバイスとして登録できます。デバイスレベルでの決定論的な修復はハードウェアを廃棄することですが、それもサーバーがパスワードを手渡すのを止めるまでしか効果ありません。
攻撃者が物理的アクセスを持っている場合(家の外に設置されているハードウェアの要件は低くても)、影響は拡大します。UART アクセスで起動時に家庭の WiFi 認証情報をプレーンテキストから引き上げられ、ドアベルフットホールドが家庭 LAN への入り口になります。同じアクセスによりなりすまし攻撃のハードウェア専用バージョンも可能:
write_device_code シェルコマンドでオンフラッシュアイデンティティを書き換えることができ、標準ファームウェアがその後もプロトコルを処理します。
フリート全体にとってより悪影響です。ID は枚挙可能で発行でき、アラートパスは改ざん可能、デバイスごとの認証情報は署名付きリクエスト一つで手に入り、乗っ取りは任意の有料アカウントが操作できる 2 つのエンドポイントチェーンです。意欲的な攻撃者はアドレススペースをスキャンし、認証情報と家庭ネットワークマップをバッチで収穫するか、標的を一つずつ選ぶことができます。
ベンダー側の簡単なパッチ物語も存在しません。このファームウェアバージョンでは OTA パーティションが存在せず、Naxclow が明日修正ビルドを出しても、既にフィールドにあるデバイスへの経路はありません。
このチェーンには複数の独立したリンクがあります:ファームウェア、オンボーディングエンドポイント、認証情報引き上げエンドポイント、予測可能な ID システム。それぞれに独自のパッチが必要です。単一リンクを閉鎖しても攻撃は別の段へシフトします。
開示(Disclosure)
- 2026-04-01: 自分のデバイスでのテストを開始(隔離実験室、副業)。
- 2026-04-29: 開示報告を配信可能なメールアドレス(イントロに連絡探しの話あり)と X Smart Home アプリ内フィードバックフォームを通じて送信。返信なし。
- 2026-05-06: センシティブな詳細を控えた状態で公開。通知後一周目。
Naxclow は Guangzhou Qiangui IoT Technology Co., Ltd. が運営しているため、デバイスがどのブランドで出荷されていても、正しい組織に開示しました。
もし連絡があれば、このブログへの返信と修正内容はアップデートします。完全な発見結果(伏字部分含む)を正式な連携チャネルを通じて共有することに対して喜んで対応いたします。
教訓(Takeaways)
これらのデバイスを所有している場合
- IoT の VLAN 化:十分に強調すべきではありませんが、ドアベルを自 LAN 内の悪意あるホストとして扱い、重要なリソースに到達できないゲストセグメントに乗せてください。ベンダーがこの問題を修復しても(それが望むとしても)、すでに壁に貼り付けられているユニットへの道はありません(テストしたファームウェアでは OTA パスが壊れています)。
- 注目に値する実践的なシグナル:
- ドアベルが理由なく静かになる:これは確認すべきことです。
- 通話に応答して違和感が感じられる:その直感を信じてください。
- X Smart Home アプリがネットワーク障害とは別に頻繁にオフラインと表示する:同じ話です。
- 不安なら捨ててください。Temu のリストにある次の 12 ドルのドアベルはおそらく同じファームウェアとバックエンドを実行しています。
これらのデバイスを構築する場合
- 安価な MCU はアイデンティティの責任から免除しません。「各ファームウェアイメージにビルドされた文字列」から作られた「署名」は認証ではありません。
- 現在の所有権請求をチェックせずに新しいバインドを受け入れるオンボーディングエンドポイントは、フリート全体の乗っ取りプリミティブです。
- 通話設定でピア間に永続的認証情報を鏡映すると、通信路が認証情報ブロードキャストとなり、回転しないデバイスごとのパスワードはそれを恒久化します。
- もし nginx ですでに TLS を終結させているなら、ファームウェアをそれへ指し示してください。プラットフォームを所有している場合は、工場出荷時リセット後に認証情報を回転させてください。
この種の評価を行う場合
- 安価な MCU ベースのデバイスは Linux シェルが待機しておらず、シリコン上にも明らかなフットホールドがないため威圧的に見えますが、そうではありません。
- 開発に UART が容易なのは真で、生産板上には同じUARTがあり、十分我慢して2時のボタン押しさえあればメモリを読み取るデバッグコマンドはファームウェアをあなたに渡します。特別なツールを探す前に午後の時間を使ってシリアルアダプターと付き合いましょう。