HITCON CTF 2017 Quals Writeup

この大会は2017/11/4 11:00(JST)~2017/11/6 11:00(JST)に開催されました。
今回もチームで参戦。結果は521点で1075チーム中107位でした。
自分で単独で解けた問題は参加表明の問題一問のみ。
あと他のメンバとの合わせ技で解けた問題はありましたが、
この参加表明の問題のみWriteupとして書いておきます。

Visual Acuity (Misc)

フォントが大きすぎても見にくいが、問題にフラグが書いてある。

hitcon{enjoy_our_adaptive_scoring_system}

School CTF 2017 Writeup

この大会は2017/11/5 15:00(JST)~2017/11/5 23:00(JST)に開催されました。
今回もチームで参戦。結果は1500点で378チーム中29位でした。
自分で解けた問題をWriteupとして書いておきます。

StRiNgMuTaToR 1.0 (ppc 100)

オペコードに従い、反転やシフトを行う。

import socket
import re

def recvuntil(s, tails):
    data = ""
    while True:
        for tail in tails:
            if tail in data:
                return data
        data += s.recv(1)

def reverse(s):
    return s[::-1]

def swap(s):
    l = len(s) / 2
    s1 = s[l:]
    s2 = s[:l]
    return s1 + s2

def reverse_halves(s, code):
    l = len(s) / 2
    s1 = s[:l]
    s2 = s[l:]
    if code == 2:
        s1 = s1[::-1]
    elif code == 3:
        s2 = s2[::-1]
    return s1 + s2

def reverse_quoters(s, code):
    l = len(s) / 4
    s1 = s[:l]
    s2 = s[l:l*2]
    s3 = s[l*2:l*3]
    s4 = s[l*3:]
    if code == 4:
        s1 = s1[::-1]
    elif code == 5:
        s2 = s2[::-1]
    elif code == 6:
        s3 = s3[::-1]
    elif code == 7:
        s4 = s4[::-1]
    return s1 + s2 + s3 + s4

def cycle_left_quoters(s, code):
    l = len(s) / 4
    s1 = s[:l]
    s2 = s[l:l*2]
    s3 = s[l*2:l*3]
    s4 = s[l*3:]
    if code == 8:
        s1 = s1[1:l] + s1[0]
    elif code == 9:
        s2 = s2[1:l] + s2[0]
    elif code == 10:
        s3 = s3[1:l] + s3[0]
    elif code == 11:
        s4 = s4[1:l] + s4[0]
    return s1 + s2 + s3 + s4

def cycle_right_quoters(s, code):
    l = len(s) / 4
    s1 = s[:l]
    s2 = s[l:l*2]
    s3 = s[l*2:l*3]
    s4 = s[l*3:]
    if code == 12:
        s1 = s1[l-1] + s1[:l-1]
    elif code == 13:
        s2 = s2[l-1] + s2[:l-1]
    elif code == 14:
        s3 = s3[l-1] + s3[:l-1]
    elif code == 15:
        s4 = s4[l-1] + s4[:l-1]
    return s1 + s2 + s3 + s4

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('nc.task.school-ctf.org', 41447))

pattern = 'program \'(.+)\' to the sequence \'(.+)\''

data = recvuntil(s, '\n')
data += recvuntil(s, '\n')
data += recvuntil(s, ' ')
print data + 'start'
s.sendall('start\n')

for i in range(100):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n')
    m = re.search(pattern, data)
    opcodes = m.group(1)
    seq = m.group(2)
    data += recvuntil(s, ' ')

    for opcode in opcodes:
        code = int(opcode, 16)
        if code == 0:
            seq = reverse(seq)
        elif code == 1:
            seq = swap(seq)
        elif code >= 2 and code <= 3:
            seq = reverse_halves(seq, code)
        elif code >= 4 and code <= 7:
            seq = reverse_quoters(seq, code)
        elif code >= 8 and code <= 11:
            seq = cycle_left_quoters(seq, code)
        else:
            seq = cycle_right_quoters(seq, code)

    print data + seq
    s.sendall(seq + '\n')
    data = recvuntil(s, '\n')
    print data

