OWASP Juice Shop 解いた記録(⭐️⭐️⭐️)
はじめに
今年の7月からCTFを始めました。
Try Hack Me、Hack the Box、Cpaw CTF、pico CTFなどを齧りつつ、今はOWASP Juice Shopに張り付いてます。
自分の学習用ログとして、こちらに解いた記録を残しておきます。
以下は完全なる解答ネタバレになりますので、ご自分で解きたい方はUターンしてください!
今回は⭐️3のタスクです。
タスク
Forged Feedback
Feedbackを送信するときに、Burp SuiteでリクエストのユーザーIDを他の人のID書き換える。
他の人がFeedbackを送信したことになり、クリア。
Login Jim
jim@juice-sh.op'-- でログイン。
Login Bender
bender@juice-sh.op'-- でログイン。
API-only XSS
Burp Suiteで何かしらのAPIを使って、与えられたiframeタグをサイトに埋め込むとよさそう。
試しに、直接商品レビューにタグを投稿してみたが、単純に文字列が表示されただけでコードが実行されない。
商品の説明欄に埋め込むことができるか確認する。
まず、adminアカウントでログインし、適当なAPIをインターセプトしてRepeaterへ送る。(この時、リクエストボディのないものを選んであとで自分で追加したら、ボディとみなされずデータ更新ができなかったので、ボディのあるものを選び直した。キーの前の空白の数があっていなかったのか?)
リクエストパスを /rest/products/1 などにしてみると、パスが存在しないというエラーが返ってきた。
/rest と /api が混在して使われているので、 /api/products/1 にしてみると使えそうだった。
メソッドをPUTにして、ボディに {"description": "<iframe src=\"javascript:alert(xss)\">"} をセットして送信すると成功。
タスクはこの時点でクリアだが、念のため更新した1番目の商品(アップルジュース)の説明欄を見に行ってみると、ちゃんとアラートが表示された!
Admin Registration
ユーザーのロールがどのような形式で保存されているかを確認したい。
/administration にアクセスして、DevツールのNetworkから authentication-details/ という項目のレスポンスをみると、ユーザーのロールが "role": "admin" という形式でやり取りされている。
ユーザー登録画面に移動して、適当にユーザー情報を入力してリクエストの "email" などと並列の場所に "role": "admin" を追加して送信すると管理者として登録できクリア。
Bjoern's Favorite Pet
Bjoernさんをネット検索すると、Youtubeチャンネルが見つかる。
アップロード動画のうちの1つに、Bjoernさんがユーザー登録画面にペットの名前を入力しているシーンがある。
その名前を使ってパスワード変更できる。
Bjoernさんのメアドは、商品ページのレビューで取得できる bjoern@owasp.org 。
CAPTCHA Bypass
CAPTCHAのタイプがIDで決まっており、Feedbackのリクエストに含まれているので、同じリクエストを何度も送るだけで良い。
Burp Suiteで、該当のリクエストを send to repeater して、10回Sendをすればクリア。
CSRF
Client-side XSS Protection
バリデーションが行われている入力フィールドでXSSを実行する。
ユーザー登録のメアド入力欄で、メアド形式でないと次に進めないようになっているので、そこにiframeのタグを入力したい。
適当なメアドを入力してリクエストをインターセプトし、ボディのメアド欄を "email":"<iframe src=\"javascript:alert(\xss`)">"` に書き換える。
iframeのタグにダブルクォーテーションが含まれていて、外側のダブルクォーテーションとバッティングするので、エスケープを忘れずに。
その状態で送信するとタスククリア。
Database Schema
Burp Suiteで、商品検索時のリクエストをインターセプトして、/rest/products/search?q= のリクエストを開く。
パスのクエリの部分を q=aaa' などに書き換えて送信。SQLの構文エラーを吐かせるためのもので、aaaの部分は適当。
以下のようにエラーとなり、実行されているSQLが表示される。

