SECCON 2016 Online CTF Writeup

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

Vigenere (Crypto 100)

Vigenere暗号の問題。平文の最初7文字が"SECCON{"で鍵が12文字あることが分かっている。まず鍵の最初7文字を求める。

al_l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ{}'

c = 'LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ'
p = 'SECCON{'

key = ''

for i in range(len(p)):
    index = al_l.index(c[i]) - al_l.index(p[i])
    if index < 0:
        index += len(al_l)
    key += al_l[index]

print key

実行すると、鍵の最初7文字が"VIGENER"であることがわかる。次の文字は"E"であると想定して、4文字のブルートフォースで問題に記載してあるハッシュ値になるものを探す。

import hashlib
import itertools

al_l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ{}'

c = 'LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ'
k = 'VIGENERE'
k_size = 12

for s in itertools.product(al_l, repeat=4):
    key = k + ''.join(s)
    print key
    p = ''
    for i in range(len(c)):
        index = al_l.index(c[i]) - al_l.index(key[i%k_size])
        if index < 0:
            index += 28
        p += al_l[index]
    if p[-1:] == '}':
        if hashlib.md5(p).hexdigest() == 'f528a6ab914c1ecf856a1d93103948fe':
            print p
            break
SECCON{ABABABCDEDEFGHIJJKLMNOPQRSTTUVWXYYZ}

Juniors CTF 2016 Writeup

この大会は2016/11/26 0:00(JST)~2016/11/28 0:00(JST)に開催されました。
今回もチームで参戦。結果は1400点で666チーム中124位でした。
自分で解けた問題をWriteupとして書いておきます。

Black Suprematic Square (trivial web)

ソースを見ると、表示されない画像を読み込んでいる。

<img src = "http://i.imgur.com/YyHPw50.jpg" width=0>

f:id:satou-y:20161203211029j:plain
この文字列がフラグ。

aima0AiwahsidupaiToehoong1PhieruqueivahphieKah7uceetair9aiGae1eSsaedoo4becooShohhu8eifahXi7EJoh2gaephechei5chiP9

Find The Gobblewonker (trivial network admin web)

OpenVPNで接続する。問題のURLにアクセスすると、フラグが表示されていた。
f:id:satou-y:20161203211423p:plain

q8mvbp8sFNPpFj2dZkEkweNEFdBIuhVYkEr2brnj2qstJ3QbAtklx0imht8pdb2PafDIXs3Sxojl3m5q

Here goes! (trivial crypto recon)

図形の換字式暗号と考え、調べる。
http://yanazlatinneko.tumblr.com/post/119699418139/cadenthegrey-bills-symbol-substitution-cipher で対応表を見つけた。
f:id:satou-y:20161203211615p:plain

FIXPROBLEMQUICKLYWITHGALVANIZEDJETS

Southern Cross (crypto)

ヴィジュネル暗号。http://www.geocachingtoolbox.com/index.php?lang=en&page=vigenereCipherSolveで復号してみる。全文は長すぎて復号できないので、途中まで切って復号する。鍵はBOLIVARであることがわかった。コードを書いて全文を復号してみる。

import string

al_l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
key = 'BOLIVAR'
k_size = len(key)

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

count = 0
plain = ''
for i in range(len(data)):
    enc_c = data[i:i+1]
    if enc_c in string.uppercase:
        key_c = key[count%k_size:count%k_size+1]
        index = al_l.index(enc_c) - al_l.index(key_c)
        if index < 0:
            index += 26
        plain += al_l[index:index+1]
        count += 1
    else:
        plain += enc_c

print plain

復号した結果、ストーリーのタイトルはSelected Storyであるとわかったが、これはフラグではない。問題に最後の部分にフラグがありそうなことが書いてある。・・・ということで最後の文がフラグ。

BOLIVAR CANNOT CARRY DOUBLE

RC3 CTF 2016 Writeup

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

What's your virus? (Trivia 20)

「once clicked mailing list unbootable virus」で検索したら、次のページに記載されていた。
http://www.hongkiat.com/blog/famous-malicious-computer-viruses/

ILOVEYOU

Horse from Tinbucktu (Trivia 30)

「Trojan horse Windows browser key logging form grabbing」で検索したら、次のページに記載されていた。
http://www.hacking-tutorial.com/hacking-tutorial/remote-administration-tool-zeus-botnet-rat/#sthash.dQ56P6EO.dpbs

Zeus

Salad (Crypto 100)

先頭がRC3に変換されることを想定して。数字含みで20シフトして復号する。

letters = '0123456789abcdefghijklmnopqrstuvwxyz'
length = len(letters)

cipher = '7sj-ighm-742q3w4t'

flag = ''
for i in range(len(cipher)):
    c = cipher[i:i+1]
    if c == '-':
        flag += c
    else:
        index = letters.find(c) + 20
        if index >= length:
            index = index - length
        flag += letters[index:index+1]

print flag.upper()
RC3-2016-ROMANGOD