data = recvuntil(s, '\n')
print data
SchoolCTF{U/c@n-n3v3r_0wN_M3}

WhiteHat Challenge 05 参戦

この大会は2017/10/28 11:00(JST)~2017/10/28 19:00(JST)に開催されました。
今回もチームで参戦。結果は35点で61チーム中24位でした。
今回は自分が得点した問題は1問もありませんでした。
暗号の問題のうち1問は復号できていたはずなのに、なぜかフラグが通らず。
何が正解だったのか教えてほしい!

Pwn2Win CTF 2017 Writeup

この大会は2017/10/21 0:37(JST)~2017/10/23 0:37(JST)に開催されました。
今回もチームで参戦。結果は1600点で207チーム中21位でした。
自分で解けた問題をWriteupとして書いておきます。

Differential Privacy (Crypto)

$ nc 200.136.213.143 9999
Hello, chose an option:
[1] Info
[2] Query the flag (in ASCII)
[3] Quit
1
You can query the flag, but the characters are private (indistinguishable).
Differential privacy mechanism: Laplace
Sensitivity: ||125 - 45|| = 80
Epsilon: 6.5

Hello, chose an option:
[1] Info
[2] Query the flag (in ASCII)
[3] Quit
2
[60, 93, 88, 40, 79, 77, 147, 80, 136, 86, 109, 82, 111, 118, 108, 116, 76, 98, 102, 74, 117, 108, 95, 100, 127, 108, 99, 115, 78, 118, 81, 123, 123, 89, 115, 114, 132]
Hello, chose an option:
[1] Info
[2] Query the flag (in ASCII)
[3] Quit
Timeout! Bye...

とりあえず数回どんな数値が返ってくるか見てみる。

[103, 132, 67, 35, 61, 120, 100, 60, 104, 94, 81, 91, 148, 115, 120, 109, 96, 105, 113, 133, 125, 100, 129, 107, 92, 103, 107, 118, 109, 119, 76, 115, 91, 105, 120, 98, 114]
[60, 83, 77, 48, 84, 117, 123, 71, 101, 100, 105, 84, 111, 107, 117, 103, 86, 99, 105, 114, 99, 101, 118, 107, 110, 116, 126, 113, 85, 73, 98, 119, 93, 111, 113, 104, 115]
[72, 81, 71, 9, 59, 74, 130, 80, 96, 109, 119, 139, 101, 122, 133, 109, 106, 124, 95, 103, 49, 76, 96, 58, 122, 110, 71, 110, 94, 100, 104, 159, 113, 123, 124, 108, 138]
[68, 110, 68, 51, 55, 87, 129, 69, 100, 109, 131, 99, 56, 144, 119, 113, 89, 105, 102, 115, 117, 103, 101, 118, 141, 98, 83, 109, 76, 70, 95, 101, 118, 103, 121, 55, 124]
[69, 106, 82, 68, 95, 68, 125, 60, 89, 82, 105, 92, 88, 116, 122, 114, 90, 131, 95, 102, 96, 94, 109, 104, 103, 112, 92, 116, 101, 127, 94, 108, 116, 126, 113, 88, 214]
[78, 82, 42, 25, 30, 94, 138, 72, 108, 84, 108, 80, 104, 113, 134, 166, 102, 93, 94, 94, 116, 93, 108, 106, 157, 76, 83, 98, 104, 76, 112, 118, 123, 111, 116, 100, 131]
[67, 77, 69, 41, 70, 76, 135, 53, 94, 90, 91, 81, 103, 121, 110, 112, 75, 129, 112, 82, 106, 101, 141, 104, 106, 75, 93, 91, 119, 128, 110, 114, 143, 89, 143, 93, 138]

フラグがCTF-BRから始まることを考えると、ASCIIコードで近い数値のような感じがする。できるだけ多くの回数の平均のコード値を取り文字にしてみる。以下のコードを使い、何回か試し、フラグを推定する。

