Cognitoの自己サインアップが有効だと、誰でもAWSの一時認証情報を取得できる:AI攻撃ツールARTEXの事例と確認したい設定3点

Cognitoの自己サインアップが有効だと、誰でもAWSの一時認証情報を取得できる:AI攻撃ツールARTEXの事例と確認したい設定3点

Datadog は、攻撃エージェント ARTEX が公開された Cognito ユーザープールに自己サインアップし、ID プールの一時認証情報で AWS を探索した様子を報告しました。成立する理由と、自分のユーザープールと認証済みロールの確かめ方を整理します。
2026.11.14

どうも!オペ部の西村祐二です!

S3 にフロントエンドを置き、Amazon Cognito のユーザープールでログインさせ、ID プールで AWS の一時認証情報を渡す。AWS で SPA を作るときによく見る構成です。この構成が攻撃用の AI エージェントにどう使われたかを、Datadog Security Labs が2026年11月9日の記事「On the hunt for ARTEX: understanding agentic attacks in AWS environments」で報告しました。

エージェントは、公開されたユーザープール(自己サインアップが有効で、アプリクライアント ID がフロントエンドから分かるもの)にサインアップを試み、拒否されても試行を続けて登録しました。得たトークンは AWS の一時認証情報に交換し、アカウント内の探索を試みています。脆弱性ではなく設定の組み合わせで成立する流れなので、自己サインアップと ID プールを併用している環境なら、設定を確かめておく必要があります。

先に結論:確認と対策

確認すること 対策
ユーザープールで自己サインアップが有効か(AllowAdminCreateUserOnly) 一般利用者の登録が要らないユーザープールは true にして、管理者だけがユーザーを作れるようにする
認可に使う属性(custom:isAdmin など)を、利用者が自分で書き込めないか(アプリクライアントの WriteAttributes) 認可に使う属性を、すべてのアプリクライアントの WriteAttributes から外す
ID プールの認証済みロールの権限と、ロールの割り当て設定(RoleMappings) 「サインアップすれば誰でも引き受けられる」前提で権限を絞る。利用者が書き込める属性で高い権限のロールを割り当てない
SignUp の急増や、ID プールの一時認証情報のセッション(ロールセッション名 CognitoIdentityCredentials)での権限不足エラーの多発に気付けるか CloudTrail の管理イベントを監視する。ID プールの操作や S3 のオブジェクト操作も追うなら、データイベントを有効にする

何が観測されたのか

Datadog の記事によると、ARTEX は AI ネイティブのペネトレーションフレームワークと説明されています。オペレーターが標的と目的を与えると、複数の LLM エージェントが計画を立てて攻撃を進めます。ARTEX は2026年7月に中国の開発者が GitHub で公開したもので、フォーク数が12,000を超えた後の10月8日にクローズドソースになったとされています。記事では、Microsoft がこれを脅威アクター Storm-0940 と結び付けていることにも触れています。

Datadog は2026年10月に、ARTEX が「複数の(several)」AWS 環境で動いた跡を観測しました。金融データへのアクセスに重点を置いていたとされていますが、被害を受けた組織の数や業種の内訳は書かれていません。観測された活動は次のとおりです。

  • 確認できたすべてのセッションは、認証なしの S3 の読み取り(GetObject、ListObjects など)から始まっていました。CloudTrail では userIdentity.type が AWSAccount、accountId が anonymous として記録されます。エージェントが S3 でホストされた Web アプリのフロントエンドを読み、ビルドマニフェストや実行時の設定を探す様子がよく見られました
  • その先は2つの系統に分かれます。1つは漏れた長期アクセスキーを使う系統で、Datadog が示した1件では、最も多く実行された操作が FilterLogEvents による CloudWatch Logs の検索でした。この系統はこの記事では扱いません。もう1つは、この記事で扱う Cognito の系統です
  • Cognito の系統では、一例として10月26日と27日に、1つの IP アドレスから公開されたユーザープールへの SignUp の呼び出しが急増しました。一部の呼び出しの User-Agent には Mozilla/5.0 ARTeX のようにツール名が入っていて、ほかは curl やブラウザーの文字列でした

サインアップから一時認証情報での探索までの流れ

