UTCTF Writeup

この大会は2019/3/9 10:00(JST)~2019/3/11 10:00(JST)に開催されました。
今回もチームで参戦。結果は6350点で581チーム中25位でした。
自分で解けた問題をWriteupとして書いておきます。

[basics] re (Reverse Engineering 100)

$ strings calculator | grep utflag
utflag{str1ng5_15_4_h4ndy_t00l}
utflag{str1ng5_15_4_h4ndy_t00l}

[basics] forensics (Forensics 100)

$ cat secret.jpg 
utflag{d0nt_tru5t_f1l3_3xt3ns10n5}
utflag{d0nt_tru5t_f1l3_3xt3ns10n5}

Regular Zips (Forensics 600)

パスワード付きZIPとproblem.txt(2回目の解凍以降はhint.txt)がある。problem.txt(2回目の解凍以降はhint.txt)には正規表現が書いてあり、パスワードが満たす条件になっている。解凍すると、パスワード付きZIPとhint.txtが展開されるということを繰り返す。

import zipfile
import exrex
import os

DIR = './archives/'

def unzip_with_pwd(filename, path=DIR, pwd=''):
    with zipfile.ZipFile(filename, 'r') as zip_file:
        try:
            zip_file.extractall(path=path, pwd=pwd)
            return True
        except:
            return False

filename = 'RegularZips.zip'
exp_file = 'problem.txt'

i = 1
finish = False
while True:
    print 'Round %d' % i
    with open(exp_file, 'r') as f:
        exp = f.read()

    for pwd in list(exrex.generate(exp)):
        if unzip_with_pwd(filename, pwd=pwd):
            print 'Found!! password =', pwd
            filename = 'archive.zip'
            exp_file = 'hint.txt'
            if os.path.exists(filename):
                os.remove(filename)
            if os.path.exists(exp_file):
                os.remove(exp_file)
            if os.path.exists(DIR + filename):
                os.rename(DIR + filename, filename)
                os.rename(DIR + exp_file, exp_file)
            else:
                finish = True
            break
    i += 1

    if finish:
        break

1000回解凍すると、flag.txtが展開されて、フラグが書いてあった。

utflag{bean_pure_omission_production_rally}

[basics] crypto (Cryptography 200)

2進数を文字にすると、途中からBase64文字列になっている。Base64デコードすると、途中からシーザー暗号などになっているので、復号する。

import string

def caesar(s, key):
    d = ''
    for c in s:
        code = ord(c)
        if c in string.uppercase:
            code = code - key
            if code < ord('A'):
                code += 26
        elif c in string.lowercase:
            code = code - key
            if code < ord('a'):
                code += 26
        d += chr(code)
    return d

with open('binary.txt', 'r') as f:
    data = f.read().strip()

codes = data.split(' ')
msg = ''
for code in codes:
    msg += chr(int(code, 2))

msg1 = msg.split('\n')[0]
msg2 = msg.split('\n')[1].decode('base64')

msg21 = msg2.split('\n')[0]
msg22 = caesar(msg2.split('\n')[1], 10)
msg23 = msg2.split('\n')[2]

print msg1
print msg21
print msg22
print msg23

ここまでの復号結果は以下の通り。

Uh-oh, looks like we have another block of text, with some sort of special encoding. Can you figure out what this encoding is? (hint: if you look carefully, you'll notice that there only characters present are A-Z, a-z, 0-9, and sometimes / and +. See if you can find an encoding that looks like this one.)
New challenge! Can you figure out what's going on here? It looks like the letters are shifted by some constant. (hint: you might want to start looking up Roman people).
alright, you're almost there! Now for the final (and maybe the hardest...) part: a substitution cipher. In the following text, I've taken my message and replaced every alphabetic character with a correspondence to a different character - known as a substitution cipher. Can you find the final flag? hint: We know that the flag is going to be of the format utflag{...} - which means that if you see that pattern, you know what the correspondences for u, t, f, l a, and g are. You can probably work out the remaining characters by replacing them and inferring common words in the English language. Another great method is to use frequency analysis: we know that 'e' shows up most often in the alphabet, so that's probably the most common character in the text, followed by 't', and so on. Once you know a few characters, you can infer the rest of the words based on common words that show up in the English language.
rghnxsdfysdtghu! qgf isak cthtuike dik zknthhkx rxqldgnxsliq risyykhnk. ikxk tu s cysn cgx syy qgfx isxe kccgxdu: fdcysn{3hrxqld10h_15_r00y}. qgf vtyy cthe disd s ygd gc rxqldgnxsliq tu pfud zftyethn gcc ditu ugxd gc zsutr bhgvykenk, she td xksyyq tu hgd ug zse scdkx syy. iglk qgf khpgqke dik risyykhnk!

最終行をquipqiupで復号する。(f=u p=j)

congratulations! you have finished the beginner cryptography challenge. here is a flag for all your hard efforts: utflag{3ncrypt10n_15_c00l}. you will find that a lot of cryptography is just building off this sort of basic knowledge, and it really is not so bad after all. hope you enjoyed the challenge!
utflag{3ncrypt10n_15_c00l}

Jacobi's Chance Encryption (Cryptography 750)

flag.encには0が多く存在する。このことからencrypt関数のyがnの倍数と考えられる。つまり、暗号化して0になっている箇所はbitが1で、そうでない場合はbitが0となる。このことから元のデータを割り出す。

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

b_data = ''
for num in enc.split(',')[:-1]:
    if num == '0':
        b_data += '1'
    else:
        b_data += '0'

flag = ''
for i in range(0, len(b_data), 8):
    code = int(b_data[i:i+8], 2)
    flag += chr(code)

print flag
utflag{did_u_pay_attention_in_number_theory}