Who's a good boy (web 100)

読み込んでいるdoge.cssを見ると、一番最後にフラグが書いてある。

.philarydufflebag{
 
/*hiya*/
/*compress your frontend*/
/*here's a flag :)*/
flag:RC3-2016-CanineSS
}
RC3-2016-CanineSS

Some Pang (Forensics 50)

全パケットのdata部の後半24バイトに2バイトずつ同じデータが繰り返されている。送受信で同じデータなので、片方のパケットだけ2バイトずつつなげBase64デコードし、ファイルに保存する。

#!/usr/bin/env python
from scapy.all import *

packets = rdpcap('somepang.pcap')

data = ''
for p in packets:
    if p['IP'].src == '192.168.1.1':
        data += p['Raw'].load[8:10]

with open('flag.jpg', 'wb') as f:
    f.write(data.decode('base64'))

f:id:satou-y:20161201230102j:plain

RC3-2016-PANG-ME-LIKE-ONE-OF-YOUR-FRENCH-GORILLAZ

Graphic Design (Forensics 200)

オブジェクトファイルをテキストエディタで開くと先頭に「Blender v2.78 (sub 0) OBJ File」と書いてある。・・ということでBlenderでこのオブジェクトファイルを開く。内容はステゴザウルスの3D画像。ステゴザウルスを非表示にすると、フラグが隠れていた。
f:id:satou-y:20161201230203p:plain

RC3-2016-St3GG3rz

Dirty Birdy (Forensics 400)

FTK Imagerでイメージファイルを開いてみると、secretfilesというフォルダがあるので、その配下を見てみる。document.txtには「password123」と書かれているので、どこかで使うのかもしれない。Workbook1.xlsx.gpgがあるので、このファイルがあやしい。とりあえずエクスポートする。
さらに探すと、.gnupgフォルダにsecring.gpg(秘密鍵リング)があるので、エクスポートして、先ほどのファイルを復号するのに使う。

$ gpg --import ./secring.gpg
gpg: 鍵8FFDF6D6: 秘密鍵を読み込みました
gpg: /home/yoshinori/.gnupg/trustdb.gpg: 信用データベースができました
gpg: 鍵8FFDF6D6: 公開鍵“ThugG (lolz) <nope@gmail.com>”を読み込みました
gpg: 処理数の合計: 1
gpg:               読込み: 1  (RSA: 1)
gpg:       秘密鍵の読出し: 1
gpg:   秘密鍵の読込み: 1

$ gpg  Workbook1.xlsx.gpg
gpg: 1024-ビットRSA鍵, ID E22CB12D, 日付2016-11-18に暗号化されました
      “ThugG (lolz) <nope@gmail.com>”

これでWorkbook1.xlsxが復号された。このファイルを開こうとすると、パスワードがかかっている。ここで先ほどのパスワードを使って、このファイルを開く。開いたら、Sheet2のフォーカスされているセルに白字でフラグが書いてある。

RC3-2016-SNEAKY21

Qiwi-Infosec CTF-2016 Writeup

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

Reverse 100_2 (Reverse 100)

pycファイルが与えられているので、Easy Python Decompilerでデコンパイルする。

# Embedded file name: task.py
import marshal
src = 'YwAAAAADAAAAGAAAAEMAAABz7wAAAGQBAGoAAGcAAGQCAGQDAGQEAGQFAGQGAGQHAGQIAGQJAGQKAGQLAGQMAGQNAGQOAGQPAGQMAGcPAERdHAB9AAB0AQB0AgB8AACDAQBkEAAXgwEAXgIAcToAgwEAfQEAdAMAZBEAgwEAfQIAfAIAfAEAawIAcuYAZAEAagAAZwAAZBIAZBMAZBQAZBUAZBYAZBcAZBgAZBkAZBoAZBsAZBwAZB0AZB4AZAsAZBwAZB8AZAMAZB0AZAgAZB4AZCAAZCEAZxYARF0VAH0AAHwAAGoEAGQiAIMBAF4CAHHGAIMBAEdIbgUAZCMAR0hkAABTKCQAAABOdAAAAAB0AQAAAF50AQAAADR0AQAAAEt0AQAAAGl0AQAAAC50AQAAAC90AQAAAE50AQAAAGp0AQAAAFB0AQAAAG90AQAAAD90AQAAAGx0AQAAADJ0AQAAAFRpAwAAAHMJAAAAWW91IHBhc3M6dAEAAABzdAEAAAB5dAEAAABudAEAAAB0dAEAAAA6dAEAAAB7dAEAAAB3dAEAAABxdAEAAABFdAEAAAA2dAEAAABmdAEAAABYdAEAAAB1dAEAAABhdAEAAAAxdAEAAAB9dAUAAABST1QxM3MFAAAATm8gOigoBQAAAHQEAAAAam9pbnQDAAAAY2hydAMAAABvcmR0CQAAAHJhd19pbnB1dHQGAAAAZGVjb2RlKAMAAAB0AQAAAGV0AwAAAHRtcHQGAAAAcGFzc3dkKAAAAAAoAAAAAHMHAAAAdGFzay5weVIcAAAAAgAAAHMKAAAAAAFfAQwBDAFvAQ=='.decode('base64')
code = marshal.loads(src)
exec code