import socket

COLLECT_TIMES = 256

codes = []
for i in range(COLLECT_TIMES):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('200.136.213.143', 9999))
    data = s.recv(256)
    print data
    print '2'
    s.sendall('2\n')
    data = s.recv(256)
    print data
    codes.append(map(int, data[1:-2].split(', ')))

avg_str = ''
for i in range(len(codes[0])):
    sum = 0
    for j in range(COLLECT_TIMES):
        sum += codes[j][i]
    avg = sum / COLLECT_TIMES
    print avg
    avg_str += chr(avg)

print avg_str

3回試した結果は以下の通り。

BRF+AR}H`bn_iutr_filtfpgmg\ujd_nnjse}
CVE-@SzJ]bl^kust]finserimf_sgf^npire{
BSD,BQyH_`l^hurt^djluesgkh_the_mohte|

この結果から意味を持つように文字を前後させる。

CTF-BR{I_am_just_filtering_the_noise}

Sum (Hello World Platform) (PPC-M)

実行するだけ。

$ python solve_sum.py
received: 4 1 1 5 7 6 1 5 8 7 8 8 3 0
sent: 64
received: 4 1 4 4 9 6 5 3 0
sent: 36
received: 8 1 7 1 6 8 4 3 3 3 0
sent: 44
received: 1 8 8 3 7 8 1 2 4 4 6 0
sent: 52
received: 1 1 1 9 1 9 7 4 4 4 7 3 1 2 7 9 0
sent: 70
received: 5 6 9 9 5 6 3 8 8 0
sent: 59
received: 2 2 3 1 8 2 1 9 9 7 4 9 2 8 1 8 8 4 0
sent: 88
received: 1 4 7 5 3 8 4 9 0
sent: 41
received: 1 7 8 3 5 6 4 8 5 5 8 4 9 2 5 0
sent: 80
received: 9 2 9 2 8 3 0
sent: 33
received: 1 3 4 9 9 5 5 8 6 7 0
sent: 57
received: 9 1 4 7 4 5 3 5 6 6 3 8 0
sent: 61
received: 5 7 8 5 2 2 9 0
sent: 38
received: 9 3 9 4 6 7 9 5 4 6 9 4 8 6 2 0
sent: 91
received: 3 8 1 1 3 7 2 7 4 9 5 1 4 3 9 5 4 9 0
sent: 85
received: 4 1 8 7 1 2 7 3 5 6 8 3 4 6 9 9 7 0
sent: 90
received: 9 1 2 3 3 4 2 1 9 5 4 4 8 2 2 5 0
sent: 64
received: 2 1 6 7 2 4 7 2 0
sent: 31
received: 5 7 7 1 5 9 1 4 6 6 8 0
sent: 59
received: 3 1 5 9 3 3 4 1 5 1 7 0
sent: 42
received: CTF-BR{Congrats!_you_know_how_to_sum!}
CTF-BR{Congrats!_you_know_how_to_sum!}

Hack.lu CTF 2017 Writeup

この大会は2017/10/17 19:00(JST)~2017/10/19 19:00(JST)に開催されました。
今回もチームで参戦。結果は1172点で241チーム中63位でした。
自分で解けた問題をWriteupとして書いておきます。

b64 (Crypto 100)

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

secret:ランダムな8バイトデータ。
secretと入力文字列のXORを取り、その結果に64をプラスし、256で割った余りをASCIIコードとして文字にする。
その結果からBase64で使われる文字以外を削除する。
文字列長を4で割って、1余る場合はその1文字を削除する。
余りが2または3の場合は、=でパディングし、4の倍数の文字列長にする。
Base64デコードしたものが提示される。

Base64文字以外がないものを探すことができれば、XORでsecretを算出することができる。それをコードにしたものは以下の通り。

import socket

PAD = 64

def recvuntil(s, tails):
    data = ""
    while True:
        for tail in tails:
            if tail in data:
                return data
        data += s.recv(1)

