前回 は、CERT C というセキュアコーディングについて調べてみました。
CodeQL の挙動を理解するに、Tinyhttpd を使ってきましたが、この OSS は、規模が小さいので良い面もありましたが、クエリの条件に当てはまるコードが無い場合が出てきました。そこで、新しく、対象の OSS として、libusb も使っていきたいと思います。
libusb は、とても有名な OSS で、USB を扱う OSS が採用してる場合が多いです。apt でパッケージを入れたときに、一緒に入ったりしてると思います。libusb は、USBデバイスへの汎用的なアクセスを提供してくれる Cソースライブラリです。USB の規格は膨大な量であり、USBデバイスにアクセスすることも結構な労力が必要になります。そこの役割をしてくれるのが libusb です。
それでは、やっていきます。
はじめに
「セキュリティ」の記事一覧です。良かったら参考にしてください。
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて
・第19回:CTF初心者向けのCpawCTFをやってみた
・第20回:hashcatの使い方(GPU実行時間の見積りとパスワード付きZIPファイル)
・第21回:Scapyの環境構築とネットワークプログラミング
・第22回:CpawCTF2にチャレンジします(クリア状況は随時更新します)
・第23回:K&Rのmalloc関数とfree関数を理解する
・第24回:C言語、アセンブラでシェルを起動するプログラムを作る(ARM64)
・第25回:機械語でシェルを起動するプログラムを作る(ARM64)
・第26回:入門セキュリhttps://github.com/SECCON/SECCON2017_online_CTF.gitティコンテスト(CTFを解きながら学ぶ実践技術)を読んだ
・第27回:x86-64 ELF(Linux)のアセンブラをGDBでデバッグしながら理解する(GDBコマンド、関連ツールもまとめておく)
・第28回:入門セキュリティコンテスト(CTFを解きながら学ぶ実践技術)のPwnable問題をやってみる
・第29回:実行ファイルのセキュリティ機構を調べるツール「checksec」のまとめ
・第30回:setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します)
・第31回:常設CTFのksnctfにチャレンジします(クリア状況は随時更新します)
・第32回:セキュリティコンテストチャレンジブックの「Part2 pwn」を読んだ
・第33回:セキュリティコンテストチャレンジブックの「付録」を読んでx86とx64のシェルコードを作った
・第34回:TryHackMeを始めてみたけどハードルが高かった話
・第35回:picoCTFを始めてみた(Beginner picoMini 2022:全13問完了)
・第36回:picoCTF 2024:Binary Exploitationの全10問をやってみた(Hardの1問は後日やります)
・第37回:picoCTF 2024:Reverse Engineeringの全7問をやってみた(Windowsプログラムの3問は後日やります)
・第38回:picoCTF 2024:General Skillsの全10問をやってみた
・第39回:picoCTF 2024:Web Exploitationの全6問をやってみた(最後の2問は解けず)
・第40回:picoCTF 2024:Forensicsの全8問をやってみた(最後の2問は解けず)
・第41回:picoCTF 2024:Cryptographyの全5問をやってみた(最後の2問は手つかず)
・第42回:picoCTF 2023:General Skillsの全6問をやってみた
・第43回:picoCTF 2023:Reverse Engineeringの全9問をやってみた
・第44回:picoCTF 2023:Binary Exploitationの全7問をやってみた(最後の1問は後日やります)
・第45回:書籍「セキュリティコンテストのためのCTF問題集」を読んだ
・第46回:書籍「詳解セキュリティコンテスト」のReversingを読んだ
・第47回:書籍「詳解セキュリティコンテスト」のPwnableのシェルコードを読んだ
・第48回:書籍「バイナリファイル解析 実践ガイド」を読んだ
・第49回:書籍「詳解セキュリティコンテスト」Pwnableのスタックベースエクスプロイトを読んだ
・第50回:書籍「詳解セキュリティコンテスト」Pwnableの共有ライブラリと関数呼び出しを読んだ
・第51回:picoCTF 2025:General Skillsの全5問をやってみた
・第52回:picoCTF 2025:Reverse Engineeringの全7問をやってみた
・第53回:picoCTF 2025:Binary Exploitationの全6問をやってみた
・第54回:書籍「詳解セキュリティコンテスト」Pwnableの仕様に起因する脆弱性を読んだ
・第55回:システムにインストールされたものと異なるバージョンのglibcを使う方法
・第56回:書籍「詳解セキュリティコンテスト」Pwnableのヒープベースエクスプロイトを読んだ
・第57回:書籍「解題pwnable」の第1章「準備」を読んだ
・第58回:書籍「解題pwnable」の第2章「login1(スタックバッファオーバーフロー1)」を読んだ
・第59回:書籍「解題pwnable」の第3章「login2(スタックバッファオーバーフロー2)」を読んだ
・第60回:書籍「解題pwnable」の第4章「login3(スタックバッファオーバーフロー3)」を読んだ
・第61回:書籍「解題pwnable」の第5章「rot13(書式文字列攻撃)」を読んだ
・第62回:GitHubが開発した静的解析ツール(脆弱性検出ツール)のCodeQLを使ってみる
・第63回:CodeQL(静的解析ツール)で使われるクエリの選ばれ方を調べた
・第64回:CodeQL(静的解析ツール)のクエリの書き方を調べた
・第65回:CodeQL(静的解析ツール)で使われているアラートクエリの中身を調べる
・第66回:CodeQL(静的解析ツール)で使われているパスクエリの中身を調べる
・第67回:CodeQL(静的解析ツール)をVSCodeで使う方法を理解する
・第68回:CodeQL(静的解析ツール)の挙動を確認するための対象ソースコードとしてTinyhttpdを調査する
・第69回:Tinyhttpdを使用してCodeQL(静的解析ツール)のクエリの挙動を確認する
・第70回:セキュアコーディング:CERT C INT02-C 「整数変換のルールを理解する」を調べる
・第71回:CodeQL(静的解析ツール)の挙動を確認するための対象ソースコードとしてlibusbを調査する ← 今回
現在の実行環境は、Windows11 に、WSL2 で、Ubuntu 24.04 を入れた環境です。
libusb の GitHub は以下です。
README.md に書かれてますが、libusb のホームページは以下です。
また、開発者向けの APIリファレンスは以下です。
以前、USB について簡単に調べた記事を書きました。
daisuke20240310.hatenablog.com
libusbの環境構築
以下のように調べてみると、私の環境では、既に libusb をインストールしていたようです。
$ dpkg -l | grep libusb ii libusb-1.0-0:amd64 2:1.0.27-1 amd64 userspace USB programming library ii libusb-1.0-0-dev:amd64 2:1.0.27-1 amd64 userspace USB programming library development files ii libusb-1.0-doc 2:1.0.27-1 all documentation for userspace USB programming
2:1.0.27-1 と表示されているのは、ChatGPT によると、以下の意味とのことです。
| 対象 | 意味 |
|---|---|
| 2: | Debian オーバーレイの Epoch |
| 1.0.27 | upstream(libusb のバージョン) |
| -1 | Debian/Ubuntu でのパッケージリビジョン |
libusb のバージョンが特定できたので、GitHub からソースコードをダウンロードします。同じバージョンの方が安定してそうですし、動かしたときに不都合が起きにくいと思います。
よって、1.0.27 を取得します。一応、既にインストールされている libusb のヘッダと差分が無いかを確認したところ、一致しました。
$ git clone https://github.com/libusb/libusb.git $ cd libusb/ $ git checkout -b v1.0.27 refs/tags/v1.0.27 $ diff -upr libusb/libusb.h /usr/include/libusb-1.0/libusb.h
ビルド方法は、以下の wiki に書かれていました。
これに従って、ビルドします。
ビルドされたソースファイルを確認したくなるかもしれないので、make のログだけ、一部貼っておきます。libusb の本体と、examples と tests がビルドされたようです。
$ ./autogen.sh $ ./bootstrap.sh $ make make all-recursive make[1]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb' Making all in libusb make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb/libusb' CC core.lo CC descriptor.lo CC hotplug.lo CC io.lo CC strerror.lo CC sync.lo CC os/events_posix.lo CC os/threads_posix.lo CC os/linux_usbfs.lo CC os/linux_udev.lo CCLD libusb-1.0.la make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb/libusb' Making all in examples make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb/examples' CC dpfp.o CCLD dpfp CC dpfp_threaded-dpfp.o CCLD dpfp_threaded CC ezusb.o CC fxload.o CCLD fxload CC hotplugtest.o CCLD hotplugtest CC listdevs.o CCLD listdevs CC sam3u_benchmark.o CCLD sam3u_benchmark CC testlibusb.o CCLD testlibusb CC xusb.o CCLD xusb make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb/examples' Making all in tests make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb/tests' CC stress.o CC testlib.o CCLD stress CC stress_mt-stress_mt.o CCLD stress_mt CC set_option.o CCLD set_option CC init_context.o CCLD init_context make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb/tests' make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb' make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb' make[1]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb'
これで libusb を動作させる準備はできました。私の環境は WSL なので、Windows側で認識している USBデバイスを WSL で認識できるようにする必要があります。以下の記事に、WSL で USBデバイスを認識させる方法を追記しました。この方法で、USBメモリを WSL側で認識できるようにしました。
daisuke20240310.hatenablog.com
それでは、USBデバイスの一覧を表示するサンプルアプリを動かしてみます。まず、パッケージでインストールされている libusb ではなく、今回ビルドした libusb が使われることを確認します。
以下の実行方法であれば、ビルドした libusb が使われることが確認できました。
$ LD_LIBRARY_PATH=libusb/.libs/ ldd examples/.libs/listdevs linux-vdso.so.1 (0x00007fff49f33000) libusb-1.0.so.0 => libusb/.libs/libusb-1.0.so.0 (0x00007ceb763ae000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ceb76000000) libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007ceb76374000) /lib64/ld-linux-x86-64.so.2 (0x00007ceb763d5000) libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007ceb76367000)
では、listdevs を実行してみます。
事前に lsusb で期待する結果を確認しました。listdevs でも正しく認識できているようです。ちなみに、以下では 3つの USBデバイスが認識されていますが、USBメモリを WSL側に認識させる前の状態では、何も出力されませんでした。
$ lsusb Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 003: ID 346d:5678 Biwin USB Device Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub $ LD_LIBRARY_PATH=libusb/.libs/ examples/.libs/listdevs 1d6b:0003 (bus 2, device 1) 346d:5678 (bus 1, device 3) path: 1 1d6b:0002 (bus 1, device 1)
libusb の環境構築は以上です。
libusbに対してCodeQLを実行する
早速、CodeQL を実行していきます。
Tinyhttpd のときと同様に、デフォルトのクエリスイート「cpp-code-scanning.qls」は、あまり指摘が出ないため、「cpp-security-and-quality.qls」を使います。
CodeQL のデータベースを作る際、CodeQL はビルドを実行するので、実行する前に、libusb を make clean しておく必要があります。
(libusb のディレクトリで以下を実行) $ make clean (以降は、CodeQL v2.22.0-bundle を置いたディレクトリで実行) $ codeql/codeql database create db/libusb -l c-cpp -s ../../oss/libusb/libusb $ codeql/codeql database analyze db/libusb cpp-security-and-quality.qls --format csv --output csv/libusb_cppsq.csv -M 6000
warning が 5件、recommendation が 5件でした。warning だけ確認するので、以下は、warning のみです。
| 検出したクエリ名 | クエリの説明 | 重要度 | 警告メッセージ | パス | 開始行 | 開始列 | 終了行 | 終了列 |
|---|---|---|---|---|---|---|---|---|
| Comparison result is always the same | When a comparison operation, such as x < y, always returns the same result, it means that the comparison is redundant and may mask a bug because a different check was intended. | warning | Comparison is always false because iso_packets >= 0. |
/libusb/io.c | 1293 | 6 | 1293 | 20 |
| Local variable address stored in non-local memory | Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. | warning | A stack address which arrived via a [["parameter"|"relative:///libusb/libusb.h:1852:24:1852:32"]] may be assigned to a non-local variable. |
/libusb/libusb.h | 1852 | 2 | 1852 | 32 |
| Local variable address stored in non-local memory | Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. | warning | A stack address which arrived via a [["parameter"|"relative:///libusb/libusb.h:1824:24:1824:32"]] may be assigned to a non-local variable. |
/libusb/libusb.h | 1824 | 2 | 1824 | 32 |
| Local variable address stored in non-local memory | Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. | warning | A stack address which arrived via a [["parameter"|"relative:///libusb/libusbi.h:235:21:235:24"]] may be assigned to a non-local variable. |
/libusb/libusbi.h | 235 | 2 | 235 | 24 |
| Local variable address stored in non-local memory | Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. | warning | A stack address which arrived via a [["parameter"|"relative:///libusb/libusbi.h:237:21:237:24"]] may be assigned to a non-local variable. |
/libusb/libusbi.h | 237 | 2 | 237 | 24 |
1つ目の warning は以下のソースコードです。Ghidra で見たところ、assert文は有効な行でした。この警告は、その次の if文に対してであり、assert文で iso_packets は 0以上なので、この if文は冗長なチェックとなります。それは何か不具合があるのかも、という警告でした。このケースでは、assert文がコンパイルで無効化されたときのための if文だと思うので、問題ないと思います。
DEFAULT_VISIBILITY struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets) { assert(iso_packets >= 0); if (iso_packets < 0) return NULL;
2番目以降は、同じ指摘内容が 4つ続きます。
以下は、該当箇所のソースコードです。
user_data を代入してる行が指摘されています。スタックを指すポインタが代入された場合、スコープが外れると、正しく値を参照できない、というよくある警告です。他のポインタが指摘されていない理由はよく分かっていません。ChatGPT に聞くと、user_data には libusb を使うアプリ?のデータが入るので、ユーザ責任になるので、libusb としては問題ないということでした。
static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *buffer, int length, libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) { transfer->dev_handle = dev_handle; transfer->endpoint = endpoint; transfer->type = LIBUSB_TRANSFER_TYPE_BULK; transfer->timeout = timeout; transfer->buffer = buffer; transfer->length = length; transfer->user_data = user_data; transfer->callback = callback; }
残り、3つも同じような感じなので割愛します。
サンプルアプリ(examples)を調査する
サンプルアプリは下表のものがあるようです(from ChatGPT)。
| アプリ | 機能 |
|---|---|
| dpfp | USBデバイスからのデータを非同期で受信する最小例 |
| dpfp_threaded | dpfp のイベント処理を別スレッドに分離した版 |
| fxload | USBデバイスへのファームウェア転送 |
| hotplugtest | USB 抜き差し検知 |
| listdevs | USBデバイスの一覧表示(lsusb) |
| sam3u_benchmark | USB 転送性能測定 |
| testlibusb | 基本の API テスト |
| xusb | USBデバイス構造の詳細表示(lsusb -t) |
必要に応じて、各アプリの内容を確認したいと思います。
サンプルアプリ:examples/listdevs
examples/listdevs.c を見ていきます。
まず、main関数です。
libusb_XXX となっているのは、libusb の API です。
引数は無いようです。
libusb の初期化(libusb_init_context)を行い、接続されている USBデバイスを探索(libusb_get_device_list)しています。あとは、次に確認する print_devs関数を実行して、終了処理をしています。
int main(void) { libusb_device **devs; int r; ssize_t cnt; r = libusb_init_context(/*ctx=*/NULL, /*options=*/NULL, /*num_options=*/0); if (r < 0) return r; cnt = libusb_get_device_list(NULL, &devs); if (cnt < 0){ libusb_exit(NULL); return (int) cnt; } print_devs(devs); libusb_free_device_list(devs, 1); libusb_exit(NULL); return 0; }
取得したデバイスリストに対して、whileループですべて確認していきます。libusb_get_device_descriptor でディスクリプタを取得し、VenderID、ProductID を取得します。また、libusb_get_bus_number で、バス番号を取得し、libusb_get_device_address で、デバイスアドレスを取得し、これらを表示しています。
また、libusb_get_port_numbers で、ポート番号を取得し、path として表示しています。
static void print_devs(libusb_device **devs) { libusb_device *dev; int i = 0, j = 0; uint8_t path[8]; while ((dev = devs[i++]) != NULL) { struct libusb_device_descriptor desc; int r = libusb_get_device_descriptor(dev, &desc); if (r < 0) { fprintf(stderr, "failed to get device descriptor"); return; } printf("%04x:%04x (bus %d, device %d)", desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev)); r = libusb_get_port_numbers(dev, path, sizeof(path)); if (r > 0) { printf(" path: %d", path[0]); for (j = 1; j < r; j++) printf(".%d", path[j]); } printf("\n"); } }
listdevs は、libusb の API をひたすら実行しているだけでした。
おわりに
今回は、GitHub に公開されている OSS の libusb について調査しました。今後は、Tinyhttpd とともに、libusb を使って、CodeQL の挙動の確認を進めていきたいと思います。その際、libusb の中身を見ていくことも必要になると思います。そのときは、この記事を更新していこうと思います。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。