Can-CWIC CTF 2017 Writeup

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

Beautiful Blue (Steg 25)

高さ10pxごとに色が微妙に変わっている。青の色情報をASCIIコードとして文字にする。

from PIL import Image

img = Image.open('beautiful_blue.png').convert('RGB')
w, h = img.size

flag = ''
for y in range(0, h, 10):
    r, g, b = img.getpixel((0, y))
    flag += chr(b)

print flag
FLAG{Such_a_beautiful_color}

sudo (Steg 25)

解凍しようとすると、同じファイル名のファイルが2つ入っていることがわかる。1つを別名で展開し、ファイルサイズの小さい方を見てみると、フラグが書いてある。

FLAG{archives_are_waaaaayyy_easier_than_sudokus}

Coffee Stain (Steg 50)

ピクロスパズルのJPG画像。一部見えにくくなっている。
JPGの終端の後に、以下のデータあり。

h14:6,1,1,4,1,4,1
h15:7,2,2,3,1,5,2
h16:1,3,6,7,2
v29:2,5,2,7,10,2
v30:2,5,2,7,10,2

見えにくい部分はこれでわかる。この情報と合わせて、パズルを解いていくと、QRコードのようになる。
f:id:satou-y:20171015150457p:plain
このままだとうまく読み取れないので、3角の部分を少し調整すると、読み取ることができた。
f:id:satou-y:20171015150543p:plain

FLAG{NQNQR_C0D3}

Always Backup (Forensic 30)

http://159.203.38.169:5676/.git/にアクセスすると、flagファイルがある。
http://159.203.38.169:5676/.git/flagにアクセスしたら、フラグが書いてあった。

FLAG{ALWAYS-BACKUP!!!-and-use-git-to-backup...}

All is lost (Forensic 34)

http://159.203.38.169:5676/.git/objects/9d/dbc445f674f2891526db63b7bc5b5faea1e748をダウンロード

$ python -c 'import zlib; print zlib.decompress(open("dbc445f674f2891526db63b7bc5b5faea1e748").read())'
blob 77F
L
A
G
{
n
o
t
h
i
n
g
-
i
s
-
l
o
s
t
-
f
o
r
e
v
e
r
-
w
i
t
h
-
g
i
t
}
FLAG{nothing-is-lost-forever-with-git}

Tri it 0 (Prog 60)

$ nc 159.203.38.169 5672
Read https://en.wikipedia.org/wiki/Balanced_ternary
TT00T + TT010
a
Wrong answer.

https://en.wikipedia.org/wiki/Balanced_ternaryを参考に数値にして計算して、元の形式に戻すというプログラムを作成する。

import socket

bal3_letter = '01T'

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

def get_elem(s):
    elems = []
    elem = ''
    for i in range(len(s)):
        if s[i] in bal3_letter:
            elem += s[i]
        else:
            if elem != '':
                elems.append(elem)
                elem = ''
        if i == len(s) - 1:
            if elem != '':
                elems.append(elem)

    return elems

def get_formula(s, elems):
    formula = ''
    elem = ''
    index = 0
    for i in range(len(s)):
        if s[i] in bal3_letter:
            elem += s[i]
            if elem == elems[index]:
                formula += str(dec_bal3(elem))
                elem = ''
                index += 1
        else:
            formula += s[i]
    return formula

def dec_bal3(s):
    val = 0
    for i in range(len(s)):
        bit = len(s) - i - 1
        if s[i] == '0':
            bit_val = 0
        elif s[i] == '1':
            bit_val = 3 ** bit
        else:
            bit_val = - 3 ** bit
        val += bit_val
    return val

def enc_bal3(n):
    s = ''
    while n:
        s = bal3_letter[n % 3] + s
        n = -~n/3
    return s or 0

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('159.203.38.169', 5672))

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

# Try it 0
for i in range(2):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n')
    print data
    data = data.replace('\n', '')
    elems = get_elem(data)
    formula = get_formula(data, elems)
    ans = enc_bal3(eval(formula))
    print ans
    s.sendall(ans + '\n')

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

2回答えると、フラグが表示された。

FLAG{I_CAN_MATH_LIKE_A_10RD_GRADER!}

Tri it 1 (Prog 150)

Tri it 0 の続き。足し算だけでなく、引き算、掛け算、かっこなども式に含まれる。

import socket

bal3_letter = '01T'

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

def get_elem(s):
    elems = []
    elem = ''
    for i in range(len(s)):
        if s[i] in bal3_letter:
            elem += s[i]
        else:
            if elem != '':
                elems.append(elem)
                elem = ''
        if i == len(s) - 1:
            if elem != '':
                elems.append(elem)

    return elems

def get_formula(s, elems):
    formula = ''
    elem = ''
    index = 0
    for i in range(len(s)):
        if s[i] in bal3_letter:
            elem += s[i]
            if elem == elems[index]:
                formula += str(dec_bal3(elem))
                elem = ''
                index += 1
        else:
            formula += s[i]
    return formula

def dec_bal3(s):
    val = 0
    for i in range(len(s)):
        bit = len(s) - i - 1
        if s[i] == '0':
            bit_val = 0
        elif s[i] == '1':
            bit_val = 3 ** bit
        else:
            bit_val = - 3 ** bit
        val += bit_val
    return val

def enc_bal3(n):
    s = ''
    while n:
        s = bal3_letter[n % 3] + s
        n = -~n/3
    return s or 0

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('159.203.38.169', 5672))

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

# Try it 0
for i in range(2):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n')
    print data
    data = data.replace('\n', '')
    elems = get_elem(data)
    formula = get_formula(data, elems)
    ans = enc_bal3(eval(formula))
    print ans
    s.sendall(ans + '\n')

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

# Try it 1
for i in range(17):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n')
    print data
    data = data.replace('\n', '')
    elems = get_elem(data)
    formula = get_formula(data, elems)
    ans = enc_bal3(eval(formula))
    print ans
    s.sendall(ans + '\n')

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

17回答えると、フラグが表示された。

FLAG{ONE_PLUS_ONE_EQ_ONE_TON}

Rich 2 (Crypto 200)

タイトル的な順番では逆だが、結果的にはRich 1よりこちらの方が先に解けた。