def decrypt(s1, s2):
    dec = ''
    for i in range(len(s1)):
        code = ord(s1[i]) ^ ((ord(s2[i]) - PAD) % 256)
        dec += chr(code)
    return dec

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('flatearth.fluxfingers.net', 1718))

data = recvuntil(s, '\n')
print data

in_str = ''
for i in range(4):
    found = False
    for code1 in range(0, 256, 16):
        for code2 in range(0, 256, 16):
            data = recvuntil(s, '>')
            data += recvuntil(s, ' ')
            print data + '1'
            s.sendall('1')
            data = recvuntil(s, '>')
            data += recvuntil(s, ' ')

            try_str = in_str + chr(code1) + chr(code2)
            print data + try_str
            s.sendall(try_str)
            data = recvuntil(s, '\n')
            print data
            dec = data[30:-1]
            if len(dec) > 0:
                b64 = dec.encode('base64').strip()
                b64_no_pad = b64.replace('=', '')
                if len(b64_no_pad) == ((i+1) * 2):
                    in_str += chr(code1) + chr(code2)
                    found = True
                    break
        if found:
            break

data = recvuntil(s, '>')
data += recvuntil(s, ' ')
print data + '2'
s.sendall('2')
data = recvuntil(s, '>')
data += recvuntil(s, ' ')

secret = decrypt(in_str, b64).encode('hex')
print data + secret
s.sendall(secret)
data = recvuntil(s, '\n')
print data
data = recvuntil(s, '\n')
print data
flag{7h3_b35t_w4y_of_h1ding_s3cr3t5_the_w0r1d_h4s_ev3r_seen_period!}

Hack Dat Kiwi 2017 Writeup

この大会は2017/10/13 13:00(JST)~2017/10/15 13:00(JST)に開催されました。
今回もチームで参戦。結果は605点で306チーム中53位でした。
自分で解けた問題をWriteupとして書いておきます。

PS 1 (Crypto 100)

AESの変形問題。内部の処理の回数などを変更し、簡易化されている。16バイトのブロック暗号で、pboxだけが位置を変更していることに注目して、復号してprintableなテキストに復号されるよう総当たりする。その際、sboxやpboxはphpのソースにデバッグログを入れて、具体的な値を取り出しておく。問題の暗号をテキストファイルenc.txtに保存しておき、次のコードで復号する。

from math import *

BLOCK_SIZE = 16

def get_sbox_for_seed_1396_rounds_1():
    sbox = [70, 162, 120, 88, 97, 191, 116, 211, 240, 171, 174, 123, 47, 59,
            64, 29, 255, 101, 196, 108, 253, 201, 35, 13, 164, 38, 77, 148,
            57, 17, 37, 132, 183, 159, 220, 20, 93, 80, 232, 78, 248, 151, 202,
            43, 214, 8, 75, 213, 113, 12, 66, 112, 217, 106, 128, 129, 145,
            203, 15, 204, 223, 53, 76, 147, 215, 34, 168, 49, 119, 144, 130,
            110, 33, 71, 158, 250, 83, 231, 208, 190, 243, 14, 44, 199, 124,
            170, 68, 5, 115, 89, 210, 79, 143, 26, 226, 99, 63, 138, 154, 184,
            18, 16, 39, 56, 94, 193, 52, 176, 172, 1, 109, 157, 22, 156, 103,
            149, 62, 179, 160, 185, 254, 104, 251, 141, 135, 225, 238, 200,
            92, 133, 126, 117, 153, 169, 180, 237, 87, 228, 150, 2, 230, 249,
            161, 244, 139, 252, 122, 207, 167, 21, 114, 173, 111, 105, 42, 0,
            73, 28, 205, 187, 166, 55, 32, 48, 229, 216, 30, 61, 182, 189, 65,
            140, 188, 233, 127, 60, 227, 7, 19, 131, 31, 146, 45, 175, 9, 91,
            177, 90, 125, 96, 23, 40, 194, 67, 86, 155, 10, 136, 224, 212, 51,
            25, 84, 241, 247, 234, 50, 222, 236, 74, 100, 6, 219, 163, 186,
            239, 245, 81, 72, 118, 195, 95, 165, 98, 178, 246, 242, 181, 102,
            198, 82, 152, 218, 192, 121, 221, 107, 197, 142, 58, 4, 24, 85,
            235, 137, 36, 206, 69, 46, 11, 27, 41, 134, 209, 54, 3]
    return sbox