pycのヘッダを付けてauth.pycを作成する。

#!/usr/bin/env python
code = 'YwAAAAADAAAAGAAAAEMAAABz7wAAAGQBAGoAAGcAAGQCAGQDAGQEAGQFAGQGAGQHAGQIAGQJAGQKAGQLAGQMAGQNAGQOAGQPAGQMAGcPAERdHAB9AAB0AQB0AgB8AACDAQBkEAAXgwEAXgIAcToAgwEAfQEAdAMAZBEAgwEAfQIAfAIAfAEAawIAcuYAZAEAagAAZwAAZBIAZBMAZBQAZBUAZBYAZBcAZBgAZBkAZBoAZBsAZBwAZB0AZB4AZAsAZBwAZB8AZAMAZB0AZAgAZB4AZCAAZCEAZxYARF0VAH0AAHwAAGoEAGQiAIMBAF4CAHHGAIMBAEdIbgUAZCMAR0hkAABTKCQAAABOdAAAAAB0AQAAAF50AQAAADR0AQAAAEt0AQAAAGl0AQAAAC50AQAAAC90AQAAAE50AQAAAGp0AQAAAFB0AQAAAG90AQAAAD90AQAAAGx0AQAAADJ0AQAAAFRpAwAAAHMJAAAAWW91IHBhc3M6dAEAAABzdAEAAAB5dAEAAABudAEAAAB0dAEAAAA6dAEAAAB7dAEAAAB3dAEAAABxdAEAAABFdAEAAAA2dAEAAABmdAEAAABYdAEAAAB1dAEAAABhdAEAAAAxdAEAAAB9dAUAAABST1QxM3MFAAAATm8gOigoBQAAAHQEAAAAam9pbnQDAAAAY2hydAMAAABvcmR0CQAAAHJhd19pbnB1dHQGAAAAZGVjb2RlKAMAAAB0AQAAAGV0AwAAAHRtcHQGAAAAcGFzc3dkKAAAAAAoAAAAAHMHAAAAdGFzay5weVIcAAAAAgAAAHMKAAAAAAFfAQwBDAFvAQ=='
PYC_HEADER = '\x03\xf3\x0d\x0a\x14\x0c\xf4\x57'

data = code.decode('base64')
data = PYC_HEADER + data

with open('auth.pyc', 'wb') as f:
    f.write(data)

auth.pycをEasy Python Decompilerでデコンパイルする。

# Embedded file name: task.py
tmp = ''.join([ chr(ord(e) + 3) for e in ['^',
 '4',
 'K',
 'i',
 '.',
 '/',
 'N',
 'j',
 'P',
 'o',
 '?',
 'l',
 '2',
 'T',
 '?'] ])
passwd = raw_input('You pass:')
if passwd == tmp:
    print ''.join([ e.decode('ROT13') for e in ['s',
     'y',
     'n',
     't',
     ':',
     '{',
     'w',
     'q',
     'E',
     '6',
     'f',
     'X',
     'u',
     'o',
     'f',
     'a',
     '4',
     'X',
     'N',
     'u',
     '1',
     '}'] ])
else:
    print 'No :('

tmpを表示させ、その値を入力すると、フラグが表示された。

>python auth.py
You pass:a7Nl12QmSrBo5WB
flag:{jdR6sKhbsn4KAh1}
jdR6sKhbsn4KAh1

Reverse 200_1 (Reverse 200)

pyファイルが与えられている、Reverse 100_2 と似たような問題。pycのヘッダを付けてauth.pycを作成する。

#!/usr/bin/env python
code = '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'
PYC_HEADER = '\x03\xf3\x0d\x0a\x14\x0c\xf4\x57'

data = code.decode('base64')
data = PYC_HEADER + data

with open('auth.pyc', 'wb') as f:
    f.write(data)

auth.pycをEasy Python Decompilerでデコンパイルする。

