TUCTF 2022 Writeup

この大会は2022/12/3 2:00(JST)~2022/12/5 2:00(JST)に開催されました。
今回もチームで参戦。結果は4915点で475チーム中24位でした。
自分で解けた問題をWriteupとして書いておきます。

Join Discord! (Informational)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

TUCTF{7h4nk5_f0r_j01n1n6_u5}

Leisurely Math (Programming)

$ nc chals.tuctf.com 30202
8903 + 1765 - 8007
Answer: 

繰り返し、四則演算の答えをevalで答えていけばよいが、途中でスクリプトを変更するコードが出てくるので、その場合はevalを使わないようにする。

#!/usr/bin/env python3
import socket
import string

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals.tuctf.com', 30202))

i = 1
while True:
    print('Round: %d' % i)
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    if data.startswith('exec'):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        ans = eval(data)
    elif data[0] in string.digits:
        ans = eval(data)
    else:
        break
    data = recvuntil(s, b': ')
    print(data + str(ans))
    s.sendall(str(ans).encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)

    i += 1

実行結果は以下の通り。

Round: 1
5996 - 2119 * 1307
Answer: -2763537
Correct!
Round: 2
8663 - 8754 - 9806 * 1984 + 382 * 7771 - 7217 + 4166
Answer: -16489724
Correct!
Round: 3
7875 - 4127 + 825 + 2391 * 5861 * 8656 - 9523 * 4459 * 3567 * 2936
Answer: -444582064432955
Correct!
        :
        :
Round: 217
384 * 2989 + 3824 + 6135 - 1115 - 6785 - 8299 - 95 + 4441 + 414
Answer: 1146296
Correct!
Round: 218
exec('\nimport os\nscript_path = os.path.realpath( __file__ )\nnew_program = ""\nwith open( script_path, "r" ) as f:\n    lines = f.readlines()\n    for line in lines:\n        for char in line:\n            if char.isalpha():\n                new_program += chr( ord( char ) + 1 )\n            else:\n                new_program += char\nwith open( script_path, "w" ) as f:\n    f.write( new_program )\nos.system( "cls" )\nos.system( "clear" )\n')
5722 - 2913 - 2852 + 4059 + 1274 - 2961 + 979 + 1529 - 9484 + 4831 - 330
Answer: -146
Correct!
Round: 219
3886 * 6860 * 7266 - 4660 * 2981 + 6424 * 7058 - 7752 - 7666
Answer: 193728171074
Correct!
        :
        :
Round: 227
7644 * 165 - 591 - 3277 - 5040 - 2399 + 6162 + 6874
Answer: 1262989
Correct!
Round: 228
3299 + 2869 - 698 - 1912 - 1759 * 7580 * 4449
Answer: -59319492222
Correct!
Round: 229
Here is your flag: TUCTF{7h4nk5_f0r_74k1n6_7h1n65_4_l177l3_5l0w_4268285}
TUCTF{7h4nk5_f0r_74k1n6_7h1n65_4_l177l3_5l0w_4268285}

That One RSA Challenge (Cryptography)

$ nc chals.tuctf.com 30003
That one RSA Challenge:

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:10683056179532275497494322908531698584344422075416105799583765788674095581794757581679062276473318546247611230358286388481761328254612195488217194458976062128428044702531786323379051979941573794294687702619017493990782102020021363739169973928110789262754216832205575535500949117118001027399864796224562441446926260573132225906490350769309555711559208398934760123161

e:5

c:8176566829021528930311087337684299656651175996237050248928778822664811561182426484128445467162985030874776007692440517236408539231151511454489256999578335906317713460573759316655060037794428043630027064839498917698728550477723280424358111591441943069024931967203574624902159252837306386334274180905622013082718527756027784085834099753091678523356301420646238112875

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:6887879171986104574373642945440619457908229375975304899880685868832943411735297206066352105744348146004806196756354114218116486611361043423428861682525854438882874870097416661654165321891064197049309913602081537960658790294231234627260856114161173287601067783735612151828577847781069158844998327582452213264058182688584580147443133837362042883406337342404940544361

e:5

c:4148676611576085237922195161446112435729578569070212737206322240063752390283190299422079072117944195256177533534862402415121898193828489180436429880219270889850632294941938463149292958581407387522369990049543234115761666709491486093520537162964731927621789946026980192125724639638737591303289779711057319610427896767911106583023234089933701878246800161241364129816

Options:
1) Generate RSA Message
2) Exit

eが5固定のようなので、n, cのペアを5つ集め、Hastad's Broadcast Attackで復号する。

