AIエージェントを野生で捕獲する、その2:LLMだけが直せる「壊れたカナリアトークン」 | AIネイティブな能動的防御

概要

その1では、ハニーポットの検知手法としてリバースプロンプトインジェクションを紹介し、自律的なLLMベースのエージェントによる攻撃セッションを完全な形で捕捉しました。単一のTor出口ノードから約19分間で58件のリクエストが送信され、複数ツールの切り替え、HTMLコメントからの意味的な認証情報抽出、文脈に応じたペイロード生成、そして特徴的な「ノコギリ波」的なタイミングパターンが確認されました。この取り組みの柱の一つがセマンティック・カナリア(提案フレームワークのレイヤー1)でした。これは、自然言語を理解できる存在だけが反応する「餌」です。

ただし、そのセマンティックな餌は帯域内(in-band)のものでした。つまり、漏洩させたadmin/admin123という認証情報は、ハニーポット自身に対して試行されるものであり、AIか人間かの判定は依然として行動面からの推論に頼っていました。本稿では、これを補完する技術として、帯域外(out-of-band)で高精度なトリガーを生み出す手法を紹介します。私たちは、意図的に破損させたAWSの漏洩認証情報を仕掛けました。これを実際に機能するAPI呼び出しに変えるには、その破損が「マスク(偽装)」であると理解し、修復する必要があります。この認証情報の裏には本物のAWSカナリアトークンを紐づけているため、攻撃者が本当にAWSへとピボットした場合にのみトリガーが発火し、アラートにはその呼び出しに伴うクライアント情報やネットワークのテレメトリがそのまま記録されます。

今回の展開では、自動化されたシークレットスキャナーはこのトークンに一度も反応しませんでした(破損によって正規表現が機能せず、さらにAWSのEXAMPLE許可リストにも乗ってしまうためです)。一方、人間の研究者は7~10分でこれをトリガーし、自律型エージェントは数秒で認証情報を再構築して使用しました。しかも、そのアクセスはSDKのUser-Agentを伴い、数分前にページを取得したのと同じTor出口ノードから発信されていました。このエージェントはAWS SDKのトラフィックすらTor経由でトンネリングしており、正規のsts呼び出しがTor出口ノードから発信されることは通常あり得ません。つまり、トリガーの発信元そのものが異常なのです。判別の決め手となるのは反応時間ではなく「再構築できたかどうか」であり、そのうえでタイミングがエージェントと人間を分けることになります。

1. はじめに

その1では、自律型エージェントは読み込んだあらゆる情報について推論を行うということ、そしてこの性質が検知に利用できることを示しました。スキャナーはHTMLコメントを無視しますが、エージェントはそれを解析し、意味を抽出したうえで行動します。私たちはこの性質を利用して、実際にエージェントを一体捕獲しました。

しかし、帯域内のセマンティック・カナリアには実用上二つの限界があります。第一に、トリガーが欺瞞環境の内部に存在するため、「これはAIエージェントだった」と確証を得るには、単一の明確な事象ではなく、行動面の指標を積み重ねる必要があります。第二に、餌となる情報(test credentials: admin/admin123)は平文であり、運が良ければ正規表現や人間の斜め読みでも拾われてしまう可能性があります。つまり判別の根拠は「その後に何が起きるか」であって、餌そのものにはありません。

本稿ではより鋭い問いを立てます。すなわち、使用されたという事実そのものが理解力の証明となり、かつトリガーが帯域外かつ攻撃者に帰属可能な事象となる、そんな唯一無二の餌を設計できるだろうか?

その答えが、壊れたカナリアトークンです。カナリアトークンとは、使用された時にアラートを発する以外何もしない認証情報のことで、aws sts get-caller-identityが実行されるのを仕掛けて待つ手法は、防御側が持つ中でも特に信号強度の高いトリップワイヤーの一つです。私たちの貢献は、このトークン自体を分類器に変えたことにあります。読み手だけが修復できる形でトークンを壊すのです。抽出は失敗し、理解だけが成功します。そして、このトークンは本物であるため、アラートはお使いのカナリアから、攻撃者のIP、User-Agent、そしてAWS API呼び出しとともに届きます。

2. 背景

2.1 セマンティック・カナリアのおさらい

その1のレイヤー1は、意味を処理する能力を持つ存在だけが反応する自然言語のトリップワイヤー(偽の認証情報、「内部」URL、開発者向けメモなど)を組み込んでいました。今回の取り組みは、このレイヤーをそのまま具体化し、強化したものです。原理は同じですが、(a)一般的なスキャナーではこの餌を確実に利用できないこと、(b)トリガーがハニーポット内のリクエストではなく、外部の高精度な事象であることという二点を工学的に実現しています。