$ nc 159.203.38.169 5682
Please give me your wallet and I'll determine if you are rich enough to access out VIP area.
Here is an example wallet:
56e3fc849a2c06c2f858615a2a17b0525a1a5020874b2ceebaf411f2753f91864dced78d862c46daf45a204e3c18b35e40360675d70c7db1f9a417e5337ec4cf01de87d8d67a5ae0e4471a5c7854d613033d2b6ad5117780eee964a36471f6cd15d8ac9c9e330ebfb3521a5c7846de1f04362b7acd0b66939b9209c43d41d1dd028987ecc97d4df5c66c3e5f5c46ee1511351b19ef1052a4fad30ff5103792d237e1a88eec2254d3f9057c155351a262
56e3fc849a2c06c2f858615a2a17b0525a1a5020874b2ceebaf411f2753f91864dced78d862c46daf45a204e3c18b35e40360675d70c7db1f9a417e5337ec4cf01de87d8d67a5ae0e4471a5c7854d613033d2b6ad5117780eee964a36471f6cd15d8ac9c9e330ebfb3521a5c7846de1f04362b7acd0b66939b9209c43d41d1dd028987ecc97d4df5c66c3e5f5c46ee1511351b19ef1052a4fad30ff5103792d237e1a88eec2254d3f9057c155351a262
Plebs be plebs. No money, no access.
Guessing you cant math... 1137 is way too low.

提示された暗号文字列をそのまま入れると、amountの値が1137になっていることがわかる。
コードを読むと、暗号の概要は以下の通りとわかる。

plaintextは以下の形式
{"CCCCCC": "TTTTTT", "EEEEEE": "FLAG{DDDDDD}", "RRRRRR": "EEEEEE", "DDDDDD": "AAAAAA"}

enc_xor_cbc(plaintext, key, iv):暗号化16進数文字列を表示
plaintextを16の倍数になるようパディング(16の倍数の場合は16バイトパディング)

16バイトごとに以下の処理
1ブロック目暗号:1ブロック目平文 ^ key ^ IV
2ブロック目暗号:2ブロック目平文 ^ key ^ 1ブロック目暗号
3ブロック目暗号:3ブロック目平文 ^ key ^ 2ブロック目暗号
          :

このことから以下のようにして平文がわかるはず。

3ブロック目平文:3ブロック目暗号 ^ key ^ 2ブロック目暗号
2ブロック目平文:2ブロック目暗号 ^ key ^ 1ブロック目暗号
1ブロック目平文:1ブロック目暗号 ^ key ^ IV

11ブロック目暗号 ^ key ^ 10ブロック目暗号 = 11ブロック目平文

パディングの長さを1~16で総当たり、さらに部分的に復号された値と平文の一部を頭に入れながら、復号する。最終的には以下のコードでフラグが得られた。

import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('159.203.38.169', 5682))

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

c_hex = data.strip()
c_list = []
for i in range(0, len(c_hex), 32):
    c_list.append(c_hex[i:i+32].decode('hex'))

for pad_length in range(16, 0, -1):
    p10_tail = '"}' + chr(pad_length) * pad_length
    block_chk_size = len(p10_tail)
    if block_chk_size > 16:
        block_chk_size = 16
    c09_tail = c_list[9][-block_chk_size:]
    c10_tail = c_list[10][-block_chk_size:]

    key = ''
    for i in range(block_chk_size):
        c09_char = c09_tail[-block_chk_size+i]
        c10_char = c10_tail[-block_chk_size+i]
        p10_char = p10_tail[-block_chk_size+i]
        code = ord(c09_char) ^ ord(c10_char) ^ ord(p10_char)
        key += chr(code)

    plain_list = []
    error = False
    for i in range(9):
        c_pre_tail = c_list[8-i][-block_chk_size:]
        c_cur_tail = c_list[9-i][-block_chk_size:]
        plain_tail = ''
        for j in range(block_chk_size):
            c_pre_char = c_pre_tail[-block_chk_size+j]
            c_cur_char = c_cur_tail[-block_chk_size+j]
            key_char = key[-block_chk_size+j]
            code = ord(c_pre_char) ^ ord(c_cur_char) ^ ord(key_char)
            if code < 32 or code > 126:
                error = True
                break
            else:
                plain_tail += chr(code)
        if error:
            break
        plain_list.append(plain_tail)

    if error == False:
        print plain_list

# 6th block
c_pre_char = c_list[4][12]
c_cur_char = c_list[5][12]
plain_char = '"'
key1_char = chr(ord(c_pre_char) ^ ord(c_cur_char) ^ ord(plain_char))

# 7th block
c_pre_char = c_list[5][:12]
c_cur_char = c_list[6][:12]
plain_char = 'unt": 1137, '
key2_char = ''
for i in range(len(c_cur_char)):
    key2_char += chr(ord(c_pre_char[i]) ^ ord(c_cur_char[i]) ^ ord(plain_char[i]))

key = key2_char + key1_char + key

# Decrypt the Later Parts
plain_tail = ''
for i in range(10):
    plain_part = ''
    c_pre = c_list[9 - i]
    c_cur = c_list[10 - i]
    for j in range(16):
        code = ord(c_pre[j]) ^ ord(c_cur[j]) ^ ord(key[j])
        plain_part += chr(code)
    plain_tail = plain_part + plain_tail

print plain_tail
FLAG{CBse4_Butch3reD_Cryp7o!WoW_R3cur51vE_@Cr0NymZ!?}

Rich 1 (Crypto 150)

Rich 2の続き。amountを1337にして、そのブロック以降を暗号化して投入する。

import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('159.203.38.169', 5682))

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

c_hex = data.strip()
c_list = []
for i in range(0, len(c_hex), 32):
    c_list.append(c_hex[i:i+32].decode('hex'))

for pad_length in range(16, 0, -1):
    p10_tail = '"}' + chr(pad_length) * pad_length
    block_chk_size = len(p10_tail)
    if block_chk_size > 16:
        block_chk_size = 16
    c09_tail = c_list[9][-block_chk_size:]
    c10_tail = c_list[10][-block_chk_size:]

    key = ''
    for i in range(block_chk_size):
        c09_char = c09_tail[-block_chk_size+i]
        c10_char = c10_tail[-block_chk_size+i]
        p10_char = p10_tail[-block_chk_size+i]
        code = ord(c09_char) ^ ord(c10_char) ^ ord(p10_char)
        key += chr(code)

    plain_list = []
    error = False
    for i in range(9):
        c_pre_tail = c_list[8-i][-block_chk_size:]
        c_cur_tail = c_list[9-i][-block_chk_size:]
        plain_tail = ''
        for j in range(block_chk_size):
            c_pre_char = c_pre_tail[-block_chk_size+j]
            c_cur_char = c_cur_tail[-block_chk_size+j]
            key_char = key[-block_chk_size+j]
            code = ord(c_pre_char) ^ ord(c_cur_char) ^ ord(key_char)
            if code < 32 or code > 126:
                error = True
                break
            else:
                plain_tail += chr(code)
        if error:
            break
        plain_list.append(plain_tail)

    if error == False:
        print plain_list

# 6th block
c_pre_char = c_list[4][12]
c_cur_char = c_list[5][12]
plain_char = '"'
key1_char = chr(ord(c_pre_char) ^ ord(c_cur_char) ^ ord(plain_char))