#!/usr/bin/env python3
import socket
from Crypto.Util.number import *
from sympy.ntheory.modular import crt
from gmpy2 import iroot

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals.tuctf.com', 30003))

ns = []
cs = []
for _ in range(5):
    data = recvuntil(s, b'Exit\n').rstrip()
    print(data)
    print('1')
    s.sendall(b'1\n')

    for i in range(9):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        if i == 3:
            n = int(data.split(':')[1])
            ns.append(n)
        elif i == 7:
            c = int(data.split(':')[1])
            cs.append(c)

e = 5
me, _ = crt(ns, cs)
m, success = iroot(me, e)
assert success
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

That one RSA Challenge:

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:7889013948113429798278972302671539175301293488242917671552573119651777676116328810587304690958329450540775764834953963992797755174487687308119082196203088232372806787156332980818261513321073406761936392321253331199806081477118647282987638479107559162387137969310056422048619776021999170787496257959124072694480190136804267975007469172872410603181761147868732789957

e:5

c:5953486994469679770285994503441003952127560441626983862332695554856261594053423814133078554689506112962517950876539765501175542911909148343795214564714450128641479662515240905344017332659420683722785908359509378975375439950023220409444882621736638309408922366910705785098535747643112190526349426892500281359982963293081336555101529088051713582355893908594036408832

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:9781918828032202227301020862283271351666744219317849324603218722306039646724834308470648898200070746966872879113627123749676229395958888244888615900825777825042111873088592488800534542293607979803443131376966471789783530459733678199040741773600277818533651486052333491184170574402584256105767797180796009373317056502606910428112243052274480898092946461179057423431

e:5

c:8170113435944326464524075068289575750545358845793484288314334989819473883035099498726712329886195759849345290631801824127952665088605205896233921421311476593732620276426091140506800339155164805502668141006548253302511728247173162090233490098087729307680305536262452712013765780411609184894552826841667345058878520300233381003870355220059980005522578719569450573198

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:5769662059493037326838681424376503062239328448812114038636566871193778089935047505302884031942579397301957967677706758993329924467218343606856460050058650455316604846034176713296394496664662112653080400130696448468600679383659096811757444833061954008631101371962483132539367134706598643240571487668085947066434513349610075539154795764258384681522588385706325517619

e:5

c:3629592579037272026198717660412535408515221248858233088365765439386846963506043926673998695613192810556232586776023771897215004262441678528361237841398460322211633765670094194192859707971613533189413078948800691532583096972856427504235972329249902748645859657904398970632271043856293772688493268839788433269868362990626308290043238343210552466272424707569581194799

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:12672626983347050769700482064853150433107232512886489783526954121293704531127239705724436679877991425030448723475239879264611762869474150595506312823796777459367225473044132028457313494861340105891319860223889804606254706484908231895459388131011195397315857928409212368475959655401650638538494730190683372092522700229306705885837860397101516387633166140379688965427

e:5

c:7055583151890955925810944571879137791643669877164314487042597917025698562615723363540387754263336918660996676704894308928828731364030459670994591146698604908799175872744040737989020760386902071371620252469362467186297027528215014806530069665443574691178609031670559029122986166342612056477057241036987428886031827132962830849264673260937763122026859588753663803894

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:6208342415876278607018244913736867659243485506847518281452593363925200041710188544123513552854427547365398266478461164952411281118259679890135871403169129139461884659610613791173072313015077905720089254421440256014907704206795531671204245879459492289676230342806734749392407834860522922765749970031180284138983779570050788310689670216150054750074864358949046129999

e:5

c:1222563905891228016808738307424690658174508026564281099701998913991287576407461876171772734361400755177373008474472491999997009555847720114492657732087810911681880774108712182858615389688812655640172979228877810604313802042778266303319003129620789641852808910781536731922579153047856223486067540989712829237856211153768250987156120927668931277322063251279404888769

TUCTF{0bl1g4t0ry_RSA_chall_l0l}
TUCTF{0bl1g4t0ry_RSA_chall_l0l}

A Sheep Jumps Over Fence (Cryptography)

まずhexデコードする。すると、base64文字列になるので、デコードする。

          111111111122222222223333333333444444444455555555556666
0123456789012345678901234567890123456789012345678901234567890123
TFiacmaraeinyghaU{swoegotctdprysCtiemsemauyctacsThslesfdsraropl}

転置暗号になっていて、16バイトごとに文字を結合していけば、フラグになる。

#!/usr/bin/env python3
from base64 import *

with open('sheep.enc', 'r') as f:
    enc = eval(f.read())

length = (enc.bit_length() + 7) // 8
b64 = enc.to_bytes(length, 'big')
print('[+] Decode 1:', b64.decode())