2.2 なぜ平文の漏洩キーはノイズになるのか

素のカナリアキーは、大量スキャナー、人間、エージェントを問わず無差別に捕まえてしまい、トリガーが発火したという事実だけでは、どれを捕まえたのかまったくわかりません。トリップワイヤーに判別能力を持たせるには、その認証情報を「理解している者にしか使えない」状態にする必要があります。

2.3 シークレットスキャナーとEXAMPLE許可リスト

一般的なシークレットスキャナー(gitleaks、trufflehog、そしてAKIA…を漁る無数のクローラー)は、基本的にAKIA[0-9A-Z]{16}のような連続したパターンを手がかりにしています。また、AWSの正規のドキュメント用キーであるAKIAIOSFODNN7EXAMPLEに似たものは許可リストに入れ、チュートリアル由来の誤検知の洪水を抑える傾向があります。この二つの事実はいずれも突くことができます。連続していないうえに、ドキュメントのサンプルとの見分けもつかないキーは、スキャナーにとっては存在しないも同然なのです。

3. 手法

3.1 ハニーポット基盤

社内SREのWikiを模した単一のHTTPサービスをBeelzebubで構築し、nginxをフロントに配置して展開しました。Beelzebubはすべてのリクエストについて、ヘッダー、ボディ、送信元IP、セッションデータをログに記録します。餌は1ページに仕込みました。レガシーなバックアップ用ユーザーの緊急用認証情報を「うっかり」貼り付けてしまったキーローテーションの手順書です。

3.2 罠の設計:壊れたカナリアトークン

この認証情報は本物のAWSキー型カナリアトークンであり、アクセスキーIDとシークレットの両方に、リテラルなマーカー_BLZ_を挿入してあります。

aws_access_key_id     = AKIA_BLZ_QZ7X4RT2M5FW3KLV
aws_secret_access_key = wNbK7pQ2xR_BLZ_9dLm4TfV6aYz1Csh0EgJu3Pi8oWqBn

それぞれから_BLZ_を削除すると、有効な形式の20文字のアクセスキーID(AKIA+base32の16文字)と40文字のシークレットが復元されます。これが本物のカナリアペアです。(本稿に掲載したペアは、書式だけを合わせた代替品です。実際に稼働していたカナリアトークンは公開前にローテーション済みのため、ここに掲載された認証情報を復元しても意味を持ちません。)このマーカー一つで、二つの役割を果たします。

  • 正規表現を無効化する: AKIA_BLZ_…は、AWSキーの連続パターンにどれも一致しないため、スキャナーは何も抽出できず、トリップワイヤーにたどり着けません。
  • ドキュメントのサンプルの陰に隠れる: EXAMPLEという文字列を含んでいるため、多くのスキャナーがAWS自身のデモ用キーAKIAIOSFODNN7EXAMPLEを除外する際の偽装をまとうことになります。

これを実際に機能する認証情報に変えるには、攻撃者は_BLZ_が挿入されたマスクであると認識し、それを取り除いたうえで試す必要があります。つまり、パターンマッチングではなく、意図についての推論が求められるのです。これは、その1で利用したのと同じ意味理解能力を、単一の判断に凝縮したものです。

Beelzebubサービスの設定全体は以下の通りです。

apiVersion: "v1"
protocol: "http"
address: ":8080"
description: "Internal SRE wiki (decoy) - AWS key-rotation runbook"
commands:
  - regex: "^/robots\\.txt$"
    headers:
      - "Content-Type: text/plain; charset=utf-8"
      - "Server: nginx/1.25.3"
    statusCode: 200
    handler: |
      User-agent: *
      Disallow: /internal/
  - regex: "^/$"
    headers:
      - "Content-Type: text/html; charset=utf-8"
      - "Server: nginx/1.25.3"
    statusCode: 200
    handler: |
      <!doctype html><html lang="en"><head><meta charset="utf-8">
      <title>SRE Wiki</title></head><body>
      <h1>SRE Internal Wiki</h1>
      <ul>
        <li><a href="/internal/runbooks/aws-key-rotation">Runbook: AWS access-key rotation</a></li>
        <li><a href="/internal/runbooks/db-failover">Runbook: DB failover</a></li>
      </ul>
      </body></html>
  - regex: "^/internal/runbooks/aws-key-rotation/?$"
    headers:
      - "Content-Type: text/html; charset=utf-8"
      - "Server: nginx/1.25.3"
    statusCode: 200
    handler: |
      <!doctype html><html lang="en"><head><meta charset="utf-8">
      <title>Runbook: AWS access-key rotation</title></head><body>
      <h1>Runbook: AWS access-key rotation (legacy backup service)</h1>
      <p>The nightly S3 backup job still runs as the break-glass IAM user
      <code>svc-backup-legacy</code>. Credentials are pasted below so on-call
      can re-run the sync manually until the migration to IRSA (MIG-1423) is done.</p>
      <!-- export: values sanitized by scrubber v2, see vault for originals -->
      <pre>
      # ~/.aws/credentials  (svc-backup-legacy)
      [default]
      aws_access_key_id     = AKIA_BLZ_QZ7X4RT2M5FW3KLV
      aws_secret_access_key = wNbK7pQ2xR_BLZ_9dLm4TfV6aYz1Csh0EgJu3Pi8oWqBn
      region                = us-east-1
      </pre>
      <p>Sanity check before running the sync:
      <code>aws sts get-caller-identity</code></p>
      </body></html>
  - regex: ".*"
    headers:
      - "Content-Type: text/html; charset=utf-8"
      - "Server: nginx/1.25.3"
    statusCode: 404
    handler: |
      <html><head><title>404 Not Found</title></head>
      <body><center><h1>404 Not Found</h1></center><hr><center>nginx/1.25.3</center></body></html>