# 7th block
c_pre_char = c_list[5][:12]
c_cur_char = c_list[6][:12]
plain_char = 'unt": 1137, '
key2_char = ''
for i in range(len(c_cur_char)):
    key2_char += chr(ord(c_pre_char[i]) ^ ord(c_cur_char[i]) ^ ord(plain_char[i]))

key = key2_char + key1_char + key

# Decrypt the Later Parts
plain_tail = []
for i in range(10):
    plain_part = ''
    c_pre = c_list[9 - i]
    c_cur = c_list[10 - i]
    for j in range(16):
        code = ord(c_pre[j]) ^ ord(c_cur[j]) ^ ord(key[j])
        plain_part += chr(code)
    plain_tail.append(plain_part)

print plain_tail

# Encrypt "amount": 1337
mod_c_list = []
for i in range(5):
    p_part = plain_tail[4-i]
    if i == 0:
        p_part = plain_tail[4-i][:7] + '3' + plain_tail[4-i][8:]
        c_pre = c_list[5+i]
    else:
        c_pre = mod_c_list[-1]
    c_part = ''
    for j in range(16):
        code = ord(p_part[j]) ^ ord(c_pre[j]) ^ ord(key[j])
        c_part += chr(code)
    mod_c_list.append(c_part)

new_c_hex = ''
for i in range(6):
    new_c_hex += c_list[i].encode('hex')

for i in range(5):
    new_c_hex += mod_c_list[i].encode('hex')

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

data = recvuntil(s, "\n")
print data
data = recvuntil(s, "\n")
print data
FLAG{nVm_74BLeZ_FL1P_B!Ts=O}

Kaspersky Industrial CTF Quals 2017 Writeup

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

Security Home Cameras (Crypto 300)

16バイトごとのXORになっていると推定して、復号する。

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

PNG_HEAD = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
    0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52]

key = []
for i in range(len(PNG_HEAD)):
    key.append(ord(data[i]) ^ PNG_HEAD[i])

flag = ''
for i in range(len(data)):
    code = ord(data[i]) ^ key[i%len(key)]
    flag += chr(code)

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

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

KLCTF{it_was_just_atbash_encryption}

DefCamp CTF Qualification 2017 Writeup

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

HEX Warm Up (Junior 1)

コードを見ながら、lock.isoをバイナリエディタで見ると、0x70バイト目から0x22Dバイト目までがbackup.zipであることがわかる。このzipを展開すると、index.txtに次のメッセージが書いてあった。

Hi man!

This is your flag!

DCTF{474dac08d29d013515a312d1a8460050634f9b3cb6a696a4c73652d1802a1872}

You could do that as:
buf = ""
g = open("lock.iso", "r")
buf += g.read()
print buf
buf = buf.encode('hex')
print buf
n = 7 * 32
buf = buf[n:]
n = 31 * 32
n = n + 8
buf = buf[:n]
print buf
buf = buf.decode('hex')
print buf
f = open("sol.zip", "w")
f.write(buf)
f.close()
g.close()

Or my simple you could change the extension.
DCTF{474dac08d29d013515a312d1a8460050634f9b3cb6a696a4c73652d1802a1872}

Loyal book (Junior 2)

各ファイルの差分をつなげていく。

$ diff 0001.txt 0002.txt
2217c2217
< glimpse last summer at the Palais-Royal. Some of 
---
> glimpse last summer DC at the Palais-Royal. Some of 

$ diff 0001.txt 0003.txt
3745c3745
< benches ranged along the walls, and in the centre of 
---
> benches ranged along TFthe walls, and in the centre of

$ diff 0001.txt 0004.txt
27559c27559
< And it must have been very strong to endure after 
---
> And it must h{ave been very strong to endure after

$ diff 0001.txt 0005.txt
27559c27559
< And it must have been very strong to endure after 
---
> And it must have been 7ba61 very strong to endure after

$ diff 0001.txt 0006.txt
5933c5933
< and Frederick, feeling sleepy, was in no great haste 
---
> and Frederick, feeling sleepy, wa0cc5ds in no great haste

$ diff 0001.txt 0007.txt
18816c18816
< communicated to him ; and he had only two phrases : 
---
> communicated to him ; aa3966nd he had only two phrases :

$ diff 0001.txt 0008.txt
25872c25872
< derstand. She longed for wealth, in order to crush 
---
> derstand. She longed fob7c64r wealth, in order to crush

$ diff 0001.txt 0009.txt
24835c24835
< They all took advantage of the occasion to denounce 
---
> They all took advantage a81c3 of the occasion to denounce

$ diff 0001.txt 0010.txt
749c749
< " Ha ! your chum ! " said Madame Moreau, with a 
---
> " Ha ! your chum ! " said Madame Morcfcdbeau, with a 
769c769
< barely maintained him. Made bitter by continuous 
---
> barely maintained him. 1b1d0 Made bitter by continuous 
3854c3854
< 82 GUSTAVE FLAUBERT 
---
> 82 GUSTAVE FLAUBERT 9e3de
7419c7419
< and the 'longshore-woman exclaimed : 
---
> and the 'longshore-woman exclaimed : 5ad11
9279c9279
< The advocate went on : 
---
> The advocate 89268 went on : 
11727c11727
< ceive either. 
---
> ceive either. bf0e6
14277c14277
< places at which the principals in the duel were to 
---
> places at 18ff7 which the principals in the duel were to 
20565c20565
< deur. 
---
> deur. 1f08}
DCTF{7ba610cc5da3966b7c64a81c3cfcdb1b1d09e3de5ad1189268bf0e618ff71f08}

Forgot my Key (Misc 400)

messageの一部のkeyの部分に注目して計算し、すべて16進数表記の文字になる鍵を探し、復号する。

$encrypted[72] = (($key[0] + $key[7] + $encrypted[71]) % 126)
$encrypted[73] = (($key[1] + $key[8] + $encrypted[72]) % 126)
$encrypted[74] = (($key[2] + $key[9] + $encrypted[73]) % 126)
    :
$encrypted[101] = (($key[29] + $key[4] + $encrypted[100] ) % 126)
$encrypted[102] = (($key[30] + $key[5] + $encrypted[101] ) % 126)
$encrypted[103] = (($key[31] + $key[6] + $encrypted[102] ) % 126)
import string

def decrypt(enc, key):
    dec = ''
    for i in range(1, len(enc)/2):
        dec += chr((int(enc[i*2:(i+1)*2], 16)
               - ord(key[(i-1)%len(key)])
               - int(enc[(i-1)*2:i*2], 16)) % 126)
    return dec

