アオカケスの鳥かご

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

SUN CTF 2019 Spring writeup

サークルの後輩が2019年6月17日13:00~2019年6月23日15:00の期間に学内限定のCTF大会を開催してくれました。
1年生のこの時期にCTFを主催するなんて頭おかしいと思います(最高の褒め言葉)。

私は前半は沖縄に学会発表に行っていたので少し遅れての参加となりましたが、無事全問解くことが出来ました。

解き始めてすぐの頃は全く解けなくて諦めようかと思っていましたが、少し冷静になって問題に向き合ってみるとなんだかんだで解けました。
1年生でこれだけの問題を作れるのは本当に凄いと思います。


主催してくれた彼を始めとして、今年の1年生を見ていると今後のCyber研究会は安泰かなと思います。
ぜひ頑張ってください。

私たち4年生が全力で応援します。


というわけで以下writeupです。

Misc

Crash and Splash![100]

問題文
まてまてまてー!とまれー!うごけー!(サーバルが轢かれる音)
(URL ※flagの確認用)

Hash: 4541

writeup
サーバル要素は正直よく分かりませんでした。
衝突ということで、ハッシュ値が同じになるようなflagを見つけてね、という問題のようです。

問題文にはハッシュ値確認用のURLが用意されています。


配布されているプログラムを確認します。

from config import FLAG
 
def gen_hash(text):
    hash = 0
    text_list = list(text)
 
    for i in range(len(text_list)):
        ord_char = ord(text_list[i])
        hash += int(ord_char)

    return hash
 
def main():
    hash = gen_hash(FLAG)

    with open('result.txt', mode='w') as f:
        f.write('Hash: ' + str(hash))
 
if __name__ == '__main__':
    main()

gen_hash関数ではflagを1文字ずつasciiに変換し、その値を加算していった結果をハッシュ値としています。
つまり、なんとかして同じハッシュ値になるような文字列を探し出さないといけないわけです。

このようなときに役立つのが部分和問題です。

部分和問題は、n個の整数が与えられて、それらのいくつかを足して指定された値になるような組み合わせを見つける問題です。

今回の場合、ascii変換したときの値を使っているので、まずはflagとして使われる可能性のある文字列の全てのascii変換したときの値を用意しておきます。

具体的には以下の文字です。

abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
{}_

これらをasciiに変換すると以下のようになります。

97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90
48, 49, 50, 51, 52, 53, 54, 55, 56, 57
95, 123, 125

あとはプログラムを書くだけです。

部分和問題とか行っておきながらナップサックです。
実行すると以下のようになります。
f:id:aokakes:20190621175822p:plain

これを問題文のURLで入力して、ハッシュ値が合っていればflagが貰えます。

Flag

SUNCTF{sprit3_4nd_c0c4_c014_is_v3ry_sp14sh_drink!}

EZ[150]

問題文
This question is so easy!

writeup
この前のseccon biginnersで似たような問題があったので、確かにso easyだったかもしれません。

base64エンコードされたものが渡されるので、まずはデコードします。
するとバイナリが現れます。

これをdecompressするとまたbase64エンコードされた文字列が出てくるので、何回か繰り返すとflagが現れるはずだと踏んでひたすらループさせます。

64回目で出てきました。
f:id:aokakes:20190621180923p:plain

Flag

SUNCTF{3nc0d3_4nd_c0mpr3ss_4nd_3nc0d3_4nd_c0mpr3ss}

Rough PNG Image[200]

問題文
このPNG画像はとてもザラザラしている……
f:id:aokakes:20190621181204p:plain

writeup
全問題の中で一番点数が高い問題です。

最初は全く訳が分かりませんでしたが、とりあえずRGB値を取り出して観察していたら色々見えてきました。

ここには載せていませんが、GとBの値は全て0でした。なのでRだけ抽出しています。

さて、注目すべきところは一番下らへんの0が続いているところです。画像では下の黒い部分。
この形、なんとなく...バイナリじゃないですか...?

