この大会は2024/8/3 10:00(JST)~2024/8/5 10:00(JST)に開催されました。
今回もチームで参戦。結果は5717点で967チーム中144位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (Misc)
Discordに入り、#announcementチャネルのメッセージを見ると、フラグが書いてあった。
n00bz{w3lc0m3_t0_n00bzCTF2024!}
Addition (Misc)
サーバの処理概要は以下の通り。
・questions: 数値入力 ・0以上questions未満の値iに対して以下を実行 ・a: 0以上10以下のランダム整数 ・b: 0以上10以下のランダム整数 ・yourans: 数値入力 ・totaltime = pow(2, i) ・合計でtotaltimeの時間だけスリープ ・yourans が a + b と一致しない場合、終了 ・flagをquestionsの文字数だけ表示
普通に計算に答えていくと、時間がかかりすぎる。questionsに-1を指定すれば、flagの最終の文字以外を取得できる。
$ nc 24.199.110.35 42189 how many questions do you want to answer? -1 n00bz{m4th_15nt_4ll_4b0ut_3qu4t10n5}
n00bz{m4th_15nt_4ll_4b0ut_3qu4t10n5}
EVM - The Basics (Blockchain)
https://app.dedaub.com/decompileでデコンパイルする。
0x0: PUSH0 0x1: CALLVALUE 0x2: PUSH2 0x1337 0x5: MUL 0x6: PUSH6 0xfdc29ff358a3 0xd: EQ 0xe: PUSH1 0x12 0x10: JUMPI 0x11: SELFDESTRUCT 0x12: STOP
0x1337をかけて0xfdc29ff358a3になる値を求める。
>>> a = 0x1337 >>> b = 0xfdc29ff358a3 >>> b % a 0 >>> hex(b // a) '0xd34db33f5'
n00bz{0xd34db33f5}
Sillygoose (Programming)
ランダムな0以上pow(10, 100)以下の値を当てる問題。ただし、時間は60秒以下に対応する必要がある。最速と思われる二分探索法で探し当てる。
#!/usr/bin/env python3 import socket def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('24.199.110.35', 41199)) ans = [0, pow(10, 100)] while True: inp = (ans[0] + ans[1]) // 2 print(inp) s.sendall(str(inp).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) if data == 'your answer is too large you silly goose': ans[1] = inp elif data == 'your answer is too small you silly goose': ans[0] = inp else: break data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 your answer is too large you silly goose 2500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 your answer is too large you silly goose 1250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 your answer is too small you silly goose 1875000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 your answer is too large you silly goose 1562500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 your answer is too small you silly goose 1718750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 your answer is too small you silly goose : 1806248900837509389389223986621313895058191308210212045285156862871201722676478271626632639416820242 your answer is too small you silly goose 1806248900837509389389223986621313895058191308210212045285156862871201722676478271626632639416820388 your answer is too small you silly goose 1806248900837509389389223986621313895058191308210212045285156862871201722676478271626632639416820461 your answer is too small you silly goose 1806248900837509389389223986621313895058191308210212045285156862871201722676478271626632639416820498 your answer is too large you silly goose 1806248900837509389389223986621313895058191308210212045285156862871201722676478271626632639416820479 your answer is too small you silly goose 1806248900837509389389223986621313895058191308210212045285156862871201722676478271626632639416820488 congratulations you silly goose n00bz{y0u_4r3_4_sm4rt_51l1y_g0053}
n00bz{y0u_4r3_4_sm4rt_51l1y_g0053}
Numbers 2 (Programming)
$ nc challs.n00bzunit3d.xyz 10224 Welcome to Numbers 2! Time to step up the game... Current round: 1 of 100 Give me the least common multiple of 190 and 43: 8170 Correct! You took too long! $ nc challs.n00bzunit3d.xyz 10224 Welcome to Numbers 2! Time to step up the game... Current round: 1 of 100 Give me the greatest common divisor of 34 and 124: 2 Correct! You took too long! $ nc challs.n00bzunit3d.xyz 10224 Welcome to Numbers 2! Time to step up the game... Current round: 1 of 100 Give me the greatest prime factor of 74: 37 Correct! You took too long!
LCM, GCD, 素因数分解したときの因数の最大値を答えるパターンがあるので、識別して答えていく。
#!/usr/bin/env python3 import socket from Crypto.Util.number import * from sympy import factorint def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def LCM(a, b): return a * b // GCD(a, b) def GPF(n): f = factorint(n) f = sorted(f.items(), reverse=True) return f[0][0] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('challs.n00bzunit3d.xyz', 10224)) data = recvuntil(s, b'\n').rstrip() print(data) for i in range(100): data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b': ') print(data, end='') if 'least common multiple' in data: a = int(data.split(' ')[7]) b = int(data.split(' ')[9][:-1]) ans = LCM(a, b) elif 'greatest common divisor' in data: a = int(data.split(' ')[7]) b = int(data.split(' ')[9][:-1]) ans = GCD(a, b) else: n = int(data.split(' ')[7][:-1]) ans = GPF(n) print(ans) s.sendall(str(ans).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
Welcome to Numbers 2! Time to step up the game... Current round: 1 of 100 Give me the greatest common divisor of 162 and 174: 6 Correct! Current round: 2 of 100 Give me the greatest prime factor of 146: 73 Correct! Current round: 3 of 100 Give me the least common multiple of 262 and 172: 22532 Correct! Current round: 4 of 100 Give me the greatest prime factor of 47: 47 Correct! Current round: 5 of 100 Give me the least common multiple of 177 and 78: 4602 Correct! : : Current round: 96 of 100 Give me the least common multiple of 2398 and 740: 887260 Correct! Current round: 97 of 100 Give me the greatest common divisor of 8228 and 5806: 2 Correct! Current round: 98 of 100 Give me the least common multiple of 4408 and 3176: 1749976 Correct! Current round: 99 of 100 Give me the greatest common divisor of 8527 and 9805: 1 Correct! Current round: 100 of 100 Give me the least common multiple of 8331 and 9207: 25567839 Correct! Good job! Here's your flag: n00bz{numb3r5_4r3_fun_7f3d4a_ed47522cf007}
n00bz{numb3r5_4r3_fun_7f3d4a_ed47522cf007}
The Gang (OSINT)
John Doeが最近チーム「n00bzUnit3d」に加わったかどうかを調べる問題。
"n00bzUnit3d"で検索すると、以下のWebサイトが見つかる。
https://n00bzunit3d.xyz/
Membersのリンクをクリックし、メンバー一覧を確認する。この中にjohndoeのリンクがあったので、アクセスする。
https://n00bzunit3d.xyz/authors/johndoe
このページにフラグが書いてあった。
n00bz{1ts_051N7_71m3_3e4a7d6f}
Pastebin (OSINT)
かなり前に作成したユーザabhinav654321のpastebinを調べる問題。
https://pastebin.com/u/abhinav654321を見てみる。そこには掲示板のスレットが1つあるので、中を見てみる。URLは以下の通り。
https://pastebin.com/j1UnKA7m
コメントがたくさんあるが、特に何も見つからない。Internet Archiveで以下のURLを見てみる。
https://pastebin.com/j1UnKA7m
2024/6/17にスナップショットがあるので、見てみると、フラグが書いてあった。
n00bz{l0ng_t1m3_ag0_m34ns_w4yb4ck}
The Gang 2 (OSINT)
問題「The Gang」で見つけた https://n00bzunit3d.xyz/authors/johndoe では、以下のページがリンクされている。
https://n00bzunit3d.xyz/blog/who-am-i/
行頭の文字をつなげると、以下のようになる。
USERNAME IS JOHN HACKER DOE
https://x.com/johnhackerdoeにアクセスすると、フラグがポストされているのを見つけた。
n00bz{5t0p_ch4s1ng_m3_4f2d1a7d}
Vacation (Rev)
ps1ファイルが添付されている。コードを見ると、flagの各文字のASCIIコードと3をXORして文字にして出力していることがわかる。出力文字列の各文字のASCIIコードと3をXORして文字にすればフラグになる。
>>> s = b'm33ayxeqln\\sbqjp\\twk\\{lq~' >>> ''.join([chr(c ^ 3) for c in s]) 'n00bz{from_paris_wth_xor}'
n00bz{from_paris_wth_xor}
FlagChecker (Rev)
xlsmファイルが添付されている。マクロコードを見てみる。
$ olevba FlagChecker.xlsm olevba 0.60.2 on Python 3.11.9 - http://decalage.info/python/oletools =============================================================================== FILE: FlagChecker.xlsm Type: OpenXML WARNING For now, VBA stomping cannot be detected for files in memory ------------------------------------------------------------------------------- VBA MACRO ThisWorkbook.cls in file: xl/vbaProject.bin - OLE stream: 'VBA/ThisWorkbook' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (empty macro) ------------------------------------------------------------------------------- VBA MACRO Sheet1.cls in file: xl/vbaProject.bin - OLE stream: 'VBA/Sheet1' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (empty macro) ------------------------------------------------------------------------------- VBA MACRO Module1.bas in file: xl/vbaProject.bin - OLE stream: 'VBA/Module1' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Sub FlagChecker() Dim chars(1 To 24) As String guess = InputBox("Enter the flag:") If Len(guess) <> 24 Then MsgBox "Nope" End If char_1 = Mid(guess, 1, 1) char_2 = Mid(guess, 2, 1) char_3 = Mid(guess, 3, 1) char_4 = Mid(guess, 4, 1) char_5 = Mid(guess, 5, 1) char_6 = Mid(guess, 6, 1) char_7 = Mid(guess, 7, 1) char_8 = Mid(guess, 8, 1) char_9 = Mid(guess, 9, 1) char_10 = Mid(guess, 10, 1) char_11 = Mid(guess, 11, 1) char_12 = Mid(guess, 12, 1) char_13 = Mid(guess, 13, 1) char_14 = Mid(guess, 14, 1) char_15 = Mid(guess, 15, 1) char_16 = Mid(guess, 16, 1) char_17 = Mid(guess, 17, 1) char_18 = Mid(guess, 18, 1) char_19 = Mid(guess, 19, 1) char_20 = Mid(guess, 20, 1) char_21 = Mid(guess, 21, 1) char_22 = Mid(guess, 22, 1) char_23 = Mid(guess, 23, 1) char_24 = Mid(guess, 24, 1) If (Asc(char_1) Xor Asc(char_8)) = 22 Then If (Asc(char_10) + Asc(char_24)) = 176 Then If (Asc(char_9) - Asc(char_22)) = -9 Then If (Asc(char_22) Xor Asc(char_6)) = 23 Then If ((Asc(char_12) / 5) ^ (Asc(char_3) / 12)) = 130321 Then If (char_22 = char_11) Then If (Asc(char_15) * Asc(char_8)) = 14040 Then If (Asc(char_12) Xor (Asc(char_17) - 5)) = 5 Then If (Asc(char_18) = Asc(char_23)) Then If (Asc(char_13) Xor Asc(char_14) Xor Asc(char_2)) = 121 Then If (Asc(char_14) Xor Asc(char_24)) = 77 Then If 1365 = (Asc(char_22) Xor 1337) Then If (Asc(char_10) = Asc(char_7)) Then If (Asc(char_23) + Asc(char_8)) = 235 Then If Asc(char_16) = (Asc(char_17) + 19) Then If (Asc(char_19)) = 107 Then If (Asc(char_20) + 501) = (Asc(char_1) * 5) Then If (Asc(char_21) = Asc(char_22)) Then MsgBox "you got the flag!" End If End If End If End If End If End If End If End If End If End If End If End If End If End If End If End If End If End If End Sub +----------+--------------------+---------------------------------------------+ |Type |Keyword |Description | +----------+--------------------+---------------------------------------------+ |Suspicious|Xor |May attempt to obfuscate specific strings | | | |(use option --deobf to deobfuscate) | |Suspicious|Hex Strings |Hex-encoded strings were detected, may be | | | |used to obfuscate strings (option --decode to| | | |see all) | |Suspicious|Base64 Strings |Base64-encoded strings were detected, may be | | | |used to obfuscate strings (option --decode to| | | |see all) | +----------+--------------------+---------------------------------------------+
フラグの条件は以下のようになっている。
・x[0] ^ x[7] == 22 ・x[9] + x[23] == 176 ・x[8] - x[21] == -9 ・x[21] ^ x[5] == 23 ・(x[11] / 5) ^ (x[2] / 12) == 130321 130321を素因数分解すると、19 ^ 4であることがわかる。 → x[11] / 5 == 19 → x[11] == 95 → x[2] / 12 == 4 → x[2] == 48 ・x[21] == x[10] ・x[14] * x[7] == 14040 ・x[11] ^ (x[16] - 5) == 5 ・x[17] == x[22] ・x[12] ^ x[13] ^ x[1] == 121 ・x[13] ^ x[23] == 77 ・1365 == x[21] ^ 1337 ・x[9] == x[6] ・x[22] + x[7] == 235 ・x[15] == x[16] + 19 ・x[18] == 107 ・x[19] + 501 == x[0] * 5 ・x[20] == x[21]
フラグの形式も条件に入れ、わかるものから割り出していく。
x[0] = ord('n') = 110 x[1] = ord('o') = 48 x[3] = ord('b') = 98 x[4] = ord('z') = 122 x[5] = ord('{') = 123 x[23] = ord('}') = 125
(x[11] / 5) ^ (x[2] / 12) == 130321から以下がわかる。
x[11] == 95 x[2] = 48
x[0] ^ x[7] == 22から以下がわかる。
x[7] = x[0] ^ 22 = 120
このようにして全部割り出していく。
#!/usr/bin/env python3 x = [0] * 24 flag_head = 'n00bz{' flag_tail = '}' for i in range(len(flag_head)): x[i] = ord(flag_head[i]) x[23] = ord(flag_tail) x[11] = 95 x[7] = x[0] ^ 22 x[9] = 176 - x[23] x[21] = x[5] ^ 23 x[8] = x[21] - 9 x[10] = x[21] x[14] = 14040 // x[7] x[16] = (x[11] ^ 5) + 5 x[13] = x[23] ^ 77 x[12] = x[13] ^ x[1] ^ 121 assert x[21] == 1365 ^ 1337 x[6] = x[9] x[22] = 235 - x[7] x[17] = x[22] x[15] = x[16] + 19 x[18] = 107 x[19] = x[0] * 5 - 501 x[20] = x[21] flag = ''.join([chr(c) for c in x]) print(flag)
n00bz{3xc3l_y0ur_sk1lls}
Plane (Forensics)
写真が撮影された場所の緯度、経度を答える問題。
$ exiftool plane.jpg ExifTool Version Number : 12.76 File Name : plane.jpg Directory : . File Size : 208 kB File Modification Date/Time : 2024:08:03 11:06:09+09:00 File Access Date/Time : 2024:08:03 11:06:20+09:00 File Inode Change Date/Time : 2024:08:03 11:06:09+09:00 File Permissions : -rwxrwxrwx File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg Exif Byte Order : Big-endian (Motorola, MM) X Resolution : 72 Y Resolution : 72 Resolution Unit : inches Y Cb Cr Positioning : Centered GPS Version ID : 2.3.0.0 GPS Latitude Ref : North GPS Longitude Ref : West Profile CMM Type : Profile Version : 2.1.0 Profile Class : Display Device Profile Color Space Data : RGB Profile Connection Space : XYZ Profile Date Time : 0000:00:00 00:00:00 Profile File Signature : acsp Primary Platform : Unknown () CMM Flags : Not Embedded, Independent Device Manufacturer : Device Model : Device Attributes : Reflective, Glossy, Positive, Color Rendering Intent : Media-Relative Colorimetric Connection Space Illuminant : 0.9642 1 0.82491 Profile Creator : Profile ID : 0 Profile Description : sRGB Red Matrix Column : 0.43607 0.22249 0.01392 Green Matrix Column : 0.38515 0.71687 0.09708 Blue Matrix Column : 0.14307 0.06061 0.7141 Red Tone Reproduction Curve : (Binary data 40 bytes, use -b option to extract) Green Tone Reproduction Curve : (Binary data 40 bytes, use -b option to extract) Blue Tone Reproduction Curve : (Binary data 40 bytes, use -b option to extract) Media White Point : 0.9642 1 0.82491 Profile Copyright : Google Inc. 2016 Image Width : 3000 Image Height : 4000 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 3000x4000 Megapixels : 12.0 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
https://www.motohasi.net/GPS/PosConv.phpで小数の形式に変換する。
n00bz{13.37,-13.37}
Wave (Forensics)
バイナリエディタを見ると、先頭4バイトと9~15バイト目、37~40バイト目が"0"になっていることがわかる。それぞれ、"RIFF"、"WAVEfmt"、"data"に修正してみる。
Audacityで開くと、モールス信号になっていることがわかる。
-... . . .--. -... --- .--. -- --- .-. ... . -.-. --- -.. .
デコードすると、以下のようになる。
BEEPBOPMORSECODE
n00bz{beepbopmorsecode}
Disk Golf (Forensics)
Autopsyで開き、/home/johnhackerdoe/flag.txtをエクスポートする。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
8進数と推測しデコードする。
>>> s = '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' >>> ''.join([chr(int(c, 8)) for c in s.split(' ')]) 'n00bz{7h3_l0ng_4w41t3d_d15k_f0r3ns1c5}'
n00bz{7h3_l0ng_4w41t3d_d15k_f0r3ns1c5}
Vinegar (Crypto)
Vigenere暗号と推測し、https://www.dcode.fr/vigenere-cipherで復号する。
vigenerecipherisfun
n00bz{vigenerecipherisfun}
RSA (Crypto)
cがnよりビット数が少なく、eが小さいため、Low Public-Exponent Attackで復号する。
#!/usr/bin/env python3 from Crypto.Util.number import * import gmpy2 with open('encryption.txt', 'r') as f: params = f.read().splitlines() e = int(params[0].split(' ')[-1]) c = int(params[2].split(' ')[-1]) m, success = gmpy2.iroot(c, e) assert success == True flag = long_to_bytes(m).decode() print(flag)
n00bz{crypt0_1s_1nc0mpl3t3_w1th0ut_rs4!!}
Vinegar 2 (Crypto)
以下の文字列を使ったVigenere暗号になっている。
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}_?'
鍵は以下であることがわかっているので、逆算して復号する。
'5up3r_s3cr3t_k3y_f0r_1337h4x0rs_r1gh7?'
#!/usr/bin/env python3 alphanumerical = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}_?' key = '5up3r_s3cr3t_k3y_f0r_1337h4x0rs_r1gh7?' with open('enc.txt', 'r') as f: enc = f.read() flag = '' for i in range(len(enc)): idx1 = alphanumerical.index(enc[i]) idx2 = alphanumerical.index(key[i]) idx = (idx1 - idx2) % len(alphanumerical) flag += alphanumerical[idx] print(flag)
n00bz{4lph4num3r1c4l_1s_n0t_4_pr0bl3m}
Random (Crypto)
サーバの処理概要は以下の通り。
・s: 入力 ・sに同じ文字が含まれている場合、エラー ・sの長さが10より小さい場合、エラー ・sの長さが69の場合、エラー ・sの各文字の順序についてシャッフル ・ret = amazingcustomsortingalgorithm(s) ・n: sの長さ ・0以上69未満のiについて、以下を実行 ・good = true ・文字の順序で小さい順から並んでいたら、good = true ・goodがtrueの場合、trueを返却 ・sの各文字の順序についてシャッフル ・falseを返却 ・retがtrueの場合、フラグを表示
適当な文字列で試してみる。
$ nc challs.n00bzunit3d.xyz 10161
abcdefghij
edhiafcbgj
afhiedjcbg
fhgdiecabj
egacibdjfh
eafbhjgcid
difgbhceaj
aegcjfidbh
bdgichfjae
fbhjiagecd
gfjebhaicd
caibdghfej
ifjcgeahdb
cdajfbhieg
fgdhciejba
fjcbdgahei
acebdjfhgi
acbgheijfd
ahfjicgedb
iceabjgfhd
hiegcfdbaj
igbchfeajd
hdbjacgief
jcebaifhgd
iegajhdcbf
gibjfadehc
igejcfhbad
eghacibfjd
cdfeiajghb
adbhgicfje
cdiaefjghb
acdbgifehj
djieabghfc
dbgaeicfjh
edicagjhfb
ifeahgcjdb
cdbeajigfh
hbigaedfjc
jbfhcaiegd
gidbehcjaf
ihafcbgejd
cjdegbfhia
hfdcgaiejb
fahcbegdij
gbdehajicf
idjhgbcafe
hfbgcajdei
jcaedfbhig
jhgdaibfce
cihgjfeadb
jgeaidfcbh
gdieafjchb
feadbhgcji
hcbjaidegf
hcdeibajgf
igfhejbadc
beadhgfcij
fdaicbgjeh
efidgbcahj
bghfadeijc
igjcdfehab
hebijadfcg
hfdgjcaibe
bjgfcdehia
cdbaefhjig
iahfbdegcj
bdcihfgaje
dhgfibaecj
dhjfbgceia
feihacgbjd
UNWORTHY USER DETECTED
最初に以下のようにシャッフルされている。
abcdefghij → edhiafcbgj
文字列の長さが変わらない場合、シャッフルのされ方は変わらないので、順番を考えて指定する。
ehgbaficdj
$ nc challs.n00bzunit3d.xyz 10161 ehgbaficdj abcdefghij n00bz{5up3r_dup3r_ultr4_54f3_p455w0rd_58dc49b79aa3}
n00bz{5up3r_dup3r_ultr4_54f3_p455w0rd_58dc49b79aa3}