e = '5616f5962674d26741d2810600a6c5647620c4e3d2870177f09716b2379012c342d3b584c5672195d653722443f1c39254360007010381b721c741a532b03504d2849382d375c0d6806251a2946335a67365020100f160f17640c6a05583f49645d3b557856221b2'

enc = ''
for i in range(0, len(e), 2):
    e_part = e[i:i+2]
    enc += e_part[::-1]

def decrypt(enc, key):
    dec = ''
    for i in range(1, len(enc)/2):
        dec += chr((int(enc[i*2:(i+1)*2], 16)
               - ord(key[(i-1)%len(key)])
               - int(enc[(i-1)*2:i*2], 16)) % 126)
    return dec

key_str = ''
for h in string.hexdigits:
    key = [0] * 32
    key[0] = ord(h)
    for i in range(32):
        ecode1 = int(enc[(72+((i*7)%32))*2:(73+((i*7)%32))*2], 16)
        ecode0 = int(enc[(71+((i*7)%32))*2:(72+((i*7)%32))*2], 16)
        key[((i+1)*7)%32] = (ecode1 - key[(i*7)%32] - ecode0) % 126
    success = True
    for i in range(32):
        if chr(key[i]) not in string.hexdigits:
            success = False
            break
    if success:
        for i in range(len(key)):
            key_str += chr(key[i])

flag = decrypt(enc, key_str)
print flag
DCTF{0d940de38493d96dc6255cbb2c2ac7a2db1a7792c74859e95215caa6b57c69b2}

Survey(Junior 3)

アンケートに答えたら、フラグが表示された。

DCTF{a6a2729cbf6bcadce577a31f7f76201d5ce63c57d6c53318000d67714bb354ef}

BackdoorCTF 2017 Writeup

この大会は2017/9/24 0:30(JST)~2017/9/25 0:30(JST)に開催されました。
今回もチームで参戦。結果は全問制覇の3600点で、212チーム中6位でした。
自分で解けた問題をWriteupとして書いておきます。

IMAGEREV (200)

RGBの値をASCIIコードとして結合(3バイト)。それに対して、暗号化してその結果、64バイトになる。暗号化処理の内容は復号するコードにすることがかなり難しい。
そこでencrypted.txtを見ると、64バイトごとに同じようなデータが多数あるので、RとGとBの値はすべて同じであると推定して、256パターンの総当たりで復元する。
またピクセルの数はデータの長さから7371。素因数分解し、その組み合わせの積を考慮して、画像の幅と高さをいろいろと試してみる。

7371 = 3^4 * 7 * 13

結果は幅が351の時にフラグが書かれた画像に復元することができた。そのときのコードは次の通り。

from PIL import Image

def bin_return(dec):
    return(str(format(dec,'b')))

def bin_8bit(dec):
    return(str(format(dec,'08b')))

def convert_32bit(dec):
    return(str(format(dec,'032b')))

def convert_64bit(dec):
    return(str(format(dec,'064b')))

def hex_return(dec):
    return expand(hex(dec).replace('0x','').replace('L',''))

def dec_return_bin(bin_string):
    return(int(bin_string,2))

def dec_return_hex(hex_string):
    return(int(hex_string,16))

def some_LP(l,n):
    l1=[]
    j=0
    k=n
    while k<len(l)+1:
        l1.append(l[j:k])
        j=k
        k+=n 
    return(l1)

def rotate_right(bit_string,n):
    bit_list = list(bit_string)
    count=0
    while count <= n-1:
        list_main=list(bit_list)
        var_0=list_main.pop(-1)
        list_main=list([var_0]+list_main)
        bit_list=list(list_main)
        count+=1
    return(''.join(list_main))

def shift_right(bit_string,n):
    bit_list=list(bit_string)
    count=0
    while count <= n-1:
        bit_list.pop(-1)
        count+=1
    front_append=['0']*n
    return(''.join(front_append+bit_list))

def addition(input_set):
    value=0
    for i in range(len(input_set)):
        value+=input_set[i]
    mod_32 = 4294967296
    return(value%mod_32)

def str_xor(s1,s2):
    return ''.join([str(int(i)^int(j)) for i,j in zip(s1,s2)])

def str_and(s1,s2):
    return ''.join([str(int(i)&int(j)) for i,j in zip(s1,s2)])

def str_not(s):
    return ''.join([str(int(i)^1) for i in s])

def not_and_and_xor(x,y,z):
    return(str_xor(str_and(x,y),str_and(str_not(x),z)))

def and_and_and_xor_xor(x,y,z):
    return(str_xor(str_xor(str_and(x,y),str_and(x,z)),str_and(y,z)))

def some_e0(x):
    return(str_xor(str_xor(rotate_right(x,2),rotate_right(x,13)),rotate_right(x,22)))

def some_e1(x):
    return(str_xor(str_xor(rotate_right(x,6),rotate_right(x,11)),rotate_right(x,25)))

def some_s0(x):
    return(str_xor(str_xor(rotate_right(x,7),rotate_right(x,18)),shift_right(x,3)))

def some_s1(x):
    return(str_xor(str_xor(rotate_right(x,17),rotate_right(x,19)),shift_right(x,10)))

def expand(s):
        return '0'*(8-len(s))+s

def get_pixels_list(filename):
    im = Image.open(filename)
    return list(im.getdata())

def data_encrypted(list_of_pixels):
        data = ''
        for i in list_of_pixels:
                d = ''.join([chr(j) for j in i])
                d = encryption(d)
                data += ''.join(d)
                print len(data)
        return data

def message_pad(bit_list):
    pad_one = bit_list + '1'
    pad_len = len(pad_one)
    k=0
    while ((pad_len+k)-448)%512 != 0:
        k+=1
    back_append_0 = '0'*k
    back_append_1 = convert_64bit(len(bit_list))
    return(pad_one+back_append_0+back_append_1)

def message_bit_return(string_input):
    bit_list=[]
    for i in range(len(string_input)):
        bit_list.append(bin_8bit(ord(string_input[i])))
    return(''.join(bit_list))

def message_pre_pro(input_string):
    bit_main = message_bit_return(input_string)
    return(message_pad(bit_main))

def message_parsing(input_string):
    return(some_LP(message_pre_pro(input_string),32))

def message_schedule(index,w_t):
    new_word = convert_32bit(addition([int(some_s1(w_t[index-2]),2),int(w_t[index-7],2),int(some_s0(w_t[index-15]),2),int(w_t[index-16],2)]))
    return(new_word)

initial=['6a09e667','bb67ae85','3c6ef372','a54ff53a','510e527f','9b05688c','1f83d9ab','5be0cd19']