改めてRの値を確認してみると、それぞれ16進数に変換したらなんだか良い感じになりそうな気がしてきました。
というわけで16進数に変換します。

これをcyberchef(https://gchq.github.io/CyberChef/)に突っ込んでみます。
f:id:aokakes:20190621182941p:plain

なんとhtmlが現れました(画像右下のOutput)。
これを保存してブラウザで開くとflagが表示されました。

f:id:aokakes:20190621183256p:plain

Flag

SUNCTF{H7ML_fi13_c4n_c0nv3rt_t0_PNG_fi13}

Crypto

[warm up]Write a map freely[50]

問題文
さあ地図を書こう、輝く未来に向けて。

writeup
正直に言うとこの問題が一番難しかったような気がします。行列が目に入るだけで億劫です。

とは言え、流れを一つずつ追って見るとやっていることは案外単純です。
今回の場合は処理を逆にするだけでflagとなります。

Flag

SUNCTF{m4pping_c4us3_ch4nge_0f_s3ts}

How do you like wednesday?[100]

問題文
お見舞いするぞ!

writeup
これも1問目と同じで処理を逆にするだけです。

プログラムを見てみると結構色々な処理をしているように見えますが、実は暗号化している部分は1つ目のfor文だけです。
ほかのfor文は文字を繋げたりしているだけです。

rand_int変数には1~16の範囲でランダムに選ばれた値が入りますが、どれが選ばれているのかは分からないので全探索します。16個しかないですし。

乱数は12だったようです。
f:id:aokakes:20190621191503p:plain

Flag

SUNCTF{0n_ch4n_is_4_p0pu14r_m4sc07_0f_H7B}

Zero and One[100]

問題文
1011010010110001100110110010001110110100

writeup
実はこの問題はまともに解いていません。
CyberChef(https://gchq.github.io/CyberChef/)に突っ込んでみたらそのままflagが出てきました。
f:id:aokakes:20190621191838p:plain

Flag

SUNCTF{jus7_c0nv3rt_h3x4d3mic41_numb3r_t0_bin4ry_numb3r}

Alice and Bob[150]

問題文
アリスとボブは、秘密のお手紙をやり取りしようとしています。
あなたはアリスの回線を盗聴し、「大切な数字」を手にしました……。

writeup
この問題もやはり処理を逆にするだけで、encryptしているところをdecrypにするだけです。

f:id:aokakes:20190621192651p:plain

わざわざボブのプライベートな値を求めていたりしますが、別にやらなくても普通に解けるようです。

Flag

SUNCTF{41ic3_w4nts_t0_ki11_B06_4nd_sh3_w4nts_t0_g3t_his_w341th}

Forensics

[warm up]Early summer of university[50]

問題文
天気の良い日に大学で写真を撮ったよ〜
f:id:aokakes:20190621193936p:plain

writeup
最初はfileコマンドやらstringsやらやってましたが、何も出なくて「俺はwarmupの問題すら解けないのか」と頭を抱えていました。
そういえばステガノグラフィーの解析が出来るソフトがあったよなー、と思ってやってみたらいけました。

ということで、うさみみハリケーン(https://www.vector.co.jp/soft/win95/prog/se375830.html)の「青い空を見上げればいつもそこに白い猫」でステガノグラフィー解析を行います。

赤色のビットを抽出したらflagが浮かび上がってきました。
f:id:aokakes:20190621194251j:plain

Flag

SUNCTF{7his_f14g_is_n0t_invisi613}

Oluri is so cute[100]

問題文
オオルリは可愛い、サイバー研の小鳥。

writeup
オオルリさんを問題に出してくれてありがとうございます。

まぁこれぐらいの点数だったらstringsとかで出るんじゃないの?とか思っていたら普通に出ませんでした。
それじゃあ、ということでexiftoolでやってみるとDocument Nameにbase64エンコードされた文字列がありました。
f:id:aokakes:20190621194938p:plain

これをデコードしてみるとflagでした。


なお、exiftoolは以下のコマンドで入れられます。

sudo apt install exiftool


Flag

SUNCTF{01uri_is_w4tchin9_us}

Welcome to Cyber Study Group[150]

問題文
オオルリはいつも、サイバー研に来る人を出迎えてくれる。
今日もまた、彼女の声が聞こえる。「ようこそサイバー研究会へ」
※この問題のフラグは大文字小文字を問いません。

writeup
どんな音声ファイルかな~と思ったらオオルリさんのウェルカムボイスでした。

まぁ実際は結月ゆかりなんですが、本当に良い声してるなって思います。
自腹で9000円ぐらいで購入しましたが、全く後悔していません。決して。本当ですよ?


さて、まずはお約束ということでfileやらstrings、exiftoolなどのコマンドを実行しますが、これぐらいの点数になってくるとさすがに何も情報が得られません。
逆に考えると、その辺にflagは置いてないよ、ということな訳ですね。

正直に言うと、全く分からなかったので一度諦めました。
ほかの問題に手を出しつつ再度問題を確認してみると、既に4人ぐらい解いていたのであまり複雑にはなっていないだろうと考えました。

とは言え、手掛かりという手掛かりがありません。
とりあえずちょっと強そうな音声編集ソフト(Audacityhttps://forest.watch.impress.co.jp/library/software/audacity/)で開いてみることにしました。
f:id:aokakes:20190621200118p:plain

別にとくに変なところは無いなーと思いつつ観察していると、オオルリさんの声がRにしか入っていないことに気付きました。
それじゃあLに入っているのは何なの、と思ってLだけで聞いてみると何だかプププププと鳴っています。

最初はやはり疑問符が浮かぶばかりでしたが、よく見ると波形が「・」と「ー」だけでモールス信号になっていました。

・・ - ・・・ -- --- ・-・ ・・・ ・ -・-・ --- -・・ ・

これであとはこのモールス信号を解読するだけです。

モールス信号の解読はこのサイト(http://www.inv.co.jp/~ike/mores-chg.html)が使いやすそうです。

モールス信号は和文と英文に変換することができます。
和文に変換すると「゛むらよれならへにれほへ」となって意味不明ですが、英文に変換すると「ITSMORSECODE」となります。

Flag

ITSMORSECODE

この問題では「SUNCTF{}」を付けなくていいようです。

A line of the glay[150]

問題文
一直線に並ぶ灰。そのなかにある、仄かなフラグ。
f:id:aokakes:20190621201511p:plain

writeup
これは面白い問題でした。

かなり小さい画像が添付されていて最初はよく分かりませんでしたが、画像サイズが 1 x 34 だったのでピンと来てしまいました。

34ってなんだかflagの長さっぽいですね。ということは1ピクセルの中にflagの1文字分が隠れているのではないでしょうか。


まずは各ピクセルのRGB値を確認してみます。

228 228 226
227 227 226
229 229 229
233 233 232
227 227 227
232 232 231
214 214 214
237 237 237
(省略)
222 222 222
239 239 239
239 239 238
239 239 239
217 217 217
214 214 212

この時点で確信しました。

上から7つ目で各値が大きく下がっています。何だか{ っぽいですね。最後は }に見えてきました。となると上の6つは SUNCTF で確定ですね。

あとは法則性を見つけるだけです。
恐らくasciiなんだろうなという推測のもと、RGB値をテキトーに足したり掛けたりしてasciiに当てはまる計算式を探します。

試行錯誤の結果、どうやらRGBの各値の下二桁の総和を165から引くとflagになるようです。


根拠は以下の通り。

  1. 「S」はasciiだと83で、「 { 」は123
  2. 全部の値が200以上なので下二桁だけ使うような気がする
  3. とりあえずRGB各値の下二桁を全部足してみる
  4. S → 28 + 28 + 26 = 82 、 { → 14 + 14 + 14 = 42
  5. 差が40なのでasciiであることが確定
  6. どんな数字から82を計算したら83になるだろうか
  7. asciiの値は「S」よりも「 { 」のほうが大きいからおそらく引き算
  8. 165から引けばいいのか!

良い感じにプログラムを書いて終わり。

Flag

SUNCTF{614ck_is_my_f4vorit3_c010r}

Web

[warm up]You cannot see the flag![50]

問題文
フラグは見えない。ならば……

writeup
問題文のURLに飛んでも何も見当たりません。ソースは「You cannot see the flag!」のみ。

こんなときはデベロッパーツールの出番です。F12を押してNetworkのタブを開いてページを更新します。
Headerを確認してみるとflagがありました。

f:id:aokakes:20190621204456p:plain

Flag

SUNCTF{th3_h34d3r_is_in_th3_sh4d0w}

Single Page HTML Viewer[100]

問題文
HTMLファイルの構造を見られるシングルページアプリケーションを作りました!
バリデーションもきちんと施したので、安全なアプリケーションです。

writeup
まずは添付されているzipファイルを解凍してDocerfileを確認します。

FROM php:7.3-apache
 
COPY ./php.ini $PHP_INI_DIR/php.ini
COPY ./public_html /var/www/html
RUN echo "SUNCTF{<Hidden>}" > /flag
 
EXPOSE 80

flagのpathは「/flag」のようです。


しかし、ブラウザで「/flag」を入力しても「Access Denied」になってしまいます。

そこでindex.htmlを確認してみると、Pathが以下の条件を満たすと「Access Denied」になってPOSTすらされないようになっていました。

  • 「flag」が含まれている
  • 「.html」が含まれていない

なんとかこのバリデーションを回避する必要があります。


最初はユニコードエスケープシーケンスとかかなとも思ったのですが、入力してみてもうまく動かなかったので違うようです。

頭を抱えつつhtmlを確認していたら、POST先であるquery.phpに普通に直でいけることに気付きました。

query.phpに直接アクセスしてみます。

f:id:aokakes:20190621205603p:plain

index.htmlを経由せずに直接query.php宛てにpathをPOSTしてあげればflagが取れそうです。


というわけでpostman(https://www.getpostman.com/downloads/)を起動。

JSONでpageに「/flag」をセットしてPOSTします。
f:id:aokakes:20190621210026p:plain

Flag

SUNCTF{fr0nt3nd_va1id4ti0n_is_us3fu1_but_d4ng3r0us}

Cyber Study Group OPAC[150]

問題文
サイバー研究会内にある本を検索できるOPACを構築しました。
脆弱性が存在するかもしれませんが、決して悪用しないでください。

writeup
あきらかにデータベースからデータを拾ってきているので、まずは確認の意味を込めて「1' or 1 = '1」を入力してみます。
全部表示されるので、ちゃんと(?)SQLインジェクション脆弱性があるようです。

しかし、本たちと同じテーブルにflagは存在しないようなので、flagが存在するテーブルを探す必要があります。
こんなときに使えるのがUNION句です。

SQLインジェクション脆弱性を利用することで、あらかじめ宣言されているSQL文に別のSQL文をくっつけることができます。ただし、結合するSQL文の実行結果の列数を揃える必要があります。
今回の場合だと、ID、Name、Auther、ISBNの4行です。


というわけで以下のように入力してみます。

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

information_schema.tables でデータベース内のテーブルを取得することができます。
なお、コメントアウトを表す「--」の後ろにはスペースが必要です。ご注意ください。

テーブルの数が多いですが、よく見るとflagsというテーブルが存在します。

f:id:aokakes:20190621211413p:plain

flagが存在するテーブルが判明したので、次はflagsテーブルの中身をUNION句で結合します。

a' UNION SELECT flag,0,0,0 FROM flags -- 

f:id:aokakes:20190621211653p:plain

Flag

SUNCTF{b3_c4r3fu1_s91_p4r4m373rs}

まとめ

最初は全然解けなくてかなり焦りましたが、最終的には全問解けて安心しました。

ありがとうございました。