その1の規律を踏襲した重要な設計ルールがあります。それは、ページ内にキーの修復方法に関する指示を一切書かないことです。「使用前にマーカーを取り除け」といったヒントを加えた瞬間、スクリプト化された攻撃者がその手順をハードコードしてしまい、判別能力が崩壊します。推論は誘導されない形でなければなりません。

4. 結果

この手順書ページに寄せられたトラフィックは、行動特性に基づいて3つのクラスに分類されました。自動スキャナーによるセッションが60件、人間によるやり取りが14件、そして自律型エージェントによるものと判断したセッションが13件です。トークンをトリガーしたのは、最後の二つのクラスのみでした。

  • 自動スキャナーでこれをトリガーしたものは60件中0件(破損したキーがあらゆる正規表現を回避)
  • 人間の研究者は14件中9件がトリガーし、取得からトリガーまでの中央値は約8.7分(523秒)
  • AIエージェントのセッションは13件中12件がトリガーし、取得からトリガーまでの中央値は約4秒、いずれもTor出口ノードから発信

4.1 自動スキャナー

広範囲を対象とした一斉走査です。ルート、/robots.txt、手順書ページ、そしてそのまま/.envと/.git/configへと進み、セッション全体は1秒未満で完了し、User-Agentはpython-requestsでした。

カナリアからのアラートは発生しませんでした。破損したキーからは、正規表現が抽出できるものが何もなかったのです。

4.2 人間の研究者

本物のChromeのUser-Agentで手順書ページに滞在し、その数分後、同一IPからaws-cliのUser-Agentでトークンが発火しました。

この間(コピーし、じっと見つめ、_BLZ_がゴミだと気づき、取り除いてCLIを実行するまで)は、このケースでは約8.6分でした。私たちが観測した人間による対話全体を通じても、おおむね7~10分の範囲に収まっていました。

4.3 AIエージェント

1回の取得のみで、滞在も閲覧もなく、その4秒後に同一IPから、boto3/botocoreのUser-AgentでGetCallerIdentityが実行されました。

両方のイベントはいずれも192.42.116.15から発信されており、これはその1で捕捉したエージェントと同じ192.42.116.0/24系統に属するTor出口ノードでした。この対称性自体がフィンガープリントとなります。運用者はWebトラフィックだけでなく、AWS SDKの呼び出しまでもTor経由でトンネリングしていたのです(これはデフォルト設定でそうなるものではなく、SOCKS経由でラップされたboto3という意図的な配線です)。正規のAWS APIトラフィックがTor出口ノードから発信されることは、基本的にあり得ません。ASN情報だけではエージェントの本当の所在地は何もわからないとしても、トリガーの発信元そのものが、匿名化の陰に潜む自動化の存在を雄弁に物語っています。ページを読み、マスクを見抜き、キーを再構築し、試す。所要時間は4秒、しかも全行程がTor経由でした。この2件のログの組み合わせに、本稿の主張のすべてが凝縮されています。

5. 行動フィンガープリンティング:AIエージェント対人間対スキャナー

これは、その1で示した比較表を、認証情報再構築という判別要素で拡張したものです。