values=['428a2f98','71374491','b5c0fbcf','e9b5dba5','3956c25b','59f111f1','923f82a4','ab1c5ed5','d807aa98','12835b01','243185be','550c7dc3','72be5d74','80deb1fe','9bdc06a7','c19bf174','e49b69c1','efbe4786','0fc19dc6','240ca1cc','2de92c6f','4a7484aa','5cb0a9dc','76f988da','983e5152','a831c66d','b00327c8','bf597fc7','c6e00bf3','d5a79147','06ca6351','14292967','27b70a85','2e1b2138','4d2c6dfc','53380d13','650a7354','766a0abb','81c2c92e','92722c85','a2bfe8a1','a81a664b','c24b8b70','c76c51a3','d192e819','d6990624','f40e3585','106aa070','19a4c116','1e376c08','2748774c','34b0bcb5','391c0cb3','4ed8aa4a','5b9cca4f','682e6ff3','748f82ee','78a5636f','84c87814','8cc70208','90befffa','a4506ceb','bef9a3f7','c67178f2']

def encryption(input_string):
    w_t=message_parsing(input_string)
    a=convert_32bit(dec_return_hex(initial[0]))
    b=convert_32bit(dec_return_hex(initial[1]))
    c=convert_32bit(dec_return_hex(initial[2]))
    d=convert_32bit(dec_return_hex(initial[3]))
    e=convert_32bit(dec_return_hex(initial[4]))
    f=convert_32bit(dec_return_hex(initial[5]))
    g=convert_32bit(dec_return_hex(initial[6]))
    h=convert_32bit(dec_return_hex(initial[7]))
    for i in range(0,64):
        if i <= 15:
            t_1=addition([int(h,2),int(some_e1(e),2),int(not_and_and_xor(e,f,g),2),int(values[i],16),int(w_t[i],2)])
            t_2=addition([int(some_e0(a),2),int(and_and_and_xor_xor(a,b,c),2)])
            h=g
            g=f
            f=e
            e=addition([int(d,2),t_1])
            d=c
            c=b
            b=a 
            a=addition([t_1,t_2])
            a=convert_32bit(a)
            e=convert_32bit(e)
        if i > 15:
            w_t.append(message_schedule(i,w_t))
            t_1=addition([int(h,2),int(some_e1(e),2),int(not_and_and_xor(e,f,g),2),int(values[i],16),int(w_t[i],2)])
            t_2=addition([int(some_e0(a),2),int(and_and_and_xor_xor(a,b,c),2)])
            h=g
            g=f
            f=e
            e=addition([int(d,2),t_1])
            d=c
            c=b
            b=a 
            a=addition([t_1,t_2])
            a=convert_32bit(a)
            e=convert_32bit(e)
    value_0 = addition([dec_return_hex(initial[0]),int(a,2)])
    value_1 = addition([dec_return_hex(initial[1]),int(b,2)])
    value_2 = addition([dec_return_hex(initial[2]),int(c,2)])
    value_3 = addition([dec_return_hex(initial[3]),int(d,2)])
    value_4 = addition([dec_return_hex(initial[4]),int(e,2)])
    value_5 = addition([dec_return_hex(initial[5]),int(f,2)])
    value_6 = addition([dec_return_hex(initial[6]),int(g,2)])
    value_7 = addition([dec_return_hex(initial[7]),int(h,2)])
    value = (hex_return(value_0),hex_return(value_1),hex_return(value_2),hex_return(value_3),hex_return(value_4),hex_return(value_5),hex_return(value_6),hex_return(value_7))
    return(value)

def get_string(d):
    remain = d
    s = ''
    for i in range(3):
        code = remain / (256 ** (2 - i))
        s += chr(code)
        remain = remain % (256 ** (2 - i))
    return s

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

e = []
for i in range(256):
    val = i + i*256 + i*256*256
    s = get_string(val)
    e.append(''.join(encryption(s)))

width = 351
height = 7371 / width
img = Image.new('RGB', (width, height), (255, 255, 255))

for i in range(0, len(data), 64):
    pixel_data = data[i:i+64]
    color_code = e.index(pixel_data)
    x = (i / 64) % width
    y = (i / 64) / width
    img.putpixel((x, y), (color_code, color_code, color_code))

img.save('flag.png')

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

$ echo -n 'CTF{d4mm17_y0u_f0und_my_h1dd3n_5h4_h45h}' | sha256sum
d66f0a384abd48764b5caecb3e204c88746661c3d7f25471d79ac265c59c9085  -
d66f0a384abd48764b5caecb3e204c88746661c3d7f25471d79ac265c59c9085

COMPLEX-RSA (200)

公開鍵が2つと暗号ファイルが渡される。2回別の鍵で暗号化しているので、それを復号する問題。

$ openssl rsa -pubin -text < pubkey1
Public-Key: (1026 bit)
Modulus:
    03:15:4c:a2:cd:44:39:ad:97:e0:38:7a:51:4c:97:
    bf:21:0c:f7:5f:3b:cf:26:25:94:68:3b:b5:3c:e6:
    99:48:12:df:6a:19:55:c4:4f:9f:12:e9:9c:2a:69:
    8c:b9:7f:0e:47:e8:e8:d1:48:a7:f3:47:2d:2b:4d:
    e2:54:10:ad:f2:1e:49:8b:b9:b4:66:5a:0c:66:d8:
    d2:66:ff:20:63:7e:ac:f2:71:8d:9a:59:c1:c4:a3:
    cf:02:2f:0b:f2:8d:53:89:d5:86:41:f3:dd:99:4e:
    f2:ff:fa:4b:70:d3:d3:98:e9:48:42:00:f8:77:93:
    06:10:13:a8:ed:07:2c:d7:7f
Exponent: 37507589401 (0x8bba06519)
writing RSA key
-----BEGIN PUBLIC KEY-----
MIGhMA0GCSqGSIb3DQEBAQUAA4GPADCBiwKBgQMVTKLNRDmtl+A4elFMl78hDPdf
O88mJZRoO7U85plIEt9qGVXET58S6ZwqaYy5fw5H6OjRSKfzRy0rTeJUEK3yHkmL
ubRmWgxm2NJm/yBjfqzycY2aWcHEo88CLwvyjVOJ1YZB892ZTvL/+ktw09OY6UhC
APh3kwYQE6jtByzXfwIFCLugZRk=
-----END PUBLIC KEY-----
yoshinori@yoshinori-virtual-machine:/mnt/hgfs/Shared$ openssl rsa -pubin -text < pubkey2
Public-Key: (1026 bit)
Modulus:
    03:15:4c:a2:cd:44:39:ad:97:e0:38:7a:51:4c:97:
    bf:21:0c:f7:5f:3b:cf:26:25:94:68:3b:b5:3c:e6:
    99:48:12:df:6a:19:55:c4:4f:9f:12:e9:9c:2a:69:
    8c:b9:7f:0e:47:e8:e8:d1:48:a7:f3:47:2d:2b:4d:
    e2:54:10:ad:f2:1e:49:8b:b9:b4:66:5a:0c:66:d8:
    d2:66:ff:20:63:7e:ac:f2:71:8d:9a:59:c1:c4:a3:
    cf:02:2f:0b:f2:8d:53:89:d5:86:41:f3:dd:99:4e:
    f2:ff:fa:4b:70:d3:d3:98:e9:48:42:00:f8:77:93:
    06:10:13:a8:ed:07:2c:d7:7f