このコマンドに結合できるように、sqlite_masterから情報を取得するパラメータを入力。
(半角スペースの部分はURLエンコード用に%20に書き換える。)
q=aaa'))%20union%20select%20sql,1,1,1,1,1,1,1,1%20from%20sqlite_master%20--
商品検索で普通にappleなど入力してレスポンスを見ると、Productsのカラムは9個あることがわかる。
カラム数を合わせるために、sqlite_masterデータ取得時の残りの8カラムを適当な値で埋めている。
レスポンスとしてテーブル情報が返り、タスククリア。
Deluxe Fraud
左メニューからDeluxe Membershipのページへいく。
適当なクレカを登録・選択し、ブラウザをインターセプトした状態で次へ進む。
リクエストの内容を "paymentMode": "" にし、 "paymentId" を項目ごと削除する。
リクエストを送信するとクリア。
Forged Review
適当なアカウントでログインして、適当な商品のレビュー欄に入力。
インターセプトしてリクエストの "author" 欄を別のユーザーのメアドに変更して送信。
別のユーザーとして投稿ができる。
GDPR Data Erasure
ログイン画面で、SQLのエラーを発生させるためにIDに admin' を入力し、パスワードに適当な文字を入力して送信する。
DevツールのNetworkタブでレスポンスを見ると、SQLITE_ERRORが発生していて、以下のコマンドが見れる。
sql: "SELECT * FROM Users WHERE email = 'admin'' AND password = '0cc175b9c0f1b6a831c399e269772661' AND deletedAt IS NULL"
「deletedAt がNULLのもの」で絞り込んでいるので、「deletedAt NULLではない(削除済み)」という条件で検索したい。
以下のコマンドをID欄に入力して送信するとクリスさんでログインできてクリア。
' OR deletedAt IS NOT NULL--
Login Amy
説明欄に書かれている、93.83 billion trillion trillion centuriesでGoogle検索してみると、以下のサイトがヒットする。(他の方々のwriteupの少し下にある)
説明欄には「エイミーは"One Important Final Note"を見ていなかった」と書かれてあり、検索でヒットしたサイトにまさに"One Important Final Note"という項目がある。
サイトの内容としては、PrXyc.N(n4k77#L!eVdAfp9 よりも D0g..................... の方が遥かに簡単で覚えやすいのみならずセキュリティ的にも強い、というもの。
しかし、"One Important Final Note"には、単純にドットで埋めるパスワードはすぐに推測されて突破されてしまうので、少々工夫が必要と書いてある。
エイミーがこの注意事項を読んでいなかったとしたら、 D0g..................... というパスワードが強いと信じ込んで、同様のパスワードを設定すると考えられる。
また、このサイトにはパスワード強度をチェックするツールも含まれていて、試しに D0g..................... を入力してみると、破られるには93.83 billion trillion trillion centuriesかかると表示される。まさに説明欄に書いてある時間と同じ!
ということで、「英字大文字」「数字」「英字小文字」「ピリオド21個」を結合したもの、というルールでパスワードを破りたい。
Burp SuiteのIntruderでブルートフォースを試みたら、コミュニティエディションの制限で中断されてしまった。
スコアボードからヒントページに飛んでみると、Futuramaというアニメの画像が貼られており、エイミーというキャラの隣にいるのはキフというキャラクターらしい。
パスワードにkifという単語が使われていると仮定すると、上記のパスワードルールにのっとるとK1f..................... が正解である可能性が浮上。
ログイン画面で amy@juice-sh.op と上記のパスワードを入力してみると、ログイン成功。
ものすごく謎解きみがあった。
Manipulate Basket
適当なユーザーでログインして、インターセプトしつつ適当な商品をカートに入れる。
まず /rest/basket/バスケットID というリクエストがあり、そのままスルー。
次に /api/BasketItems というリクエストがあるので、そのボディの "BasketId" を変更したら良さそう。
しかし、単純に別のIDに変更してみても、エラーが発生してしまう。
すでにボディに設定されている "BasketId" をコピペして下に貼り付け、2つ目の "BasketId" を変更することでチェックをすり抜けて、別ユーザーのカートに商品を追加できる。
セキュリティチェックに使うキーは先頭一致で、カート追加処理に使うキーは最後のものが優先される様子。同じキーがあったら上書き処理されるということなのか。
リアルワールドバグハンティングという本で、URLのクエリに同じキーを2つ付与すると、最初のキーでセキュリティチェックをすり抜けられるという事例があってそれに似ていた。
Mint the Honey Pot
Payback Time
商品を買うときのリクエストで、金額をマイナスにしてしまえばいいかと思ったが、金額の項目がなかった。
遡って、適当な商品をカートに追加する時にリクエストボディを "quantity: -1" に変えた状態で送信すると成功し、カートの合計額がマイナスの値になった。
そのまま商品購入完了まで進めるとタスククリア。
Privacy Policy Inspection
プライバシーポリシーの文章にカーソルを当てながら読んでいると、ところどころ背景にぼんやりしたオレンジの光が浮かぶ。
ソースを見てみると、 hot というクラスが割り当てられていて、検索すると他にも該当する箇所があった。
該当箇所は
- http://localhost
- we may also
- instruct you
- to refuse all
- reasonably necessary
- responsibility
となり、localhost以外を繋げると文章になっているように見える。(「当社は、合理的に必要なすべての責任を拒否するよう指示する場合もあります。」)
localhostのあとに文章を繋げてURLにすればよいのでは、ということで、それぞれの単語をディレクトリとみなしスラッシュ区切りで入力し、アクセスした瞬間クリア。
アクセス先はエラーになってしまったが、/private/thank-you.jpg というファイルが存在しないと表示されているので、何か感謝の画像が表示される想定だったのか否か。
Product Tampering
タスク説明欄のリンクを開くと、商品が1件だけ表示された商品ページに飛ぶ。
一度、商品詳細を開くときのリクエストをインターセプトしてRepeaterに送る。
メソッドとパスを PUT /api/products/9 に変更して送信してみると、レスポンスに商品データが返る。
descriptionの内容をコピーしておき、今度はレビューを適当に書いて送信した時のリクエストをインターセプトする。
またRepeaterに送り、先ほどと同じパスに書き換えた上でボディを "description": ... にし、値の部分には先ほどコピーしたdescriptionの内容をペーストする。
リンク先を、タスクの説明欄で与えられているslackのURLに書き換え、送信するとクリア。
slackはowasp会員のドメインを持っていれば入れて、非会員でも招待をもらえるよう連絡すれば入れそう。
Reset Jim's Password
パスワードリセット画面でJimのメアド jim@juice-sh.op を入力すると、秘密の質問が表示される。
最年長の兄弟のミドルネームは何?という質問なので、そもそもJimとは誰なのかを特定する必要がある。
Jimのアカウントにログインし、保存された住所を見てみる。
検索すると、USS Enterpriseというのはスタートレックに登場する宇宙艦のよう。
そしてその住所に住むJimというのは、ジェームズ・T・カークであることがわかる。
さらに調べると、兄のミドルネームがSamuelであることがわかる。
秘密の質問フィールドに入力してみるとパスワードリセットが成功する。
Security Advisory
ヒントに、Advisoryについては security.txt に記載されていると書いてあるのでアクセスしてみる。
Csafというのが、Common Security Advisory Frameworkの略で、セキュリティアドバイザリー標準とのこと。
リンク先にアクセスするとさらにメタデータが表示される。
distributions > directory_url のリンク先にアクセスすると、ファイルシステムが表示される。
2017と2021のディレクトリに入りJSONを確認すると、CVEで始まるイシュー番号が記載されている。
CVEはCommon Vulnerabilities and Exposuresの略で、公開されている脆弱性を識別するための番号。
2017の方の文言を見ると、近々パッチを当てると書いてある。が、進捗がなさそうなのでこのことを問合せページで指摘する。
イシュー番号と、いつ直りますか?的な文章を書いて送信したが、クリア判定にならなかった。
調べてみると、以前はその方法で通ったようだが、現在はできないと言っている方がいた。
個人的にメッセージのやり取りをすることになった痕跡だけが残っていて、結局解決方法がわからなかった。
Upload Size
500KBほどのXMLファイルを用意して、圧縮して40KBほどに。
そのZIPファイルをComplaintから送信し、インターセプトしてリクエストの内容を書き換えた。
まず、ファイルの拡張子から .zip を外して .xml のみにし、さらにファイルコンテンツを圧縮前のものに書き換えた。
送信したがクリア判定にならず、該当のレスポンスを見ると MulterError: File too large というエラーが発生していた。
調べてみると、以前はそのやり方で通った方々ばかりのようす。仕様が変わったのか否か。
Upload Type
Complaintページでは、.pngや.zip形式のファイルしかアップロードできない。
まず適当な.zip形式のファイルをアップロードし、リクエストをインターセプトする。
リクエストに記載されているファイル名の拡張子を.zipから.jpgなどに変更する。Content-Typeは変更しなくてok。
その状態でリクエストを送るとクリア。
XXE Data Access
XXEとはXML External Entityの略で、悪意のあるXMLデータが送られた時に、サーバーが処理して機密ファイルなどにアクセスしてしまうという脆弱性を利用した攻撃。
よくあるアクセス方法として、調べると以下の構文が紹介されている。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<foo>&xxe;</foo>
これをXMLファイルとしてローカルに保存して、例のごとく拡張子を.xml.zipとする。
該当ファイルをComplaintからアップロードして、リクエストをインターセプトし、拡張子の.zipを取り除いて送信。
タスクはクリアになったが、上記のリクエストの結果、レスポンスのどこに不正アクセスした情報が書かれているのかわからなかった。
Discussion