概要
第1回では、ハニーポットの検知手法として「リバース・プロンプトインジェクション」を紹介し、自律型LLMエージェントによる攻撃セッション全体の捕捉に成功しました。単一のTor出口ノードから約19分間で58件のリクエストがあり、複数ツールの切り替え、HTMLコメントからの意味的な認証情報抽出、文脈に応じたペイロード生成、そして特徴的な「ノコギリ波」型のタイミングパターンが確認できました。この研究の柱の一つが「セマンティック・カナリア」(提案フレームワークのレイヤー1)であり、これは自然言語理解が可能な存在だけが反応する「餌」でした。
ただし、そのセマンティックな餌は「帯域内」のものでした。漏洩させたadmin/admin123という認証情報はハニーポット自体に対して再テストされる形であり、AIか人間かの判定は依然として行動面の推論に頼っていました。本稿では、これを補完する手法を紹介します。すなわち「帯域外」かつ高精度なトリガーを生み出す手法です。私たちは意図的に「破損」させたAWS認証情報を仕掛けました。これを動作するAPI呼び出しへと変換するには、その破損が偽装であると理解し、修復する必要があります。この認証情報の裏側には実際のAWSカナリアトークンを紐づけているため、攻撃者が本当にAWSへとピボットした場合にのみトリガーが発動し、その際に攻撃者が使用したクライアントやネットワークのテレメトリ情報とともにアラートが届きます。
私たちの展開環境では、自動化されたシークレットスキャナーはこのトークンを一度も検知しませんでした(破損によって正規表現をすり抜け、さらにAWSのEXAMPLE許可リストにも乗ってしまうためです)。人間の研究者は7分から10分でトークンを発動させ、自律型エージェントはわずか数秒で認証情報を再構築し使用しました。しかもそれは、数分前に同じページを取得したのと同じTor出口ノードから、SDKのUser-Agentを使って行われたものでした。このエージェントは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 ハニーポット基盤
私たちはBeelzebubを使い、nginxを前段に配置した単一のHTTPサービスを展開し、社内SREウィキを模倣しました。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+16文字のbase32文字列)と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. 結果
この手順書ページには、行動パターンに基づき三つの分類に分けられるトラフィックが集まりました。自動スキャナーによるセッションが60件、人間によるやり取りが14件、そして自律型エージェントによるものと判断したセッションが13件です。トークンを発動させたのは後者二つの分類のみでした。
- 60件中0件の自動スキャナーが発動(破損したキーがすべての正規表現をすり抜けました)
- 14件中9件の人間の研究者が発動、取得から発動までの中央値は約8.7分(523秒)
- 13件中12件のAIエージェントのセッションが発動、取得から発動までの中央値は約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情報がエージェントの実際の所在地について何も教えてくれない場合でも、トリガーの発信元だけで、匿名化ツールの背後にある自動化の存在が明白に分かります。ページを読み、偽装を見抜き、キーを再構築し、それを試す。すべてTorを経由して4秒。このログの組み合わせ一つに、本稿の主張のすべてが凝縮されています。
5. 行動指紋:AIエージェント vs 人間 vs スキャナー
ここでは、第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回)の両方を仕込み、確度をさらに高めることができます。
検知ロジックは四つの条件に集約されます。(1)おとりの手順書に対して発信元がアクセスすること、(2)そのページに紐づいたカナリアが発動すること、(3)発動までの遅延が秒単位であること、(4)トリガーの実行が、個人向け回線上の対話的なCLIではなく、匿名化インフラやデータセンターのインフラ(私たちの捕捉事例ではTor出口ノード)からのSDKランタイム(boto3/botocore)によって行われることです。正規のAWS APIトラフィックがTor出口ノードから届くことは基本的にないため、この条件単独でも十分な重みを持ちます。条件2だけでも、誰かが壊れたキーを再構築したことを意味しますが、そこに条件3と4を加えることで、この罠を共有する人間・スキャナーの集団からAIエージェントを切り分けることができます。破損したキーはスキャナーにとって見えない存在であるため、誤検知率はほぼゼロに近く、偶然トリップワイヤーに到達するものは事実上存在しません。
8. 結論
第1回では、エージェントが読み取った内容について「推論する」ことで、自らの正体を露呈してしまうことを示しました。第2回では、これを単一の、スキャナーには突破できない帯域外トリップワイヤーへと昇華させました。理解した者だけが解錠できるよう破損させたカナリアトークンです。私たちの展開環境では、スキャナーが発動させることは一度もなく、人間は数分を要し、自律型エージェントは数秒で再構築・使用に至りました。しかもそのAWS SDK呼び出しさえも、ページ取得に使ったのと同じTor出口ノードを経由してトンネリングされていました。決定的な根拠となるのは「速かった」ということではなく、「スキャナーが物理的に解析できない秘密情報を修復し、それを瞬時に行い、しかも正規のAWSトラフィックが決して発生しないネットワーク上の場所からAWS API呼び出しを行った」という事実です。この罠全体は、1つのYAMLファイルと1つのnginxルート、そして1つの破損したカナリアだけで構成されています。自律型エージェントが今後さらに普及していく中で、帯域外かつ攻撃者に紐づけ可能なトリガーを備えたセマンティック・カナリアは、防御側のツールキットに加えるべき有効な手法であると私たちは考えています。
Beelzebubはオープンソースです:github.com/beelzebub-labs/beelzebub
翻訳元: https://beelzebub.ai/blog/catching-ai-agents-in-the-wild/