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}

STEM CTF: Cyber Challenge 2017 Writeup

この大会は2017/9/16 4:00(JST)~2017/9/17 4:00(JST)に開催されました。
今回もチームで参戦。結果は50点でした。終了後スコアボードは見れず、順位はわからずじまいです。
自分で解けた問題をWriteupとして書いておきます。

Forgotten (Forensics 50)

smtpTCP Streamで見る。

EHLO default-VirtualBox.localdomain
MAIL FROM:<default@default-VirtualBox> SIZE=770
RCPT TO:<ctf@mitre.org> ORCPT=rfc822;ctf@mitre.org
DATA
250 2.1.0 Ok
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
Received: by default-VirtualBox.localdomain (Postfix, from userid 1000)
	id 17CE26F300; Tue, 16 May 2017 14:21:32 -0400 (EDT)
Content-type: text/html;
Subject: Forgot Your Password?
To: <ctf@mitre.org>
X-Mailer: mail (GNU Mailutils 2.99.99)
Message-Id: <20170516182132.17CE26F300@default-VirtualBox.localdomain>
Date: Tue, 16 May 2017 14:21:32 -0400 (EDT)
From: default@default-VirtualBox (default)

<!DOCTYPE html>
<html>
<head>
	<title>Password Reset Email</title>
</head>
<body>
	We hear you have forgotten your password.<br>
	Not to worry.  You can click <a href="https://www.aSuperRealWebsite.notFake/users/password/edit?resetPasswordToken=TUNBezU4MDc2MjY2NzZ9">this link</a> to reset your password!<br><br>
	Have a great day!
</body>
</html>
.
QUIT
250 2.0.0 Ok: queued as B705C6CC00F
221 2.0.0 Bye

URLに含まれるBase64文字列をデコードする。

$ echo TUNBezU4MDc2MjY2NzZ9 | base64 -d
MCA{5807626676}
MCA{5807626676}

SEC-T CTF Writeup

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

Handle (Misc 10)

freenodeで#sect-ctfチャネルに入る。

06:04 *topic : SEC-T CTF - FLAG:SECT{CRASH_OVERR1D3} - DOWNLOAD SERVER: - http://178.62.89.173/ - https://sect.ctf.rocks - https://ctftime.org/event/499 - Wednesday 13/9 15:00 UTC - Friday 15/9 14:30 UTC - Hackers of the world unite!
SECT{CRASH_OVERR1D3}

Report (Misc 200)

PDFStreamDumperで与えられたPDFを開き、最後の 0 HLen: 0xC3A の箇所を見る。

0000000000 65535 f 
0000013999 00000 n 
0000000019 00000 n 
0000001039 00000 n 
0000022799 00000 n 
0000014372 00000 n 
0000014507 0007d n 
0000014635 00000 n 
0000014772 00000 n 
0000014900 00059 n 
0000015028 00000 n 
0000015164 00000 n 
0000015293 00000 n 
0000015429 00000 n 
0000015558 00000 n 
0000015694 00000 n 
0000015823 00000 n 
0000015959 00000 n 
0000016088 00000 n 
0000016226 00043 n 
0000016355 00000 n 
0000016491 00000 n 
0000016620 00000 n 
0000016757 00000 n 
0000016886 00000 n 
0000017024 00000 n 
0000017155 00000 n 
0000017294 00055 n 
0000017425 00000 n 
0000017564 00000 n 
0000017695 00000 n 
0000017834 00000 n 
0000017965 00000 n 
0000018104 00000 n 
0000018235 00000 n 
0000018374 00000 n 
0000018513 00000 n 
0000018644 0004c n 
0000018783 00000 n 
0000018914 00000 n 
0000019053 00000 n 
0000019184 00000 n 
0000019323 00000 n 
0000019454 00000 n 
0000019593 00000 n 
0000019724 00000 n 
0000019863 00000 n 
0000019994 00000 n 
0000020133 00000 n 
0000020264 00000 n 
0000020403 00000 n 
0000020534 0005f n 
0000020673 0004b n 
0000014160 00041 n 
0000001059 00033 n 
0000001648 00000 n 
0000020804 00000 n 
0000020943 00000 n 
0000021075 00052 n 
0000021214 00000 n 
0000021346 00000 n 
0000021485 00000 n 
0000021617 00042 n 
0000021758 00000 n 
0000021890 00000 n 
0000022029 00000 n 
0000022168 0005f n 
0000022307 00000 n 
0000022439 00000 n 
0000022578 00030 n 
0000022710 00054 n 
0000023720 0005f n 
0000001669 00000 n 
0000012723 00000 n 
0000012746 00000 n 
0000012941 00033 n 
0000013505 00043 n 
0000013911 00000 n 
0000013944 00000 n 
0000023310 00000 n 
0000014323 00000 n 
0000014458 00031 n 
0000014586 0004e n 
0000014723 00000 n 
0000014851 00000 n 
0000014979 00000 n 
0000015115 00000 n 
0000015244 00000 n 
0000015380 00000 n 
0000015509 00000 n 
0000015645 00000 n 
0000015774 00000 n 
0000015910 0005f n 
0000016039 00000 n 
0000016177 00000 n 
0000016306 00000 n 
0000016442 00000 n 
0000016571 00000 n 
0000016708 00000 n 
0000016837 00000 n 
0000016974 00000 n 
0000017105 00000 n 
0000017244 00037 n 
0000017375 00000 n 
0000017514 00000 n 
0000017645 00000 n 
0000017784 00000 n 
0000017915 00000 n 
0000018054 00000 n 
0000018185 00000 n 
0000018324 00000 n 
0000018463 00000 n 
0000018594 00000 n 
0000018733 00000 n 
0000018864 00000 n 
0000019003 00000 n 
0000019134 00000 n 
0000019273 00000 n 
0000019404 00000 n 
0000019543 00000 n 
0000019674 00000 n 
0000019813 00000 n 
0000019944 00030 n 
0000020083 00000 n 
0000020214 0004e n 
0000020353 00000 n 
0000020484 00000 n 
0000020623 00000 n 
0000020754 00000 n 
0000020893 00000 n 
0000021025 00000 n 
0000021164 00000 n 
0000021296 00000 n 
0000021435 0007b n 
0000021567 00000 n 
0000021708 00000 n 
0000021840 00000 n 
0000021979 00054 n 
0000022118 00000 n 
0000022257 00000 n 
0000022389 00043 n 
0000022528 00045 n 
0000022660 00000 n 
0000023428 00000 n 
0000023827 00053 n 
0000023998 00000 n 