Exponent:
    1a:1b:48:c3:a6:ce:4a:6a:f6:e3:cb:78:c5:5f:ac:
    7f:59:29:6f:41:bb:11:e5:8a:30:1f:b8:d2:f3:c4:
    7e:bb:15:fa:3e:68:e9:f7:7f:8c:52:39:3b:e2:22:
    17:5d:e4:a7:bc:55:d9:9d:51:80:1b:5a:1d:be:72:
    dd:6e:ff:ae:52:39:f3:a4:48:d3:e9:ef:5a:a1:c9:
    54:5e:1a:38:49:18:9b:e6:3c:49:fe:3b:94:92:54:
    d4:20:d9:81:76:77:a7:db:4a:d1:f3:f5:df:99:b3:
    25:7d:d6:04:c5:72:ce:e2:5e:43:c2:69:43:cf:d1:
    64:91:80:3b
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBGjANBgkqhkiG9w0BAQEFAAOCAQcAMIIBAgKBgQMVTKLNRDmtl+A4elFMl78h
DPdfO88mJZRoO7U85plIEt9qGVXET58S6ZwqaYy5fw5H6OjRSKfzRy0rTeJUEK3y
HkmLubRmWgxm2NJm/yBjfqzycY2aWcHEo88CLwvyjVOJ1YZB892ZTvL/+ktw09OY
6UhCAPh3kwYQE6jtByzXfwJ8GhtIw6bOSmr248t4xV+sf1kpb0G7EeWKMB+40vPE
frsV+j5o6fd/jFI5O+IiF13kp7xV2Z1RgBtaHb5y3W7/rlI586RI0+nvWqHJVF4a
OEkYm+Y8Sf47lJJU1CDZgXZ3p9tK0fP135mzJX3WBMVyzuJeQ8JpQ8/RZJGAOw==
-----END PUBLIC KEY-----

nの値が同じで、eの値はそれぞれ次の通り。

n = 0x03154ca2cd4439ad97e0387a514c97bf210cf75f3bcf262594683bb53ce6994812df6a1955c44f9f12e99c2a698cb97f0e47e8e8d148a7f3472d2b4de25410adf21e498bb9b4665a0c66d8d266ff20637eacf2718d9a59c1c4a3cf022f0bf28d5389d58641f3dd994ef2fffa4b70d3d398e9484200f87793061013a8ed072cd77f
e1 = 0x8bba06519
e2 = 0x1a1b48c3a6ce4a6af6e3cb78c55fac7f59296f41bb11e58a301fb8d2f3c47ebb15fa3e68e9f77f8c52393be222175de4a7bc55d99d51801b5a1dbe72dd6effae5239f3a448d3e9ef5aa1c9545e1a3849189be63c49fe3b949254d420d9817677a7db4ad1f3f5df99b3257dd604c572cee25e43c26943cfd16491803b
c1 = pow(m, e1, n)
c = pow(c1, e2, n)
→ c = pow(m, e1*e2, n)

Wiener's attackで復号する。

from fractions import Fraction

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, r = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
        gcd = b
    return gcd, x, y

def decrypt(p, q, e, c):
    n = p * q
    phi = (p - 1) * (q - 1)
    gcd, a, b = egcd(e, phi)
    d = a
    pt = pow(c, d, n)
    return hex(pt)[2:-1].decode('hex')

def continued_fractions(n,e):
    cf = [0]
    while e != 0:
        cf.append(int(n/e))
        N = n
        n = e
        e = N%e
    return cf

def calcKD(cf):
    kd = list()
    for i in range(1,len(cf)+1):
        tmp = Fraction(0)
        for j in cf[1:i][::-1]:
            tmp = 1/(tmp+j)
        kd.append((tmp.numerator,tmp.denominator))
    return kd

def int_sqrt(n):
    def f(prev):
        while True:
            m = (prev + n/prev)/2
            if m >= prev:
                return prev
            prev = m
    return f(n)

def calcPQ(a,b):
    if a*a < 4*b or a < 0:
        return None
    c = int_sqrt(a*a-4*b)
    p = (a + c) /2
    q = (a - c) /2
    if p + q == a and p * q == b:
        return (p,q)
    else:
        return None

def wiener(n,e):
    kd = calcKD(continued_fractions(n,e))
    for (k,d) in kd:
        if k == 0:
            continue
        if (e*d-1) % k != 0:
            continue
        phin = (e*d-1) / k
        if phin >= n:
            continue
        ans = calcPQ(n-phin+1,n)
        if ans is None:
            continue
        return (ans[0],ans[1])

n = 0x03154ca2cd4439ad97e0387a514c97bf210cf75f3bcf262594683bb53ce6994812df6a1955c44f9f12e99c2a698cb97f0e47e8e8d148a7f3472d2b4de25410adf21e498bb9b4665a0c66d8d266ff20637eacf2718d9a59c1c4a3cf022f0bf28d5389d58641f3dd994ef2fffa4b70d3d398e9484200f87793061013a8ed072cd77f
e1 = 0x8bba06519
e2 = 0x1a1b48c3a6ce4a6af6e3cb78c55fac7f59296f41bb11e58a301fb8d2f3c47ebb15fa3e68e9f77f8c52393be222175de4a7bc55d99d51801b5a1dbe72dd6effae5239f3a448d3e9ef5aa1c9545e1a3849189be63c49fe3b949254d420d9817677a7db4ad1f3f5df99b3257dd604c572cee25e43c26943cfd16491803b

e = e1 * e2
p, q = wiener(n, e)

with open('flag.enc', 'rb') as f:
    c = int(f.read().encode('hex'), 16)

flag = decrypt(p, q, e, c)
print flag

実行結果は CTF{c0n6r47zzz_y0u_f0und_0ur_h1dd3n_w13n3r!!}

$ echo -n 'CTF{c0n6r47zzz_y0u_f0und_0ur_h1dd3n_w13n3r!!}' | sha256sum
c0081b21d2c1d3f581914be8bc4ce98158b9eafbcf68dda83f6eddae99d4b1e2  -
c0081b21d2c1d3f581914be8bc4ce98158b9eafbcf68dda83f6eddae99d4b1e2

STEREOTYPES (300)