次の図は、Datadog の記事をもとに自作した概念図です。入口の検証で一部の登録は拒否されましたが、探索を止めたのは認証済みロールの権限不足でした。

  1. 前提条件: ユーザープールで自己サインアップが有効になっていることです。加えて、エージェントがユーザープールのアプリクライアント ID を知っている必要があります。一時認証情報まで進むには、このユーザープールを認証プロバイダーとして受け付ける ID プールがあることも必要です。Datadog の記事は、エージェントがどこでこれらの ID を得たかを書いていません。ただ、SPA はブラウザーから SignUp などを直接呼び、SignUp の呼び出しにはアプリクライアント ID(ClientId)が必須なので、アプリクライアント ID はフロントエンドのコードや設定に含まれます。AWS のユーザー作成のポリシーに関するドキュメントも、サインアップを受け付けるユーザープールでは、公開されていて見つけられるアプリクライアント ID を知っていれば、インターネットの誰でもユーザーを作れると説明しています。S3 のフロントエンドを探していたという観測と合わせると、そこから得た可能性があります
  2. 悪用: エージェントは SignUp を呼びました。このユーザープールにはテナントのドメインを確かめる PreSignUp の Lambda トリガーがあり、試行の一部はこのトリガーで拒否されました(無効なテナントのドメイン、ドメインの形式の不正、テナントの不一致)。ほかに、custom:isAdmin の型を判定できないというスキーマのエラーも出ています。エージェントはそれでも試行を続けました。登録に成功した試行では、SignUp、InitiateAuth、RespondToAuthChallenge、GetUser、GetTokensFromRefreshToken の順に呼んでトークンを得ています。続けて Get* 系の呼び出しと ListUserPools も試しました。登録の確認(ConfirmSignUp など)の段階がどう通ったかは、記事に書かれていません
  3. 影響: エージェントは ID プールでトークンを AWS の一時認証情報に交換しました。CloudTrail は、この認証情報によるセッションをロールセッション名 CognitoIdentityCredentials で記録しています。エージェントは S3 のバケット一覧(ListBuckets)、Secrets Manager のシークレット一覧(ListSecrets)、Systems Manager のパラメーター(DescribeParameters)、CloudWatch Logs のロググループ(DescribeLogGroups)、Lambda の関数一覧(ListFunctions20150331)など、読み取り系の探索をほぼ一通り試しました。ID プールの認証済みロールに必要な権限がなかったため、すべて失敗しました

認証済みロールに ListBuckets や ListSecrets が許可されていたら、エージェントは同じ流れでバケットやシークレットの一覧を取得できていたことになります。

なぜ成立するのか

自己サインアップは「誰でもユーザーを作れる」設定

ユーザープールの自己サインアップは、AdminCreateUserConfigType の AllowAdminCreateUserOnly で決まります。API リファレンスによると、true なら管理者だけがユーザーを作れます。false なら、利用者が SignUp 操作で自分を登録できます。

一般の利用者が登録するアプリでは、自己サインアップは必要な機能です。一方、社内向けや招待制のアプリで自己サインアップを残していると、意図していない人もユーザーを作れる状態になります。

ID プールの「認証済み」は「承認済み」ではない

ID プールは、認証されたユーザーに IAM ロールの一時認証情報を渡します。ロールベースのアクセス制御のドキュメントでは、認証済みユーザーのデフォルトのロールと、未認証のゲスト用のロールを分けて設定できると説明されています。

ここでの「認証済み」は、ユーザープールでログインできたという意味です。自己サインアップが有効なら、PreSignUp で検証していても、その条件を満たす値を用意できれば誰でもログインできるユーザーを作れます。そのため、認証済みロールは事実上誰でも引き受けられます。認証済みロールの権限は、「信頼できる社員や顧客が使う権限」ではなく「インターネットの誰かが使える権限」として設計する必要があります。

利用者は自分の属性に書き込める

Datadog の記事に出てくる custom:isAdmin のスキーマのエラーは、エージェントがこのカスタム属性を設定しようとしていたことを示しています。記事に載っているのは、この属性の型を判定できずに拒否されたエラーです。その後に設定できたかどうか、どのアプリクライアントで試したかは書かれていません。ただ、利用者が認可に使う属性を書き込めると、自分で custom:isAdmin のような値を設定して権限を上げられます。

ユーザー属性のドキュメントは、この点について次のように書いています。

  • アプリクライアントの WriteAttributes を指定しない場合、そのアプリクライアントはすべての標準属性とカスタム属性に書き込めます。利用者は UpdateUserAttributes で自分の属性の値を変えられます。SignUp で登録するときも、書き込み権限のあるアプリクライアントなら属性に値を入れられます。変更不可(immutable)にしたカスタム属性でも、ユーザーを作るときには値を設定できます
  • 属性はアプリクライアント間で共有されるため、1つのアプリクライアントから書いた値が、ほかのアプリクライアントが発行するトークンにも入ります

