ちょっと話題の記事

[アップデート]GuardDutyが盗まれたEC2のクレデンシャルが別AWSアカウントで利用されたことを検知できるようになったので実際に試してついでにDetectiveで調査してみた

GuardDutyがEC2から搾取されたクレデンシャルを別AWSアカウントで利用しても検知してくれるようになりました。実際にインシデントが発生した場合の対処方法も合わせて解説しています。
2022.01.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、臼田です。

みなさん、GuardDuty使ってますか?(挨拶

今日は素晴らしいアップデートが来ました。EC2から搾取されたクレデンシャルが別AWSアカウントで利用されたときにAmazon GuardDutyで検知することが出来るようになりました!

Amazon GuardDuty now detects EC2 instance credentials used from another AWS account

このFindings Typeについて解説をし、実際に検知させて動作を見たり、実際に検知した場合の対応方法を紹介します。

概要

AWS環境の侵害方法の1つに、外部向けサービスなどで動いているEC2からクレデンシャルを搾取する攻撃があります。例えばSSRF等の脆弱性がアプリケーション上に存在する場合、クレデンシャルが搾取されます。

これまでGuardDutyはEC2から搾取されたクレデンシャルについて、AWSの外から利用された場合にのみ検知することができました。そちらはUnauthorizedAccess:IAMUser/InstanceCredentialExfiltrationというFindings Typeで検知されていました。つまり、実は攻撃者が搾取したクレデンシャルをAWSアカウントから利用していたら気づくことができませんでした。(もちろん別の脅威が検知される事が多いので気づくタイミングは他にもありますが)

今回のアップデートでUnauthorizedAccess:IAMUser/InstanceCredentialExfiltrationが2種類になりました。これまでのAWS外で搾取されたクレデンシャルを利用した場合にはUnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWSと末尾に.OutsideAWSが追加され、別のAWSアカウントで利用された場合にUnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWSと末尾に.InsideAWSがついたFindings Typeが新しく追加されました。

このアップデートに合わせてGuardDutyのFindingsにremoteAccountDetailsという要素が追加されました。このフォーマットは以下になっています。

"remoteAccountDetails": {
    "accountId": "string",
    "affiliated": boolean
},

中には2つの要素があり、accountIdには搾取されたクレデンシャルの呼び出し元AWSアカウントIDが入り、affiliatedにはこのAWSアカウントが関連したAWSアカウントであるかがbooleanで入ります。

関連しているAWSアカウントというのが具体的にどのようなものであるかは詳細に書かれていませんが、AWS Organizationsで同じ組織である場合やAWS Transit Gatewayで接続されている場合には関連されているとGuardDutyが判断するような記述が、what's newには書かれています。

また、このFindingsはデフォルトの重要度は高となりますが、関連しているAWSアカウントでの利用を検知した場合には中となります。Findingsの詳細はUser Guideを参照してください。(現状は英語のみで確認可能)

やってみた

それでは実際に検知させて、動作を確認して調査していきましょう。

攻撃

まずは事前に立てておいたEC2にアクセスし、Metadataからクレデンシャルを搾取します。

[ec2-user@ip-172-31-24-107 ~]$ mu=http://169.254.169.254/latest/meta-data/iam/security-credentials/;curl -s $mu | echo $mu$(cat) | xargs -n1 curl
{
  "Code" : "Success",
  "LastUpdated" : "2022-01-22T07:57:56Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAXXXXXXXXXXXXXXG4",
  "SecretAccessKey" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "Token" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==",
  "Expiration" : "2022-01-22T14:24:21Z"
}

クレデンシャルを搾取したら別のAWSアカウントでCloudShellを立ち上げ、クレデンシャルをセットします。

export AWS_ACCESS_KEY_ID=ASIAXXXXXXXXXXXXXXG4
export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export AWS_SESSION_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==

適当にAPIを実行してみます。今回はほぼ権限のないIAM RoleをアタッチしたEC2であったため、S3バケットの取得に失敗しています。

[cloudshell-user@ip-10-0-106-126 ~]$ aws sts get-caller-identity
{
    "UserId": "AROAXXXXXXXXXXXXXXXXX:i-0f8xxxxxxxxxxxxxx",
    "Account": "999999999999",
    "Arn": "arn:aws:sts::999999999999:assumed-role/ExfiltrationEC2Role/i-0f8xxxxxxxxxxxxxx"
}
[cloudshell-user@ip-10-0-106-126 ~]$ aws s3 ls

An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

さて、仕込みが終わったので検知まで待ちましょう。今回私の手元では6分後にGuardDutyから通知がありました。えらいですね。

初動調査

GuardDutyの画面で確認していきます。UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWSが検知されています。重要度は高(High)です。つまり関連のないAWSアカウントから利用されていますね。

Findingsの詳細を確認します。下の方に行くとActorに今回の呼び出し元AWSアカウントIDが書かれていました。Affiliatedはfalseなので、ここでも関連性がないAWSアカウントであることがわかります。実際にはAWS側で関連性が確認できなくても、自社で管理しているものやパートナーのものである可能性もありますので、このアカウントIDが心当たりがあるか確認しましょう。

また、FindingsのJSONを直接確認し、搾取されたアクセスキーIDを確認しておきます。これは調査で利用します。

詳細を調査していきます。「Detectiveで調査する」からAmazon Detectiveへ移動します。リンク先はいくつかありますが、このFindings TypeであればEC2インスタンスかロールセッションあたりがいいでしょう。

「全体的な API 呼び出し量」のプロファイルパネルにて、検知した時間のグラフが黒枠で囲われているのでこれをクリックすると下側に詳細が出てきます。「アクセスキーID」タブに切り替えここで先程控えたアクセスキーIDから何が実行されたかを確認します。これはほぼ攻撃者が実行したコマンドとして考えます。このプロファイルパネルではざっくりと何が実行されたかが集計されているので直感的に影響範囲を確認しやすいです。

ここまでで、この検知が誤検知なのかや、緊急度の高いものであるかなど最低限の確認はできると思います。もう少し詳細な調査が必要な場合にはCloudTrailでアクセスキーIDを入れて各API実行履歴を確認したり、とはいえデフォルトではクエリが限定的であるのでCloudTrail LakeやAmazon Athenaで詳細なクエリをかけて調査していきます。

特に変更系のAPIが実行されているかは重要です。新しいIAMが作成されていればそれがバックドアとして利用されます。また、そのIAMでどのような操作がされたかまで調査を広げる必要があります。データアクセスも注意です。S3の重要な情報にアクセスされていないかなど、確認する必要があります。データイベントの証跡はデフォルトのCloudTrailでは記録されませんので、事前に重要な情報を保存するS3バケットにはデータイベントの記録設定をしておきましょう。Systems ManagerやSecrets Managerに保存されているクレデンシャルが盗まれていないかも確認ポイントです。

暫定対処

不正に搾取されたクレデンシャルが利用されていた場合、これを無効にします。すでに発行されたクレデンシャルを無効化する場合、IAM Roleの詳細画面にアクセスし「セッションの無効化」タブから「アクティブなセッションの無効化」を押します。これで、これ以前に発行された一時クレデンシャルが無効になります。

具体的にはインラインポリシーとして該当時間以前のクレデンシャルはDenyとなるConditionが設定されます。誤って無効化しても最悪インラインポリシーを削除すればいいだけなので積極的に活用しましょう。

とはいえ、攻撃者はこのクレデンシャルを再発行できる可能性が高いので、根本原因の調査と対策も素早く実施しましょう。またクレデンシャルが搾取されても影響が小さくなるように、明らかに権限を絞れそうなら絞ります。絞れていなかったら最小権限の原則に普段従っていなかったことを悔やみましょう。

またEC2内部の調査も重要ですが、状態を保存するためにAMIバックアップを再起動無しで取得しておきます。焦っていろいろ触ってしまうと証拠として役に立たなくなります。また、場合によってはEC2の隔離を検討します。隔離はSecurity Groupを外し、すべての通信を通さない(ルールがない)Security Groupを当てることで対処が可能です。

封じ込め・再発防止

このインシデントはアプリケーションの脆弱性に起因している事が多いので、アプリケーションがそのままである限り再度クレデンシャルが搾取される可能性があります。アプリケーションのアクセスログなどから搾取された経路を特定し、WAFによる一時的なブロックや脆弱性の修正を検討しましょう。

専門の調査会社に入ってもらうことも大事です。

インシデント対応が落ち着いたら再発防止を検討しましょう。アプリケーションレイヤーももちろんですが、AWSレイヤーでも実施しなければならない事があるでしょう。最小権限の原則を守れているか確認したいですね、特に本番環境であれば。また初動の調査やセッション無効化までは速攻できるようなプレイブックや体制づくりが必要でしょう。有効なログが取得できているかもポイントです。大きなレイヤーとしてはAWSアカウントをきちんと分離できているかも、影響範囲を抑える観点で非常に有効であることが理解できると思います。AWSアカウント内の最悪のケースとしてAdministratorAccess権限に昇格されても、AWSアカウントを超えることは容易ではありません。

また、AWSとしてはこの件を侵害レポートとして報告することも推奨しています。攻撃者のものと思われるAWSアカウントのアクティビティについて詳細を報告することで、AWSの調査が捗り他の環境への被害を防ぐことができます。

まとめ

GuardDutyが搾取されたクレデンシャルを別AWSアカウントで利用しても検知してくれるようになりました。非常に頼もしいですね。

調査方法や対処なども合わせて解説しました。日頃からの準備が影響するところですので、きちんと準備しておきましょう。