$ openssl rsa -pubin -text < pubkey
Public-Key: (2048 bit)
Modulus:
    00:87:ae:91:10:c6:f2:df:9a:d5:a7:47:c9:c2:5b:
    60:57:6a:c2:bf:17:76:00:2d:4e:5d:ba:17:72:ae:
    a0:b8:70:34:94:29:9c:f8:51:38:be:84:bf:2d:9f:
    4f:88:79:5e:9c:cd:63:45:4f:df:55:32:57:da:09:
    5a:0d:ea:cd:44:6f:d9:a9:4d:90:e4:06:d3:86:1c:
    c7:4e:cb:5c:a1:1c:6e:ce:b3:fd:0b:13:bc:23:26:
    7f:9f:14:ef:20:19:3e:61:02:70:3c:c3:b3:fd:f7:
    a7:f3:e7:20:3b:9d:43:4e:c9:66:d9:04:ab:d8:0b:
    68:37:3f:fb:87:f3:b4:06:ff:53:af:8b:9f:85:48:
    dd:3a:da:63:4e:06:f0:87:ed:4a:d5:98:cd:a1:75:
    20:02:45:e0:b5:ec:d4:5e:83:11:60:ef:7e:c0:b9:
    32:7a:28:18:70:97:1e:c7:0d:63:2e:ed:79:a2:1d:
    a6:35:18:77:b9:46:dd:54:b2:eb:a1:6d:ad:7b:f5:
    d2:11:73:d1:5b:22:3a:4c:58:12:c6:8c:e9:41:4f:
    3f:ba:bf:03:ee:d2:8f:fc:65:64:6d:4f:32:81:cc:
    6a:fe:8d:21:c1:45:27:ef:50:dd:0d:50:d6:58:99:
    69:5b:53:c6:b6:37:4c:ec:05:e3:81:53:ca:65:e8:
    73:f1
Exponent: 7 (0x7)
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAh66REMby35rVp0fJwltg
V2rCvxd2AC1OXboXcq6guHA0lCmc+FE4voS/LZ9PiHlenM1jRU/fVTJX2glaDerN
RG/ZqU2Q5AbThhzHTstcoRxuzrP9CxO8IyZ/nxTvIBk+YQJwPMOz/fen8+cgO51D
Tslm2QSr2AtoNz/7h/O0Bv9Tr4ufhUjdOtpjTgbwh+1K1ZjNoXUgAkXgtezUXoMR
YO9+wLkyeigYcJcexw1jLu15oh2mNRh3uUbdVLLroW2te/XSEXPRWyI6TFgSxozp
QU8/ur8D7tKP/GVkbU8ygcxq/o0hwUUn71DdDVDWWJlpW1PGtjdM7AXjgVPKZehz
8QIBBw==
-----END PUBLIC KEY-----
n = 0x0087ae9110c6f2df9ad5a747c9c25b60576ac2bf1776002d4e5dba1772aea0b8703494299cf85138be84bf2d9f4f88795e9ccd63454fdf553257da095a0deacd446fd9a94d90e406d3861cc74ecb5ca11c6eceb3fd0b13bc23267f9f14ef20193e6102703cc3b3fdf7a7f3e7203b9d434ec966d904abd80b68373ffb87f3b406ff53af8b9f8548dd3ada634e06f087ed4ad598cda175200245e0b5ecd45e831160ef7ec0b9327a281870971ec70d632eed79a21da6351877b946dd54b2eba16dad7bf5d21173d15b223a4c5812c68ce9414f3fbabf03eed28ffc65646d4f3281cc6afe8d21c14527ef50dd0d50d65899695b53c6b6374cec05e38153ca65e873f1
e = 7

平文は末尾以外わかっているので、Coppersmith's Attackを実行。

# coppersmith_attack.sage
n = 0x0087ae9110c6f2df9ad5a747c9c25b60576ac2bf1776002d4e5dba1772aea0b8703494299cf85138be84bf2d9f4f88795e9ccd63454fdf553257da095a0deacd446fd9a94d90e406d3861cc74ecb5ca11c6eceb3fd0b13bc23267f9f14ef20193e6102703cc3b3fdf7a7f3e7203b9d434ec966d904abd80b68373ffb87f3b406ff53af8b9f8548dd3ada634e06f087ed4ad598cda175200245e0b5ecd45e831160ef7ec0b9327a281870971ec70d632eed79a21da6351877b946dd54b2eba16dad7bf5d21173d15b223a4c5812c68ce9414f3fbabf03eed28ffc65646d4f3281cc6afe8d21c14527ef50dd0d50d65899695b53c6b6374cec05e38153ca65e873f1
e = 7

with open('ciphertext', 'rb') as f:
    c = int(f.read().encode('hex'), 16)

with open('plaintext', 'r') as f:
    m0 = f.read()

m0 = int(m0.replace('X', '\x00').encode('hex'), 16)

kbits = 15 * 8

PR.<x> = PolynomialRing(Zmod(n))
f = (m0 + x)^e - c

x0 = f.small_roots(X=2^kbits, beta=1)[0]
plaintext = ('%x' % (m0 + x0)).decode('hex')
print plaintext

復号結果は次の通り。

Welcome to BackdoorCTF17. Just one advice Never ignore the challenge names, they provides the maximum hint. Okay now let's leave the boring stuff apart i know you are eagerly waiting to gain some points So here it is : CTF{y0u_607_17}
$ echo -n 'CTF{y0u_607_17}' | sha256sum
229eab315f374015ad0e8a0374b6df89a2510538a305cbd2b88df81eb05349a6  -
229eab315f374015ad0e8a0374b6df89a2510538a305cbd2b88df81eb05349a6

Codefest CTF 2017 Writeup

この大会は2017/9/22 0:30(JST)~2017/9/23 0:30(JST)に開催されました。
今回もチームで参戦・・・でしたが、あまりにも問題サーバの反応が遅い。
問題も全問見ることすらできず、結果は500点で276チーム中103位でした。
自分で解けた問題をWriteupとして書いておきます。

Saudi Arabia - SimplyBlack (Steganography 50)

Stegsolveで開き、Red plane 0にするとフラグが表示された。
f:id:satou-y:20170926211851p:plain

flag{LETHAL}

Ukraine - Digging Deeper (Steganography 200)

バイナリエディタで見ると、zipのファイルにflag.txtが含まれているようだ。オフセット0x8F以降を切り出し、zipファイルとして保存する。
展開すると、flag.txtに以下のように書いてある。

666c61677b6b3333705f75705f793075725f7a6970703372357d
$ echo 666c61677b6b3333705f75705f793075725f7a6970703372357d | xxd -r -p
flag{k33p_up_y0ur_zipp3r5}
flag{k33p_up_y0ur_zipp3r5}

CSAW CTF Qualification Round 2017 Writeup

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

Super Difficult Recon (Recon 1)