ID プールのルールベースのマッピング(トークンのクレーム、つまりユーザー属性などの値に応じてロールを選ぶ設定)にも同じ注意点があります。ロールベースのアクセス制御のドキュメントにあるベストプラクティスによると、利用者が変更できるクレームをロールに対応させると、どの利用者もそのロールを引き受けられます。

拒否された後も試行が続いた

Datadog の記事には、テナントのドメインの不一致やスキーマのエラーといった拒否の理由が載っています。こうした理由が SignUp の応答として呼び出し元にも返るなら、判定の条件を推測する手がかりになり得ると考えられます。今回のエージェントも、拒否された後に試行を続けて登録まで進んでいます。

自分の環境が影響を受けるか確かめる

AWS CLI で、ユーザープールと ID プールの設定を確かめられます。<...> の部分は自分の環境の ID に置き換えて実行します。リージョンは CLI のプロファイルや AWS_REGION の設定が使われるので、必要なら --region を付けます。コマンドは API リファレンスをもとに書いたもので、実行結果は載せていません。ID が分からない場合は、aws cognito-idp list-user-pools --max-results 60 と aws cognito-identity list-identity-pools --max-results 60 で一覧できます。

自己サインアップが有効かどうかは、DescribeUserPool の AdminCreateUserConfig で確かめます。true 以外(false の場合や、値が返らない場合)は自己サインアップが有効とみなして確認を進めます。

aws cognito-idp describe-user-pool \
  --user-pool-id <ユーザープールID> \
  --query 'UserPool.AdminCreateUserConfig.AllowAdminCreateUserOnly'

属性の書き込み権限はアプリクライアントごとに決まるので、先に ListUserPoolClients でアプリクライアントの一覧を取得します。

aws cognito-idp list-user-pool-clients \
  --user-pool-id <ユーザープールID> \
  --query 'UserPoolClients[].ClientId'

取得したアプリクライアントごとに、describe-user-pool-client で WriteAttributes を確認します。ユーザー属性のドキュメントによると、ReadAttributes と WriteAttributes はデフォルト以外の権限を設定したときだけ返ります。--query の結果が null の場合は、すべての属性に書き込めるデフォルトの状態です。なお、同じドキュメントによると、必須属性は WriteAttributes の設定にかかわらず、すべてのアプリクライアントが書き込めます。

aws cognito-idp describe-user-pool-client \
  --user-pool-id <ユーザープールID> \
  --client-id <アプリクライアントID> \
  --query 'UserPoolClient.WriteAttributes'

ID プールがどのユーザープールを受け付けているかは、DescribeIdentityPool の CognitoIdentityProviders で確かめます。あわせて、ゲストアクセス(AllowUnauthenticatedIdentities)が有効かも見ます。true なら、サインアップしなくても未認証ロールの一時認証情報を得られるので、未認証ロールの権限も同じ観点で確かめます。

aws cognito-identity describe-identity-pool \
  --identity-pool-id <IDプールID> \
  --query '{Providers: CognitoIdentityProviders[].ProviderName, Guest: AllowUnauthenticatedIdentities}'

ID プールのロールは GetIdentityPoolRoles で調べます。Roles に authenticated と unauthenticated のロールが入ります。RoleMappings には ID プロバイダーごとのロールの割り当て設定が入ります。Type が Rules ならルールに従ってロールが決まり、Token ならトークンに含まれるロール(ユーザープールの場合は、グループに設定したロール)が使われます。

aws cognito-identity get-identity-pool-roles \
  --identity-pool-id <IDプールID>

ロールの権限は、ARN の末尾のロール名を使って IAM で確かめます。Roles のロールに加えて、RoleMappings で割り当てられるロールも対象にします。

aws iam list-attached-role-policies --role-name <ロール名>
aws iam list-role-policies --role-name <ロール名>

結果が次のどれかに当てはまれば、見直しが必要です。

  • 自己サインアップが要らないユーザープールで、AllowAdminCreateUserOnly が true になっていない
  • 認可に使う属性が、いずれかのアプリクライアントの WriteAttributes に含まれている(または WriteAttributes が null のデフォルトのまま)
  • 認証済みロールに、アプリが使わない操作(S3 のバケット一覧、Secrets Manager、Systems Manager のパラメーターなど)が許可されている
  • RoleMappings のルールが、利用者が書き込める属性をもとに高い権限のロールを割り当てている
  • ゲストアクセスが要らないのに AllowUnauthenticatedIdentities が true になっている

守り方:自己サインアップの要否を決め、属性とロールで止める

自己サインアップの要否と属性の書き込み権限を見直す