これを c3a.txtに保存する。2列目の16進数をASCIIコードとして、0以外の値を下から順に文字として読む。

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

flag = ''
for line in lines:
    data = line.strip().split(' ')
    if data[2] == 'n':
        code = int(data[1], 16)
        if code != 0:
            flag += chr(code)

flag = flag[::-1]
print flag
SECT{N07_N1C3_T0_BR3AK_LUCY}

Acid burn (Misc 200)

https://convertio.co/ja/でwebpからpngに変換。Stegsolveで開き、Red plane 2でなんとなくフラグが見える。
f:id:satou-y:20170918203030p:plain
隠れて見えない部分は文章になるように推定しながらフラグを組み立てる。

SECT{I_LOVE_CRASH_OVERFLOW_BUT_I_CAN_NOT_TELL_HIM_HOW_I_FEEL_ABOUT_HIM}

Joeys screenshot (Misc 50)

TweakPNGで見ると、たくさんのiTXtチャンクが入っている。そのテキスト情報を並べると、以下のようになっている。

_36
327
C2
134
524
N22
H41
{4
_11
_33
_13
021
E1
530
342
H43
038
535
P26
U25
G37
415
39
B19
S0
U29
R28
R32
D14
212
_23
D5
D39
_40
G17
K8
Y10
344
!45
520
T3
331
118
46
}46
_16
_7

フラグの最初が"SECT{"となることから、[文字(1バイト)][順番]という構成になっていることに気づく。順番に並べてみる。

S 0
E 1
C 2
T 3
{ 4
D 5
4 6
_ 7
K 8
3 9
Y 10
_ 11
2 12
_ 13
D 14
4 15
_ 16
G 17
1 18
B 19
5 20
0 21
N 22
_ 23
5 24
U 25
P 26
3 27
R 28
U 29
5 30
3 31
R 32
_ 33
1 34
5 35
_ 36
G 37
0 38
D 39
_ 40
H 41
3 42
H 43
3 44
! 45
} 46
SECT{D4_K3Y_2_D4_G1B50N_5UP3RU53R_15_G0D_H3H3!}

ASIS CTF Finals 2017 Writeup

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

V.I.R (Warm-up)

Rulesのページの最下部にフラグが書いてある。

Good luck, ASIS{_rUL35_4r3_4Lw4y5_ImP0rt4nt}
ASIS{_rUL35_4r3_4Lw4y5_ImP0rt4nt}

Vivid Spying (Forensics, Network)

pcapファイルが与えられている。FQDNで0,1の名前を1文字ずつ長くしてDNSに問い合わせしている。続きを行い、0,1を2進数として文字列にすることを考える。

