この大会は2018/11/24 8:00(JST)~2018/11/26 8:00(JST)に開催されました。
今回もチームで参戦。結果は 7219点で764チーム中65位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome (Welcome)
Discordの#welcomeチャネルにフラグが書いてあった。
TUCTF{my_f1r57_fl46}
Mrs. White's <br> Messy Maids (Web)
ソースのコメントに以下が書いてある。
<!-- I might kill if I could find him. Stupid Mr. /Boddy -->
http://18.218.152.56/Boddy/にアクセスすると、フラグが書いてあった。
TUCTF{1_4ccu53_Mr5._Wh173_w17h_7h3_c4ndl3571ck_1n_7h3_c0mm3n75}
Literal (Misc)
$ curl http://18.222.124.7 <html> <head> <meta http-equiv="refresh" content="0; URL='Literal.html'" /> </head> </html> $ curl http://18.222.124.7/Literal.html <html> <head> <meta http-equiv="Refresh" content="1; url=https://en.wikipedia.org/wiki/Fork_bomb"> </head> <body> <!-- * * f f f * ** * ff ff ff * * ** || ff ff ff ** ||||T|| fUffffffff * |C|||T| oooooooooooo fFff |||||||{ooooooooooRfff3o ooo4ooooooooooooooLff.ooooo0 oooNooooooooooooooo3ooooooo5ooo. oooo4oooooRoooooooooo3oooooooooo. oooDooooo4oooooNooooooooooooooooo ooooooooooooooGoooooooooooooooooo ooooooooooooooooooooo3oooooooooRo oooooooo0oooooooooooooooooooooo oooooooffUoooooooooooooooooo ooofff5ooooooooooooooooo fff }ooooooooooooooo fff --> Redirecting to Wikipedia...! </body> </html>
英大文字、数字、'{', '}', '.'を抜き出す。
import string comment = ''' * * f f f * ** * ff ff ff * * ** || ff ff ff ** ||||T|| fUffffffff * |C|||T| oooooooooooo fFff |||||||{ooooooooooRfff3o ooo4ooooooooooooooLff.ooooo0 oooNooooooooooooooo3ooooooo5ooo. oooo4oooooRoooooooooo3oooooooooo. oooDooooo4oooooNooooooooooooooooo ooooooooooooooGoooooooooooooooooo ooooooooooooooooooooo3oooooooooRo oooooooo0oooooooooooooooooooooo oooooooffUoooooooooooooooooo ooofff5ooooooooooooooooo fff }ooooooooooooooo fff ''' chars = string.uppercase + string.digits + '{}.' flag = '' for c in comment: if c in chars: flag += c print flag
TUCTF{R34L.0N35.4R3.D4NG3R0U5}
RSAyyyy (Crypto)
RSAの基本的な計算をして答えていく。
import socket import re from Crypto.Util.number import * import sympy def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('3.16.57.250', 12345)) ## Level 1 ## data = recvuntil(s, 'n?\n').strip() print data pattern = 'p = (.+)\nq = (.+)\n' m = re.search(pattern, data) p = int(m.group(1)) q = int(m.group(2)) n = p * q print n s.sendall(str(n) + '\n') ## Level 2 ## data = recvuntil(s, 'm?\n').strip() print data pattern = 'message = \"(.+)\"' m = re.search(pattern, data) message = m.group(1) m = bytes_to_long(message) print m s.sendall(str(m) + '\n') ## Level 3 ## data = recvuntil(s, 'n?\n').strip() print data pattern = 'p = (.+)\nq = (.+)\n' m = re.search(pattern, data) p = int(m.group(1)) q = int(m.group(2)) n = p * q print n s.sendall(str(n) + '\n') data = recvuntil(s, 'c?\n').strip() print data pattern = 'e = (.+)\nm = (.+)\n' m = re.search(pattern, data) e = int(m.group(1)) m = int(m.group(2)) c = pow(m, e, p * q) print c s.sendall(str(c) + '\n') ## Level 4 ## data = recvuntil(s, '(n)?\n').strip() print data pattern = 'p = (.+)\nq = (.+)\n' m = re.search(pattern, data) p = int(m.group(1)) q = int(m.group(2)) phi = (p - 1) * (q - 1) print phi s.sendall(str(phi) + '\n') data = recvuntil(s, 'd?\n').strip() print data pattern = 'e = (.+)\n' m = re.search(pattern, data) e = int(m.group(1)) d = inverse(e, phi) print d s.sendall(str(d) + '\n') ## Level 5 ## data = recvuntil(s, 'p?\n').strip() print data pattern = 'n = (.+)\n' m = re.search(pattern, data) n = int(m.group(1)) fac = sympy.factorint(n) p = fac.keys()[0] print p s.sendall(str(p) + '\n') data = recvuntil(s, 'q?\n').strip() print data q = n / p print q s.sendall(str(q) + '\n') ## Level 6 ## data = recvuntil(s, 'p?\n').strip() print data pattern = 'c = (.+)\nn = (.+)\ne = (.+)\n' m = re.search(pattern, data) c = int(m.group(1)) n = int(m.group(2)) e = int(m.group(3)) fac = sympy.factorint(n) p = fac.keys()[0] print p s.sendall(str(p) + '\n') data = recvuntil(s, 'q?\n').strip() print data q = n / p print q s.sendall(str(q) + '\n') data = recvuntil(s, '(n)?\n').strip() print data phi = (p - 1) * (q - 1) print phi s.sendall(str(phi) + '\n') data = recvuntil(s, 'd?\n').strip() print data d = inverse(e, phi) print d s.sendall(str(d) + '\n') data = recvuntil(s, 'm?\n').strip() print data m = pow(c, d, n) print m s.sendall(str(m) + '\n') data = s.recv(2048) print data
実行結果は以下のとおり。
$ python solve.py AAA YYYYYYY YYYYYYYYYYYYYY YYYYYYYYYYYYYY YYYYYYY A:::A Y:::::Y Y:::::YY:::::Y Y:::::YY:::::Y Y:::::Y A:::::A Y:::::Y Y:::::YY:::::Y Y:::::YY:::::Y Y:::::Y A:::::::A Y::::::Y Y::::::YY::::::Y Y::::::YY::::::Y Y::::::Y A:::::::::A YYY:::::Y Y:::::YYYYYY:::::Y Y:::::YYYYYY:::::Y Y:::::YYY A:::::A:::::A Y:::::Y Y:::::Y Y:::::Y Y:::::Y Y:::::Y Y:::::Y A:::::A A:::::A Y:::::Y:::::Y Y:::::Y:::::Y Y:::::Y:::::Y A:::::A A:::::A Y:::::::::Y Y:::::::::Y Y:::::::::Y A:::::A A:::::A Y:::::::Y Y:::::::Y Y:::::::Y A:::::AAAAAAAAA:::::A Y:::::Y Y:::::Y Y:::::Y A:::::::::::::::::::::A Y:::::Y Y:::::Y Y:::::Y A:::::AAAAAAAAAAAAA:::::A Y:::::Y Y:::::Y Y:::::Y A:::::A A:::::A Y:::::Y Y:::::Y Y:::::Y A:::::A A:::::A YYYY:::::YYYY YYYY:::::YYYY YYYY:::::YYYY A:::::A A:::::A Y:::::::::::Y Y:::::::::::Y Y:::::::::::Y AAAAAAA AAAAAAA YYYYYYYYYYYYY YYYYYYYYYYYYY YYYYYYYYYYYYY This challenge is designed to act as an introduction to RSA. If you have a team member that is not already familiar with RSA, then give this challenge to them. For the first level, I recommend looking at https://simple.wikipedia.org/wiki/RSA_algorithm but any description of the RSA algorithm will do. Later levels will probably require further research. Let's get started! Level 1: Calculating n p = 4069127677 q = 2789804453 What is n? 11352070513120145681 Whoop whoop! Congratulations! You beat Level 1! In order to calculate the ciphertext, the message needs to be converted to an integer. Level 2: Calculating m message = "epidemiology ruminant posy provident condemnatory" What is m? 3996904368156142538059084847112340597446843790219386982825830622566068551003043414749611727228669241263353749935452793 Yeah! You do that RSA! Congratulations! You beat Level 2! Now, we are going to actually calculate ciphertext. Level 3: Calculating c p = 2443367911 q = 2356648727 What is n? 5758159877050799297 Whoop whoop! e = 65537 m = 113667960107631 What is c? 1146535438296311460 Nice job! Congratulations! You beat Level 3! In order for RSA to be asymmetrical, the private exponent, d, needs to be calculated. Level 4: Calculating d p = 3288422173 q = 4282201841 What is tot(n)? 14081687475635196480 Ayyyyy e = 65537 What is d? 4110390793122988673 Way to go! Congratulations! You beat Level 4! The easiest way to break RSA is factoring n, if it is possible. Level 5: Factoring n n = 6158577329169411137 What is p? 2316690071 Nice job! What is q? 2658351847 Yeah! You do that RSA! Congratulations! You beat Level 5! Now, let's put everything together and break RSA! Level 6: Breaking simple RSA c = 5755984274299477791 n = 13316707131108976583 e = 65537 What is p? 3710062907 Way to go! What is q? 3589348069 Yeah! You do that RSA! What is tot(n)? 13316707123809565608 Way to go! What is d? 4877462163356955809 Nice job! Finally, what is m? 418548049262 That was adequate. Congratulations! You beat Level 6! Congratulations on finishing this introduction to RSA! I hope this was fun and informative. Here's your flag: TUCTF{RSA_1$_R34LLY_C00L_4ND_1MP0RT4NT_CRYPT0}
TUCTF{RSA_1$_R34LLY_C00L_4ND_1MP0RT4NT_CRYPT0}
AESential Lesson (Crypto)
$ nc 18.218.238.95 12345 Lol. You think you can steal my flag? I'll even encrypt your input for you, but you can't get my secrets! Enter your text here: 11 Here's your encrypted text: 61537177fe7ceb9cc7eb9e57c64fc9a5492544386adac7645752ed14e253dc501c2da954187caa36ed893ca14c05e767ae1c6abac2df0bbffea785521c616e54
フラグを1文字ずつはみ出させ、ブルートフォースでブロック単位で暗号が一致するものを求めていく。
0123456789abcdef 1111111111111111 111111111111111A 1111111111111111 111111111111111F FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFP ※F: フラグ(32文字)
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('18.218.238.95', 12345)) flag = '' for i in range(32): for code in range(32, 127): plain = '#' * (31 - i) + flag + chr(code) + '#' * (31 - i) data = recvuntil(s, 'here: ') print data + plain s.sendall(plain + '\n') data = recvuntil(s, '\n') cipher = recvuntil(s, '\n').strip() print data + cipher if cipher[32:64] == cipher[96:128]: flag += chr(code) break print flag
TUCTF{A3S_3CB_1S_VULN3R4BL3!!!!}
Jimmy's Crypto (Crypto)
2つのファイルが同じXOR鍵で暗号化されている。https://github.com/SpiderLabs/cribdragのツールを使って、英単語を推測しながら復号していく。
まず、2つの暗号ファイルのXORを16進数で取得する。
def str_xor(s1, s2): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2)) with open('secret', 'rb') as f: enc_secret = f.read() with open('flag', 'rb') as f: enc_flag = f.read() xor_str = str_xor(enc_secret, enc_flag) print xor_str.encode('hex')
'secret' や'this'などよく出てきそうな英単語から推測していく。フラグより後は復号していないが、最終的には以下の結果。
>cribdrag.py 2a1106060001125604001100061c45155512010317475a310b50461c1743060d1d5d00370a0d5511540d2030223866163d103d313c2736472c7a687a39734d22467f2a413c7f2a5f3d343d26547f32406d4737244073355d2b56515e0f00010c5200031415411a06184f532765 : Your message is currently: 0 This is a secret file. Top secret. Don't 40 steal my secrets. If you are looking at 80 this file,__________________ Your key is currently: 0 ~you have been authorized for this secre 40 t~TUCTF{D0NT_US3_TH3_S4M3_K3Y_F0R_TH3_S4 80 M3_M3SS4G3}__________________
TUCTF{D0NT_US3_TH3_S4M3_K3Y_F0R_TH3_S4M3_M3SS4G3}