自己サインアップが不要なら、AllowAdminCreateUserOnly を true にします。一般の利用者が登録するアプリで自己サインアップが必要な場合は、PreSignUp などの入口の検証だけに頼らず、属性の書き込み権限と認証済みロールの権限を絞ります。

認可に使う属性(custom:isAdmin、custom:tenantId など)は、前述のユーザー属性のドキュメントが求めているとおり、すべてのアプリクライアントの WriteAttributes から外します。同じドキュメントでは、アプリクライアントを作るときに読み書きの権限を明示し、アプリに必要な最小限の属性だけを許可することがベストプラクティスとされています。

PreSignUp のエラーメッセージに、判定の条件を推測できる内容を含めないことも検討できます。これは Datadog の観測から考えた運用上の工夫で、AWS のドキュメントに明記された推奨ではありません。

ID プールの認証済みロールを絞る

認証済みロールには、アプリが必要とする操作とリソースだけを許可します。ルールベースのマッピングを使う場合は、ロールベースのアクセス制御のドキュメントのベストプラクティスのとおり、高い権限のロールには、利用者が直接設定できないクレームだけを対応させます。

ロールの信頼ポリシーには、同じドキュメントの例と同じく、2つの条件を付けます。cognito-identity.amazonaws.com:aud で自分の ID プールに限定する条件と、cognito-identity.amazonaws.com:amr が authenticated であることを求める条件です。JSON の <IDプールID> には、<リージョン>:<GUID> 形式の ID プール ID を入れます。

trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "<IDプールID>"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "authenticated"
        }
      }
    }
  ]
}

この条件では、自己サインアップしたユーザーは除けません。そのため、信頼ポリシーの条件は、権限ポリシー側の絞り込みと組み合わせて使います。

CloudTrail で検知する手がかり

ユーザープールの API(SignUp、InitiateAuth など)は、CloudTrail に管理イベントとして記録されます。証跡を CloudWatch Logs に送っていれば、ドキュメントにある次のようなクエリで、送信元 IP アドレスと操作の組み合わせごとの件数を確かめられます。期間を区切って実行し、SignUp が特定の IP アドレスから急に増えていないかを比べます。

CloudWatch Logs Insights
filter eventSource = "cognito-idp.amazonaws.com"
| stats count(*) by sourceIPAddress, eventName

一方、ID プールの GetId や GetCredentialsForIdentity は CloudTrail のデータイベントで、デフォルトでは記録されません。追跡したい場合は、証跡でデータイベントを有効にし、AWS::Cognito::IdentityPool のイベントセレクターを追加する必要があります。Cognito の CloudTrail のドキュメントには、データイベントには追加料金がかかると書かれています。交換した一時認証情報で呼んだ ListBuckets などの API は管理イベントとして記録されるので、ロールセッション名 CognitoIdentityCredentials で見分けられます。

S3 の GetObject や ListObjects もデータイベントで、S3 のドキュメントによるとデフォルトでは記録されません。データイベントを有効にしていない環境では、Datadog が見た匿名の S3 読み取り(accountId が anonymous)を CloudTrail から追えません。

ほかに、Datadog の記事は次のような手がかりを挙げています。

  • 権限不足のエラーが大量に出ること。今回の観測では、ロールセッション名 CognitoIdentityCredentials のセッションで起きていました。errorCode の表記は AccessDenied や AccessDeniedException など、サービスによって異なります
  • S3 のオブジェクトの列挙や、AWS 外の IP アドレスからインスタンスロールの認証情報が使われること。後者は Cognito の系統に限らない手がかりです
  • 原文の付録にある User-Agent(Mozilla/5.0 ARTeX、artex-readonly/1.0 など10件)と IP アドレス10件。ただし、User-Agent は変更できると原文自身も注意を促しています

まとめ

  • Datadog の観測では、攻撃エージェントが公開された Cognito ユーザープールへのサインアップを試み、拒否されても試行を続けて登録しました。その後、ID プールの一時認証情報で AWS の探索まで進んでいます。探索が失敗したのは、認証済みロールに必要な権限がなかったからです
  • 設定で確かめるのは次の3点です
    • 自己サインアップが本当に必要か(AllowAdminCreateUserOnly)
    • 認可に使う属性を利用者が書き込めないか(WriteAttributes)
    • 認証済みロールの権限とロールの割り当てが、「誰でも使える」前提で絞られているか
  • 検知には CloudTrail の管理イベント(SignUp の急増)が使えます。ID プールの操作や S3 のオブジェクト操作まで追うなら、データイベントの有効化が必要です

誰かの参考になれば幸いです。


参考リンク:

この記事をシェアする

関連記事