この大会は2019/11/30 8:00(JST)~2019/12/2 8:00(JST)に開催されました。
今回もチームで参戦。結果は13518点で1005チーム中3位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome (Welcome)
Discordに入っても、フラグが見つからない。TUCTF Botがあったので、$flagとか入力してみる。入力したことと関係ないと思うが、ダイレクトメッセージにフラグが書かれていた。
TUCTF{W3lc0m3_70_TUCTF._H4v3_fun}
Open Door (Web)
HTMLソースのコメントにこう書いてある。
<!--Laziness leads me to leave this here: TUCTF{f1r5t_fl46_345135t_fl46}-->
TUCTF{f1r5t_fl46_345135t_fl46}
faker (Reversing)
$ ./faker Menu: 1. Flag A 2. Flag B 3. Flag C 4. Exit > 1 TUCTF{n0p3_7h15_15_4_f4k3_fl46} Menu: 1. Flag A 2. Flag B 3. Flag C 4. Exit > 2 TUCTF{50rry_n07_h3r3_317h3r} Menu: 1. Flag A 2. Flag B 3. Flag C 4. Exit > 3 TUCTF{600d_7ry_bu7_k33p_l00k1n6} Menu: 1. Flag A 2. Flag B 3. Flag C 4. Exit > 4
ダミーフラグが表示される。Ghidraでデコンパイルしてみる。関係するコードは以下のようになっている。
void printFlag(char *pcParm1) { char *__dest; size_t sVar1; int local_30; __dest = (char *)malloc(0x40); memset(__dest,0,0x40); strcpy(__dest,pcParm1); sVar1 = strlen(__dest); local_30 = 0; while (local_30 < (int)sVar1) { __dest[(long)local_30] = (char)((int)((((int)__dest[(long)local_30] ^ 0xfU) - 0x1d) * 8) % 0x5f) + ' '; local_30 = local_30 + 1; } puts(__dest); return; } void A(void) { printFlag("\\PJ\\fCaq(Lw|)$Tw$Tw@wb@ELwbY@hk"); return; } void B(void) { printFlag("\\PJ\\fCTq00;waq|w)L0LwL$|)L0k"); return; } void C(void) { printFlag("\\PJ\\fChqqZw|0;w2l|wELL(wYqqE$ahk"); return; } void thisone(void) { printFlag("\\PJ\\fC|)L0LTw@Yt@;Twmq0Lw|qw@w2$a@0;w|)@awmLL|Tw|)LwZL2lhhL0k"); return; }
A, B, Cがダミーフラグになっている。thisoneが本物のフラグになると考えられる。printFlagの処理をPythonコードにして実行してみる。
enc = '\\PJ\\fC|)L0LTw@Yt@;Twmq0Lw|qw@w2$a@0;w|)@awmLL|Tw|)LwZL2lhhL0k' dest = list(enc) for i in range(len(dest)): dest[i] = chr((((ord(dest[i]) ^ 0xf) - 0x1d) * 8) % 0x5f + ord(' ')) print ''.join(dest)
TUCTF{7h3r35_4lw4y5_m0r3_70_4_b1n4ry_7h4n_m3375_7h3_d3bu663r}
Red Yarn (Misc)
$ strings DEBUG.COM | grep TUCTF TUCTF{D0NT_F0RG3T_TH3_B4S1CS!}
TUCTF{D0NT_F0RG3T_TH3_B4S1CS!}
Onions (Misc)
$ binwalk shrek.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 275566 0x4346E 7-zip archive data, version 0.4
jpgの後ろに7-zipがあるので、7-zip部分を切り出し、flag.7zとして保存する。問題タイトルから何重にも圧縮されていることが推測できる。とりあえず展開していく。
$ 7z x flag.7z 7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18 p7zip Version 9.20 (locale=ja_JP.UTF-8,Utf16=on,HugeFiles=on,1 CPU) Processing archive: flag.7z Extracting flag.tar.gz Everything is Ok Size: 428 Compressed: 538 $ ls flag.7z flag.tar.gz $ gzip -dc flag.tar.gz | tar xvf - flag.cpio $ cpio -idv < flag.cpio flag.lzma 1 ブロック $ unlzma flag.lzma unlzma: flag: ファイルのパーミッションを設定できません: 定義されたデータ型に対して値が大きすぎます $ ls flag flag.7z flag.cpio flag.tar.gz $ file flag flag: current ar archive $ ar x flag $ ls flag flag.7z flag.cpio flag.tar.gz flag1.txt $ file flag1.txt flag1.txt: bzip2 compressed data, block size = 900k $ bzip2 -d flag1.txt bzip2: Can't guess original name for flag1.txt -- using flag1.txt.out $ file flag1.txt.out flag1.txt.out: XZ compressed data $ mv flag1.txt.out flag1.xz $ xz -d flag1.xz xz: flag1: ファイルのパーミッションを設定できません: 定義されたデータ型に対して値が大きすぎます $ ls flag flag.7z flag.cpio flag.tar.gz flag1 $ cat flag1 TUCTF{F1L3S4R3L1K30N10NSTH3YH4V3L4Y3RS}
TUCTF{F1L3S4R3L1K30N10NSTH3YH4V3L4Y3RS}
Super Secret (Misc)
odtファイルを解凍する。document\Basic\Standard\flag.xmlにフラグが書いてある。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd"> <script:module xmlns:script="http://openoffice.org/2000/script" script:name="flag" script:language="StarBasic" script:moduleType="normal">REM ***** BASIC ***** Sub Main TUCTF{ST0P_TRUST1NG_M4CR0S_FR0M_4N_UNKN0WN_S0URC3} End Sub </script:module>
TUCTF{ST0P_TRUST1NG_M4CR0S_FR0M_4N_UNKN0WN_S0URC3}
Crypto Infinite (Crypto)
指定した文字列を暗号化して返してくれるので、それを参考に50問の暗号を復号して答えていく。Level 0から始まるが、Level 4までは換次式暗号とわかるので、変換テーブルを作れれば、簡単に復号できる。Level 5以降は試しながら、暗号の特性を考える。
Level 5: AAAAAAAAAA |- [.] [ |- [.] |- > [] |- [.] ABCDEFGHIJKLMN |- [. |-| |._ |.- [.] .^ |._ |.-| .v .-| > .^ ^ |- [. |-| |._ |.- [.] .^ |._ |.-| .v .-| > .^ ^ AAAAAABCAA |- [.] [ |- [.] |- < -| |- [.] BBBBBBBBBB ._| [. -| ._| [. ._| < [ ._| [. 'A' * 26 |- [.] [ |- [.] |- > [] |- [.] [ |- [.] |- > [] |- [.] [ |- [.] |- > [] |- [.] 'A' * 48 |- [.] [ |- [.] |- > [] |- [.] [ |- [.] |- > [] |- [.] [ |- [.] |- > [] |- [.] [ |- [.] |- > [] |- [.] [ |- [.] |- > [] |- [.] [ |- [.] |- > [] 以下の繰り返し |- [.] [ |- [.] |- > [] 鍵の長さは8。 26*8パターンの暗号を取得して、それを元に復号する。 Level 6: 'A' * 8 + ... + 'Z' * 8 _| _| |_| |_| |_ |_ ] ] [] [] [ [ -| -| |-| |-| |- |- ._| ._| |._| |._| |._ |._ .] .] [.] [.] [. [. .-| .-| |.-| |.-| |.- |.- v v > > < < ^ ^ .v .v .> .> .< .< .^ .^ _| _| |_| |_| |_ |_ ] ] [] [] [ [ -| -| |-| |-| |- |- ._| ._| |._| |._| |._ |._ .] .] [.] [.] [. [. .-| .-| |.-| |.-| |.- |.- v v > > < < ^ ^ .v .v .> .> .< .< .^ .^ _| _| |_| |_| |_ |_ ] ] [] [] [ [ -| -| |-| |-| |- |- ._| ._| |._| |._| |._ |._ .] .] [.] [.] [. [. .-| .-| |.-| |.-| |.- |.- v v > > < < ^ ^ .v .v .> .> .< .< .^ .^ _| _| |_| |_| |_ |_ ] ] [] [] [ [ -| -| |-| |-| |- |- ._| ._| |._| |._| |._ |._ .] .] [.] [.] [. [. .-| .-| |.-| |.-| |.- |.- v v > > < < ^ ^ .v .v .> .> .< .< .^ .^ 'A' * 24 _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| 'B' * 64 |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| |_| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| AAAAAAAABBBBBBBB _| _| |_| |_| _| _| |_| |_| _| _| |_| |_| _| _| |_| |_| AAAAAAAAAAAABBBBBBBB _| _| _| |_| |_| _| _| _| |_| |_| _| _| _| |_| |_| _| _| _| |_| |_| A~Z、それぞれ数を変えて試す。 ABBCCCDDDDEEEEEFFFFFFGGGGGGGHHHHHHHHIIIIIIIIIJJJJJJJJJJKKKKKKKKKKKLLLLLLLLLLLLMMMMMMMMMMMMMNNNNNNNNNNNNNNOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZ _| |_ ] [] [ [ -| |-| |-| |- |- |- ._| ._| |._| |._| |._| |._ |._ |._ .] .] .] [.] [.] [.] [.] [. [. [. .-| .-| .-| .-| |.-| |.-| |.-| |.-| |.-| |.- |.- |.- |.- v v v v v > > > > > < < < < < ^ ^ ^ ^ ^ ^ .v .v .v .v .v .> .> .> .> .> .> .< .< .< .< .< .< .< .^ .^ .^ .^ .^ .^ |_| |_ ] [] [ -| -| |-| |-| |- |- ._| ._| ._| |._| |._| |._| |._ |._ |._ .] .] .] [.] [.] [.] [. [. [. [. .-| .-| .-| .-| |.-| |.-| |.-| |.-| |.- |.- |.- |.- |.- v v v v v > > > > > < < < < < ^ ^ ^ ^ ^ .v .v .v .v .v .v .> .> .> .> .> .> .< .< .< .< .< .< .^ .^ .^ .^ .^ .^ .^ |_| ] [] [] [ -| -| |-| |-| |- |- ._| ._| ._| |._| |._| |._ |._ |._ .] .] .] .] [.] [.] [.] [. [. [. [. .-| .-| .-| .-| |.-| |.-| |.-| |.-| |.- |.- |.- |.- |.- v v v v > > > > > < < < < < < ^ ^ ^ ^ ^ .v .v .v .v .v .v .> .> .> .> .> .> .< .< .< .< .< .< .^ .^ .^ .^ .^ .^ .^ |_ ] [] [ [ -| -| |-| |-| |- |- ._| ._| |._| |._| |._| |._ |._ |._ .] .] .] [.] [.] [.] [.] [. [. [. [. .-| .-| .-| .-| |.-| |.-| |.-| |.-| |.- |.- |.- |.- v v v v v > > > > > < < < < < ^ ^ ^ ^ ^ ^ .v .v .v .v .v .v .> .> .> .> .> .> .< .< .< .< .< .< .^ .^ .^ .^ .^ .^ 以下のような文字の対応となり、並べ替えられていることがわかる。 A: _| B: |_| C: |_ D: ] E: [] F: [ G: -| H: |-| I: |- J: ._| K: |._| L: |._ M: .] N: [.] O: [. P: .-| Q: |.-| R: |.- S: v T: > U: < V: ^ W: .v X: .> Y: .< Z: .^ ABCDEFGHIJKLMNOPQRSTUVWXYZ _| [] |- .] |.-| < .< |_| [ ._| [.] |.- ^ .^ |_ -| |._| [. v .v ] |-| |._ .-| > .> A E I M Q U Y B F J N R V Z C G K O S W D H L P T X スキュタレー暗号になっている。 Level 7: ABCDEFGHIJKLMNOPQRSTUVWXYZ [] |- ] -| |-| |._| |._ [. ._| [.] |.- .] .-| |.-| < v ^ > .< .v .^ .> |_| |_ [ _| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] ZYXWVUTSRQPONMLKJIHGFEDCBA _| [ |_ |_| .> .^ .v .< > ^ v < |.-| .-| .] |.- [.] ._| [. |._ |._| |-| -| ] |- [] また換次式暗号に戻ったみたい。 Level 8: ABCDEFGHIJKLMNOPQRSTUVWXYZ -[ ^ .] ^ ]v. - ]v. ]v| ]|] -v[ ]|] v^ v[ v^ > ]. ^ ]. - < - v_ v.] ]|] v.] v[ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ < -[ ]v. -v[ |] ]v| -[ -[ < -[ ]v. -v[ |] ]v|の繰り返し Level 5と同様で鍵の長さは7。 26*7パターンの暗号を取得して、それを元に復号する。 Level 9: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] _ _ _ _ _ _ _ _ ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] _ _ _ _ _ _ _ _ ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] _ _ _ _ _ _ _ _ ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] ]v.] _ _ _ _ _ _ _ _ ABBCCCDDDDEEEEEFFFFFFGGGGGGGHHHHHHHHIIIIIIIIIJJJJJJJJJJKKKKKKKKKKKLLLLLLLLLLLLMMMMMMMMMMMMMNNNNNNNNNNNNNNOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZ > _ -[ - v_ v_ v[ ]v. ]v. ]|] ]|] ]|] ]| ]| |] |] |] ]. ]. ]. [ [ [ .] .] .] .] ].] ].] ].] ]v|] ]v|] ]v|] ]v|] ]v| ]v| ]v| ]v| ]v| -v -v -v -v v|] v|] v|] v|] v|] -v[ -v[ -v[ -v[ -v[ v.] v.] v.] v.] v.] ^ ^ ^ ^ ^ ^ < < < < < v^ v^ v^ v^ v^ v^ v> v> v> v> v> v> v> v< v< v< v< v< v< ]v.] _ -[ - v_ v[ v[ ]v. ]v. ]|] ]|] ]| ]| ]| |] |] |] ]. ]. ]. [ [ [ .] .] .] ].] ].] ].] ].] ]v|] ]v|] ]v|] ]v|] ]v| ]v| ]v| ]v| -v -v -v -v -v v|] v|] v|] v|] v|] -v[ -v[ -v[ -v[ -v[ v.] v.] v.] v.] v.] ^ ^ ^ ^ ^ < < < < < < v^ v^ v^ v^ v^ v^ v> v> v> v> v> v> v< v< v< v< v< v< v< ]v.] -[ - - v_ v[ v[ ]v. ]v. ]|] ]|] ]| ]| ]| |] |] ]. ]. ]. [ [ [ [ .] .] .] ].] ].] ].] ].] ]v|] ]v|] ]v|] ]v|] ]v| ]v| ]v| ]v| -v -v -v -v -v v|] v|] v|] v|] -v[ -v[ -v[ -v[ -v[ v.] v.] v.] v.] v.] v.] ^ ^ ^ ^ ^ < < < < < < v^ v^ v^ v^ v^ v^ v> v> v> v> v> v> v< v< v< v< v< v< v< _ -[ - v_ v_ v[ v[ ]v. ]v. ]|] ]|] ]| ]| |] |] |] ]. ]. ]. [ [ [ .] .] .] .] ].] ].] ].] ].] ]v|] ]v|] ]v|] ]v|] ]v| ]v| ]v| ]v| -v -v -v -v v|] v|] v|] v|] v|] -v[ -v[ -v[ -v[ -v[ v.] v.] v.] v.] v.] ^ ^ ^ ^ ^ ^ < < < < < < v^ v^ v^ v^ v^ v^ v> v> v> v> v> v> v< v< v< v< v< v< A: > B: ]v.] C: _ D: -[ E: - F: v_ G: v[ H: ]v. I: ]|] J: ]| K: |] L: ]. M: [ N: .] O: ].] P: ]v|] Q: ]v| R: -v S: v|] T: -v[ U: v.] V: ^ W: < X: v^ Y: v> Z: v< ABCDEFGHIJKLMNOPQRSTUVWXYZ > - ]|] [ ]v| v.] v> ]v.] v_ ]| .] -v ^ v< _ v[ |] ].] v|] < -[ ]v. ]. ]v|] -v[ v^ A E I ... Level 6と同じ暗号。
以上から、最終的なコードは以下の通り。
import socket import string 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(('chal.tuctf.com', 30102)) def decrypt_scytale(s): divs = len(s) // 4 remains = len(s) % 4 blocks = [] index = 0 for i in range(4): if remains != 4 and i < remains: length = divs + 1 else: length = divs blocks.append(s[index:index+length]) index += length dec = '' for i in range(divs + 1): for j in range(4): if i != divs: dec += blocks[j][i] elif j < remains: dec += blocks[j][i] return dec #### Level 0-4 #### for level in range(5): data = recvuntil(s, ':\n').rstrip() print data send_data = string.uppercase print send_data s.sendall(send_data + '\n') data = recvuntil(s, '\n').rstrip() print data enc_tbl = data.split(' ')[3:] for _ in range(2): data = recvuntil(s, '\n').rstrip() print data for i in range(50): print 'Level %d - Round %d' % (level, i + 1) enc = data.split(' ')[1:] dec = '' for e in enc: idx = enc_tbl.index(e) dec += string.uppercase[idx] print dec s.sendall(dec + '\n') for _ in range(4): data = recvuntil(s, '\n').rstrip() print data #### Level 5 #### for level in range(5, 6): data = recvuntil(s, ':\n').rstrip() print data send_data = '' for c in string.uppercase: send_data += c * 8 print send_data s.sendall(send_data + '\n') data = recvuntil(s, '\n').rstrip() print data enc_tbl = data.split(' ')[3:] enc_tbl2 = [] for i in range(8): tmp_enc_tbl = [] for j in range(26): tmp_enc_tbl.append(enc_tbl[i + j * 8]) enc_tbl2.append(tmp_enc_tbl) for _ in range(2): data = recvuntil(s, '\n').rstrip() print data for i in range(50): print 'Level %d - Round %d' % (level, i + 1) enc = data.split(' ')[1:] dec = '' for j in range(len(enc)): idx = enc_tbl2[j % 8].index(enc[j]) dec += string.uppercase[idx] print dec s.sendall(dec + '\n') for _ in range(4): data = recvuntil(s, '\n').rstrip() print data #### Level 6 #### for level in range(6, 7): data = recvuntil(s, ':\n').rstrip() print data send_data = string.uppercase s.sendall(send_data + '\n') data = recvuntil(s, '\n').rstrip() print data enc_tbl = data.split(' ')[3:] enc_tbl2 = [enc_tbl[:7], enc_tbl[7:14], enc_tbl[14:20], enc_tbl[20:]] enc_tbl3 = [] for i in range(6): for j in range(4): enc_tbl3.append(enc_tbl2[j][i]) enc_tbl3.append(enc_tbl2[0][6]) enc_tbl3.append(enc_tbl2[1][6]) for _ in range(2): data = recvuntil(s, '\n').rstrip() print data for i in range(50): print 'Level %d - Round %d' % (level, i + 1) enc = data.split(' ')[1:] dec = '' for e in enc: idx = enc_tbl3.index(e) dec += string.uppercase[idx] dec = decrypt_scytale(dec) print dec s.sendall(dec + '\n') for _ in range(4): data = recvuntil(s, '\n').rstrip() print data #### Level 7 #### for level in range(7, 8): data = recvuntil(s, ':\n').rstrip() print data send_data = string.uppercase print send_data s.sendall(send_data + '\n') data = recvuntil(s, '\n').rstrip() print data enc_tbl = data.split(' ')[3:] for _ in range(2): data = recvuntil(s, '\n').rstrip() print data for i in range(50): print 'Level %d - Round %d' % (level, i + 1) enc = data.split(' ')[1:] dec = '' for e in enc: idx = enc_tbl.index(e) dec += string.uppercase[idx] print dec s.sendall(dec + '\n') for _ in range(4): data = recvuntil(s, '\n').rstrip() print data #### Level 8 #### for level in range(8, 9): data = recvuntil(s, ':\n').rstrip() print data send_data = '' for c in string.uppercase: send_data += c * 7 print send_data s.sendall(send_data + '\n') data = recvuntil(s, '\n').rstrip() print data enc_tbl = data.split(' ')[3:] enc_tbl2 = [] for i in range(7): tmp_enc_tbl = [] for j in range(26): tmp_enc_tbl.append(enc_tbl[i + j * 7]) enc_tbl2.append(tmp_enc_tbl) for _ in range(2): data = recvuntil(s, '\n').rstrip() print data for i in range(50): print 'Level %d - Round %d' % (level, i + 1) enc = data.split(' ')[1:] dec = '' for j in range(len(enc)): idx = enc_tbl2[j % 7].index(enc[j]) dec += string.uppercase[idx] print dec s.sendall(dec + '\n') for _ in range(4): data = recvuntil(s, '\n').rstrip() print data #### Level 9 #### for level in range(9, 10): data = recvuntil(s, ':\n').rstrip() print data send_data = string.uppercase s.sendall(send_data + '\n') data = recvuntil(s, '\n').rstrip() print data enc_tbl = data.split(' ')[3:] enc_tbl2 = [enc_tbl[:7], enc_tbl[7:14], enc_tbl[14:20], enc_tbl[20:]] enc_tbl3 = [] for i in range(6): for j in range(4): enc_tbl3.append(enc_tbl2[j][i]) enc_tbl3.append(enc_tbl2[0][6]) enc_tbl3.append(enc_tbl2[1][6]) for _ in range(2): data = recvuntil(s, '\n').rstrip() print data for i in range(50): print 'Level %d - Round %d' % (level, i + 1) enc = data.split(' ')[1:] dec = '' for e in enc: idx = enc_tbl3.index(e) dec += string.uppercase[idx] dec = decrypt_scytale(dec) print dec s.sendall(dec + '\n') for _ in range(4): data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, ':\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
: Decrypt ]v|] ].] v.] v|] -v - ]v|] Level 9 - Round 49 PURPOSE Nice job! Decrypt -v[ - ]v. > ].] v.] -v Level 9 - Round 50 THOREAU Ayyyyy Congratulations! You beat Level 9! Congratulations on finding infinity! Here's your flag: TUCTF{1nf1n1t3_1s_n0t_4_g00d_n4m3}
TUCTF{1nf1n1t3_1s_n0t_4_g00d_n4m3}
Sonic (Crypto)
$ nc chal.tuctf.com 30100 ___------__ |\__-- /\ _- |/ __ - //\ / \ /__ | o| 0|__ --_ \____-- __ \ ___- (@@ __/ / /_ -_____--- --_ // \ \\ ___- //|\__/ \ \ \_-\_____/ \-\ // \\--\| ____// ||_ /_____\ /___\ Gotta go fast! Hey, decode this: RUWKRGRA a Hey, decode this: RUWKRGRA a Hey, decode this: RUWKRGRA aaa Hey, decode this: RUWKRGRA aa Hey, decode this: RUWKRGRA aaa Hey, decode this: RUWKRGRA aaa Hey, decode this: RUWKRGRA
短い暗号文なので、簡単な古典暗号と推測できる。基本的なシーザー暗号を試す。何回も試せるので、シフト数を変えて最大26回答えるスクリプトで実行する。
import socket import string def decode_ceaser(s, i): dec = '' for c in s: idx = string.uppercase.index(c) - i if idx < 0: idx += 26 dec += string.uppercase[idx] return dec 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(('chal.tuctf.com', 30100)) data = recvuntil(s, ':') data += recvuntil(s, '\n').rstrip() print data enc = data.split('\n')[-1].split(': ')[1] for i in range(26): dec = decode_ceaser(enc, i) print dec s.sendall(dec + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data if ':' not in data: break for i in range(3): data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
___------__ |\__-- /\ _- |/ __ - //\ / \ /__ | o| 0|__ --_ \____-- __ \ ___- (@@ __/ / /_ -_____--- --_ // \ \\ ___- //|\__/ \ \ \_-\_____/ \-\ // \\--\| ____// ||_ /_____\ /___\ Gotta go fast! Hey, decode this: ZVCROVG ZVCROVG Hey, decode this: ZVCROVG YUBQNUF Hey, decode this: ZVCROVG XTAPMTE Hey, decode this: ZVCROVG WSZOLSD Hey, decode this: ZVCROVG VRYNKRC Hey, decode this: ZVCROVG UQXMJQB Hey, decode this: ZVCROVG TPWLIPA Hey, decode this: ZVCROVG SOVKHOZ You got it! Here's your prize: TUCTF{W04H_DUD3_S0_F4ST_S0N1C_4PPR0V3S}
TUCTF{W04H_DUD3_S0_F4ST_S0N1C_4PPR0V3S}
Something in Common (Crypto)
RSA暗号。同じ平文に対して、nが1つ、eが2パターンで暗号化したデータ2つがわかっている。Common Modules Attackで復号する。
import gmpy from Crypto.Util.number import * def commom_modules_attack(c1, c2, e1, e2, n): gcd, s1, s2 = gmpy.gcdext(e1, e2) if s1 < 0: s1 = -s1 c1 = gmpy.invert(c1, n) elif s2 < 0: s2 = -s2 c2 = gmpy.invert(c2, n) v = pow(c1, s1, n) w = pow(c2, s2, n) x = (v*w) % n return x n = 5196832088920565976847626600109930685983685377698793940303688567224093844213838345196177721067370218315332090523532228920532139397652718602647376176214689 e1 = 15 e2 = 13 c1 = 2042084937526293083328581576825435106672034183860987592520636048680382212041801675344422421233222921527377650749831658168085014081281116990629250092000069 c2 = 199621218068987060560259773620211396108271911964032609729865342591708524675430090445150449567825472793342358513366241310112450278540477486174011171344408 m = commom_modules_attack(c1, c2, e1, e2, n) flag = long_to_bytes(m) print flag
TUCTF{Y0U_SH0ULDNT_R3US3_TH3_M0DULUS}
Warren (Crypto)
$ nc chal.tuctf.com 30101 Welcome to the Warren Buffet! You must solve all of the ciphers to receive the flag. I'd recommend filling your plate and solving all at once. (The timeout is 90 seconds) MENU: 1) Affine 2) Baconian 3) Caesar 4) Atbash 5) Vigenere 1 Have you heard of this one? Because I just read about it. Here's your cipher: UBBAHK AO U LUT CAPJKX
どうやら、各暗号の復号を全部解いたら、フラグが取れるらしい。
以下のオンラインツールで復号して答える。 - https://www.dcode.fr/affine-cipher - https://www.dcode.fr/bacon-cipher - https://www.dcode.fr/caesar-cipher - https://www.dcode.fr/atbash-mirror-cipher - https://www.dcode.fr/vigenere-cipher
$ nc chal.tuctf.com 30101 Welcome to the Warren Buffet! You must solve all of the ciphers to receive the flag. I'd recommend filling your plate and solving all at once. (The timeout is 90 seconds) MENU: 1) Affine 2) Baconian 3) Caesar 4) Atbash 5) Vigenere 1 Have you heard of this one? Because I just read about it. Here's your cipher: UBBAHK AO U LUT CAPJKX Give the plaintext: AFFINE IS A BAD CIPHER★ Correct! MENU: 1) Affine [solved] 2) Baconian 3) Caesar 4) Atbash 5) Vigenere 2 Have some bacon! Here's your cipher: aaaabaaaaaaaabaabbababbaaabaaaaaaaaabbaa abaaabaaab aabaaabbaaabaaaabbabbabbaaaaaaaaaabababaaabaa Give the plaintext: BACONIAN IS ENJOYABLE★ Correct! MENU: 1) Affine [solved] 2) Baconian [solved] 3) Caesar 4) Atbash 5) Vigenere 3 Ready for a classic? Here's your cipher: WUYMUL CM UH YUMS WCJBYL Give the plaintext: CAESAR IS AN EASY CIPHER★ Correct! MENU: 1) Affine [solved] 2) Baconian [solved] 3) Caesar [solved] 4) Atbash 5) Vigenere 4 This is a fun one that's less common! Here's your cipher: ZGYZHS RH Z UFM XRKSVI Give the plaintext: ATBASH IS A FUN CIPHER★ Correct! MENU: 1) Affine [solved] 2) Baconian [solved] 3) Caesar [solved] 4) Atbash [solved] 5) Vigenere 5 This is a tough one! Here's your cipher: OCIXSXLG BX T BCKI VCRAJK Give the plaintext: VIGENERE IS A HARD CIPHER★<- KEYはTUCTF Correct! Thanks for dining at the Warren buffet! Here's your flag: TUCTF{th4nks_f0r_d1n1ng_4641n_4t_th3_W4rr3n_buff3t}
TUCTF{th4nks_f0r_d1n1ng_4641n_4t_th3_W4rr3n_buff3t}
The Oracle (Crypto)
$ nc chal.tuctf.com 30103 Welcome! The Oracle will see you now! Your ciphertext is: Rq+qCucqmWTpwguvkEngCaz5K04v9qqBcUF/b3r3HDJDbR2GFt4NGn55Qlf2vc1h MENU: 1) Check padding 2) Enter password 1 Give me your input: Rq+qCucqmWTpwguvkEngCaz5K04v9qqBcUF/b3r3HDJDbR2GFt4NGn55Qlf2vc1h Padding Valid MENU: 1) Check padding 2) Enter password 1 Give me your input: CucqCucqmWTpwguvkEngCaz5K04v9qqBcUF/b3r3HDJDbR2GFt4NGn55Qlf2vc1h Padding Valid MENU: 1) Check padding 2) Enter password
おそらくCBC Padding Oracle Attackの問題。スクリプトに組み、実行する。サーバへの接続が途中で切断される。できるだけ不要な情報の表示もなくして実行しなおす。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def str_xor(s1, s2): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2)) def unpad(s): return s[:-ord(s[-1])] def is_valid(s, enc): send_data = enc.encode('base64') #print '1' s.sendall('1\n') data = recvuntil(s, ': ') #print data + send_data s.sendall(send_data + '\n') data = recvuntil(s, 'password\n').rstrip() #print data msg = data.split('\n')[1] data = recvuntil(s, '\n').rstrip() #print data if msg == 'Padding Valid': return True else: return False s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('chal.tuctf.com', 30103)) data = recvuntil(s, 'password\n').rstrip() print data enc = data.split('\n')[6].decode('base64') data = recvuntil(s, '\n').rstrip() print data enc_blocks = [] for i in range(0, len(enc), 16): enc_blocks.append(enc[i:i+16]) xor_blocks = [] for i in range(len(enc_blocks)-1, 0, -1): xor_block = '' for j in range(16): for code in range(256): print '%d - %d - %d: %s' % (i, j, code, xor_block.encode('hex')) print '****', str_xor(xor_block, enc_blocks[i-1][-j:]), '****' print '****', str_xor(enc_blocks[1], ''.join(xor_blocks)), '****' try_pre_block = '\x00' * (16 - j - 1) + chr(code) + str_xor(xor_block, chr(j+1)*j) try_cipher = try_pre_block + enc_blocks[i] if is_valid(s, try_cipher): xor_code = (j+1) ^ code xor_block = chr(xor_code) + xor_block break xor_blocks.append(xor_block) password = '' for i in range(len(xor_blocks)): password += str_xor(enc_blocks[i], xor_blocks[i]) password = unpad(password) print '2' s.sendall('2\n') data = recvuntil(s, '? ') print data + password s.sendall(password + '\n') data = recvuntil(s, ':') print data for i in range(4): data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
Welcome! The Oracle will see you now! Your ciphertext is: zMSCpDMiONJCFJz0ZFSWCvf+eibttk4/F1hCiFmHmNEVm3GBuRg2hX5MpFtiWVxH MENU: 1) Check padding 2) Enter password : : 1) Check padding 2) Enter password 2 What is the password? SUPERSECRETPASSWORDKEEPAWAY! That's it! Congratulations! Here's your flag: TUCTF{D0nt_l3t_y0ur_s3rv3r_g1v3_f33db4ck}
TUCTF{D0nt_l3t_y0ur_s3rv3r_g1v3_f33db4ck}