読者です 読者をやめる 読者になる 読者になる

TU CTF 2016 Writeup

CTF writeup

この大会は2016/5/14 7:00(JST)~2016/5/16 7:00(JST)に開催されました。
今回もチームで参戦。結果は2110点で、社会人(Overall)ランキング435チーム中31位でした。
自分で解けた問題をWriteupとして書いておきます。

I'm playing! (Misc 10)

IRCの問題ということで、freenodeで#TUCTFに入ると、こんなメッセージが...。

07:43 *topic : vm_decryption_key: DescendIntoDarkness     A wild string appeared: "TUCTF{Just_A_Test_" + Flag from logo (in caps) + "}"

TUCTFのロゴにモールス信号が書いてあり、復号するとFLAGになる。

TUCTF{Just_A_Test_FLAG}

The Neverending Crypto Level 1 (Crypto 10)

モールス信号を復号する。50回復号するとフラグが表示された。

TUCTF{i_wi11_n0t_5teal}

The Neverending Crypto Level 2 (Crypto 10)

Level 1の続き。ASCIIコードで13引いて、復号する。50回復号するとフラグが表示された。

TUCTF{c4n_s0me0ne_turn_a_1ight_0n}

The Neverending Crypto Level 3 (Crypto 10)

Level 2の続き。2パターンの換字暗号。どちらのパターンかを判定して復号する。50回復号するとフラグが表示された。

TUCTF{5omething_is_b3tt3r_th4n_n0thing}

The Neverending Crypto Level 4 (Crypto 10)

Level 3の続き。シフト暗号。テストした暗号からシフト数を算出して復号する。50回復号するとフラグが表示された。

TUCTF{7urn_th4t_fr0wn_up5ide_d0wn}

The Neverending Crypto Level 5 (Crypto 10)

Level 4の続き。ASCIIコードで159から引いて、復号する。50回復号するとフラグが表示された。

TUCTF{gn0mes_4re_p3ople_t00}

The Neverending Crypto Level 6 (Crypto 50)

Level 5の続き。テストした暗号からASCIIコードが一定幅で割り当てが変わったものを算出して復号する。50回復号するとフラグが表示された。

TUCTF{mirr0r_mirr0r_0n_th3_w4ll}

The Neverending Crypto Level 7 (Crypto 50)

Level 6の続き。暗号時にASCIIコードの差が一定の変化をする。

abcdefghijklm encrypted is \i~_l"bo%er(h
a(97)  -> \(92)
b(98)  -> i(105) +13
c(99)  -> ~(126) +21
d(100) -> _(95) -31
e(101) -> l(108) +13
f(102) -> "(34) +21
g(103) -> b(98) -31
h(104) -> o(111) +13
i(105) -> %(37) +21
j(106) -> e(101) -31
k(107) -> r(114) +13

abcdefghijklm encrypted is '/!*2$-5'08*3
a(97)  -> '(39)
b(98)  -> /(47) +8
c(99)  -> !(33) -14
d(100) -> *(42) +9
e(101) -> 2(50) +8
f(102) -> $(36) +-14
g(103) -> -(45) +9
h(104) -> 5(53) +8
i(105) -> '(39) -14
j(106) -> 0(48) +9
k(107) -> 8(56) +8

このことを利用してコードを書き、50回復号するとフラグが表示された。

TUCTF{c4ll_m3_ishmae1}

The Neverending Crypto Level 8 (Crypto 150)

Level 7の続き。スキュタレー暗号。必ず6周の暗号になっていることから6で割って、スキップ数を算出し復号する。50回復号するとフラグが表示された。
Level 9は解けなかったので、このレベル以下のコードを記載しておく。

#!/usr/bin/env python
import socket
import re

morse = {'.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E',
    '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K',
    '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q',
    '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W',
    '-..-':'X' , '-.--': 'Y', '--..': 'Z', '-----': '0', '.----': '1',
    '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6',
    '--...': '7', '---..': '8', '----.': '9'
}

