目次
はじめに
日本時間2024年8月3日~5日に開催されたn00bzCTF 2024に参加しました。
結果はチーム「maybe yes beginner」で7419ポイント(全967チーム中85位)、個人では3726ポイントでした。
今回は前に一緒にCTF参加した方からお声がけをいただきました。本当にありがとうございます……。
公式Writeupはこちら: n00bzUnit3d/n00bzCTF2024-Official-Writeups (github.com)
Crypto
Vinegar
解法
enc.txt
が渡される。
Encrypted flag: nmivrxbiaatjvvbcjsf
Key: secretkey
ヴィジュネル暗号というものらしいので復号する(Vigenère Decode – CyberChef (gchq.github.io))。
Flag
n00bz{vigenerecipherisfun}
Random
解法
サーバープログラムを読むとamazingcustomsortingalgorithm()
内で69回入力をシャッフルしており、そのうち1回でも昇順に並べばflagを貰えることが分かる。
また、プログラム内でシードが定義されておらず、実行ごとに同じ乱数が生成されていることが分かる。
例えば、入力に0123456789を入れたとき、出力の1番上は必ず4378052169となる。
> nc challs.n00bzunit3d.xyz 10402
0123456789 # 入力
4378052169 # 出力1番目
0578439216 # 出力2番目
5763842019 # 出力3番目
...
この4378052169が0123456789となるように入力の順番を調整すれば良い。
> nc challs.n00bzunit3d.xyz 10402
4761058239
0123456789
n00bz{5up3r_dup3r_ultr4_54f3_p455w0rd_8bc6b6d36d24}
Flag
n00bz{5up3r_dup3r_ultr4_54f3_p455w0rd_8bc6b6d36d24}
Forensics
Plane
解法
画像ファイルのEXIFを見るとGPS情報を得られる。
> exiftool plane.jpg
...
GPS Latitude : 13 deg 22' 12.00" N
GPS Longitude : 13 deg 22' 12.00" W
GPS Position : 13 deg 22' 12.00" N, 13 deg 22' 12.00" W
この座標数値は60進数なので10進数に変換する必要があるので変換サイト等を利用して変換する。
Flag
n00bz{13.37,-13.37}
Disk Golf
解法
とりあえずファイル形式を確認して解凍
> file disk.img.tar.gz
disk.img.tar.gz: gzip compressed data, from Unix, original size modulo 2^32 2671779840 gzip compressed data, reserved method, ASCII, extra field, from FAT filesystem (MS-DOS, OS/2, NT), original size modulo 2^32 2671779840
> tar -xzvf disk.img.tar.gz
マウントを試みるがエラーが出た。
> sudo mount -t ext4 -o loop ./disk.img ./mounted/
# エラー
> dmseg | tail
[ 304.357735] loop0: detected capacity change from 0 to 5218304
[ 304.359707] EXT4-fs (loop0): bad geometry: block count 12844795 exceeds size of device (652288 blocks)
エラーで検索するとこのような質問が見つかったので試してみる。
> sudo e2fsck -f ./disk.img
# 最初だけN、後はY連打
> sudo resize2fs -f ./disk.img
これでマウントできるようになる。
> sudo mount -t ext4 -o loop ./disk.img ./mounted/
適当に探すと/home/johnhackerdoe/flag.txt
に怪しい数字の羅列があるのが分かる。
/mounted/home/johnhackerdoe/flag.txt:
156 60 60 142 172 173 67 150 63 137 154 60 156 147 137 64 167 64 61 164 63 144 137 144 61 65 153 137 146 60 162 63 156 163 61 143 65 175
Decode:
n00bz{7h3_l0ng_4w41t3d_d15k_f0r3ns1c5}
Flag
n00bz{7h3_l0ng_4w41t3d_d15k_f0r3ns1c5}
OSINT
Tail
解法
まずは飛行機の尾翼の画像から航空会社を特定する。Wikipediaから探してエア タヒチ ヌイ (Air Tahiti Nui)であること判明。
エア タヒチ ヌイのハブ空港はタヒチ・ファアア国際空港であり、そのIATAはPPTである。
Flag
n00bz{PPT}
Web
Focus on yourSELF
解法
GETリクエストのimageパラメータでパストラバーサルができることが分かる。
例えば、image=../../../../../../etc/passwd
としたとき、/etc/passwd
の中身がbase64でエンコードされてから渡される。
色々探し回った結果、Dockerの環境変数は/proc/self/environ
に書かれているのでimage=../../../../../proc/self/environ
としてファイルを参照した。
Flag
n00bz{Th3_3nv1r0nm3nt_det3rmine5_4h3_S3lF_ca8686b2a4cc}
Rev
Vacation
解法
入力の各バイトを3とXORして出力しているだけなので再度3とXORすれば良い。
$bytes = [System.Text.Encoding]::ASCII.GetBytes((cat .\output.txt))
[System.Collections.Generic.List[byte]]$newBytes = @()
$bytes.ForEach({
$newBytes.Add($_ -bxor 3)
})
$newString = [System.Text.Encoding]::ASCII.GetString($newBytes)
echo $newString | Out-File -Encoding ascii .\flag.txt
Flag
n00bz{from_paris_wth_xor}
FlagChecker
解法
マクロの中身を見ると文字を参照して様々な条件を満たしているか判定している。
Flagの1~6文字目が”n00bz{“、24文字目(最後)が”}”なのは明らかなのでここから逆算すれば良い。
import math
char_all = [-1] * 25
char_all[1] = ord('n')
char_all[2] = ord('0')
char_all[3] = ord('0')
char_all[4] = ord('b')
char_all[5] = ord('z')
char_all[6] = ord('{')
char_all[24] = ord('}')
# Asc(char_1) Xor Asc(char_8) = 22
char_all[8] = char_all[1] ^ 22
# Asc(char_19) = 107
char_all[19] = 107
# Asc(char_20) + 501 = (Asc(char_1) * 5)
char_all[20] = char_all[1] * 5 - 501
# Asc(char_23) + Asc(char_8) = 235
char_all[23] = 235 - char_all[8]
# Asc(char_18) = Asc(char_23)
char_all[18] = char_all[23]
# Asc(char_10) + Asc(char_24) = 176
char_all[10] = 176 - char_all[24]
# (Asc(char_12) / 5) ^ (Asc(char_3) / 12) = 130321
char_all[12] = int(math.pow(130321, 1 / (char_all[3] // 12))) * 5
# Asc(char_12) Xor (Asc(char_17) - 5) = 5
char_all[17] = (char_all[12] ^ 5) + 5
# Asc(char_16) = Asc(char_17) + 19
char_all[16] = char_all[17] + 19
# Asc(char_22) Xor Asc(char_6) = 23
char_all[22] = char_all[6] ^ 23
# 1365 = Asc(char_22) Xor 1337
assert(char_all[22] ^ 1337 == 1365)
# Asc(char_9) - Asc(char_22) = -9
char_all[9] = char_all[22] - 9
# char_22 = char_11
char_all[11] = char_all[22]
# Asc(char_21) = Asc(char_22)
char_all[21] = char_all[22]
# Asc(char_14) Xor Asc(char_24) = 77
char_all[14] = char_all[24] ^ 77
# Asc(char_15) * Asc(char_8) = 14040
char_all[15] = 14040 // char_all[8]
# Asc(char_13) Xor Asc(char_14) Xor Asc(char_2) = 121
char_all[13] = char_all[14] ^ char_all[2] ^ 121
# Asc(char_10) = Asc(char_7)
char_all[7] = char_all[10]
for c in char_all:
if c != -1:
print(chr(c), end="")
print()
> python solver.py
n00bz{3xc3l_y0ur_sk1lls}
Flag
n00bz{3xc3l_y0ur_sk1lls}
Programming
Sillygoose
解法
0~10^100の間で二分探索をするだけ。
from pwn import *
section_min = 0
section_max = pow(10, 100)
r = remote("24.199.110.35", 41199)
while True:
section_mid = (section_min + section_max) // 2
r.sendline(f"{section_mid}".encode())
result = r.recvline().decode()
if "large" in result:
section_max = section_mid
elif "small" in result:
section_min = section_mid
else:
r.interactive()
> python ./solver.py
n00bz{y0u_4r3_4_sm4rt_51l1y_g0053}
Flag
n00bz{y0u_4r3_4_sm4rt_51l1y_g0053}
Back From Brazil
上位チームの検証のために書いたらあかんっぽいのでそれが済んでからOKになって覚えていたら書きます……。
Blockchain
EVM – The Basics
解法
EVM Codes – An Ethereum Virtual Machine Opcodes Interactive Reference を見て頑張って解読する。
5f346113370265fdc29ff358a314601257ff00
5f: PUSH0
34: CALLVALUE
61: PUSH2 0x1337
02: MUL (0x1337 * INPUT)
65: PUSH6 0xfdc29ff358a3
14: EQ (0x1337 * INPUT == 0xfdc29ff358a3)
60: PUSH1 12
57: JUMPI ff 00
EQの条件を満たすINPUTを計算する。
>>> 0xfdc29ff358a3 / 0x1337
56721355765.0
>>> hex(0xfdc29ff358a3 // 0x1337)
'0xd34db33f5'
Flag
n00bz{0xd34db33f5}
EVM – Conditions
解法
こちらも同様に解読する。
5f600f607002610258525f60056096046090525f600760090A61FFFA526105396126aa18620bfabf52600361fffa5102620bfabf51013461025851600402016090510114604857ff00
5f: PUSH0
60 0f: PUSH1 0x0f
60 70: PUSH1 0x70
02: MUL (0x70 * 0x0f = 0x690)
61 02 58: PUSH2 0x0258
52: MSTORE (0x0258 <= 0x690)
5f: PUSH0
60 05: PUSH1 0x05
60 96: PUSH1 0x96
04: DIV (0x96 // 0x05 = 0x1e)
60 90: PUSH1 0x90
52: MSTORE (0x90 <= 0x1e)
5f: PUSH0
60 07: PUSH1 0x07
60 09: PUSH1 0x09
0A: EXP (0x09 ** 0x07 = 0x48fb79)
61 FF FA: PUSH2 0xfffa
52: MSTORE (0xfffa <= 0x48fb79)
61 05 39: PUSH2 0x0539
61 26 aa: PUSH2 0x26aa
18: XOR (0x26aa ^ 0x0539 = 0x2393)
62 0b fa bf: PUSH3 0x0bfabf
52: MSTORE (0x0bfabf <= 0x2393)
60 03: PUSH1 0x03
61 ff fa: PUSH2 0xfffa
51: MLOAD (0x48fb79)
02: MUL (0x48fb79 * 0x03 = 0xdaf26b)
62 0b fa bf: PUSH3 0x0bfabf
51: MLOAD (0x2393)
01: ADD (0x2393 + 0xdaf26b = 0xdb15fe)
34: CALLVALUE (INPUT)
61 02 58: PUSH2 0x0258
51: MLOAD (0x690)
60 04: PUSH1 0x04
02: MUL (0x04 * 0x690 = 0x1a40)
01: ADD (0x1a40 + INPUT)
60 90: PUSH1 0x90
51: MLOAD (0x1e)
01: ADD (0x1e + (0x1a40 + INPUT))
14: EQ (INPUT + 0x1a5e == 0xdb15fe)
60 48: PUSH1 0x48
57: JUMPI (0x48)
ff: SELFDESTRUCT
00: STOP
条件を満たすINPUTは0xdb15fe - 0x1a5e = 0xdafba0
である。
Flag
n00bz{0xdafba0}
おわりに
最近少しドタバタしてたので久しぶりのCTF参加でしたが楽しめました。
他にも勉強モチベ爆上がりイベントもあったのでまた勉強頑張ります。