# Embedded file name: task1.py
passwd = raw_input('You pass:')
if ''.join([ chr(ord(e[0]) ^ ord(e[1])) for e in zip('PQ1OJiRWAitBJg5QP2A='.decode('base64'), passwd) ]) == ''.join((e.decode('ROT13') for e in ['t',
 'P',
 'a',
 'B',
 'J',
 'i',
 'b',
 'E',
 'n',
 'W',
 't',
 '6',
 'M',
 'N'])):
    print ''.join([ [ chr(ord(e) + 30) for e in ''.join([ e.decode('ROT13') for e in ['K',
     'm',
     'L',
     'g',
     'K',
     'G',
     'A',
     'C',
     'A',
     'E',
     'k',
     'U',
     'F',
     'I',
     'E',
     'W',
     'S',
     'H',
     'k',
     'p',
     'D',
     'm',
     'x',
     'x',
     'D',
     '0',
     '4',
     'J',
     'E',
     'F',
     'q',
     'V'] ]).decode('base64')[::-1] ][i] + [ chr(ord(e) + 30) for e in ''.join([ e.decode('ROT13') for e in ['K',
     'm',
     'L',
     'g',
     'K',
     'G',
     'A',
     'C',
     'A',
     'E',
     'k',
     'U',
     'F',
     'I',
     'E',
     'W',
     'S',
     'H',
     'k',
     'p',
     'D',
     'm',
     'x',
     'x',
     'D',
     '0',
     '4',
     'J',
     'E',
     'F',
     'q',
     'V'] ]).decode('base64')[::-1] ][i + 4] + [ chr(ord(e) + 30) for e in ''.join([ e.decode('ROT13') for e in ['K',
     'm',
     'L',
     'g',
     'K',
     'G',
     'A',
     'C',
     'A',
     'E',
     'k',
     'U',
     'F',
     'I',
     'E',
     'W',
     'S',
     'H',
     'k',
     'p',
     'D',
     'm',
     'x',
     'x',
     'D',
     '0',
     '4',
     'J',
     'E',
     'F',
     'q',
     'V'] ]).decode('base64')[::-1] ][i + 4 + 4] + [ chr(ord(e) + 30) for e in ''.join([ e.decode('ROT13') for e in ['K',
     'm',
     'L',
     'g',
     'K',
     'G',
     'A',
     'C',
     'A',
     'E',
     'k',
     'U',
     'F',
     'I',
     'E',
     'W',
     'S',
     'H',
     'k',
     'p',
     'D',
     'm',
     'x',
     'x',
     'D',
     '0',
     '4',
     'J',
     'E',
     'F',
     'q',
     'V'] ]).decode('base64')[::-1] ][i + 4 + 4 + 4] + [ chr(ord(e) + 30) for e in ''.join([ e.decode('ROT13') for e in ['K',
     'm',
     'L',
     'g',
     'K',
     'G',
     'A',
     'C',
     'A',
     'E',
     'k',
     'U',
     'F',
     'I',
     'E',
     'W',
     'S',
     'H',
     'k',
     'p',
     'D',
     'm',
     'x',
     'x',
     'D',
     '0',
     '4',
     'J',
     'E',
     'F',
     'q',
     'V'] ]).decode('base64')[::-1] ][i + 4 + 4 + 4 + 4] + [ chr(ord(e) + 30) for e in ''.join([ e.decode('ROT13') for e in ['K',
     'm',
     'L',
     'g',
     'K',
     'G',
     'A',
     'C',
     'A',
     'E',
     'k',
     'U',
     'F',
     'I',
     'E',
     'W',
     'S',
     'H',
     'k',
     'p',
     'D',
     'm',
     'x',
     'x',
     'D',
     '0',
     '4',
     'J',
     'E',
     'F',
     'q',
     'V'] ]).decode('base64')[::-1] ][i + 4 + 4 + 4 + 4 + 4] for i in range(4) ])
else:
    print 'No :('

最初の条件の==を!=にして、適当なpassを入力すると、フラグが表示された。

>python auth.py
You pass:aa
flag:{EazrSKcBjgmT4W3eQ}
EazrSKcBjgmT4W3eQ

Crypto 100_1 (Crypto 100)

数字とアンダースコア(_)のみの暗号。数字2文字で、1つのアルファベットに対応すると考え、コードを書く。

alp = {'11': 'a', '12': 'b', '13': 'c', '14': 'd', '15': 'e',
    '21': 'f', '22': 'g', '23': 'h', '24': 'i', '25': 'k',
    '31': 'l', '32': 'm', '33': 'n', '34': 'o', '35': 'p',
    '41': 'q', '42': 'r', '43': 's', '44': 't', '45': 'u',
    '51': 'v', '52': 'w', '53': 'x', '54': 'y', '55': 'z'
}

with open('code.txt', 'r') as f:
    lines = f.readlines()

for line in lines:
    s = ''
    code = ''
    for i in range(len(line)):
        c = line[i]
        if c == '_':
            s += c
        else:
           code += c
           if len(code) == 2:
               s += alp[code]
               code = ''
    print s

実行すると、以下の文になる。

wake_up_neo
the_matrix_has_you
follow_the_white_qabbit
knock_knock_neo
the_flag_is_the_third_line

follow_the_white_qabbitだと通らないので、最後の単語を意味のある単語に変える。

follow_the_white_rabbit

Crypto 400_1 (Crypto 400)

3組のN,Cが与えられ、eは3...ということでHastad's broadcast attackで攻撃する。