問題に書かれているフラグが答え。

flag{f00led_uuuuuuu}

Another Xor (Crypto 100)

plaintext = msg + key + md5(msg + key).hexdigest()
cipher = plaintext ^ [key * (msgの長さ/keyの長さ + 1) (plaintextの長さまで)]

plaintextとkey(繰り返し)のXORになっている。plaintextのkeyが含まれている部分に注目して、キーの組み合わせを絞りながら総当たりする。

import itertools
import string

def xor(s1,s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2))

def repeat(s, l):
    return (s*(int(l/len(s))+1))[:l]

def chk_printchr(k):
    for i in range(len(k)):
        if k[i] < 32 or k[i] > 126:
            return False
    return True

with open('encrypted', 'rb') as f:
    enc = f.read().strip()

cipher = enc.decode('hex')
len_c = len(cipher)

key_list = []
for len_k in range(2, 105):
    for i in range(32, 127):
        try_key = [-1] * len_k
        try_key[0] = i

        index = 0
        base = len_c - 32 - len_k
        for j in range(len_k):
            if try_key[(base + index) % len_k] == -1:
                try_key[(base + index) % len_k] = ord(cipher[base+index]) ^ try_key[index]
                index = (base + index) % len_k
            else:
                if try_key[(base + index) % len_k] == ord(cipher[base+index]) ^ try_key[index]:
                    key = try_key
                    if chk_printchr(key):
                        key_list.append(key)
                    break
                else:
                    break

for key in key_list:
    key_str = ''
    for i in range(len(key)):
        key_str += chr(key[i])
    plaintext = xor(cipher, repeat(key_str, len(cipher)))

    ng = False
    for i in range(32):
        if plaintext[-32+i] not in string.hexdigits:
            ng = True
            break

    if ng == False:
        print 'key =', key_str
        print 'plain =', plaintext

実行結果は以下の通り。

key = A quart jar of oil mixed with zinc oxide makes a very bright paint|
plain = flag{sti11_us3_da_x0r_for_my_s3cratz}|A quart jar of oil mixed with zinc oxide makes a very bright paint|d5111350bbbe105121b9a9496ac08df2
flag{sti11_us3_da_x0r_for_my_s3cratz}

EKOPARTY CTF 2017 Writeup

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

Shopping (PWNING)

$ nc shopping.ctf.site 21111
Enter a raw string (max. 32 bytes) that meets the following condition: hex(sha1(input))[0:6] == 3e9fdf

まずこの問題を解く必要がある。その後、買い物のメニューに変わる。

Welcome to Ekoparty shopping Center
Feel free to buy up to 5 items that you may like
Use wisely your coins, have fun!

You have 50 coins
What do you wanna buy today?
1. t-shirt              10
2. short                20
3. stickers             1
4. flag                 ?
2
How many?
2

50コインではflagは購入できず。数値がマイナスの限界を超えて、プラスになることを考え、調整しながらコードを書く。

import socket
import re
import string
import hashlib
import itertools

pattern = '\[0:6\] == (.+)'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('shopping.ctf.site', 21111))

data = s.recv(256)
print data
m = re.search(pattern, data)
h_head = m.group(1)

for c in itertools.product(string.printable, repeat=4):
    text = ''.join(c)
    if hashlib.sha1(text).hexdigest()[0:6] == h_head:
        ans = text
        break

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

data = s.recv(2048)
print data
print '2'
s.sendall('2\n')
data = s.recv(2048)
print data
coin = (50 + 2**63) / 20 - 10000000
print coin
s.sendall(str(coin) + '\n')

data = s.recv(2048)
print data
print '4'
s.sendall('4\n')
data = s.recv(2048)
print data
print '1'
s.sendall('1\n')
data = s.recv(1024)
print data
data = s.recv(1024)
print data
EKO{d0_y0u_even_m4th?}

Shopwn (PWNING)

ncの接続先以外、Shoppingと同じコードで解けた。

EKO{dude_where_is_my_leak?}

EKOVM (VM)

$ ./ekovm
=[ Secure Flag Generator ]=
[+] Type your flag: 
[+] Securing your flag
[+] Flag secured:
052353120053246640010524440

毎回同じ値を入力しても出力結果は変わる。

■123の場合
044675504045476710046300114
007414450
  ↓
044675504
045476710
046300114
007414450

■111の場合
043705360043705360043705360
007245540
  ↓
043705360
043705360
043705360
007245540

■abcの場合
075203454075706730076412204
006241270
  ↓
075203454
075706730
076412204
006241270

■flagの場合
117354350124105520113442074
120173244007624530
  ↓
117354350
124105520
113442074
120173244
007624530

数字9桁が1文字に対応しているようだ。さらにASCIIコード順に大きさは並んでいるが、幅は一定ではない。あとは、末尾が4になっている場合は、ASCIIコードが奇数の文字に対応している。
以上の特徴を踏まえ、問題の数字をASCIIコード表と見比べながら、EKO{xxx}の形式になるよう割り当てていく。

064325164 E 
070762714 K
074006534 O
135340214 {
127270554 s
045162244 1
072374624 M
125051700 p
122026060 l
046574154 3
042135424 -
131507430 v
122633024 m
136752124 }
007461350
EKO{s1Mpl3-vm}

Malbolge (MISC)

$ nc malbolge.ctf.Send a malbolge code that print: 'Welcome to EKOPARTY!' (without single quotes)
Bye bye!!!

調べたら、malbolgeという難解プログラミング言語があるらしい。
http://zb3.me/malbolge-tools/#generator で Welcome to EKOPARTY! と表示するコード生成する。

D'`_M?![m}YFWVCBu@,+*/_L&8$kGF3}BAzRb}=_):[Zp6WVlqpih.ONjiha'H^]\[Z~^W\[ZYRvVOTSLp3INGFjJ,BAe?'=<;_?8=<;4X216543,P0p(-,+*#G'&f|{"y?}|ut:[qvutml2poQPlejc)gfedFb[!_X]V[ZSRvP8TSLpJ2NMLEDhU
$ nc malbolge.ctf.Send a malbolge code that print: 'Welcome to EKOPARTY!' (without single quotes)
D'`_M?![m}YFWVCBu@,+*/_L&8$kGF3}BAzRb}=_):[Zp6WVlqpih.ONjiha'H^]\[Z~^W\[ZYRvVOTSLp3INGFjJ,BAe?'=<;_?8=<;4X216543,P0p(-,+*#G'&f|{"y?}|ut:[qvutml2poQPlejc)gfedFb[!_X]V[ZSRvP8TSLpJ2NMLEDhU
Running code...
Welcome to EKOPARTY!
Your flag is: EKO{0nly4nother3soteric1anguage}
EKO{0nly4nother3soteric1anguage}