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

NDIAS Automotive / IoT CTF 2026 Recap - Keyfob ...

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

NDIAS Automotive / IoT CTF 2026 Recap - Keyfob & OSINT

NDIAS Automotive / IoT CTF 2026の振り返り回の資料になります
https://cartagaitai.connpass.com/event/396283/

Avatar for tokina (himitu)

tokina (himitu)

July 03, 2026

More Decks by tokina (himitu)

Other Decks in Education

Transcript

  1. NDIAS Automotive / IoT CTF 2026 - RECAP Keyfob &

    OSINT 2026/07 1 Tokina (Himitu) @_tokina23
  2. 自己紹介 / Self-Introduction • Automotive Cybersecurity Consultant / 自動車に特化したサイバーセキュリティコンサルを提供 •

    得意領域 • 業界動向の調査や、法規・規格対応、リスクアセスメント等のコンサルティング • 最近は自動運転、 AIロボット関連の仕事をすることが多いです • CTF Experiences • DEF CON Car Hacking Village への参加(2024, 2025, 2026) • その他自動車関連のトピックがあればたまに参加する程度 • OSINT 関係のCTFが楽しいのでたまに参加しています • Maltego OSINT CTF, DIVER OSINT CTF, xINT CTF • その他 • 面白そうなガジェットがあれば入手して遊んでいます • 基本的に山に登っています • 山梨県に移住しています 2 最近は飯豊山(いいでさん )に登りました @_tokina23 Discord; tokina.himitu
  3. CTF Challenges 3 # Category Title Difficulty 配布ファイル 概要 1

    Keyfob Parking Lot Whisper Tutorial capture_c1c2.bin 与えられた信号ファイルの解読・理解 2 Read the simple fob Easy Keyfob 信号の確認と概要の理解 3 Next Counter Easy capture_c3.bin Keyfob 信号の概要の理解 4 Predict Next UNLOCK Easy capture_c4.bin Keyfob 信号の概要の理解、フレームの再計算 5 Classic KeeLoq Garage: Find the Key Medium capture_c5c6.bin memo_c5c6.txt シンプルな Keyfob 信号の理解と解析 6 Classic KeeLoq Garage: Next HOP Medium シンプルな Keyfob 信号の予測 7 Real KeeLoq Garage: Normal Learn Hard capture_c7c8.bin memo_c7c8.txt 実際のKeyfob 信号の解析 8 Real KeeLoq Garage: Next HOP Hard 実際のKeyfob 信号の予測 9 OSINT It’s not a car. Easy - 自動運転サービスに関する OSINT 10 AV Striking Medium - 自動運転サービスに関する OSINT 11 Unexpected Braking Hard - 自動運転サービスに関する OSINT 12 Joby First Flight Easy - 空飛ぶモビリティに関する OSINT 13 Joby 2025 Last Flight Medium - 空飛ぶモビリティに関する OSINT
  4. ツール: Universal Radio Hacker ( URH ) • Keyfob 問題を解くにあたって、基本的に

    Universal Radio Hacker を使う想定だった • https://github.com/jopohl/urh ※作問にはGNU Radio を用いましたが、問題を解くに当たっては不要 5
  5. Parking Lot Whisper 8 Tutorial • 本チャレンジでは、ファイルの開き方、シグナルの確 認方法と読み取りを検証してもらうことを目的とした • 配布ファイルは

    "capture_c1c2.bin" ですが、拡張子 を.ctfile などに変更することで URH で開けるようになる • その他、 inspectrum などのツールでも確認可能
  6. Parking Lot Whisper 9 Tutorial • ファイルを開くと以下が確認できる URH このような 3連バーストが

    Keyfob の特徴 Inspectrum でも同じもの が確認できる Inspectrum このような 3連バーストが Keyfob の特徴
  7. Parking Lot Whisper Tutorial • 拡大すると赤いラインが中心になっていることがわかる • 中央周波数 ... 与えられた情報(中央周波数)とそのまま一致しているため

    433.92MHz • Keyfob でよく使われる周波数 • 変調方式 ... ON/OFF で区別しているため、 OOK( ASKの一種) ※多少混同されることあり • シンボル周期は、「信号の最小時間単位」 • 問題文から、 "Sample Rate: 2,000,000 samples/sec " • これは 1秒間に2,000,000 回取得されているため、 逆に計算すると 1sample / 2,000,000sec • 1075 samples × 0.5 us ≒ 500us • Flag: flag{433920000_OOK_500} 10
  8. Read the simple fob 11 Easy • ここから実際に Decode した内容を問う問題としました

    • Manchester 符号化を理解したうえで、ヒントになって いる `0xD5` が出てくるかを確認する必要がある • Manchester 符号化は、単純に言うと `10` →1, `01` →0といったように 01の組み合わせをま とめて扱うことで復号する符号化方式 ※その逆パターンもあるため注意 シグナル 10011001100110011001100110011001101001100110011001101010100110010101 10010101011010100101101001010101100101010101010101010101011001011010 011010100110011010011001
  9. Read the simple fob Easy • Flag: flag{7A21CC} HEX, Manchester

    にすると いい感じに復号してくれる 3連バーストはいずれも同じフレーム Sync Preamble Sync から 3バイト 分がDevice ID 12
  10. Next Counter • バーストを除くと以下 • aaaad59c4e2b2001375a • aaaad59c4e2b2001385a • aaaad59c4e2b2001395a

    • aaaad59c4e2b20013a5a • aaaad59c4e2b20013b5a 14 Easy Sync Preamble Device ID Counter? AAAA D5 9C4E2B 2001375A 2001385A 2001395A 20013A5A 20013B5A 20013C5A AAAA D5 9C4E2B • Flag: flag{AAAAD59C4E2B 20013C 5A} どこまでが何かわからないもの の、カウントアップしている箇所 があることがわかる
  11. Predict Next UNLOCK 16 Easy • Decode されたフレーム • AAAA

    D5 B84F62 20 0204 C18B 5A • AAAA D5 B84F62 20 0205 D4C2 5A • AAAA D5 B84F62 20 0206 E7F9 5A • AAAA D5 B84F62 20 0207 FB30 5A Counter? Random? Trailer • カウントアップしている部分、ランダムに変化 している部分、固定部分を確認する • ランダム部分が 2バイトであり、カウンタ部分 も同様に 2バイトと想定する • 相関を分析すると、以下の法則がわかる • したがって、カウンター "0208 "、次の Random 部分の値はFB30 + 1337= 1 0E67 ※カウンタは 2バイト D4C2 - C18B = 0x1337 E7F9 - D4C2 = 0x1337 FB30 - E7F9 = 0x1337 • Flag: flag{AAAAD5B84F6220 0208 0E67 5A}
  12. Classic KeeLoq Garage: Find the Key • 問題文にある "KeeLoq" について理解を深めてもらう

    ことを目的としていた問題 • KeeLoq はKeyfob で使われるプロトコルの一つ • 1990 年代にガレージリモコン向けの方式として普及 し、 Microchip 社が複数の製品をリリースした • 現在は古典的な手法として扱われる 17 Medium <<NDIAS -GARAGE Install Note>> Model: KG -370 Remote SN: ??? Button map: 0x01 = OPEN Fixed part: SERIAL(32) || BTN(8) Legacy derive: K = SEED || (SEED XOR SERIAL) Seed: 6D3A91C4 Disc bits: lower 10 bits Learn window: 16 memo_c5c6.txt ※配布ファイル ←Flipper Zero でも解析 がサポートされている https://youtu.be/x4ml1JAH1q0?si=Feza2ZfHOufXAjet
  13. What is the KeeLoq ... • 例・参考 • https://ww1.microchip.com/downloads/en/AppNotes/00001683A.pdf •

    https://ww1.microchip.com/downloads/en/devicedoc/41378b.pdf • "KeeLoq Frame" などで調べると、以下のようなフレーム情報がわかる • 34bit のFixed Portion (固定部)、 32bit のEncrypted Portion (暗号部) 18
  14. What is the KeeLoq ... • X 19 DISCはDiscrimination Bits(識別用ビット)

    これは今回のチャレンジ用であり、チップ によって異なる
  15. Classic KeeLoq Garage: Find the Key 20 Medium • Decode

    されたフレーム • AAAA D5 913B00D7 01D4A2B7 01 • AAAA D5 0020D1FB 01D4A2B7 01 • AAAA D5 80ED145B 01D4A2B7 01 • AAAA D5 314C03E4 01D4A2B7 01 <<NDIAS -GARAGE Install Note>> Model: KG -370 Remote SN: ??? Button map: 0x01 = OPEN Fixed part: SERIAL(32) || BTN(8) Legacy derive: K = SEED || (SEED XOR SERIAL) Seed: 6D3A91C4 Disc bits: lower 10 bits Learn window: 16 固定部 BTN (ボタン ) 変化している =暗号部 これまで 同様 この情報を 主に使用 • Key = SEED || (SEED XOR SERIAL) = 0x6D3A91C4 || (0x6D3A91C4 XOR 0x01D4A2B7 ) = 0x6D3A91C46CEE3373 seed = 0x6D3A91C4 serial = 0x01D4A2B7 device_key = (seed << 32) | (seed ^ serial) print(f"{device_key:016X}") • Flag: flag{ 6D3A91C46CEE3373 }
  16. Classic KeeLoq Garage: Next HOP • 先ほどの連続問題にあたる • Frame の理解、

    DeviceKey の算出方法の特定、 理解ができているはず • フレームを復号し、 Counter 部分( Rolling Code 部分)を取り出し、そこから次の HOPを もとめ、再暗号化し、全体の Frame を得る 21 Medium 再暗号化 Find the Key Next HOP • 一つ先のフレームが予測できるということ は、まだ使われていないシグナルフレームを 計算できるということであり、車やガレージ を解錠/施錠できるということになる
  17. 公開情報の調査 -KeeLoq 暗号化&復号アルゴリズム 参考: https://en.wikipedia.org/wiki/KeeLoq 22 #--- KEELOQ: Non -Linear

    -Function 0x3A5C742E in algebraic normal form def keeloqNLF(a, b, c, d, e): return (d + e + a*c + a*e + b*c + b*e + c*d + d*e + a*d*e + a*c*e + a*b*d + a*b*c) % 2; #---- KEELOQ: Decrypt def keeloqDecryptCalcLSB(data, keybit): nlf = keeloqNLF((data>>30)%2, (data>>25)%2, (data>>19)%2, (data>>8)%2, (data>>0)%2) lsb = (keybit ^ (data>>31) ^ (data>>15) ^ nlf) % 2 return lsb def keeloqDecryptKeybit(data, keybit): return ((data & 0x7FFFFFFF)<<1) ^ keeloqDecryptCalcLSB(data, keybit) def keeloqDecrypt(data, key): for round in range(0,528): keybit = (key >> ((591 -round) % 64)) % 2 data = keeloqDecryptKeybit(data, keybit) return data #---- KEELOQ: Encrypt def keeloqEncryptCalcMSB(data, keybit): nlf = keeloqNLF((data>>31)%2, (data>>26)%2, (data>>20)%2, (data>>9)%2, (data>>1)%2) msb = (keybit ^ (data>>0) ^ (data>>16) ^ nlf) % 2 return msb def keeloqEncryptKeybit(data, keybit): return (keeloqEncryptCalcMSB(data, keybit) << 31) ^ (data >> 1) 参考: https://github.com/marc -invalid/chipwhisperer -marc/blob/master/doc/marc/keeloq/keeloq_algorithm/keeloq_algorithm.md 調べると KeeLoq の暗号・復号 のコードが複数ヒットします NLF = Non -Linear Function 非線形ブール関数 5bit を1bit に折りたたんでいる RECON
  18. Classic KeeLoq Garage: Next HOP Medium • Decode されたフレーム •

    AAAA D5 913B00D7 01D4A2B7 01 • AAAA D5 0020D1FB 01D4A2B7 01 • AAAA D5 80ED145B 01D4A2B7 01 • AAAA D5 314C03E4 01D4A2B7 01 変化している =暗号部 暗号部 KeeLoq 復号 913B00D7 -> ADC4 0040 0020D1FB -> ADC4 0041 80ED145B -> ADC4 0042 314C03E4 -> ADC4 0043 下位16bit はカウンタ 上位16bit は、 DISC、 BTN、 RSV (ここでは固定値として扱う) 次のKeeLoq 復号 ADC4 004 4 KeeLoq 暗号化 65317ADF • Flag: flag{ AAAAD565317ADF 01D4A2B701 } もしカウンタのような結果が得ら れなければ、復号が間違っている 可能性が高い 計算 • Key = 0x6D3A91C46CEE3373 23
  19. def enc(x, k): x &= 0xFFFFFFFF k &= 0xFFFFFFFFFFFFFFFF for

    r in range(528): a = nlf(bit(x, 31), bit(x, 26), bit(x, 20), bit(x, 9), bit(x, 1)) x = ((x >> 1) | ((a ^ bit(x, 16) ^ bit(x, 0) ^ bit(k, r & 63)) << 31)) & 0xFFFFFFFF return x def dec(x, k): x &= 0xFFFFFFFF k &= 0xFFFFFFFFFFFFFFFF for r in range(528, 0, -1): a = nlf(bit(x, 30), bit(x, 25), bit(x, 19), bit(x, 8), bit(x, 0)) x = (((x << 1) & 0xFFFFFFFF) | (a ^ bit(x, 15) ^ bit(x, 31) ^ bit(k, (r - 1) & 63))) & 0xFFFFFFFF return x solver.py ( 1/2) #!/usr/bin/env python3 import sys import re NLF = 0x3A5C742E SEED = 0x6D3A91C4 def bit(x, i): return (x >> i) & 1 def nlf(a, b, c, d, e): return ((a & b & c) ^ (a & b & d) ^ (a & c & e) ^ (a & d & e) ^ (d & e) ^ (c & d) ^ (b & e) ^ (b & c) ^ (a & e) ^ (a & c) ^ e ^ d) Classic KeeLoq Garage: Next HOP Medium 復号化 暗号化 NLF関数 24
  20. 25 if len(sys.argv) != 2: print("Usage: python3 solver.py AAAAD5913B00D701D4A2B701") sys.exit(1)

    frame = clean(sys.argv[1]) if len(frame) != 24 or not frame.startswith("AAAAD5"): raise ValueError("expected full frame like AAAAD5913B00D701D4A2B701") hop = int(frame[6:14], 16) serial = int(frame[14:22], 16) button = int(frame[22:24], 16) key = ((SEED << 32) | (SEED ^ serial)) & 0xFFFFFFFFFFFFFFFF plain = dec(hop, key) next_plain = (plain & 0xFFFF0000) | ((plain + 1) & 0xFFFF) next_hop = enc(next_plain, key) print(f"DeviceKey : {key:016X}") print(f"HopPlain : {plain:08X}") print(f"NextHop : {next_hop:08X}") print(f"flag{{AAAAD5{next_hop:08X}{serial:08X}{button:02X}}}") solver.py ( 2/2) Classic KeeLoq Garage: Next HOP Medium 下位16bit (カウンタ)に 対して+1 $ python3 solver.py AAAAD5314C03E401D4A2B701 DeviceKey : 6D3A91C46CEE3373 HopPlain : ADC40043 NextHop : 65317ADF flag{AAAAD565317ADF01D4A2B701} $ python3 solver.py AAAAD5913B00D701D4A2B701 DeviceKey : 6D3A91C46CEE3373 HopPlain : ADC40040 NextHop : 0020D1FB flag{AAAAD50020D1FB01D4A2B701} solver.py 実行結果 4フレーム目を対象に次のフレーム を作成 1フレーム目を対象に実行して確認 AAAA D5 913B00D7 01D4A2B7 01 AAAA D5 0020D1FB 01D4A2B7 01 AAAA D5 80ED145B 01D4A2B7 01 AAAA D5 314C03E4 01D4A2B7 01 観測フレーム AAAA D5 65317ADF 01D4A2B7 01 予測
  21. Real KeeLoq Garage: Normal Learn • 先ほどの問題はCTF用に 作成した簡易的な KeeLoq フレームだった

    • 本問は、チャレンジ名からわ かる通り、もう少し本格的な 仕様に寄せている • ここまでの情報を踏まえ、 公開情報を調査しつつ、解き 進めていくことを想定 • まずはDeviceKey を計算する • 今回は計算式はMemo にない ため調べる必要がある 26 Hard <<NDIAS -GARAGE Install Note >> Model: KG -401 RX board: HCS500 rev.B Encoder family: HCS301 -compatible Remote label: ??7A4C3D Button wiring: S0 = OPEN Learn mode: Normal Algorithm: KeeLoq Mfr Code: 89ABCDEF01234567 Disc check: lower bits Window: 16 / 32K Seed TX: disabled Repeat: enabled Old sticker: one sample looked clipped memo_c7c8.txt ※配布ファイル Recon 対象 HCS301 (送信側) HCS500 (受信側)
  22. 公開情報の調査 -HCS301 の送信フレーム 27 Memo を踏まえ、 HCS301 の送信フレーム仕様を調査すると以下が見つかる ただし、今回与えられるフレームは Fixed

    Portion と Encrypted Portion がこの仕様と逆になっていることに注意する 明らかに後ろの部分が固定で、前半が変動しているため、仕様が 逆になっていると推測できると想定 URH による Decode RECON
  23. Real KeeLoq Garage: Normal Learn 29 Hard ①KeeLoq 復号アルゴリズム ②Serial

    Number ③Manufacturer Code #--- KEELOQ: Non -Linear -Function 0x3A5C742E in algebraic normal form def keeloqNLF(a, b, c, d, e): return (d + e + a*c + a*e + b*c + b*e + c*d + d*e + a*d*e + a*c*e + a*b*d + a*b*c) % 2; #---- KEELOQ: Decrypt def keeloqDecryptCalcLSB(data, keybit): nlf = keeloqNLF((data>>30)%2, (data>>25)%2, (data>>19)%2, (data>>8)%2, (data>>0)%2) lsb = (keybit ^ (data>>31) ^ (data>>15) ^ nlf) % 2 return lsb def keeloqDecryptKeybit(data, keybit): return ((data & 0x7FFFFFFF)<<1) ^ keeloqDecryptCalcLSB(data, keybit) def keeloqDecrypt(data, key): for round in range(0,528): keybit = (key >> ((591 -round) % 64)) % 2 data = keeloqDecryptKeybit(data, keybit) return data ... 参考: https://github.com/marc -invalid/chipwhisperer - marc/blob/master/doc/marc/keeloq/keeloq_algorithm/keeloq_algo rithm.md 知る必要がある情報 Classic KeeLoq で調べた アルゴリズム <<NDIAS -GARAGE Install Note >> Model: KG -401 RX board: HCS500 rev.B Encoder family: HCS301 -compatible Remote label: ??7A4C3D Button wiring: S0 = OPEN Learn mode: Normal Algorithm: KeeLoq Mfr Code: 89ABCDEF01234567 Disc check: lower bits Window: 16 / 32K Seed TX: disabled Repeat: enabled Old sticker: one sample looked clipped ??? これは実際のフレームで 確認する
  24. Real KeeLoq Garage: Normal Learn • フレームの復号と把握 30 Hard 10011001100110011001100110011001101001100110011010010101100101100110011001100110101001010101011010

    011010011001010101010101100101010101101010100110010110010110100101010110101010011 0 Signal をURH で復号 179bit になっている場合、 0を末尾に追加 (180bit) 1010101010101010 11010101 10001001010101011100000110110100 000001 0000011110100100110000111101 8bit:D5 Frame の理解(ここが重要) 16bit: AAAA 32bit: 8955C1B4 (Encrypted Data ) 34bit:(Fixed Code Data) <<NDIAS -GARAGE Install Note >> Model: KG -401 RX board: HCS500 rev.B Encoder family: HCS301 -compatible Remote label: ??7A4C3D ... 0001 00 0000011110100100110000111101 →0x07A4C3D メモにあった Remote Label と一致 (これが Serial Number )
  25. Real KeeLoq Garage: Normal Learn 31 Hard ①KeeLoq 復号アルゴリズム ②Serial

    Number ③Manufacturer Code #--- KEELOQ: Non -Linear -Function 0x3A5C742E in algebraic normal form def keeloqNLF(a, b, c, d, e): return (d + e + a*c + a*e + b*c + b*e + c*d + d*e + a*d*e + a*c*e + a*b*d + a*b*c) % 2; #---- KEELOQ: Decrypt def keeloqDecryptCalcLSB(data, keybit): nlf = keeloqNLF((data>>30)%2, (data>>25)%2, (data>>19)%2, (data>>8)%2, (data>>0)%2) lsb = (keybit ^ (data>>31) ^ (data>>15) ^ nlf) % 2 return lsb def keeloqDecryptKeybit(data, keybit): return ((data & 0x7FFFFFFF)<<1) ^ keeloqDecryptCalcLSB(data, keybit) def keeloqDecrypt(data, key): for round in range(0,528): keybit = (key >> ((591 -round) % 64)) % 2 data = keeloqDecryptKeybit(data, keybit) return data ... 参考: https://github.com/marc -invalid/chipwhisperer - marc/blob/master/doc/marc/keeloq/keeloq_algorithm/keeloq_algo rithm.md 知る必要がある情報 Classic KeeLoq で調べた アルゴリズム <<NDIAS -GARAGE Install Note >> Model: KG -401 RX board: HCS500 rev.B Encoder family: HCS301 -compatible Remote label: ??7A4C3D Button wiring: S0 = OPEN Learn mode: Normal Algorithm: KeeLoq Mfr Code: 89ABCDEF01234567 Disc check: lower bits Window: 16 / 32K Seed TX: disabled Repeat: enabled Old sticker: one sample looked clipped 0x07A4C3D
  26. Real KeeLoq Garage: Normal Learn 32 Hard SourceH = 0x60000000

    | SERIAL = 0x60000000 | 0x07A4C3D = 607A4C3D SourceL = 0x20000000 | SERIAL = 0x20000000 | 0x07A4C3D = 207A4C3D KeyH = KeeLoqDecrypt( 607A4C3D , 89ABCDEF01234567) = BF61DA58 KeyL = KeeLoqDecrypt( 207A4C3D , 89ABCDEF01234567) = FE545128 Key = KeyH || KeyL = BF61DA58FE545128 Classic KeeLoq で調べた アルゴリズム def keeloq_decrypt(data, key): x = data & 0xFFFFFFFF key &= 0xFFFFFFFFFFFFFFFF for r in range(528): x = ( (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ bit(key, (15 -r) & 63) ^ nlf_bit(g5(x, 0, 8, 19, 25, 30)) ) & 0xFFFFFFFF return x • Flag: flag{ BF61DA58FE545128 }
  27. Real KeeLoq Garage: Normal Learn 33 Hard #!/usr/bin/env python3 import

    sys import re NLF = 0x3A5C742E def bit(x, n): return (x >> n) & 1 def g5(x, a, b, c, d, e): return ( (bit(x, a) << 0) | (bit(x, b) << 1) | (bit(x, c) << 2) | (bit(x, d) << 3) | (bit(x, e) << 4) ) def nlf_bit(idx): return (NLF >> idx) & 1 solver.py def keeloq_decrypt(data, key): x = data & 0xFFFFFFFF key &= 0xFFFFFFFFFFFFFFFF for r in range(528): x = ( (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ bit(key, (15 - r) & 63) ^ nlf_bit(g5(x, 0, 8, 19, 25, 30)) ) & 0xFFFFFFFF return x def clean_hex(s): return re.sub(r"[^0-9a-fA-F]", "", s).upper() def derive_device_key(serial_hex, mfr_code_hex): serial = int(clean_hex(serial_hex), 16) & 0x0FFFFFFF mfr_code = int(clean_hex(mfr_code_hex), 16) & 0xFFFFFFFFFFFFFFFF source_h = 0x60000000 | serial source_l = 0x20000000 | serial key_h = keeloq_decrypt(source_h, mfr_code) key_l = keeloq_decrypt(source_l, mfr_code) device_key = ((key_h << 32) | key_l) & 0xFFFFFFFFFFFFFFFF return device_key, source_h, source_l, key_h, key_l def main(): if len(sys.argv) != 3: print("Usage: python3 solver.py <SerialHex> <MfrCodeHex>") print("Example: python3 solver.py 07A4C3D 89ABCDEF01234567") sys.exit(1) serial_hex = sys.argv[1] mfr_code_hex = sys.argv[2] device_key, source_h, source_l, key_h, key_l = derive_device_key(serial_hex, mfr_code_hex) print(f"Serial : {clean_hex(serial_hex)}") print(f"Mfr Code : {clean_hex(mfr_code_hex)}") print(f"SourceH : {source_h:08X}") print(f"SourceL : {source_l:08X}") print(f"KeyH : {key_h:08X}") print(f"KeyL : {key_l:08X}") print(f"DeviceKey : {device_key:016X}") print(f"flag{{{device_key:016X}}}") if __name__ == "__main__": main() $ python3 solver.py 07A4C3D 89ABCDEF01234567 Serial : 07A4C3D ... DeviceKey : BF61DA58FE545128 flag{BF61DA58FE545128}
  28. Real KeeLoq Garage: Next HOP • ひとつ前の問題で DeviceKey が得られたので、 Classic

    KeeLoq と同様に得られたフレームを 復号し、カウンタ( Rolling Code 部分)を特 定した後、再暗号化することを想定した問題 34 Hard 再暗号化 Normal Learn Next HOP
  29. Real KeeLoq Garage: Next HOP 35 Hard • Decode されたフレーム

    • AAAA | D5 | 8955C1B4 | 0b00 0b0001 0x07A4C3D • AAAA | D5 | 867E2B59 | 0b00 0b0001 0x07A4C3D • AAAA | D5 | 39EB128D | 0b00 0b0001 0x07A4C3D • AAAA | D5 | 13472C1C | 0b00 0b0001 0x07A4C3D • DeviceKey: 0xBF61DA58FE545128 固定部 = Fixed Portion 変化している暗号部 = Encrypted Portion これまで 同様 • 暗号部の復号 • 8955C1B4 -> 103D 0040 • 867E2B59 -> 103D 0041 • 39EB128D -> 103D 0042 • 13472C1C -> 103D 0043 • ????????? <-103D 0044 カウンタは今回も 16bit 下位16bit がカウンタ KeeLoq 復号 もしカウンタのような結果が得ら れなければ、復号が間違っている 可能性が高い 次のKeeLoq 復号 103D 004 4 KeeLoq 暗号化 2675F6FA 計算 • Flag: flag{ AAAAD52675F6FA 107A4C3D } 想定Flag では、 0b000001 = 0x1としてまとめていました。 情報が不足+混乱させてしまいすみません ...
  30. Real KeeLoq Garage: Next HOP 36 Hard def enc(x, k):

    x &= 0xFFFFFFFF k &= 0xFFFFFFFFFFFFFFFF for r in range(528): a = nlf(bit(x, 31), bit(x, 26), bit(x, 20), bit(x, 9), bit(x, 1)) x = ((x >> 1) | ((a ^ bit(x, 16) ^ bit(x, 0) ^ bit(k, r & 63)) << 31)) & 0xFFFFFFFF return x def dec(x, k): x &= 0xFFFFFFFF k &= 0xFFFFFFFFFFFFFFFF for r in range(528, 0, -1): a = nlf(bit(x, 30), bit(x, 25), bit(x, 19), bit(x, 8), bit(x, 0)) x = (((x << 1) & 0xFFFFFFFF) | (a ^ bit(x, 15) ^ bit(x, 31) ^ bit(k, (r - 1) & 63))) & 0xFFFFFFFF return x solver.py ( 1/2) #!/usr/bin/env python3 import sys import re NLF = 0x3A5C742E DEVICE_KEY = 0xBF61DA58FE545128 def bit(x, i): return (x >> i) & 1 def nlf(a, b, c, d, e): return ((a & b & c) ^ (a & b & d) ^ (a & c & e) ^ (a & d & e) ^ (d & e) ^ (c & d) ^ (b & e) ^ (b & c) ^ (a & e) ^ (a & c) ^ e ^ d)
  31. Real KeeLoq Garage: Next HOP 37 Hard solver.py ( 2/2)

    def clean(s): return re.sub(r'[^0-9A-Fa-f]', '', s).upper() if len(sys.argv) != 2: print("Usage: python3 solver.py AAAAD513472C1C107A4C3D") sys.exit(1) frame = clean(sys.argv[1]) if len(frame) != 22 or not frame.startswith("AAAAD5"): raise ValueError("expected frame like AAAAD513472C1C107A4C3D") hop = int(frame[6:14], 16) fixed = frame[14:] # flagでは 107A4C3D 形式を使う plain = dec(hop, DEVICE_KEY) next_plain = (plain & 0xFFFF0000) | ((plain + 1) & 0xFFFF) next_hop = enc(next_plain, DEVICE_KEY) print(f"DeviceKey : {DEVICE_KEY:016X}") print(f"HopPlain : {plain:08X}") print(f"NextPlain : {next_plain:08X}") print(f"NextHop : {next_hop:08X}") print(f"flag{{AAAAD5{next_hop:08X}{fixed}}}") 下位16bit (カウンタ)に 対して+1 AAAA | D5 | 8955C1B4 | 107A4C3D AAAA | D5 | 867E2B59 | 107A4C3D AAAA | D5 | 39EB128D | 107A4C3D AAAA | D5 | 13472C1C | 107A4C3D 観測フレーム AAAA D5 2675F6FA 107A4C3D 予測 $ python3 solver.py AAAAD513472C1C107A4C3D DeviceKey : BF61DA58FE545128 HopPlain : 103D0043 NextPlain : 103D0044 NextHop : 2675F6FA flag{AAAAD52675F6FA107A4C3D} $ python3 solver.py AAAAD58955C1B4107A4C3D DeviceKey : BF61DA58FE545128 HopPlain : 103D0040 NextPlain : 103D0041 NextHop : 867E2B59 flag{AAAAD5867E2B59107A4C3D} solver.py 実行結果 4フレーム目を対象に次のフレーム を作成 1フレーム目を対象に実行して確認
  32. It’s not a car. • Zoox が昨年Lasvegas のResorts World でロボ

    タクシーサービスを提供開始した箇所を特定する 39 Easy Zoox はAmazon 系の自動運転タクシーサービス これはどこのエントランスか?
  33. It’s not a car. 41 Easy 微妙に違う 微妙に違う 全然違う Google

    Map 上で Zoox と検索する と、コンシェルジュ booth が出てくる
  34. AV Striking • URLを開くと、米国の NHTSA(米国運輸省道路交 通安全局)の ODIレポート(欠陥調査レポート) が表示される • ODIレポートの内容を把握したうえで、事故に関

    連する公開情報を調査して、そこから実際の現場 位置情報を調べる 43 Medium Waymo は世界で一番走っている Google 系 の自動運転タクシーサービス
  35. AV Striking • 対象車両: Waymo • 搭載システム: 5th Generation Automated

    (完全自動運転) • 事故発生日: 2026/1/23 • NHTSA報告日: 2026/1/28 • 事故発生場所: • カリフォルニア州サンタモニカ • 小学校から約 2ブロック圏内 • 通学時間帯(登校時間帯) • 事故の概要 • 小学校付近で、複数の子供・横断補助員・路上駐車車両が 存在する状況 • 子供が二重駐車された SUVの陰から道路へ飛び出し、 Waymo の自動運転車が衝突(接触)した • 衝突被害:軽傷( minor injuries ) 44 Medium 公式からのアナウンス: https://waymo.com/blog/2026/01/a -commitment -to-transparency -and -road -safety/
  36. Unexpected Braking • 先ほどまでと異なり、特定の車種などを問題文 では提示しない問題とした • また、当初は公開情報調査の結果得られた文書 はAIが読み取れない想定であったものの、 最終的にAIが解けるようになってしまったた め、想定より簡単な問題になってしまいました

    • ざっくり想定解法 • "2024 Unexpected Braking" などで調べ、 車種や事故の概要などを把握する • 問題文から、事故ではなくリコールに関す る情報と推察し、公式のリコールに関する 情報を調査する • リコールの対象となっている車両情報を確 認し、文書に記載されている情報をそのま まFlag とする 46 Hard ※(言い訳)本来 OSINT はおまけ問題程度の想定で あったため、 AIを活用して一応解けるくらいの想定 難易度としていました(汗)
  37. Unexpected Braking 47 Hard https://thebrakereport.com/zoox -recalls - ads -software -for-braking

    -issue/ 調査・対象車両の想定 Recall 情報の調査 地域は北米 https://www.nhtsa.gov/recalls Recall はNHTSAが 取り扱っている 合ってそう SGOレポート (Standing General Order 、 報告義務レポート ) flag{Highlander_5TDDGRFH3KS} <Nevada> flag{Highlander_5TDDGRFH0KS} <California> https://static.nhtsa.gov/odi/inv/2024/INOT -PE24015 -12311P2.pdf
  38. Joby First Flight • 「空飛ぶクルマ」について知ってもらう機会 として、簡単なチャレンジを設定した • いわゆる大きなドローンのようなものであり、 運用のためにはヘリポートの使用が必須 •

    日本では、空港と異なりヘリポートの番号には 公的なものはなく、今回は非公式 Web サイトに 登録されている識別番号を使用させていただいた 48 Easy • トヨタ自動車は Joby Aviation と合弁会社を設立して おり、大規模な出資を行っている • その他、 ANAやUber 、 Delta Airなども提携 • 日本での初試験フライトは、トヨタと共同で行った
  39. What is the " 空飛ぶクルマ (Flying Car)" • 都市や近距離の移動に使うための小型航空機(次世代モビリティ) •

    空飛ぶクルマは” AAM; Advanced Air Mobility ”とも呼ばれ、日本国内では経済産業省主導で、 社会実装に向けて推進されている • 現在の主流はeVTOL(electric Vertical Take -Off and Landing) 49 参考: https://www.meti.go.jp/press/2025/03/20260327005/20260327005.html Joby Aviation (米) https://www.jobyaviation.com/ SkyDrive (日) https://skydrive.co.jp/ Lilium Air Mobility (独) https://lilium.com/
  40. Joby First Flight 50 Easy 参考: https://toyotatimes.jp/toyota_news/1067.html Map などから、トヨタ東富士研究所を確認すると、 ヘリポートがあることがわかる

    flag{JP -3604} https://ourairports.com/ (補足)このヘリポートが実際に使われていることが、以下の動画から分かる。 https://www.youtube.com/watch?v=d_mp2QymbFw
  41. Joby 2025 Last Flight • 純粋なOSINT の問題 • 想定手順 •

    Joby の2025 年の最後のフライトを調べる • フライトが行われている場所の、離発着し ている場所を特定する • OpenStreetMap のWay 番号を探す 51 Medium
  42. Joby 2025 Last Flight 52 Medium • 公開情報を調べると、富士スピードウェイで実施したことがわかる • 問題は、富士スピードウェイのどこで行われたか、ということ

    https://response.jp/article/2026/01/30/406781.html https://ir.jobyaviation.com/news -events/press - releases/detail/164/joby -caps -year -of-flight - demonstrating -global -commercial https://globe.adsbexchange.com/ フライトレーダーのログだと短すぎてどこで 離発着したかわからないが、 12月初週に富士 スピードウェイで飛んでいたことはわかる (おまけ) FAA認証とは、米国連邦航空局 ( FAA)が機体の安全性・適合性を公式に認 める制度。型式証明と耐空証明からなり、 様々な条件での飛行実績が求められる。
  43. 所感 / Wrap -up • Keyfob (リモートキー)の仕組みを理解する良い機会を提供できた • GNU -Radio

    を用いて、実機不要で作問できたこともよい機会であった • OSINT は問題数のバランスや難易度を踏まえ 5問程度としたが、想定より多くの人に 取り組んでもらえたため、もう少しちゃんと作問したほうが良かったかもしれない • 個人的には、特に北米で事故やリコール情報がオープンになっていることを知っても らえてよかった • AI対策を意識しすぎると、難易度が異常に高い問題しか作れず、バランスが難しい • 次回もよい問題を作れるよう、フィードバックがあればお気軽にご連絡ください 57