#!/usr/bin/env python3
n1 = 95118357989037539883272168746004652872958890562445814301889866663072352421703264985997800660075311645555799745426868343365321502734736006248007902409628540578635925559742217480797487130202747020211452620743021097565113059392504472785227154824117231077844444672393221838192941390309312484066647007469668558141
n2 = 98364165919251246243846667323542318022804234833677924161175733253689581393607346667895298253718184273532268982060905629399628154981918712070241451494491161470827737146176316011843738943427121602324208773653180782732999422869439588198318422451697920640563880777385577064913983202033744281727004289781821019463
n3 = 68827940939353189613090392226898155021742772897822438483545021944215812146809318686510375724064888705296373853398955093076663323001380047857809774866390083434272781362447147441422207967577323769812896038816586757242130224524828935043187315579523412439309138816335569845470021720847405857361000537204746060031
c1 = 64830446708169012766414587327568812421130434817526089146190136796461298592071238930384707543318390292451118980302805512151790248989622269362958718228298427212630272525186478627299999847489018400624400671876697708952447638990802345587381905407236935494271436960764899006430941507608152322588169896193268212007
c2 = 96907490717344346588432491603722312694208660334282964234487687654593984714144825656198180777872327279250667961465169799267405734431675111035362089729249995027326863099262522421206459400405230377631141132882997336829218810171728925087535674907455584557956801831447125486753515868079342148815961792481779375529
c3 = 43683874913011746530056103145445250281307732634045437486524605104639785469050499171640521477036470750903341523336599602288176611160637522568868391237689241446392699321910723235061180826945464649780373301028139049288881578234840739545000338202917678008269794179100732341269448362920924719338148857398181962112

import functools
import itertools

def chinese_remainder(n, a):
    sum = 0
    prod = functools.reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod
 
def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a // b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

def inv_pow(c, e):
    low = -1
    high = c+1
    while low + 1 < high:
        m = (low + high) // 2
        p = pow(m, e)
        if p < c:
            low = m
        else:
            high = m
    m = high
    assert pow(m, e) == c
    return m
 
N = [n1, n2, n3]
C = [c1, c2, c3]
e = len(N)
a = chinese_remainder(N, C)
for n, c in zip(N, C):
    assert a % n == c
m = inv_pow(a, e)
print(bytes.fromhex(hex(m)[2:]).decode())

実行すると復号される。

theoretical_computer_scientist_johan_torkel_hastad

PPC 100_1(PPC 100)

年月日、曜日がファイル名に含まれているファイルが7937個与えられる。どうやら日付と曜日が合っているものを探せばよいらしい。コードは以下の通り。

import os
import datetime

WEEK_DAYS = ['Monday', 'Tuesday', 'Wednesday',
    'Thursday', 'Friday', 'Saturday', 'Sunday']

files = os.listdir('messages\\')

for file in files:
    str_date = file.split('.')[0]
    dweek = str_date.split('_', 1)[0]
    date = str_date.split('_', 1)[1]
    d = datetime.datetime.strptime(date, '%d_%B_%Y')
    if WEEK_DAYS[d.weekday()] == dweek:
        print file
        with open('messages\\' + file, 'r') as f:
            print f.read()

実行すると該当するファイルが1つだけ見つかり、ファイルの中身がフラグであった。

4eec2cd9e4bb0062d0e41c8af1bd8a0f

Hack The Vote 2016 Writeup

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

Sanity (Vote 1)

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

flag{th3r3_1s_0nly_on3_ch0ic3}

Vermatrix Supreme (Crypto 100)

暗号のコードが与えられ、読み解くと処理の概要は以下の通りであることがわかる。

・IV の文字とseedのASCIIコードを連結
・連結した配列の長さが9の倍数になるように0をパディングする。
・9バイトごとに&や|、~の計算をし、その結果とseedが表示される。
・IVを求め、定型フォーマットで指定すれば、フラグが表示される。

IVはXORを繰り返せば、求められる。コードは以下の通り。

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

def pad(s):
    if len(s)%9 == 0:
        return s
    for i in xrange((9-(len(s)%9))):
        s.append(0)
    return s

def genBlockMatrix(s):
    outm = [[[7 for x in xrange(3)] for x in xrange(3)] for x in xrange(len(s)/9)]
    for matnum in xrange(0,len(s)/9):
        for y in xrange(0,3):
            for x in xrange(0,3):
                outm[matnum][y][x] = s[(matnum*9)+x+(y*3)]
    return outm

def copyMatrix(m):
    copym = [[0 for i in range(3)] for i in range(3)]
    for y in xrange(3):
        for x in xrange(3):
            copym[y][x] = m[y][x]
    return copym

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('vermatrix.pwn.democrat', 4201))

data = s.recv(256)
print data

pattern = 'SEED: (.+)\n(.+)\n(.+)\n(.+)\n'
m = re.search(pattern, data)
seed = m.group(1)
seed_blocks = genBlockMatrix(pad([ord(c) for c in seed]))

matrixz = []
matrixz.append(map(int, m.group(2).split(' ')))
matrixz.append(map(int, m.group(3).split(' ')))
matrixz.append(map(int, m.group(4).split(' ')))