import dns.resolver

r = dns.resolver.Resolver()
r.nameservers = ['95.85.26.168']

domain = '1110.000011101100101011111010110111101100101010010010110010101000001.asisctf.com'

while True:
    if len(domain) % 64 == 11:
        domain = '.' + domain
    print domain

    finish = False
    for i in [0, 1]:
        try_domain = str(i) + domain
        try:
            rdata = r.query(try_domain, 'A')
            domain = try_domain
            break
        except:
            if i == 0:
                continue
            else:
                finish = True
    if finish:
        break

enc = domain.replace('.asisctf.com', '').replace('.', '')
enc = '0' + enc[::-1]

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

print flag
ASIS{_Spying_with_DNS_!}

Cryptocurrency Coding (PPC)

$ nc 178.62.22.245 58901
Hi all, let's go to BTCing!!
Are you ready? [Y]es or [N]o:
y
Send us a valid BTC address with same length of given damaged address that correct it :)
----------------------------------------------------------------------------------------
the damaged BTC address is: 1CnmjSXKaPZRNb7pUR8rNGduP5UEfs5yBb
Send the correted BTC:

Bitcoinアドレスとして間違っているので、訂正する問題。チェックディジットを修正するプログラムを書く。

import socket
import re
import base58
import hashlib

def correct(btc):
    btcbytes = base58.b58decode(btc)
    chk = hashlib.sha256(hashlib.sha256(btcbytes[-25:-4]).digest()).digest()[:4]
    correct_btcbytes = btcbytes[:-4] + chk
    correct_btc = base58.b58encode(correct_btcbytes)
    return correct_btc

pattern = 'the damaged BTC address is: (.+)\n'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('178.62.22.245', 58901))

data = s.recv(256)
print data
print 'Y'
s.sendall('Y\n')
data = s.recv(256)
print data

i = 1
while True:
    print 'Round %d' % i
    data = s.recv(256)
    print data
    if data.startswith('Good job'):
        data = s.recv(256)
        print data
    m = re.match(pattern, data)
    dmg_btc = m.group(1)
    crct_btc = correct(dmg_btc)
    print crct_btc
    s.sendall(crct_btc + '\n')
    i += 1

data = s.recv(256)
print data
ASIS{!0h_BTC_h4S_aT_leas7_3_3rr0r_cOrr3ct1ng_fe4tur3!}

Simple Crypto (Crypto)

flagをkeyで繰り返し、XORしている。flagに戻すとPNGのフォーマットなっていることがわかるので、それを踏まえコードを書く。

KEY = 'musZTXmxV58UdwiKt8Tp'
key = KEY.encode('hex')

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

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

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

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

ASIS{juSt_S!mpl3_Cryp7o_f0r_perFect_guy5_l1ke_You!}

Hash collision (Crypto)

phishで定義されたハッシュはコードをよく読むと、X[-1]の値が同じだと同じ結果になる。さらによく考えると、p, g, sの値に関係なく、ある文字列にパディング文字 '/' を追加したそのハッシュもほぼ同じ結果になる。出題者の意図とは違う気がするが、このことからコードを書き、フラグをゲットすることができた。

import socket
import re
import base58
import hashlib
import itertools
import string

def get_btc(s):
    h = hashlib.new('ripemd160')
    h.update(s)
    ripemd160 = h.digest()

    btcbytes_head = '\x00' + ripemd160
    chk = hashlib.sha256(hashlib.sha256(btcbytes_head).digest()).digest()[:4]
    btcbytes = btcbytes_head + chk

    return base58.b58encode(btcbytes)

pattern = '\| Send a BTC valid address that starts with (.+)\:'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('66.172.27.77', 11019))

data = s.recv(256)
print data
print 'Y'
s.sendall('Y\n')
data = s.recv(256)
print data
m = re.match(pattern, data)
start = m.group(1)

for c in itertools.product(string.printable, repeat=4):
    text = ''.join(c)
    try_btc = get_btc(text)
    if try_btc.startswith(start):
        print try_btc
        s.sendall(try_btc + '\n')
        break

data = s.recv(1024)
print data
data = s.recv(1024)
print data
print 'P'
s.sendall('P\n')
data = s.recv(1024)
print data
data = s.recv(1024)
print data
print 'R'
s.sendall('R\n')
data = s.recv(1024)
print data
data = s.recv(1024)
print data
print 'S'
s.sendall('S\n')
data = s.recv(1024)
print data
print 'ab'
s.sendall('ab\n')
data = s.recv(1024)
print data
print 'ab/'
s.sendall('ab/\n')
data = s.recv(1024)
print data
ASIS{ce8a30c725bdc9fea1da21102fdb480f}

