Security-JAWS DAYS AWS CTF Writeup
所感
Security-JAWS DAYS AWS CTFに参加してきまして、4/97位でした。
問題は数が多く、幅広い難易度、問われる知識も多く、初心者から経験者まで多くの方が楽しめるイベントだったと思います。(作問者さんすげー)
個人的にはTOP3に入れなかったのが悔しいですが、Hard問のFirst bloodを取れたので悔いはありません!
以下、Writeupです。
Warmup
AWS CLI practice
アクセスキーIDとシークレットアクセスキーが渡され、アカウントIDを提出する問題です。
aws configure
でもらった資格情報をプロフィールに設定し、aws sts get-caller-identity
を使用することでアカウントIDが取得できます。
Run function
アクセスキーIDとシークレットアクセスキーが渡されます。 問題名からlambdaに関する問題だとわかりました。
とりあえずaws configure
します。
ユーザー名を取得します。ctf_challenge_6
というユーザーでした。
ctf_challenge_6
に割り当てられたポリシーを確認するとrunlambda
というポリシーが存在します。
ポリシーの詳細を確認すると、run_me
というリソースに対してlambda:InvokeFunction
が許可されています。
関数を実行し、出力を確認すると「ログを見ろ」と言われています。
もう一度関数を実行し、今度は--log-type Tail
を指定し、ログを表示します。
表示されたBase64文字列をデコードするとフラグが取得できます。
Find data 1
問題文からS3の問題ということがわかります。
また、今回はAWSコンソールを使用する問題のようです。
サインインURLにアクセスし、もらった資格情報でログインします。
S3のバケットを探索するとhimituno-bucket1
の中にFLAG
というオブジェクトがあることに気づきました。
ダウンロードするとフラグが記載されています。
Find data 2
Find data 1
と同じS3問題のようです。
アクセスキーIDとシークレットアクセスキーが渡されますのでとりあえずaws configure
します。
アクセスできるバケットを列挙しているとhimituno-bucket2
にSECRET
というディレクトリが存在していることがわかりました。
SECRET/
以下を表示すると、1000個近くのディレクトリが存在していました。
aws s3 sync
でローカルに全てのファイルをコピーします。
適当なディレクトリを除くとハズレが表示されたのであたりのディレクトリを探す必要があるようです。
ファイルサイズでソートすると444
ディレクトリだけサイズが異なります。
(公式Writeupを見るとaws s3 ls
だけでサイズが違うファイルを見つけることができるようでした…)
aws s3 ls "s3://himituno-bucket2/SECRET/" --recursive --human --sum --profile jaws2 | awk -F ' +' '{printf "%s%s %s\n",$3,$4,$5}' | sort -n -r | head -n10
※ 正解のディレクトリが444なのは、ぱちんこ海物語シリーズでサメ図柄が揃うと444だから、だそうです。 ラウンド数が少なかったり時短だったりしょっぱい図柄ですね。
正解のディレクトリの中にフラグが書かれた画像がありました。
Show IAM policy
ポリシーについての問題です。実はWarmup問題でこれだけ解けませんでした。
ただ学ぶことがあったので書きます。
アクセスキーIDとシークレットアクセスキーが渡されますのでとりあえずaws configure
します。
ユーザー名を取得します。
ここから、aws iam list-user-policies
やaws iam list-attached-user-policies
などを利用してポリシーを確認しようとしましたが、ポリシーが存在せず、お手上げとなりました。
公式のWriteupを確認するとポリシーはIAMユーザーには直接アタッチされておらずグループにアタッチされている
とあり、ほぇ〜となりました。
まず、aws iam list-groups-for-user
でユーザーが所属するグループを確認します。
selfcheck
というポリシーがあたっていることが確認できます。
ポリシーの詳細を確認するとSid
にBase64っぽい文字列が入っているのでデコードするとフラグが得られます。
Where is the password?
AWSコンソールを利用する問題です。
問題文からSecretManagerの問題っぽいことがわかりました。
与えられた資格情報でAWSコンソールにログインします。
Secret Managerにアクセスすると、一つだけシークレットが存在していました。
シークレットの値を取得するとそれがフラグになっています。
Easy
Recon the website
Webサイトを列挙してフラグを取得する問題です。
Webサイトにアクセスしてみますが特に機能と言った機能は無いようです。
静的サイトのようでしたので、静的サイト→S3?となり、WebサイトのURLをS3バケット名としてアクセスしてみると、ディレクトリが表示され、FLAGファイルが見えました。
アクセスするとFLAGファイルがダウンロードでき、中にフラグが記載されていました。
Find data 3
S3問題の3問目です。
アクセスキーIDとシークレットアクセスキーが渡されますのでとりあえずaws configure
します。
himituno-bucket3
というバケットにreadme.txt
が存在していることがわかりました。
readme.txt
をダウンロードして内容を確認すると、「S3に重要情報を置いてたけど削除しました。」という旨のメッセージでした。
ここで解法として浮かんだのがS3のバージョニングでした。
aws s3api list-object-versions
を使用してオブジェクトのバージョンを表示するとSECRET_DATA
という削除マーカーが付与されたオブジェクトが存在することに気づきました。
あとはaws s3api get-object
でバージョンIDを指定してオブジェクトをダウンロードするだけです。
u nix Path?
問題文から署名付きURLに関する問題だと推測しました。
サイトを確認すると、3つのファイルをダウンロードする機能がありました。
Download
ボタンをクリックした時に発生するリクエストは以下の2つでした。
GET /v1/api/file/README.md
GET /public/README.md
1つ目のリクエストで署名付きURLを生成し、2つめのリクエストで署名付きURLにアクセスする、といった流れのようでした。
署名付きURLに関する脆弱性でパッと浮かんだのはパスの生成が不適切で誤ったパスへの署名付きURLを生成することです。
試しに以下のようなパスへリクエストを送信してみると、署名付きURLはroot以下のtestを参照するように出力されました。
/v1/api/file/README.md%2f%2e%2e%2f%2e%2e%2ftest
なので、/v1/api/file/README.md%2f%2e%2e%2f%2e%2e%2fprivate%2fflag
に対してアクセスすると/private/flag
への署名付きURLが得られます。
あとはこのURLにアクセスするとフラグが取得できます。
後で気づいたんですが、ダウンロードできるファイルに色々ヒントなどがあったみたいです。
完全に推測で動き出してしまったのが良くなかったですね。
Get Provision
インスタンスのプロビジョニングデータを取得する問題です。
Webサイトにアクセスすると、URLを入力して結果を表示するサイトでした。
これはSSRFからのIMDSアクセスだろうと気づきます。
169.254.169.254にアクセスすると結果が表示されますのでリクエストに成功したことがわかります。
色々探索した結果、/latest/user-data
にフラグがありました。
Medium
Get Access Key
解けたんですが、時間切れでフラグが提出できませんでした。
アクセスキーを取得する問題です。
サイトにアクセスするとGet Provision
とほぼ同じ構成のサイトであることがわかります。
Get Provision
と同じ要領でSSRFしようとするとフロント側でフィルターが存在するようでした。
pattern
属性を外してhttp://169.254.169.254/latest
にアクセスするとサーバー側でもフィルターが存在するようでした。
ご丁寧にブラックリストが表示されるのでこれをバイパスできるホスト名、IPを探します。
ちょうど、CTFチームのDiscordでns-gcp-private.googledomains.comを正引きすると169.254.169.254
になるという知識があったのでこれを使用しました。
フィルターをバイパスできたのでアクセスキーを取りに行きます。
取得したクレデンシャルを設定します。IAM Roleを設定するのでTOKENも含めます。
(今思うと環境変数より~/.aws/credentials
に設定するほうが楽ですね…)
ここからの解法がわからず長いこと悩んでいたのですが、サイトにDynamoDBとあったことを思い出し、DynamoDBのテーブルをリストすると一つのテーブルが存在することがわかりました。
(このへんで時間切れになったと思います…)
テーブルをスキャンするとフラグが表示されました。
Hard
AWS Pentesting Journey
First bloodを取れた問題です。
複合的な知識を使う必要がありました。
大まかな手順を先に示しますが以下のような流れで攻略していきました。
- Nginxの設定不備によるパストラバーサルを利用して.htpasswdの内容を取得する
- .htpasswdに記載されたハッシュをクラックする
- クラックした資格情報でBasic認証をパスし、管理画面にアクセスする
- 管理画面のProxy機能を利用したSSRFでアクセスキーを取得する
- アクセスキーを利用して、lamdaにアクセスする
- lambdaのバージョニングされた古いFunctionからハードコードされた認証情報を見つける
- 取得した認証情報でphpMyAdminにアクセスし、フラグを取得するSQLを実行する
順番に見ていきます。
まず、問題として、以下のようなnginx.conf
が与えられます。
server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; index index.html index.htm index.nginx-debian.html index.php; server_name _; location / { try_files $uri $uri/ =404; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } location /assets { alias /usr/share/static/; } location /admin/ { auth_basic "Restricted"; auth_basic_user_file /usr/share/secret/.htpasswd; location ~^/admin/proxy/(?<proxy_host>.*?)/(?<proxy_path>.*)$ { proxy_pass http://$proxy_host/$proxy_path; proxy_set_header Host $proxy_host; } } }
この設定ファイルを見るとlocation /assets
の部分が/
で終端していないため、/assets../secret/.htpasswd
とすることで.htpasswd
にアクセスできることがすぐにわかります。
またnginx.confから、Basic認証のかかった管理画面にProxy機能っぽいものがあるため、SSRFでアクセスキーを取るんだろうなという推測ができます。
なので、まず.htpasswd
を取得します。
パスワードの方はハッシュ化されているようだったのでJohnでクラックしていきます。
Basic認証の資格情報が手に入ったので管理者画面にアクセスします。
IMDSにアクセスできました。
アクセスキーを取得します。
/admin/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/ec2role_p1lhf6h4q395qu1
~/.aws/credentials
にクレデンシャルを設定します。
しばらく列挙をしていたところ、S3のbackup-37szjp8pny7xx01
というバケットにアクセスでき、データベースのバックアップのようなものが見えました。
とりあえず全てのファイルをローカルに持ってきました。
そのうちのCSVファイルはdboperator
ユーザーのアクセスキーが記載されていました。
とりあえずaws configure
します。
ポリシーを確認するためaws iam list-attached-user-policies
を使用するとdboperator
というポリシーの存在が確認できました。
aws iam get-policy-version
でポリシーの内容を確認するために必要なVersionIdをaws iam get-pocicy
で取得します。
取得したVersionIdを指定してaws iam get-policy-version
を実行します。
するとdb-buckup
(タイポ?)というFunctionの実行権限があることが確認できます。
この関数は名の通りDBのバックアップを行う関数のようですが、バックアップをしたデータ自体には重要な情報は含まれていませんでした。
ここでだいぶ時間を溶かしましたが、lambdaのFunctionに複数のバージョンが存在することに気づきました。
rikoteki@kali:~/test3 ➤ aws lambda list-versions-by-function --function-name db-buckup --profile dboperator 14:06 { "Versions": [ { "FunctionName": "db-buckup", "FunctionArn": "arn:aws:lambda:ap-northeast-1:055450064556:function:db-buckup:$LATEST", "Runtime": "nodejs16.x", "Role": "arn:aws:iam::055450064556:role/service-role/db-buckup-role-pfcx4tak", "Handler": "index.handler", "CodeSize": 1738324, "Description": "", "Timeout": 10, "MemorySize": 128, "LastModified": "2023-08-13T17:46:30.000+0000", "CodeSha256": "tvg1Tv8vAuc7PBV1Slf+LnVPxoZ1RRzCpB1H3fKAVOU=", "Version": "$LATEST", "VpcConfig": { "SubnetIds": [ "subnet-0c623137b8878ec48" ], "SecurityGroupIds": [ "sg-052bdaf7b4fc1e9fe" ], "VpcId": "vpc-0130f67a6fac3ed45" }, "Environment": { "Error": { "ErrorCode": "AccessDeniedException", "Message": "Lambda was unable to decrypt your environment variables because the KMS access was denied. Please check your KMS permissions. KMS Exception: AccessDeniedException KMS Message: User: arn:aws:iam::055450064556:user/dboperator is not authorized to perform: kms:Decrypt on resource: arn:aws:kms:ap-northeast-1:055450064556:key/d6ecc772-b4b6-4cac-88fd-47a745c279f6 because no identity-based policy allows the kms:Decrypt action" } }, "KMSKeyArn": "arn:aws:kms:ap-northeast-1:055450064556:key/d6ecc772-b4b6-4cac-88fd-47a745c279f6", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "cff7a050-f06b-4462-9aea-ebd63d3099c8", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" } }, { "FunctionName": "db-buckup", "FunctionArn": "arn:aws:lambda:ap-northeast-1:055450064556:function:db-buckup:1", "Runtime": "nodejs16.x", "Role": "arn:aws:iam::055450064556:role/service-role/db-buckup-role-pfcx4tak", "Handler": "index.handler", "CodeSize": 1738107, "Description": "first release", "Timeout": 10, "MemorySize": 128, "LastModified": "2023-08-13T17:07:55.000+0000", "CodeSha256": "aZkXCiorEVslW/pmOsb+K7/Si4yjZvfSWiUbWN+LokE=", "Version": "1", "VpcConfig": { "SubnetIds": [ "subnet-0c623137b8878ec48" ], "SecurityGroupIds": [ "sg-052bdaf7b4fc1e9fe" ], "VpcId": "vpc-0130f67a6fac3ed45" }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "5b0a3c1a-e0b3-4ce6-9df3-cca5a5987283", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" } }, { "FunctionName": "db-buckup", "FunctionArn": "arn:aws:lambda:ap-northeast-1:055450064556:function:db-buckup:2", "Runtime": "nodejs16.x", "Role": "arn:aws:iam::055450064556:role/service-role/db-buckup-role-pfcx4tak", "Handler": "index.handler", "CodeSize": 1738324, "Description": "Implement password encryption", "Timeout": 10, "MemorySize": 128, "LastModified": "2023-08-13T17:46:30.000+0000", "CodeSha256": "tvg1Tv8vAuc7PBV1Slf+LnVPxoZ1RRzCpB1H3fKAVOU=", "Version": "2", "VpcConfig": { "SubnetIds": [ "subnet-0c623137b8878ec48" ], "SecurityGroupIds": [ "sg-052bdaf7b4fc1e9fe" ], "VpcId": "vpc-0130f67a6fac3ed45" }, "Environment": { "Error": { "ErrorCode": "AccessDeniedException", "Message": "Lambda was unable to decrypt your environment variables because the KMS access was denied. Please check your KMS permissions. KMS Exception: AccessDeniedException KMS Message: User: arn:aws:iam::055450064556:user/dboperator is not authorized to perform: kms:Decrypt on resource: arn:aws:kms:ap-northeast-1:055450064556:key/d6ecc772-b4b6-4cac-88fd-47a745c279f6 because no identity-based policy allows the kms:Decrypt action" } }, "KMSKeyArn": "arn:aws:kms:ap-northeast-1:055450064556:key/d6ecc772-b4b6-4cac-88fd-47a745c279f6", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "e0fd892d-78a9-4bdc-919f-399943bd1b3d", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" } } ] }
Descriptionを見るとVersion2ではImplement password encryption
と記載されており、Version1との差異が気になるところです。
のでaws lambda get-function
の--function-nameにVersionIdまでを記載して実行します。
rikoteki@kali:~/test3 ➤ aws lambda get-function --function-name "db-buckup:1" --profile dboperator 14:09 { "Configuration": { "FunctionName": "db-buckup", "FunctionArn": "arn:aws:lambda:ap-northeast-1:055450064556:function:db-buckup:1", "Runtime": "nodejs16.x", "Role": "arn:aws:iam::055450064556:role/service-role/db-buckup-role-pfcx4tak", "Handler": "index.handler", "CodeSize": 1738107, "Description": "first release", "Timeout": 10, "MemorySize": 128, "LastModified": "2023-08-13T17:07:55.000+0000", "CodeSha256": "aZkXCiorEVslW/pmOsb+K7/Si4yjZvfSWiUbWN+LokE=", "Version": "1", "VpcConfig": { "SubnetIds": [ "subnet-0c623137b8878ec48" ], "SecurityGroupIds": [ "sg-052bdaf7b4fc1e9fe" ], "VpcId": "vpc-0130f67a6fac3ed45" }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "5b0a3c1a-e0b3-4ce6-9df3-cca5a5987283", "State": "Active", "LastUpdateStatus": "Successful", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" }, "RuntimeVersionConfig": { "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:96f95344fddd7e2267f42f2bcf8be9879aa43babe436b7d63ffe2ff1effeb0de" } }, "Code": { "RepositoryType": "S3", "Location": "https://awslambda-ap-ne-1-tasks.s3.ap-northeast-1.amazonaws.com/snapshots/055450064556/db-buckup-c327446a-406b-481b-8234-70635c542033?versionId=qXI2pBnyn6z1yaP54atZwsshNVS4fx7O&X-Amz-Security-Token=IQoJb3JpZ2luX2VjELv%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDmFwLW5vcnRoZWFzdC0xIkgwRgIhANw8jW1D2vJRsAzjA%2BPlpdTYIQdApCxuZmf9mJYTRhEgAiEAnhMUeSZ6Rj07cgFvg6%2FONn6LEeOuqTzpmnz4g%2BJiQpEqygUIhf%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARAEGgw5MTk5ODA5MjUxMzkiDGsuHoDDYSfI%2BpOQ8CqeBWmUGI2doQw3MciQ9rt2UNW1qZU1WohujZRFGiUiv%2BMRkjpi%2FDZQjODJMtO%2FS4eAoaXDn47FAY8THzaBbkumCF0JkfuLdN%2FfaCjQcXlNyIKr3Cr66wKSMlFkNh2uMf4%2BUcdkhZdH%2BYSEr79bAY%2BIUCHGHq3V4oa6BCOMXj%2BEGa5nfWAcpAtEucmrEsYcjYC0p6EkVdKO%2By4vESdq9ckV3EyrZkGzvGooPZIUGkwgOIWL5BKMJhQuY2Ta%2BBonQpEnvBabWskX8HcmazMs9YP1H3aEbX4k0bB2RZ6Lrg%2Ffvs7h8T6G8M9Je93R29UicOQvmD9gHL7uAv05fPqcRznU%2F4MuipdFcM9q6oz0L%2FzjLPlv1zWFjgWupF%2BJywF90S0XGYp8NvnzatbUINpwSl%2FbEjSKRFJhXkF2LcRfd5IYpvjG2liYoGV8KTVWNVhBy6NjanWzJzQRRsjnhoEkdFy3WqiXtGQH9t1S6l5CC8Vv8qK2QjU8Tp124cblyzBfO1Iu05ra9AbEbdGcPdQ1nKSmiKkyVFJO13tbNfluUm8l0fSqlMoMYKNS21P0zxrjsYWQlePMjAWzKo2Nu7Ygc%2BOH%2FYXF3%2F4U3e1imecDNWWHoMP3EWDnUTAwCBHb%2BoaX7VXdbEnNCdJc%2F%2BWt%2F4e8H%2Ba6W%2FkgNq5ui%2BcJuRZTYauVnKdgcZikrRVsfTluLykGnYs5Desmy3uc68BfDBHlnUG1h43bL828qIOg%2FHIVcxlUxlnomqmI0P0oQZ4m7lEHpKC7wOoZUjwRol3F8N5vQIPK7fRsspGO6Xh%2BhHbo7Jlv9igyIyeS2ylr6rEpnr947zO%2FNmOP2wfUXOT99LSQYboWV6Fyzg5nfMv3WCbZbk1lYAuHyWp7NapHtyYSS6FgI%2BMw56ywpwY6sAF06qpQ%2BAaACyfyqu0kOsDG3nofBpJZZY%2BEzuRfTGewyK5SlsWD5fFOwrvFLH7CCFR3WWazlKWPy10I6XKBioJvexX4NvQh1jIUQ8LGzgFjywspU06WtJTKu9UGmrgv3zEvMQOavujw0ohGN8JZ8p1g%2BaRCtP6YHjgH2GSNi2zox0VS09qAReYv1fGJNgskM%2BTgxfOLh6ix1p8Q9Ls5ZVG11Ywc1mfYZlMTMNyV82nfCw%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20230828T050936Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIA5MMZC4DJUGSDJVOW%2F20230828%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=9702f45254332a696db7246c98fc8eac6daea06cd6b8bf221da3ddf402ad318d" } }
出力の下部にソースコードが保存されているURLが出力されますのでアクセスして取得してみると、DBへの認証情報がハードコードされていることがわかります。
この認証情報を使用してWebサイトにあるphpMyAdminへアクセスすることが可能でした。
あとはDBを調査してフラグを出力するSQLを実行すればフラグが取得できます。