def get_pbox_for_seed_1396_rounds_1():
    pbox = [4, 10, 7, 5, 6, 13, 8, 14, 15, 9, 11, 2, 0, 1, 3, 12]
    return pbox

def pad(message, block_size):
    length = int(max(floor((len(message)-1)/block_size)+1, 1)*block_size)
    return message + ' ' * (length - len(message))

def sbox(block, reverse = False):
    sbox = get_sbox_for_seed_1396_rounds_1()
    out = ''
    if reverse:
        for i in range(BLOCK_SIZE):
            out += chr(sbox.index(ord(block[i])))
    else:
        for i in range(BLOCK_SIZE):
            out += chr(sbox[ord(block[i])])
    return out

def pbox(block, reverse = False):
    pbox = get_pbox_for_seed_1396_rounds_1()
    out = [' '] * BLOCK_SIZE
    if reverse:
        for i in range(BLOCK_SIZE):
            out[pbox[i]] = block[i]
    else:
        for i in range(BLOCK_SIZE):
            out[i] = block[pbox[i]]
    return ''.join(out)

def xbox(block, key):
    out = ''
    for i in range(BLOCK_SIZE):
        out += chr((ord(block[i]) ^ ord(key[i])) % 256)
    return out

def ps2_block(block, key, decrypt = False):
    key = pad(key, BLOCK_SIZE)

    if decrypt:
        block = xbox(block, key)
        block = pbox(block, decrypt)
        block = sbox(block, decrypt)
    else:
        block = sbox(block, decrypt)
        block = pbox(block, decrypt)
        block = xbox(block, key)
    return block

with open('enc.txt', 'r') as f:
    data = f.read().split(' ')

blocks = []
block = ''
for i in range(len(data)):
    block += chr(int(data[i], 16))
    if i % BLOCK_SIZE == BLOCK_SIZE - 1:
        blocks.append(block)
        block = ''

keys = []
indexes = [4, 10, 7, 5, 6, 13, 8, 14, 15, 9, 11, 2, 0, 1, 3, 12]

for index in indexes:
    for code in range(256):
        fail = False
        len_keys = len(keys)
        if len_keys > 0:
            try_key = keys[len_keys - 1] + chr(code)
        else:
            try_key = chr(code)
        for block in blocks:
            res = ps2_block(block, try_key, True)
            if (ord(res[index]) >= 32 and ord(res[index]) <= 126) \
                or ord(res[index]) == 10 or ord(res[index]) == 13:
                pass
            else:
                fail = True
                break
        if fail == False:
            keys.append(try_key)
            break

dec = ''
for block in blocks:
    dec += ps2_block(block, keys[15], True)

print dec

復号結果は次の通り。

A flag is a piece of fabric (most often rectangular or quadrilateral) with a distinctive design that is used as a symbol, as a signaling device, or as decoration. The term flag is also used to refer to the graphic design employed, and flags have since evolved into a general tool for rudimentary signalling and identification, especially in environments where communication is similarly challenging (such as the maritime environment where semaphore is used). National flags are potent patriotic symbols with varied wide-ranging interpretations, often including strong military associations due to their original and ongoing military uses. Flags are also used in messaging, advertising, or for other decorative purposes. The study of flags is known as vexillology, from the Latin word vexillum, meaning flag or banner.

Due to the use of flags by CTFs, "flag" is also '2qR4twRCoMq7SQb3' in this challenge.
2qR4twRCoMq7SQb3