lv3_1 = {'a': 'a', 'b': 'b','c': 'c', 's': 'd', 'f': 'e',
    't': 'f', 'd': 'g', 'h': 'h', 'u': 'i', 'n': 'j',
    'e': 'k', 'i': 'l', 'm': 'm', 'k': 'n', 'y': 'o',
    ';': 'p', 'q': 'q', 'p': 'r', 'r': 's', 'g': 't',
    'l': 'u', 'v': 'v', 'w': 'w', 'x': 'x', 'j': 'y', 'z': 'z'
}

lv3_2 = {'a': 'a', 'x': 'b','j': 'c', 'e': 'd', '.': 'e',
    'u': 'f', 'i': 'g', 'd': 'h', 'c': 'i', 'h': 'j',
    't': 'k', 'n': 'l', 'm': 'm', 'b': 'n', 'r': 'o',
    'l': 'p', '\'': 'q', 'p': 'r', 'o': 's', 'y': 't',
    'g': 'u', 'k': 'v', ',': 'w', 'q': 'x', 'f': 'y', ';': 'z'
}

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('146.148.102.236', 24069))

######## Level 1 ########
for i in range(1, 51):
    data = s.recv(256)
    print data
    s.sendall('a\n')
    data = s.recv(256)
    print data

    pattern = 'What is (.+) decrypted'
    m = re.search(pattern, data)
    codes = m.group(1).strip().split(' ')
    print codes

    ans = ''
    sp = 0
    for code in codes:
        if code == '' and sp == 0:
            ans += ' '
            sp = 1
        elif code == '' and sp ==1:
            sp = 0
        else:
            ans += morse[code]

    print ans
    s.sendall(ans + '\n')

######## Level 2 ########
for i in range(1, 51):
    data = s.recv(256)
    print data
    s.sendall('012ABCxyz\n')
    data = s.recv(256)
    print data

    pattern = 'What is (.+) decrypted'
    m = re.search(pattern, data)
    enc = m.group(1)

    ans = ''
    for j in range(len(enc)):
        code = ord(enc[j:j+1]) - 13
        if code < 32:
            code = code + 95
        ans += chr(code)

    print ans
    s.sendall(ans + '\n')

######## Level 3 ########
for i in range(1, 51):
    data = s.recv(256)
    print data
    s.sendall('b\n')
    data = s.recv(256)
    print data

    pattern = 'b encrypted is (.+)\nWhat is (.+) decrypted'
    m = re.search(pattern, data)
    sample = m.group(1)
    enc = m.group(2)

    ans = ''
    if sample == lv3_1['b']:
        for j in range(len(enc)):
            if enc[j:j+1] == ' ':
                ans += ' '
            else:
                ans += lv3_1[enc[j:j+1]]
    else:
        for j in range(len(enc)):
            if enc[j:j+1] == ' ':
                ans += ' '
            else:
                ans += lv3_2[enc[j:j+1]]

    print ans
    s.sendall(ans + '\n')

######## Level 4 ########
for i in range(1, 51):
    data = s.recv(256)
    print data
    s.sendall('giant\n')
    data = s.recv(256)
    print data

    pattern = 'giant encrypted is (.+)\nWhat is (.+) decrypted'
    m = re.search(pattern, data)
    sample = m.group(1)
    enc = m.group(2)

    shift = ord(sample[0:1]) - ord('g')
    ans = ''
    for j in range(len(enc)):
        code = ord(enc[j:j+1]) - shift
        if code < 32:
            code = code + 95
        elif code > 126:
            code = code - 95
        ans += chr(code)

    print ans
    s.sendall(ans + '\n')

######## Level 5 ########
for i in range(1, 51):
    data = s.recv(256)
    print data
    s.sendall('giant\n')
    data = s.recv(256)
    print data

    pattern = 'What is (.+) decrypted'
    m = re.search(pattern, data)
    enc = m.group(1)

    ans = ''
    for j in range(len(enc)):
        code = 159 - ord(enc[j:j+1])
        ans += chr(code)

    print ans
    s.sendall(ans + '\n')