Tokyo Westerns CTF 3rd 2017 Writeup

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

Welcome!! (Misc, Warmup)

問題で書かれているフラグを投入するだけ。

TWCTF{Welcome_To_TWCTF2017!!}

Palindromes Pairs - Coding Phase - (PPC, Warmup)

$ nc ppc1.chal.ctf.westerns.tokyo 8765
####################################
#        Palindromes Pairs         #
####################################

A word is called "palindrome" if it reads the same backward as forward.
For instance, "cba", "a", "caab" and "deaed" are palindromes.

Given the list of words s_1, s_2, s_3, ..., s_n, 
your task is the count pair (i, j) that the concatenation of s_i and s_j is palindrome.


Input Format:
The first line contains n.
The second line contains s_1, s_2, s_3, ..., s_n separated by space. 
n
s_1 s_2 s_3 ... s_n

Output Format:
Your program must output the number of pairs in one line.

Conditions:
 * n <= 50
 * |TestCase| = 50
 * Each word only contains lower alphabets.

Example Input 1:
3
a ba cab

Example Output 1:
3

Explanation of Example1:
'aa' (1,1), 'aba' (1,2), 'bacab' (2,3)

Example Input 2:
5
a aa aaa aaaa aaaaa

Example Output 2:
25


----- START -----
Input 1/50
50
xxx xxc x rx rcr sm c rs mm m s cr rmr xx cc mmx s m xx x m s c r sx cs r rrx r cs rx rx cs rrr xc sxr s s m cr sc scs r x x xmr rc rsm rmm xxm

与えられた文字から2つを組み合わせて回文がいくつできるかを答える。

import socket
import re
import itertools

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('ppc1.chal.ctf.westerns.tokyo', 8765))

data = s.recv(1024)
print data

for i in range(50):
    data = s.recv(1024)
    print data

    pattern = 'Input (.+)\n(.+)\n(.+)'
    m = re.search(pattern, data)
    chars = m.group(3).split(' ')

    count = 0
    for c in itertools.product(chars, repeat=2):
        text = ''.join(c)
        if text == text[::-1]:
            count += 1

    print count
    s.sendall(str(count) + '\n')
    data = s.recv(1024)
    print data

data = s.recv(1024)
print data
TWCTF{find_favorite_smell}

BabyDLP (Crypto)

与えられたコードから暗号化は以下のようになっていることがわかる。

c = pow(g, m^s, p)
・c: 暗号
・g, pは固定値で値はわかっている。
・m: フラグを数値化したもの
・s: 指定可能

0を指定した時はm^sはフラグを数値化したものになる。下位ビットから1,2,4,8,16,…と順に1ビットずつ判定する。

・取得した暗号がフラグの暗号のg**(g**i)倍(mod p)であれば、フラグの該当ビットは立っていない。
・フラグの暗号が取得した暗号のg**(g**i)倍(mod p)であれば、フラグの該当ビットは立っている。
import socket
import re

p = 160634950613302858781995506902938412625377360249559915379491492274326359260806831823821711441204122060415286351711411013883400510041411782176467940678464161205204391247137689678794367049197824119717278923753940984084059450704378828123780678883777306239500480793044460796256306557893061457956479624163771194201
g = 2

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('ppc2.chal.ctf.westerns.tokyo', 28459))

print '0'
s.sendall('0\n')
base = s.recv(1024)
print base
base = int(base[2:], 16)

bin_flag = ''
flag = ''
i = 0
times = g**(g**i)
while True:
    bit_str = hex(g**i)[2:].replace('L', '')
    print bit_str
    s.sendall(bit_str + '\n')
    data = s.recv(1024)
    print data
    data = int(data[2:], 16)
    if i != 0:
        times = (times**2) % p
    if (base * times) % p == data:
        bin_flag = '0' + bin_flag
    elif (data * times) % p == base:
        bin_flag = '1' + bin_flag
    i += 1
    if i % 8 == 0:
        flag = chr(int(bin_flag, 2)) + flag
        bin_flag = ''
    if flag.startswith('TWCTF{'):
        break

print 'flag: ' + flag
TWCTF{0f97c1c3ac2aedbd7fb8cd39d50f2b561d31f770}

HackIT CTF 2017 Writeup

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

USB ducker (Foren 100)

USBの通信パケットをキャプチャしたpcapファイルが与えられる。DESCRIPTOR Response DECICEを探してキーボードの種類を調べると、No.267のパケットから Aluminum Keyboard (ISO)とわかる。
そこでhttp://www.usb.org/developers/hidpage/Hut1_12v2.pdfを参考に押したキーを割り当てていく。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from scapy.all import *