PS 2 (Crypto 100)

考え方はPS 1と同じ。ただRound数が2になり、複雑性が増す。影響する位置が2箇所に増えることを考慮して、総当たりする。

from math import *

BLOCK_SIZE = 16
ROUNDS = 2

def get_sbox_for_seed_7_rounds_2():
    sbox = []
    sbox1 = [124, 222, 151, 54, 2, 132, 254, 8, 155, 15, 135, 24, 196, 209,
            228, 43, 57, 201, 200, 18, 110, 14, 88, 130, 4, 70, 95, 193, 11,
            23, 51, 138, 241, 198, 187, 244, 77, 185, 251, 227, 202, 128, 249,
            141, 83, 220, 178, 136, 165, 123, 154, 27, 143, 240, 150, 145, 53,
            245, 84, 63, 9, 126, 192, 248, 73, 121, 233, 147, 60, 229, 113, 7,
            102, 109, 149, 186, 79, 74, 69, 247, 205, 223, 20, 91, 212, 168,
            236, 13, 160, 66, 72, 169, 197, 19, 162, 26, 140, 139, 170, 206,
            112, 36, 213, 225, 144, 99, 159, 226, 177, 232, 217, 117, 195, 238,
            216, 156, 137, 199, 167, 49, 28, 252, 218, 215, 10, 114, 242, 133,
            29, 142, 92, 119, 180, 55, 97, 71, 161, 22, 48, 85, 255, 31, 219,
            189, 6, 182, 98, 152, 127, 35, 210, 164, 33, 179, 118, 40, 62, 108,
            183, 81, 38, 44, 234, 224, 93, 82, 52, 21, 101, 104, 105, 96, 129,
            76, 50, 148, 30, 172, 56, 175, 203, 17, 89, 235, 208, 239, 12, 34,
            107, 204, 111, 153, 0, 87, 116, 94, 188, 176, 120, 46, 42, 246,
            163, 181, 65, 214, 67, 90, 125, 122, 32, 68, 166, 115, 58, 103,
            100, 61, 158, 237, 25, 3, 146, 39, 106, 1, 173, 47, 207, 16, 131,
            243, 5, 37, 190, 86, 231, 250, 194, 134, 157, 230, 191, 41, 78,
            253, 171, 211, 64, 45, 184, 80, 59, 75, 221, 174]
    sbox2 = [53, 33, 193, 239, 51, 36, 212, 61, 41, 115, 145, 9, 96, 84, 119,
            209, 49, 52, 226, 144, 47, 105, 95, 127, 130, 15, 233, 162, 72,
            126, 45, 129, 160, 240, 109, 206, 17, 67, 7, 63, 181, 158, 74, 13,
            236, 187, 223, 26, 235, 194, 172, 25, 39, 4, 152, 168, 23, 131, 73,
            98, 248, 116, 217, 150, 99, 71, 101, 118, 137, 108, 178, 55, 0,
            243, 70, 237, 175, 32, 6, 155, 224, 182, 179, 8, 188, 75, 174, 207,
            196, 234, 42, 184, 92, 255, 77, 189, 68, 165, 40, 200, 14, 214,
            247, 16, 198, 59, 249, 112, 90, 253, 254, 57, 170, 167, 64, 103,
            230, 222, 46, 164, 204, 88, 97, 37, 89, 157, 219, 147, 78, 3, 93,
            86, 215, 82, 106, 154, 135, 91, 10, 213, 87, 5, 18, 252, 180, 76,
            113, 159, 43, 146, 79, 244, 227, 163, 29, 58, 69, 250, 203, 140, 1,
            35, 216, 218, 124, 60, 132, 238, 142, 134, 197, 220, 136, 205, 211,
            50, 24, 65, 201, 80, 192, 22, 56, 173, 183, 102, 229, 246, 94, 171,
            133, 100, 208, 107, 31, 81, 148, 156, 48, 21, 27, 251, 241, 153,
            195, 199, 191, 225, 12, 149, 34, 202, 169, 111, 125, 120, 161, 114,
            110, 245, 20, 228, 66, 231, 54, 122, 38, 186, 11, 121, 221, 44,
            123, 190, 177, 30, 151, 141, 19, 176, 83, 85, 139, 232, 138, 2,
            117, 104, 143, 210, 62, 166, 185, 128, 28, 242]
    sbox.append(sbox1)
    sbox.append(sbox2)
    return sbox