dec = b64decode(b64)
print('[+] Decode 2:', dec.decode())

flag = ''
for i in range(16):
    for j in range(len(dec) // 16):
        flag += chr(dec[i + j * 16])
print('[*] flag:', flag)

実行結果は以下の通り。

[+] Decode 1: VEZpYWNtYXJhZWlueWdoYVV7c3dvZWdvdGN0ZHByeXNDdGllbXNlbWF1eWN0YWNzVGhzbGVzZmRzcmFyb3BsfQ==
[+] Decode 2: TFiacmaraeinyghaU{swoegotctdprysCtiemsemauyctacsThslesfdsraropl}
[*] flag: TUCTF{thisisawelcomemessagefromdatasecurityandcryptographyclass}
TUCTF{thisisawelcomemessagefromdatasecurityandcryptographyclass}

More Effort (Cryptography)

暗号化処理の概要は以下の通り。

・rsa = RSA()
 ・rsa.p: 512ビット素数
 ・rsa.s = 0
 ・i: 1以上18000000未満に対して、以下を実行
  ・rsa.s += pow(i, rsa.p-2, rsa.p)
 ・rsa.s = rsa.s % rsa.p
 ・rsa.q: rsa.sの次の素数
 ・rsa.n = rsa.p * rsa.q
 ・rsa.phi = (rsa.p - 1) * (rsa.q - 1)
 ・rsa.e = 65537
 ・rsa.d = pow(rsa.e, -1, rsa.phi)
・rsa.p、rsa.eを出力
・c = pow(<フラグの数値>, rsa.e, rsa.n)
・cを出力

以下の式が成り立ち、右辺の方の計算の方が早いので、置き換えて、sを割り出し、qを算出し復号する。

pow(i, rsa.p-2, rsa.p) = pow(i, -1, rsa.p)
#!/usr/bin/env python3
from Crypto.Util.number import *
import gmpy2

p = 11545307730112922786664290405312669819594345207377186481347514368962838475959085036399074594822885814719354871659183685801279739518405830244888530641898849
e = 65537
c =  114894293598203268417380013863687165686775727976061560608696207173455730179934925684529986102237419507146768083815607566149240438056135058988227916482404733131796310418493418060300571541865427288945087911872630289527954636816219365941817260989104786329938318143577075200571833575709614521758701838099810751

s = 0
for i in range(1, 18000000):
    s += pow(i, -1, p)
s = s % p
q = gmpy2.next_prime(s)
n = p * q
phi = (p - 1) * (q - 1)
e = 65537
d = pow(e, -1, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
TUCTF{syqow82pam_%shsjQF; ^7dagsWCpsp_#aes}

Two Part Hashing (Cryptography)

XORと推測し、鍵を求め、PDFを復号する。

#!/usr/bin/env python3
with open('crypto.enc.pdf', 'rb') as f:
    enc = f.read()

PDF_HEAD = b'%PDF-1.'

key = []
for i in range(len(PDF_HEAD)):
    key.append(enc[i] ^ PDF_HEAD[i])

assert key[:3] == key[4:7]
key = key[:4]

dec = b''
for i in range(len(enc)):
    dec += bytes([enc[i] ^ key[i % len(key)]])

with open('flag.pdf', 'wb') as f:
    f.write(dec)

復号したPDFには、ハッシュに関する内容が書かれ、どこにもフラグは見当たらない。PDFのプロパティを見ると、作成者に以下が設定されている。

nc chal.tuctf.com 30000
$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 1
[ + ] Hashing...
[ + ] Hashed data: ba89

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 2
[ + ] Hidden flag: fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5

このハッシュアルゴリズムからフラグを求める必要があるようだ。

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 11
[ + ] Hashing...
[ + ] Hashed data: 9362

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 12
[ + ] Hashing...
[ + ] Hashed data: 9463

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 13
[ + ] Hashing...
[ + ] Hashed data: 9564

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 111
[ + ] Hashing...
[ + ] Hashed data: 9362ba89

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1   
[ + ] Enter data to hash: 1211
[ + ] Hashing...
[ + ] Hashed data: 94639362

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 21
[ + ] Hashing...
[ + ] Hashed data: 9563

2バイトごとに対応するhashが決まるようだ。もう少し規則を見てみる。

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: !!
[ + ] Hashing...
[ + ] Hashed data: 6342

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: "!
[ + ] Hashing...
[ + ] Hashed data: 6543

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: #!
[ + ] Hashing...
[ + ] Hashed data: 6744

1バイト目のASCIIコードを1増やすと、hashの1バイト目は2増え、2バイト目は1増える。

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: !"
[ + ] Hashing...
[ + ] Hashed data: 6443

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: !#
[ + ] Hashing...
[ + ] Hashed data: 6544

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: ab
[ + ] Hashing...
[ + ] Hashed data: 25c3

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: zz
[ + ] Hashing...
[ + ] Hashed data: 6ff4

2バイト目のASCIIコードを1増やすと、hashの1バイト目は1増え、2バイト目は1増える。
以上から以下のことが言える。

・hashの1バイト目 = 1バイト目のASCIIコード * 2 + 2バイト目のASCIIコード
・hashの2バイト目 = 1バイト目のASCIIコード + 2バイト目のASCIIコード

255を超えた場合は255で割った余りになる。
このことから逆算し、フラグのハッシュからフラグを復号する。

#!/usr/bin/env python3

def rev_hash(s):
    code1 = int(s[:2], 16)
    code2 = int(s[2:], 16)
    char1 = code1 - code2
    if char1 < 0:
        char1 += 255
    char2 = code2 - char1
    return chr(char1) + chr(char2)

flag_hash = 'fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5'

flag = ''
for i in range(0, len(flag_hash), 4):
    flag += rev_hash(flag_hash[i:i+4])
print(flag)

復号結果は以下の通り。

TUCTF{5m4ll_5um_h45h1n6_f7w_475928}X

最後についている"X"を削除すれば、フラグになる。

TUCTF{5m4ll_5um_h45h1n6_f7w_475928}

Unmix The Flag (Cryptography)

問題文に以下の点字が書かれている。

⠱⠁ ⠹⠣ ⠱⠹ ⠱⠁ ⠹⠣ ⠹⠳ ⠱⠁ ⠹⠣ ⠹⠪ ⠱⠁ ⠹⠣ ⠱⠩ ⠹⠣ ⠱⠩ ⠹⠱ ⠻⠁ ⠫⠩ ⠫⠡ ⠻⠻ ⠹⠣ ⠱⠩ ⠹⠱ ⠻⠁ ⠻⠪ ⠻⠁ ⠻⠱ ⠫⠡ ⠻⠻ ⠫⠡ ⠫⠩ 

GS-8 Brailleとしてhttps://www.dcode.fr/gs8-braille-codeでデコードする。

5a42545a42485a42495a42534253457a6361774253457a797a7561776163

hexデコードする。

$ echo 5a42545a42485a42495a42534253457a6361774253457a797a7561776163 | xxd -r -p
ZBTZBHZBIZBSBSEzcawBSEzyzuawac

フラグにはおそらく"_"が含まれているので、numShiftをブルートフォースし、"CTF"が含まれているものを探す。あとは逆順で復号していけば、フラグがわかる。

#!/usr/bin/env python3
import string

upperFlag = string.ascii_uppercase[:26]
lowerFlag = string.ascii_lowercase[:26]
MIN_LETTER = ord("a")
MIN_CAPLETTER = ord("A")

def unmix(oneLetter, num):
    if(oneLetter.isupper()):
        word = ord(oneLetter) - MIN_CAPLETTER
        shift = ord(num) - MIN_CAPLETTER
        return upperFlag[(word - shift) % len(upperFlag)]
    if(oneLetter.islower()):
        word = ord(oneLetter) - MIN_LETTER
        shift = ord(num) - MIN_LETTER
        return lowerFlag[(word - shift) % len(lowerFlag)]

def unpuzzled(puzzle):
    data = ''
    i = 0
    while i < len(puzzle):
        if puzzle[i].isupper():
            if puzzle[i:i+3] == 'CTF':
                data += '_'
            else:
                b1 = '{0:05b}'.format(upperFlag.index(puzzle[i]))
                b2 = '{0:05b}'.format(upperFlag.index(puzzle[i+1]))
                b3 = '{0:05b}'.format(upperFlag.index(puzzle[i+2]))
                data += chr(int(b1 + b2 + b3, 2))
            i += 3
        if puzzle[i].islower():
            b1 = '{0:01x}'.format(lowerFlag.index(puzzle[i]))
            b2 = '{0:01x}'.format(lowerFlag.index(puzzle[i+1]))
            data += chr(int(b1 + b2, 16))
            i += 2
    return data

enc = 'ZBTZBHZBIZBSBSEzcawBSEzyzuawac'

for numShift in lowerFlag:
    unmixed = ''
    for count, alpha in enumerate(enc):
        unmixed += unmix(alpha, numShift)
    if 'CTF' in unmixed:
        break

flag = unpuzzled(unmixed)
print(flag)

復号結果は以下の通り。

THIS_is_easy
TUCTF{THIS_is_easy}