keymap = { 0x04: ('a', 'A'), 0x05: ('b', 'B'), 0x06: ('c', 'C'),
           0x07: ('d', 'D'), 0x08: ('e', 'E'), 0x09: ('f', 'F'),
           0x0a: ('g', 'G'), 0x0b: ('h', 'H'), 0x0c: ('i', 'I'),
           0x0d: ('j', 'J'), 0x0e: ('k', 'K'), 0x0f: ('l', 'L'),
           0x10: ('m', 'M'), 0x11: ('n', 'N'), 0x12: ('o', 'O'),
           0x13: ('p', 'P'), 0x14: ('q', 'Q'), 0x15: ('r', 'R'),
           0x16: ('s', 'S'), 0x17: ('t', 'T'), 0x18: ('u', 'U'),
           0x19: ('v', 'V'), 0x1a: ('w', 'W'), 0x1b: ('x', 'X'),
           0x1c: ('y', 'Y'), 0x1d: ('z', 'Z'), 0x1e: ('1', '!'),
           0x1f: ('2', '@'), 0x20: ('3', '#'), 0x21: ('4', '$'),
           0x22: ('5', '%'), 0x23: ('6', '^'), 0x24: ('7', '&'),
           0x25: ('8', '*'), 0x26: ('9', '('), 0x27: ('0', ')'),
           0x28: ('[Enter]', '[Enter]'), 0x29: ('\x1b', '\x1b'),
           0x2a: ('\x08', '\x08'), 0x2b: ('\x09', '\x09'),
           0x2c: ('\x20', '\x20'), 0x2d: ('-', '_'),
           0x2e: ('=', '+'), 0x2f: ('[', '{'), 0x30: (']', '}'),
           0x31: ('\\', '|'), 0x32: ('#', '~'), 0x33: (';', ':'),
           0x34: ('\'', '\"'), 0x35: ('`', '~'), 0x36: (',', '<'),
           0x37: ('.', '>'), 0x38: ('/', '?'), 
           0x51: ('[Down]', '[Down]'), 0x52: ('[Up]', '[Up]') }

def read_usb_data_from_pcap():
    packets = rdpcap('task.pcap')
    usb_data = []
    for p in packets:
        buf = p['Raw'].load
        if buf[19] == '\x03' and buf[22] == '\x01':
            usb_data.append(buf[27:])
    return usb_data

def analyze_usb_data(usb_data):
    flag = ''
    for d in usb_data:
        if d[2] == '\x00' or not('\x00' in d[3:8]):
            continue
        if d[0] == '\x02' or d[0] == '\x20':
            c = keymap[ord(d[2])][1]
            flag += c
        else:
            c = keymap[ord(d[2])][0]
            flag += c
    print flag

def main():
    data = read_usb_data_from_pcap()
    analyze_usb_data(data)

if __name__ == '__main__':
    main()

これを実行すると、以下のようなキー入力があったことがわかる。

w[Enter]k[Enter]f[Enter]b[Enter]3'[Up][[Up]l[Up]#[Up]{w$[Down]>b[Down]ag[Down][e[Down]ci.[[Up][f[Up]{k[Up]n$[Up]ju}[Down]:[Down]3[Down]u[Down]%=[Up]~[Up]y[Up]6[Up],'[Down]p[Down]b[Down]7[Down]%&[Up]d[Up]0[Up]j[Up]pt[Down]i[Down]a[Down][[Down]k([Up]=[Up]r[Up]m[Up]]=[Down]0[Down]d[Down]>[Down]lc[Up]*[Up]_[Up]{[Up]j%[Down]u[Down]s[Down]([Down]*2[Up]0[Up]n[Up]'[Up];9[Down]h[Down]4[Down]][Down]y4[Up]'[Up]k[Up];[Up]+p[Down]f[Down]e[Down]$[Down]!}[Up]1[Up]_[Up]k[Up]s&[Down]s[Down]2[Down]c[Down]%q[Up]$[Up].[Up]![Up]#,[Down]s[Down]0[Down]c[Down]z3[Up]e[Up]}[Up]-[Up]i

上記の結果通りキーを押していくと、3段目にフラグが出来上がる。

w{w$ju},'pt]=j%;9+ps&#,i
k#>bn$:6pjim0{u'h;fks!s-
flag{k3yb0ard_sn4ke_2.0}
b[[e[fu~7d[=>*(0]'$1c$ce
3'ci.[%=%&k(lc*2y4!}%qz3
flag{k3yb0ard_sn4ke_2.0}