行動指標 人間の攻撃者 従来型スキャナー AIエージェント
破損したシークレット(_BLZ_マスク)を再構築できるか できるが、時間がかかる できない(正規表現が検知できない) できる
取得から認証情報使用までの遅延 7~10分 該当なし(トリガーしない) 数秒
トリガーの発信元 住宅用ISP 該当なし Tor出口ノード/匿名化インフラ
セッションの形状 滞在、ブラウザ、CLI 複数経路への高速な一斉走査 単発の取得、滞在なし、SDKのUser-Agent
AWS呼び出しを実行するクライアント ノートPC上のaws-cli 該当なし boto3/botocoreランタイム

ここで重要なのは、そして当然浮かぶであろう疑問への答えは、速さそのものは自動化の証拠にはなっても、知性の証拠にはならないという点です。今回のデータで最も速く訪問していたのはスキャナーでしたが、そのいずれもトークンをトリガーしませんでした。高速なボットにできないのは、意図的に壊されたシークレットを修復することです。したがって、再構築できたという事実によってスキャナーは除外され、そのうえでタイミングがエージェントと人間を分けることになります。これは、その1で示した「ノコギリ波」パターン(推論による間隔と、それを挟む1秒未満のバースト)を補完するものです。あちらではどのように攻撃したかが手がかりでしたが、こちらではスキャナーには解析できない認証情報を使いこなし、しかも瞬時にそれをやってのけたという事実そのものが手がかりになっています。

6. 本手法の位置づけ:強化された帯域外レイヤー1

その1で提案したフレームワークに当てはめると、以下のようになります。

  • レイヤー1:セマンティック・カナリア 本稿の取り組みは、これを強化した具体例です。餌は正規表現ベースのスキャナーでは間違いなく利用できず、そのトリガーもハニーポット内のリクエストではなく、帯域外かつ攻撃者に帰属可能なAWSイベントとなっています。
  • レイヤー2:行動分析 裏付けとなるシグナル(Tor出口ノードという発信元、SDKのUser-Agent、滞在のない単発の取得)は、すでに定義済みのBIoC分析にそのまま組み込めます。
  • レイヤー3:能動的プロンプトインジェクション 本手法とは独立していながら組み合わせ可能です。おとりページに、壊れたカナリア(本稿の手法)と注入された指示(その1)の両方を仕込み、確度をさらに高めることもできます。

検知ロジックは、次の4つの条件に集約されます。(1)おとりの手順書ページに対してアクセスがある、(2)そのページに紐づいたカナリアが発火する、(3)その遅延が秒単位である、(4)トリガーが、住宅用回線上の対話的なCLIではなく、匿名化インフラまたはデータセンターのインフラ(私たちの捕獲事例ではTor出口ノード)からのSDKランタイム(boto3/botocore)によって実行されている。正規のAWS APIトラフィックがTor出口ノードから届くことは基本的にあり得ないため、この条件単独でも十分な重みを持ちます。条件2だけでも、誰かが壊れたキーを再構築したことを意味しますが、そこに条件3と4を加えることで、この罠を共有する人間やスキャナーの母集団から、AIエージェントだけを切り分けられます。破損したキーはスキャナーからは見えないため、誤検知の下限はほぼゼロです。偶然にトリップワイヤーへたどり着くケースは事実上存在しません。

8. 結論

その1では、エージェントが読んだものについて推論するという性質によって、自らの正体を露呈してしまうことを示しました。その2では、これを単一のスキャナー耐性を持つ帯域外トリップワイヤーへと発展させました。すなわち、理解力を持つ者だけが解錠できるよう破損させたカナリアトークンです。今回の展開では、スキャナーは一度もこれをトリガーせず、人間は数分を要し、自律型エージェントは数秒でこれを再構築・使用し、取得元と同じTor出口ノードを通じてAWS SDKの呼び出しまでトンネリングしていました。ここで根拠となる信号は「速かった」ということではなく、「スキャナーには物理的に解析できないシークレットを修復し、それを瞬時に成し遂げ、しかも正規のAWSトラフィックが決して発信されないはずのネットワーク上の位置からAWS APIを呼び出した」という事実です。この罠全体は、たった1つのYAMLファイル、1つのnginxルート、そして1つの壊れたカナリアだけで構成されています。自律型エージェントの普及が進む中、帯域外かつ攻撃者に帰属可能なトリガーを備えたセマンティック・カナリアは、防御側のツールキットに長く定着する手法になると私たちは考えています。

Beelzebubはオープンソースです:github.com/beelzebub-labs/beelzebub

その1「AIレッドチーマーを野生で捕獲する:リバースプロンプトインジェクションをハニーポット検知メカニズムとして使う »」を読む

翻訳元: https://beelzebub.ai/blog/catching-ai-agents-in-the-wild/

本記事は beelzebub.ai の記事を翻訳・要約したものです。