def get_pbox_for_seed_7_rounds_2():
    pbox = []
    pbox1 = [7, 14, 9, 2, 0, 8, 15, 1, 10, 3, 11, 4, 13, 12, 6, 5]
    pbox2 = [3, 12, 13, 0, 7, 1, 6, 9, 2, 5, 10, 14, 4, 8, 11, 15]
    pbox.append(pbox1)
    pbox.append(pbox2)
    return pbox

def pad(message, block_size):
    length = int(max(floor((len(message)-1)/block_size)+1, 1)*block_size)
    return message + ' ' * (length - len(message))

def sbox(block, round = 0, reverse = False):
    sbox = get_sbox_for_seed_7_rounds_2()
    out = ''
    if reverse:
        for i in range(BLOCK_SIZE):
            out += chr(sbox[round].index(ord(block[i])))
    else:
        for i in range(BLOCK_SIZE):
            out += chr(sbox[round][ord(block[i])])
    return out

def pbox(block, round = 0, reverse = False):
    pbox = get_pbox_for_seed_7_rounds_2()
    out = [' '] * BLOCK_SIZE
    if reverse:
        for i in range(BLOCK_SIZE):
            out[pbox[round][i]] = block[i]
    else:
        for i in range(BLOCK_SIZE):
            out[i] = block[pbox[round][i]]
    return ''.join(out)

def xbox(block, key):
    out = ''
    for i in range(BLOCK_SIZE):
        out += chr((ord(block[i]) ^ ord(key[i])) % 256)
    return out

def ps2_block(block, key, decrypt = False):
    key = pad(key, BLOCK_SIZE)

    if decrypt:
        for i in range(ROUNDS-1, -1, -1):
            block = xbox(block, key)
            block = pbox(block, i, decrypt)
            block = sbox(block, i, decrypt)
    else:
        for i in range(ROUNDS):
            block = sbox(block, i, decrypt)
            block = pbox(block, i, decrypt)
            block = xbox(block, key)
    return block

with open('enc.txt', 'r') as f:
    data = f.read().split(' ')

blocks = []
block = ''
for i in range(len(data)):
    block += chr(int(data[i], 16))
    if i % BLOCK_SIZE == BLOCK_SIZE - 1:
        blocks.append(block)
        block = ''

keys = []
indexes1 = [7, 14, 9, 2, 0, 8, 15, 1, 10, 3, 11, 4, 13, 12, 6, 5]
indexes2 = [3, 12, 13, 0, 7, 1, 6, 9, 2, 5, 10, 14, 4, 8, 11, 15]

for index1 in indexes2:
    index = indexes1[index1]
    for code1 in range(256):
        for code2 in range(256):
            fail = False
            len_keys = len(keys)
            if len_keys > 0:
                try_key = ''.join(keys) + chr(code1)
            else:
                try_key = chr(code1)
            try_key += chr(code2) * (16 - len(try_key))
            for block in blocks:
                res = ps2_block(block, try_key, True)
                if (ord(res[index]) >= 32 and ord(res[index]) <= 126) \
                    or ord(res[index]) == 10 or ord(res[index]) == 13:
                    pass
                else:
                    fail = True
                    break
            if fail == False:
                keys.append(chr(code1))
                break
        if fail == False:
            break

dec = ''
for block in blocks:
    dec += ps2_block(block, ''.join(keys), True)

print dec

実行結果は以下の通り。

An Easter egg is an intentional inside joke, hidden message, or feature in an interactive work such as a computer program, flag is 4y7u1t2CELXdPVoI, video game or DVD menu screen. The name is used to evoke the idea of a traditional Easter egg hunt.

