6月18日、orchidfiles.comが「How I found 10,000 GitHub repositories distributing Trojan malware」と題した記事を公開した。GitHubでトロイの木馬マルウェアを配布する1万件のリポジトリを発見した調査プロセスを、著者自身が詳細に記録したものだ。
GitHubを悪用したマルウェア配布は近年増加傾向にある。信頼性の高いプラットフォームであるがゆえにセキュリティツールやアンチウイルスに検出されにくく、開発者が無意識にダウンロードしてしまうリスクがある。今回の調査が示した1万件という規模——しかも数ヶ月から1年以上にわたって野放しになっていた——は、その問題の深刻さを改めて浮き彫りにした。
発端:自分のリポジトリが「コピーされていた」
発端は偶然だった。著者がGitHubで公開している自分のプロジェクトを検索エンジンで検索したところ、Bing上に全く同じ名前・説明文を持つ別のリポジトリが表示された。中身は自分のリポジトリの全コミット履歴をコピーしたもので、著者自身もコントリビューターとして名を連ねていた。そして直近のコミットには、zipアーカイブへのリンクがREADMEに追加されていた。
このzipアーカイブのURLをVirusTotalに投げると検出ゼロ。しかしzipファイル本体を投げると、トロイの木馬が検出された。URLだけでは安全に見えるという、巧妙な回避手法だ。
zipには以下の4ファイルが含まれていた:
Application.cmdまたはLauncher.cmdloader.exe/luajit.exeなど- ランダムな名前の
.csoまたは.txt lua51.dll
GitHubサポートに報告したが、2週間音沙汰なし。1ヶ月後にようやく「削除した」とのメールが届いた。
「パターン」を見抜いてスクリプトで自動検出
しばらく忘れていたが、ある朝、著者はひらめいた。「共通パターンがあるなら、スクリプトで全GitHubリポジトリから同じものを探せる」。
特定したパターンはこうだ:
- 数時間ごとに直前のコミットを削除し、新しいコミットをプッシュ
- 変更されるのはREADMEファイルのみ
- READMEにzipアーカイブへのリンクが含まれる
- 別リポジトリのコミット履歴をコピー(フォークではなく独立したリポジトリ)
- コントリビューターも名前も全て異なる
問題はGitHubには5億件のリポジトリが存在すること。GitHub APIのレート制限は1トークンあたり1時間5,000リクエストで、全件スキャンすると1年かかる計算だ。
ここで著者が活用したのがGH Archiveだ。GitHubで発生した全パブリックイベント(プッシュ、スター、フォーク等)のログをJSONダンプで公開しており、BigQueryや自前スクリプトで効率よく絞り込める。著者はこれを使い「数時間ごとにコミットプッシュがあるリポジトリ」に絞り込む戦略をとった。
過去5日間のコミットプッシュイベントは1,600万件。そのうち「10時間に2〜10回更新」の条件に該当したのは3,000件。ここからさらにAPIで個別確認し、以下のフィルターを追加した:
- コミットがボットではなく人間から
- 最新コミットと1つ前のコミットの間隔が1ヶ月以上(=頻繁な更新が不自然なリポジトリ)
- コントリビューターが複数人(=他リポジトリからコピーされたコミット履歴に複数名が含まれる)
この段階では14件しか残らなかった。「GitHubに14件しかないのか」と受け入れかけたそのとき、再確認すると全件が20時間前に更新されていた。「数時間ごと」というフィルターが機能しておらず、低頻度更新のリポジトリを全て弾いていたことに気づく。
手動確認でさらに発見があった。最新コミットの変更差分がゼロのリポジトリや、全リポジトリで最終コミット名が「Update README.md」に統一されていること。
フィルターを「24時間に1〜24回更新」に緩めたところ、4万件のリポジトリがヒットした。そのうち1万件(25%)が全パターンに合致した。
なぜ検出されないのか
1万件のリポジトリは数ヶ月〜1年以上前から存在し続けており、GitHubの自動検出をくぐり抜けている。著者はその理由についていくつかの仮説を立てている。
なぜ人気リポジトリではなく新規リポジトリをコピーするのか? 新規リポジトリは検索エンジンのインデックスに早期掲載されやすく、低競合キーワードで上位に表示されやすい。GitHubの人気タグにも追加することで流入を狙っている。
なぜコミット履歴とコントリビューターをコピーするのか? 訪問者に信頼感を与えるためだ。コントリビューターのプロフィールをたどると古参アカウントが並んでいるように見え、正規のプロジェクトと誤認させられる。
なぜ定期的にコミットを削除して同じコミットをプッシュし直すのか? GitHubのセキュリティアルゴリズムを回避するためではないかと著者は推測している。コミット名を「Update README.md」に統一しているのも同じ理由の可能性がある。
比較として、今年4月に公開されたHexaStrikeのレポートでは、同様の手口でSmartLoader(ローダー型マルウェア)およびStealC(情報窃取型マルウェア)が配布されていたことが報告されており、その時点での発見数は109件だった。今回の調査ではその約90倍にあたる1万件に達しており、この種の攻撃キャンペーンが急速に拡大していることがうかがえる。
リストとスクリプトは公開済み
著者は該当リポジトリの完全なリストと、同様のリポジトリを検出するスクリプト「Git Malware Finder」をGitHubで公開している。
GitHubへの直接報告については、著者自身が「1万件は多すぎる」と判断して見送ったとのことだ。GitHubのセキュリティチームへのコネクションを持つ人がいれば、この記事を転送してほしいと呼びかけている。5億件のリポジトリをスキャンし、アーカイブや実行ファイルを検出・ウイルススキャンする手段はGitHub側にあるはずだ、というのが著者の主張だ。
詳細はHow I found 10,000 GitHub repositories distributing Trojan malwareを参照していただきたい。