matrix_tmp = copyMatrix(matrixz)
matrix_tmp2 = [[0 for i in range(3)] for i in range(3)]
for i in range(len(seed_blocks)-1, -1, -1):
    for row in range(3):
        for col in range(3):
           matrix_tmp2[row][col] = matrix_tmp[col][row] ^ seed_blocks[i][col][row]
    matrix_tmp = copyMatrix(matrix_tmp2)

ans = ''
for row in range(3):
    for col in range(3):
        ans += str(matrix_tmp2[row][col])
        ans += ','

ans = ans[:-1]
print ans
s.sendall(ans + '\n')

data = s.recv(256)
print data
flag{IV_wh4t_y0u_DiD_Th3r3}

School CTF 2016 Writeup

この大会は2016/11/6 14:00(JST)~2016/11/6 22:00(JST)に開催されました。
今回もチームで参戦。結果は700点でNon-School428チーム中80位でした。
自分で解けた問題をWriteupとして書いておきます。

MassQR (ppc 300)

QRコードがたくさん書いてあるPNGファイルが与えられている。
まず、QRコードを1つずつの画像に分解する。

import Image

img = Image.open('qr_eb3bdc766963045a2afa3d0ce8747c7cf4f291c8.png').convert('RGB')
num = 65250 / 290
for i in range(num):
    img_div = Image.new('RGB', (290,290), (255, 255, 255))
    for y in range(290):
        for x in range(290):
            r, g, b = img.getpixel((i * 290 + x, y))
            img_div.putpixel((x, y), (r, g, b))
    filename = 'div\\qr%03d.png' % i
    img_div.save(filename)

次にQRコードを黒の部分をX、白の部分を空白のテキストデータに変換する。

from PIL import Image

BLACK = 'X'
WHITE = ' '

quiet_zone_size = 40
cell_size = 10

num = 65250 / 290
for i in range(num):
    s_filename = 'div\\qr%03d.png' % i
    d_filename = 'qrtxt\\qr%03d.txt' % i

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

    data = ''
    for y in range(quiet_zone_size, h - quiet_zone_size, cell_size):
        for x in range(quiet_zone_size, w - quiet_zone_size, cell_size):
            r, g, b = img.getpixel((x, y))
            if r == 255 and g == 255 and b == 255:
                data += WHITE
            else:
                data += BLACK
        data += '\n'

    with open(d_filename, 'w') as f:
        f.write(data)

sqrd.pyを使って、QRコードを順に読む。

import subprocess

CMD_FORMAT = 'python sqrd.py qrtxt\\qr%03d.txt'

data = ''
for i in range(225):
    cmd = CMD_FORMAT % i
    ret = subprocess.check_output( cmd.split(" ") )
    ret = ret.replace('\r\n', '')
    ret = ret.replace('\r', '')
    ret = ret.replace('\n', '')
    data += ret

print data

PHPのコードになり、実行するとフラグが表示される。

SchoolCTF{Aw3s0m3_p5p_3wrerYwhEr3}

0011000100110000 (stegano 100)

英文の文章が与えられているが、問題文からZ,E,R,O,Nのみ抜き出し、ZEROは0に、ONEは1に置き換えてみる。
この01の文字列を2進数として読み込み、文字に変換する。

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

data01 = ''
for i in range(len(data)):
    c = data[i:i+1]
    if c == 'Z' or c == 'E' or c == 'R' or c == 'O' or c == 'N':
        data01 += c

data01 = data01.replace('ZERO', '0')
data01 = data01.replace('ONE', '1')

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

print flag
SchoolCTF{ZOMG_u_G00d_R3ader}

Virus (stegano 200)

PNGファイルが与えられている。PNGファイルに他のファイルが結合しているようなので、foremostで分解してみる。

$ foremost task.png 

抽出したjpgファイルにフラグが書かれている。
f:id:satou-y:20161110221654j:plain

SchoolCTF{v1rus_ar7_is_r34l_art}

Wonder Web (web 100)

レスポンスヘッダを見てみると、フラグに関する情報が含まれている。

Content-Meaning: none; flag-part-number=5 part-content=54f3};

全部でパーツは5つ。最後のパーツは 54f3}

Flag-First-Part-Is-Here: encoding=base64; part1=U2Nob29sQ1RGezUwbTNkNHk=;

1つ目のパーツの情報。Base64デコードすればよい。