Released in 1979, Atari's Adventure contained the first hidden message in a video game to have been discovered by its players; the message is "Created by Warren Robinett", and was inserted by Robinett, the game's programmer.[2] According to Robinett, the term Easter egg was applied by Atari personnel on being alerted to the secret addition.[3] Since Atari did not publicly credit game designers, Robinett inserted the message after the game's completion partially in an attempt to gain some recognition for his work.[2] In 2004, an earlier Easter egg was found in Video Whizball, a 1978 game for the Fairchild Channel F system, displaying programmer Bradley Reid-Selth's surname.
4y7u1t2CELXdPVoI

Survey (25)

アンケートに答えるだけ。

hsABf8jG3VK93hQf

Square CTF 2017 Writeup

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

Book of Allies (Grace Hopper 5)

scheduleタブを選択し、そこからリンクされているPDFを見てみる。
ホッチキスの露出しているページ、つまり真ん中のページの左側の一番上のセッションの会社名がフラグということらしい。全部で35ページあるので、18ページ目の左側を見る。

flag-thought-leadership-lab

Password checker (Web Security 50)

OSコマンドが実行できるが、結果は1行だけ表示される。

https://neget-kymud-nihat-fagoh-mifip.capturethesquare.com/run.php?cmd=cat%20../password.txt
password123

https://neget-kymud-nihat-fagoh-mifip.capturethesquare.com/run.php?cmd=ls%20-l%20..
-rw-r--r-- 3 root root 15 Oct 4 10:34 xxx_not_a_flag.txt

https://neget-kymud-nihat-fagoh-mifip.capturethesquare.com/run.php?cmd=ls%20-l%20../..
drwxr-xr-x 3 root root 4096 Oct 4 10:47 www

https://neget-kymud-nihat-fagoh-mifip.capturethesquare.com/run.php?cmd=ls%20-l%20../../..
drwxr-xr-x 1 root root 4096 Oct 4 10:47 var

https://neget-kymud-nihat-fagoh-mifip.capturethesquare.com/run.php?cmd=ls%20../f*
../flag.txt

https://neget-kymud-nihat-fagoh-mifip.capturethesquare.com/run.php?cmd=cat%20../flag.txt
line 2: flap-31aac7e26de449ee

https://neget-kymud-nihat-fagoh-mifip.capturethesquare.com/run.php?cmd=tac%20../flag.txt
line 1: flag-bc0a804287546c09
flag-bc0a804287546c09

The General's Cat (Crypto 50)

シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号。

Rotation 17:
The domestic cat (Felis silvestris catus or Felis catus) is a small, typically furry, carnivorous mammal. They are often called house cats when kept as indoor pets or simply cats when there is no need to distinguish them from other felids and felines. Cats are often valued by humans for companionship and for their ability to hunt vermin. There are more than 70 cat breeds, though different associations proclaim different numbers according to their standards. The flag is the phrase with dashes: flag what is a domestic cat.
flag-what-is-a-domestic-cat

The Robot's Grandmother (Forensics 50)

SMTPTCP Streamで見る。

220 x.shh.sh ESMTP Exim 4.86 Wed, 06 Sep 2017 22:11:43 +0000
ehlo x.shh.sh
250-x.shh.sh Hello x.shh.sh [::1]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH LOGIN
250-STARTTLS
250-PRDR
250 HELP
auth login
334 VXNlcm5hbWU6
bWFsbG9yeQ==
334 UGFzc3dvcmQ6
ZmxhZy1zcGluc3Rlci1iZW5lZml0LWZhbHNpZnktZ2FtYmlhbg==
535 Incorrect authentication data
421 x.shh.sh lost input connection
$ echo ZmxhZy1zcGluc3Rlci1iZW5lZml0LWZhbHNpZnktZ2FtYmlhbg== | base64 -d
flag-spinster-benefit-falsify-gambian
flag-spinster-benefit-falsify-gambian