TUCTF 2019 Writeup

この大会は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}