$ echo 'U2Nob29sQ1RGezUwbTNkNHk=' | base64 -d
SchoolCTF{50m3d4y
Task: category=joy; ucucuga=sure; encoding=none; justString=true; flagPresent=1; flagPart2=17; flagPart4=b3

2つ目と4つ目のパーツの情報。

X-ShellShock-vector: (){;}; echo "Want flag?"; python -c 'part3="77316c6c"; print part3.decode("hex")'

3つ目のパーツの情報。hexデコードする。

>python
Python 2.7.10 (default, May 23 2015, 09:40:32) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> part3="77316c6c"
>>> part3.decode("hex")
'w1ll'
Flag-Parts-Connector: character=_; charCode=95; hexCharCode=0x5f;

各パーツをつなげる文字の情報。

以上の情報からフラグを作る。

SchoolCTF{50m3d4y_17_w1ll_b3_54f3}

EKOPARTY CTF 2016 Writeup

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

Hidden inside EKO (Misc 50)

EKO pixelsに隠されているフラグを見つける問題。
背景画像を取り出すと、左上にフラグがある。
f:id:satou-y:20161029193858p:plain

EKO{th3_fl4g}

Old but gold (Misc 250)

キーパンチカードの画像14枚が添付されている。まずこのキーパンチの情報を解読する。コードは以下の通り。

import Image
import sys
import os

IBM_MODEL_029_KEYPUNCH = [
"    /&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ:#@'=x`.<(+|!$*);^~,%_>? |",
"12 / O           OOOOOOOOO                        OOOOOO             |",
"11|   O                   OOOOOOOOO                     OOOOOO       |",
" 0|    O                           OOOOOOOOO                  OOOOOO |",
" 1|     O        O        O        O                                 |",
" 2|      O        O        O        O       O     O     O     O      |",
" 3|       O        O        O        O       O     O     O     O     |",
" 4|        O        O        O        O       O     O     O     O    |",
" 5|         O        O        O        O       O     O     O     O   |",
" 6|          O        O        O        O       O     O     O     O  |",
" 7|           O        O        O        O       O     O     O     O |",
" 8|            O        O        O        O OOOOOOOOOOOOOOOOOOOOOOOO |",
" 9|             O        O        O        O                         |",
"  |__________________________________________________________________|",
]

files = ["07d561df3da01f31590066f014652e995f7b76f1.png","4a95fea0f5e9af0af550b94fb960222e934ad09b.png","a034586b253b057c96da0b6707364853886b22b6.png","d3860afefe98f2408e24218a882aaf227d9287b9.png","19756efa72339faa9c9b5fe1743c3abedbc5079d.png","85a749d44bcba42869f21fb58f9725a443066a4f.png","a8a103961eccf8a991edfed1aaa39a8f9a3fe622.png","f7191b128c49ecfef0b27cd049550ae75249f86b.png","24c1e220c056210e6507c4c57079ffb99ffeb96c.png","89596be1f6463cb83abaecac7a375546069ecf0f.png","a9aba85ebcb160a7b18ea22abfb9589bd3ce1914.png","2d77fbd5eda9ed661a7834d8273815722fb97ccc.png","93ec404ba9266f5d059a727a6460b2693fc4c440.png","cdeea42d7f7216f93a9f1eb93b2723c70e693bea.png"]

def getRow(q):
    out = ''
    for i in range(len(IBM_MODEL_029_KEYPUNCH)):
        out += IBM_MODEL_029_KEYPUNCH[i][q]
    return out

def check(n, need):
    for i in range(len(need)):
        if(need[i] != getRow(n)[i+1]):
            return False
    return True

def find(need):
    for i in range(5, 69):
        if(check(i, need)):
            return i
    return -1

for f in files:
    im = Image.open('misc250_100ff979353dd452\\' + f)
    pix = im.load()

    def isWhite(a):
        for q in a:
            if q != 255:
                return False
        return True

    for x in range(2, 82):
        column = ''
        for y in range(2, 25, 2):
            if(isWhite(pix[x*7 + 4, y*10 + 5])):
                column += ("O")
            else:
                column += (" ")
        sys.stdout.write(IBM_MODEL_029_KEYPUNCH[0][find(column)])
    print ''

実行すると、それぞれ以下の14個の英文の一部になる。

・OF TIME PUNCHING THOSE NARDS, CAN YOU IMAGINE WHAT COULD                        
・MANUALS TRY1NG TO LEARN HOW TO PROGRAM AND SPEND A LOT                          
・AND COBOL, B(T EVEN AFTER ALL THOSE YEARS HE DOESNT KNOW                        
・THAT WILL TAKE MORE TIME TO MEBUG AND FIGURE OUT WHERE WAS                      
・THE BUG, BUT THOSE WER3 THE OLD DAYS. CAN YOU FIND THE FLAG                     
・HAPPEN IF YOU FAKE A SMALL MISTAKE IN ON OF THOSE PUNCHED                       
・CARDS? AFTER THOSE HOURS WAITING ROR A RESULT, THEN IT SAYS                     
・ONCE UPON A TEME, THERE WAS A YOUNG HACKER CALLED MJ                            
・USING THIS OLD TECHNOLOGY? GOOD LUCK, YOU WILL NEED IT)                         
・USE THOSE PONCHED CARDS, HE LIKES TO PROGRAM IN FORTRAN                         
・IN THOSE DAYS YOUR ONLY OPTION W4S READ LARGE BOOKS AND                         
・IT WAS THE SIXTIES, HE WAS TRYKNG TO FIGURE OUT HOW TO                          
・ERROR DUE TO A SMALL AND ALMOST INSIGNIFICANT MIST4KE BUT                       
・HOW TO PROPERLY MRITE SECURE CODE IN THOSE LANGUAGES  

それぞれスペルミスがあるので、英文として意味の通る順番に入れ替え、スペルミスの文字を列挙する。

E: ONCE UPON A TEME, THERE WAS A YOUNG HACKER CALLED MJ
K: IT WAS THE SIXTIES, HE WAS TRYKNG TO FIGURE OUT HOW TO
O: USE THOSE PONCHED CARDS, HE LIKES TO PROGRAM IN FORTRAN
(: AND COBOL, B(T EVEN AFTER ALL THOSE YEARS HE DOESNT KNOW
M: HOW TO PROPERLY MRITE SECURE CODE IN THOSE LANGUAGES
4: IN THOSE DAYS YOUR ONLY OPTION W4S READ LARGE BOOKS AND
1: MANUALS TRY1NG TO LEARN HOW TO PROGRAM AND SPEND A LOT
N: OF TIME PUNCHING THOSE NARDS, CAN YOU IMAGINE WHAT COULD
F: HAPPEN IF YOU FAKE A SMALL MISTAKE IN ON OF THOSE PUNCHED
R: CARDS? AFTER THOSE HOURS WAITING ROR A RESULT, THEN IT SAYS
4: ERROR DUE TO A SMALL AND ALMOST INSIGNIFICANT MIST4KE BUT
M: THAT WILL TAKE MORE TIME TO MEBUG AND FIGURE OUT WHERE WAS 
3: THE BUG, BUT THOSE WER3 THE OLD DAYS. CAN YOU FIND THE FLAG
): USING THIS OLD TECHNOLOGY? GOOD LUCK, YOU WILL NEED IT)
EKO{M41NFR4M3}

Mr. Robot (Web 25)

問題タイトルからも予想できるが、robots.txtの問題。
https://ctf.ekoparty.org/robots.txtにアクセスすると、次のような表示。

User-agent: *
Disallow: /static/wIMti7Z27b.txt

https://ctf.ekoparty.org/static/wIMti7Z27b.txtにアクセスすると、フラグが見つかる。

EKO{robot_is_following_us}

RFC 7230 (Web 50)

ctf.ekoparty.orgのサーバから基本的な情報を取得してフラグを得るという問題。
curlコマンドでレスポンスを見てみる。

$ curl -v -b 'NULL=<クッキーの値>' https://ctf.ekoparty.org/tasks
* Hostname was NOT found in DNS cache
*   Trying 52.204.197.190...
* Connected to ctf.ekoparty.org (52.204.197.190) port 443 (#0)
* successfully set certificate verify locations:
  :
> GET /tasks HTTP/1.1
> User-Agent: curl/7.35.0
> Host: ctf.ekoparty.org
> Accept: */*
> Cookie: NULL=<クッキーの値>
> 
< HTTP/1.1 200 OK
* Server EKO{this_is_my_great_server} is not blacklisted
< Server: EKO{this_is_my_great_server}
< Date: Wed, 26 Oct 2016 21:52:54 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 9397
< Connection: keep-alive
< Vary: Accept-Encoding
< Set-Cookie: NULL=<クッキーの値>; Secure; HttpOnly; Path=/
< X-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Strict-Transport-Security: max-age=63072000; preload
  :

Serverにフラグが設定されている。

EKO{this_is_my_great_server}

Welcome to the dark side (FBI 25)

TORネットワークのみ接続できるURLが与えられている。TOR Browserで接続し、ソースを見るとコメントにフラグが書かれている。

EKO{buy_me_some_b0ts}

Metadata (FBI 50)

FBI 25と同じURLが与えられ、メタデータにフラグが書かれているような問題文。証明書を見ると、部門にフラグが書かれている。
f:id:satou-y:20161029195524p:plain

EKO{is_this_just_real_life_is_this_just_fantasy}

JVM (Reversing 25)

classが与えられている。JD-GUIで開き、デコンパイルすると、コードは次の通り。

public class EKO
{
  public static void main(String[] paramArrayOfString)
  {
    int i = 0;
    for (int j = 0; j < 1337; ++j) {
      i += j;
    }

    String str = "EKO{" + i + "}";
  }
}

計算結果を表示しないようになっているが、同様の処理をPythonでコードを書き、実行する。

i = 0
for j in range(1337):
    i += j

str = 'EKO{' + str(i) + '}'
print str
EKO{893116}

Damaged (Forensics 75)

壊れたBMPファイルが添付されている。バイナリエディタで見ると、先頭部分が欠けていることがわかる。
「42 4D 36 75 00 00 00 00 00 00 36 00 00 00」を先頭に追加すると、BMPファイルとして開け、フラグを見ることができる。
f:id:satou-y:20161029201247j:plain

EKO{b1tm4p_r3c}