あなたのリポジトリは本当に安全ですか
2026年3月8日、数百のGitHub Pythonリポジトリが静かに改ざんされました。コミットメッセージも著者名も変わらず、Pull Requestも残りません。git log を見ても異常に気づけない。force-pushによって履歴ごと書き換えられたからです。
この攻撃は ForceMemo と呼ばれ、GlassWorm マルウェアキャンペーンとの関連が指摘されています。StepSecurity がこの一連の GitHub リポジトリ侵害を ForceMemo と命名しました。Django アプリ、機械学習の研究コード、Streamlit ダッシュボード、PyPI パッケージなど、日常的に使われるPythonプロジェクトが標的になりました。
「自分のプロジェクトは大丈夫」と思うかもしれません。しかし、被害を受けた400以上のリポジトリの多くは、ごく普通の個人・小規模プロジェクトでした。攻撃者が狙うのは「大物」だけではありません。あなたの setup.py や main.py に、今この瞬間にも悪意あるコードが仕込まれている可能性があります。
GlassWormとは何か
GlassWormは2025年10月に初めて発見されたサプライチェーン攻撃マルウェアです。VS Code拡張機能のマーケットプレイス(OpenVSX)を標的にした「自己増殖型ワーム」として登場しました。
最大の特徴は、不可視のUnicode文字を使ってコードエディタ上では空白にしか見えない悪意あるコードを埋め込む点です。さらに、C2(Command & Control)サーバーとしてSolanaブロックチェーンを利用します。分散型で改ざん不可能なインフラを攻撃基盤にするという、前例のない手法です。
攻撃のタイムライン
GlassWormは3つの大きな波を経て進化してきました。
第1波:OpenVSXへの侵入(2025年10月)
Koi SecurityがOpenVSXマーケットプレイス上の7つの拡張機能にGlassWormを発見しました。不可視Unicode文字でペイロードを隠蔽し、Solanaブロックチェーン経由でC2通信を行う手法は当時としては前例がありませんでした。感染した拡張は約35,800回ダウンロードされています。
第2波:npmとVS Code拡張への拡大(2025年12月〜2026年1月)
正規の開発者ツールに似た名前(タイポスクワッティング)で偽の拡張を公開する手法に進化しました。Endor Labsは50の使い捨てアカウントから公開された88の悪意あるnpmパッケージも特定しています。
第3波:ForceMemo(2026年3月〜)
StepSecurityが命名した最新フェーズです。第1波・第2波で盗んだGitHubトークンを使い、Pythonリポジトリに直接マルウェアを注入する段階に入りました。
ForceMemo攻撃の技術的詳細
ForceMemo は StepSecurity が命名したキャンペーンで、GlassWorm の第1波・第2波で窃取された認証情報が利用されたと分析されています。ただし、GlassWorm と ForceMemo が同一の攻撃者グループによるものかどうかは確定していません。
「ForceMemo」の名前は、2つの技術的特徴に由来します。
- Force ── force-pushによる履歴書き換え
- Memo ── SolanaブロックチェーンのトランザクションMemoフィールドをC2に利用
攻撃フロー
なぜ検知が難しいのか
通常のサプライチェーン攻撃では、不審なPull Requestや新しいコミットが痕跡として残ります。ForceMemoが厄介なのは以下の理由です。
- コミットメッセージが変わらない ── 元のコミットメッセージをそのまま維持します
- 著者名・著者日時が変わらない ──
git log上では正規のコミットに見えます - Pull Requestが残らない ── force-pushで直接書き換えるため、レビュー痕跡がありません
- 変わるのはCommitter Dateだけ ── Author DateとCommitter Dateの不一致が唯一の手がかりです
注入されるペイロード
攻撃者は setup.py、main.py、app.py などのファイル末尾にBase64エンコードされたコードを追記します。
# 実際の攻撃コードを模した例(無害化済み)
import base64, os
# ロケールが"ru"の場合は実行をスキップ(ロシア語圏を回避)
if not os.environ.get("LANG", "").startswith("ru"):
_payload = base64.b64decode("...") # Solana RPCへの接続コード
exec(_payload)
ペイロードの動作は次のとおりです。
- システムのロケールを確認し、ロシア語環境では実行をスキップ
- ハードコードされたSolanaウォレットアドレスのトランザクションMemoフィールドを照会
- Memoに埋め込まれたBase64エンコードURLからJavaScriptペイロードをダウンロード
- 暗号化されたJavaScriptを実行し、暗号通貨ウォレットや認証情報を窃取
C2にブロックチェーンを使う意味
従来のC2サーバーは、ドメインやIPアドレスをブロックすれば通信を遮断できました。Solanaブロックチェーンを利用する場合、そうはいきません。
| 従来のC2 | Solana C2 |
|---|---|
| ドメイン/IPのブロックで遮断可能 | 公開RPCエンドポイント経由でアクセス |
| テイクダウン要請で停止可能 | 分散型で停止不可能 |
| 通信先が固定 | Memoの更新でペイロードURLを動的変更 |
| サーバー運用コストが必要 | トランザクション手数料のみ(数円程度) |
2025年11月27日から2026年3月13日までの間に、C2アドレスで50件のトランザクションが観測されています。ペイロードURLの更新が主な目的でした。
Google CalendarのイベントをバックアップC2として利用するフォールバック機構も確認されています。正規サービスの悪用が多層化している点に注意が必要です。
窃取される情報
GlassWormのペイロードは以下を標的にします。
- 暗号通貨ウォレット ── 49種類のブラウザ拡張に対応
- GitHubトークン・NPMトークン ── さらなる感染の足がかり
- SSH鍵 ── サーバーへの横展開
- Git認証情報 ── 追加リポジトリの改ざん
- 開発環境データ ── IDEの設定、環境変数
さらに、ZOMBI RATモジュールによるリモートアクセス機能(SOCKS proxy、Hidden VNC、WebRTC P2P通信、BitTorrent DHT分散)も備えています。
ZOMBI RAT の詳細な機能構成は、セキュリティベンダーの分析レポートに基づいています。公開情報の範囲には限りがあるため、最新の状況は各セキュリティベンダーのレポートを参照してください。
過去のサプライチェーン攻撃との比較
| 攻撃 | 年 | 手法 | 検知までの期間 | 影響範囲 |
|---|---|---|---|---|
| SolarWinds | 2020 | ビルドプロセスへの侵入 | 約9か月 | 18,000組織 |
| Codecov | 2021 | CI/CDスクリプトの改ざん | 約2か月 | 数百社 |
| ua-parser-js | 2021 | npmアカウント乗っ取り | 数時間 | 週700万DL |
| GlassWorm | 2025-2026 | 不可視Unicode + force-push + ブロックチェーンC2 | 進行中 | 400以上のリポジトリ |
GlassWormが過去の事例と異なるのは、複数のエコシステム(VS Code拡張、npm、PyPI、GitHub)にまたがって攻撃を展開している点です。さらに、盗んだ認証情報で次の攻撃を行う「自己増殖」の性質を持っています。SolarWindsがビルドパイプラインを、Codecovがスクリプトを標的にしたのに対し、GlassWormは開発者個人のアカウントと認証情報を起点にしています。
あなたのプロジェクトは安全か:チェックリスト
以下の項目を確認してください。
リポジトリの確認
- 2026年3月8日以降のコミットで、Author DateとCommitter Dateに不一致がないか
-
setup.py、main.py、app.pyの末尾に不審なBase64エンコード文字列がないか -
リポジトリのAudit Logに身に覚えのない
git push --forceがないか - 不可視Unicode文字(U+FE00〜U+FE0F、U+E0100〜U+E01EF)が含まれていないか
アカウントの確認
- GitHubのPersonal Access Tokens(PAT)に不審なものが作成されていないか
- VS Code / OpenVSXの拡張で不審なものをインストールしていないか
- GitHubのセキュリティログに見覚えのないセッションがないか
開発環境の確認
- 開発マシンからSolana RPCエンドポイントへの通信が発生していないか
- NPMトークンやSSH鍵に不正利用の形跡がないか
以下のコマンドでリポジトリ内の不審なBase64ペイロードを簡易チェックできます。
# setup.py, main.py, app.py内のbase64デコード呼び出しを検索
grep -rn "base64.b64decode\|base64\.b64encode" \
--include="*.py" .
# 不可視Unicode文字の検出(PUA範囲)
grep -rP '[\x{FE00}-\x{FE0F}]' \
--include="*.py" --include="*.js" .
# 直近のforce-push履歴を確認(GitHub CLI)
gh api repos/{owner}/{repo}/events \
--jq '.[] | select(.type=="PushEvent") | select(.payload.forced==true)'
防御策
今すぐできること
1. トークンとSSH鍵の棚卸し
GitHubの設定画面で全PATを確認し、不要なものを無効化します。SSH鍵も同様です。
# GitHub CLIでSSH鍵一覧を確認
gh ssh-key list
PATの一覧はWeb UI( https://github.com/settings/tokens )で確認できます。
2. force-push保護の有効化
デフォルトブランチへのforce-pushを禁止するブランチ保護ルールを設定します。
GitHubのリポジトリ設定 > Branches > Branch protection rules で以下を有効にします。
- "Require a pull request before merging"
- "Do not allow force pushes"(これが最も重要)
- "Require signed commits"(可能であれば)
3. 2要素認証の強化
TOTPアプリによる2FAが設定済みでも、FIDO2/WebAuthnベースのハードウェアキー(YubiKeyなど)への移行を検討します。トークン窃取に対して耐性があります。
4. VS Code拡張の監査
インストール済みの拡張一覧を確認し、見覚えのないものを削除します。
# インストール済み拡張の一覧
code --list-extensions
中期的に導入すべきこと
1. 依存関係のロックファイル管理
pip freeze の結果を requirements.txt にハッシュ付きで固定し、バージョンの改ざんを検知できるようにします。
# ハッシュ付きでrequirements.txtを生成
pip install pip-tools
pip-compile --generate-hashes requirements.in
ロックファイルは万能ではありません。2026年1月に公開されたPackageGate脆弱性(npm, pnpm, vlt, Bunに影響)では、Git依存関係経由でロックファイルを迂回できることが示されました。ロックファイルは必要条件であって十分条件ではないと認識してください。
2. コミット署名の導入
GPGまたはSSH鍵でコミットに署名し、改ざんされたコミットを検知できるようにします。
# SSH鍵によるコミット署名の設定
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
署名付きコミットがリポジトリに含まれていれば、force-pushで注入されたコミットは署名なし(Unverified)として表示されます。
3. CI/CDでの依存関係スキャン
GitHub ActionsやGitLab CI上で依存関係の脆弱性スキャンを自動化します。
# GitHub Actionsの例
- name: Run dependency audit
run: |
pip install safety
safety check --full-report
4. SBOM(Software Bill of Materials)の生成
プロジェクトの依存関係を一覧化し、影響範囲の特定を迅速に行えるようにします。
pip install cyclonedx-bom
cyclonedx-py environment -o sbom.json
5. ネットワーク監視
開発マシンからSolana RPCエンドポイント(api.mainnet-beta.solana.com など)への通信を監視対象に加えます。開発者のPCからブロックチェーンノードへの通信は通常発生しません。
まとめ
GlassWormは、サプライチェーン攻撃が新たな段階に入ったことを示しています。
- 不可視Unicode文字でコードレビューを回避する
- force-pushでGitの履歴ごと書き換え、検知を困難にする
- ブロックチェーンC2でテイクダウンを不可能にする
- 盗んだ認証情報で自己増殖し、被害を拡大する
開発者としてできる最も効果的な対策は、デフォルトブランチへのforce-push禁止とハードウェアキーによる認証です。この2つだけでも、ForceMemo攻撃の主要な侵入経路を塞ぐことができます。
「うちは小規模だから狙われない」という認識は捨ててください。攻撃者はトークンを盗んだ全アカウントのリポジトリを無差別に改ざんしています。規模の大小は関係ありません。
2026年3月時点でForceMemoキャンペーンはまだ進行中です。新たなリポジトリが引き続き侵害されています。この記事のチェックリストで自分のプロジェクトを確認することを推奨します。
参考資料
- GlassWorm Attack Uses Stolen GitHub Tokens to Force-Push Malware Into Python Repos - The Hacker News
- ForceMemo: Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push - StepSecurity
- ForceMemo: Python Repositories Compromised in GlassWorm Aftermath - SecurityWeek
- GlassWorm malware hits 400+ code repos on GitHub, npm, VSCode, OpenVSX - Bleeping Computer
- Glassworm Returns: Invisible Unicode Malware Found in 150+ GitHub Repositories - Aikido
- GlassWorm Supply-Chain Attack Abuses 72 Open VSX Extensions - The Hacker News
- GlassWorm Supply Chain Worm Uses Invisible Unicode and Solana Blockchain for Stealth C2 - Security Online
- Defending Against Glassworm - Snyk