######## Level 6 ########
for i in range(1, 51):
    data = s.recv(256)
    print data
    s.sendall(' !\"\n')
    data = s.recv(256)
    print data

    pattern = ' !\" encrypted is (.+)\nWhat is (.+) decrypted'
    m = re.search(pattern, data)
    sample = m.group(1)
    enc = m.group(2)

    diff = ord(sample[1:2]) - ord(sample[0:1])
    a_code = ord(sample[0:1]) % diff

    ans = ''
    for j in range(len(enc)):
        for k in range(diff + 1):
            e_code = ord(enc[j:j+1])
            code = e_code + k * 95
            if code % diff == a_code:
                break
        if code < ord(sample[0:1]):
            code = ord(enc[j:j+1]) + diff * 95
            code = (code - ord(sample[0:1])) / diff + 32
        else:
            code = (code - ord(sample[0:1])) / diff + 32
        ans += chr(code)

    print ans
    s.sendall(ans + '\n')

######## Level 7 ########
for i in range(1, 51):
    data = s.recv(256)
    print data
    s.sendall('abcd\n')
    data = s.recv(256)
    print data

    pattern = 'abcd encrypted is (.+)\nWhat is (.+) decrypted'
    m = re.search(pattern, data)
    sample = m.group(1)
    enc = m.group(2)

    diff_head = ord('a') - ord(sample[0:1])

    diff = []
    for j in range(3):
        diff.append(ord(sample[j+1:j+2]) - ord(sample[j:j+1]) - 1)

    ans = ''
    integ = 0
    for j in range(len(enc)):
        if j == 0:
            code = ord(enc[0:1]) + diff_head
        else:
            code = ord(enc[j:j+1]) - diff[(j-1) % 3] + diff_head - integ
            integ = integ + diff[(j-1) % 3]
        if code < 32:
            code = code + 95
        elif code > 126:
            while True:
                if code > 126:
                    code = code - 95
                else:
                    break
        ans += chr(code)

    print ans
    s.sendall(ans + '\n')

######## Level 8 ########
for i in range(1, 100):
    data = s.recv(256)
    print data
    s.sendall('abcdefghijklmnop\n')
    data = s.recv(256)
    print data

    pattern = 'What is (.+) decrypted'
    m = re.search(pattern, data)
    enc = m.group(1)

    skip = len(enc) / 6
    ans = ''
    for j in range(skip):
        for k in range(6):
            letter = enc[k*skip+j:k*skip+j+1]
            if ord(letter) == 0:
                break
            ans += letter

    print ans
    s.sendall(ans + '\n')
TUCTF{it5_4ll_in_y0ur_mind}

Magic Image (Crypto 100)

暗号スクリプトPNGファイルの暗号化ファイルが与えれている。
PNGファイル最初の12バイトは決まっているので、それを前提にkeyを求めた後に、そのkeyで元に戻す。

#!/usr/bin/env python

def xor(s1, s2):
    res = [chr(0)]*12
    for i in range(len(res)):
        q = ord(s1[i])
        d = ord(s2[i])
        k = q ^ d
        res[i] = chr(k)
    res = ''.join(res)
    return res

plain = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
    0x00, 0x00, 0x00, 0x0D]

with open('encrypted.png', 'rb') as f:
    data = f.read()

png_head = ''
for i in plain:
    png_head += chr(i)

key = xor(png_head, data[0:12])

flag = ''
for i in range(0, len(data), 12):
    flag += xor(data[i:i+12], key)

if ord(flag[-1:]) != 0x82:
    pad_cnt = ord(flag[-1:])
    flag = flag[:-pad_cnt]

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

復号したPNG画像にフラグが書かれている。

TUCTF{st@llowning_xOR_5ince_Apollo}

f:id:satou-y:20160519224739p:plain