RCTF 2018 Writeup

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

Number Game (Misc)

4つの数字を言うと、位置が合っている数と位置が違うが含まれている数を教えてもらい、正しい数字を推測するゲーム。Bulls and cows という名前らしい。
https://rosettacode.org/wiki/Bulls_and_cows/Player#Python を参考にコードを書く。チャンスは6回。

import socket
import re
import itertools
import string
import hashlib

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

def parse_score(score):
    score = score.strip().split(',')
    return tuple(int(s.strip()) for s in score)

def scorecalc(guess, chosen):
    bulls = cows = 0
    for g,c in itertools.izip(guess, chosen):
        if g == c:
            bulls += 1
        elif g in chosen:
            cows += 1
    return bulls, cows

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('149.28.139.172', 10002))

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

pattern = 'sha256\(\*\*\*\*\+(.+)\) == (.+)'
m = re.match(pattern, data)
text_tail = m.group(1)
h = m.group(2)

data = recvuntil(s, ':')
print data

for c in itertools.product(string.digits + string.letters, repeat=4):
    text_head = ''.join(c)
    text = text_head + text_tail
    if hashlib.sha256(text).hexdigest() == h:
        print text_head
        s.sendall(text_head + '\n')
        break

data = ''
for i in range(17):
    data += recvuntil(s, '\n')
print data

digits = '0123456789'

for round in range(8):
    choices = list(itertools.permutations(digits, 4))
    answers = []
    scores  = []

    data = ''
    for i in range(2):
        data += recvuntil(s, '\n')
    print data

    while True:
        ans = choices[0]
        answers.append(ans)
        dig_str = ' '.join(ans)
        print dig_str
        s.sendall(dig_str + '\n')
        data = recvuntil(s, '\n')
        print data
        if 'Nope.' in data:
            score = parse_score(data[6:])
            scores.append(score)
            choices = [c for c in choices if scorecalc(c, ans) == score]
        else:
            break

data = s.recv(1024)
print data

失敗することも多々あるが、8ラウンド成功すればよいので、このコードで何回も挑戦。

RCTF{0lD_GaM3_nAmed_Bu11s_4nd_C0ws}

ECDH (Crypto)

$ nc ECDH.2018.teamrois.cn 42000

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 1

Hello nobody...I'm Alice... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Bob's public key
4. tell me Bob's public key
input here: 1
Bob sent me something.Bob said: Just kidding~

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 1

Hello nobody...I'm Alice... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Bob's public key
4. tell me Bob's public key
input here: 2
pub: 03474f81154bb8b931ddc36f9746484517

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 1

Hello nobody...I'm Alice... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Bob's public key
4. tell me Bob's public key
input here: 3
pub: 034320a6aeb0b093332d883a0c17fac4ce

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 1

Hello nobody...I'm Alice... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Bob's public key
4. tell me Bob's public key
input here: 4

Alice have a new public key? Thank you for telling me!
input here with hex string (e.g deadbeef): deadbeef
Oops!

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 2

Hello nobody...I'm Bob... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Alice's public key
4. tell me Alice's public key
input here: 1
I'v already told Alice...bye

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 2

Hello nobody...I'm Bob... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Alice's public key
4. tell me Alice's public key
input here: 2
pub: 034320a6aeb0b093332d883a0c17fac4ce

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 2

Hello nobody...I'm Bob... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Alice's public key
4. tell me Alice's public key
input here: 3
pub: 03474f81154bb8b931ddc36f9746484517

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 2

Hello nobody...I'm Bob... you can:
1. ask for flag
2. ask me about my public key
3. ask me about Alice's public key
4. tell me Alice's public key
input here: 4

Bob have a new public key? Thank you for telling me!
input here with hex string (e.g deadbeef): deadbeef
Oops!

Welcome to my GETFLAG system
1. visit Alice
2. visit Bob
3. about
input here: 3
ECDH.....https://github.com/esxgx/easy-ecc..secp128r1..AES...EBC.......

ECDHで共通鍵を渡し、AESのECBモードで暗号化したメッセージをBobが送っているということのようだ。

https://github.com/esxgx/easy-ecc
・public keyの2バイト目以降:l_public.x
・public keyの1バイト目:2 + (l_public.y[0] & 0x01)

上記の仕様に基づいているが、タイトル通りECDHの問題。

dA: Aliceの秘密鍵
dB: Bobの秘密鍵
QA: Aliceの公開鍵(QA = dA * G)
QB: Bobの公開鍵(QB = dB * G)

dA * QB = dA * (dB * G) = dB * (dA * G) = dB * QA

これが共通鍵になる。
Aliceの公開鍵をGにすると、dB * G、つまりBobの公開鍵が共通鍵となる。共通鍵を取得できたら、AESのECBモードで復号するとフラグが得られる。スクリプトにすると以下のようになる。

import socket
from Crypto.Cipher import AES

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

def send_data(s, data):
    r = recvuntil(s, ': ')
    print r + data
    s.sendall(data + '\n')

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('ECDH.2018.teamrois.cn', 42000))

# Alice's public key setting
send_data(s, '2')
send_data(s, '4')
send_data(s, '03161ff7528b899b2d0c28607ca52c5b86')

# get Bob's public key
send_data(s, '2')
send_data(s, '2')
r = recvuntil(s, '\n')
print r[:-1]
key = r[7:-1].decode('hex')

# get encrypted flag
send_data(s, '2')
send_data(s, '1')
send_data(s, '1')
send_data(s, '1')
r = recvuntil(s, '\n')
print r[:-1]
enc = r[32:-1].decode('hex')

# decrypt
aes = AES.new(key, AES.MODE_ECB)
flag = aes.decrypt(enc)
while True:
    if flag[-1] == '\x00':
        flag = flag[:-1]
    else:
        break
print flag
RCTF{UgotTHEpoint}

DEF CON CTF Qualifier 2018 Writeup

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

You Already Know (warmup)

ブラウザのデベロッパーツールで確認する。

{"success": true, "message": "Stop overthinking it, you already know the answer here.\n\n[comment]: <> (OOO{Sometimes, the answer is just staring you in the face. We have all been there})\n\nYou already have the flag.\n\n**Seriously**, _if you can read this_, then you have the flag.\n\nSubmit it!\n"}

コメントにフラグが書かれている。

OOO{Sometimes, the answer is just staring you in the face. We have all been there}

Say Hi! (human interaction)

母の日という前置きがあって、親兄弟、配偶者や友人など特別な人に言う言葉を入れればよいみたい。他の言葉でも正解があるのかもしれないが、母の日と言えばこれかと思い、この言葉を入れたら通った。

Thanks

PlaidCTF 2018 Writeup

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

macsh (Crypto 125)

'<|>'区切りで1個目がmac, 2個目がcmdline

$ nc macsh.chal.pwning.xxx 64791
|$|> aaa<|>ls
macsh: bad tag
|$|> aaa<|>oo
macsh: oo: command not found
|$|> aaa<|>tag echo 123
feb086773d17f6438c7630d7da1dba79
|$|> bbb<|>tag echo 123
feb086773d17f6438c7630d7da1dba79
|$|> aaa<|>tag echo 456
d610717ba31f48a0cbde88926e95bd82
|$|>

k0, k1はランダムな16バイト文字列でmtagを先頭につけたときはmacを表示してくれる。
k0の鍵を割り出そうと考えたが、それは難しい。よく見ると129ブロック目は1ブロック目から鍵を再度使うようになっている。このことを使い、例えば以下のように考える。

ブロック データ1 データ2
1ブロック目 echo 0123456789; echo 0123456789;
2ブロック目 echo 0123456789; echo 0123456789;
128ブロック目 echo 0123456789; echo 0123456789;
129ブロック目 echo ls .
130ブロック目 コマンド長情報 コマンド長情報
131ブロック目 padding(\x10*16) padding(\x10*16)

こう考えると、データ1とデータ2の129ブロック以外の各ブロックの暗号化のXORは0となるため、echoコマンドとlsコマンドのmacのXORの値がわかる。さらに1ブロック目がechoコマンドの場合とXORを取れば1ブロック目がlsコマンドの場合のmacを取得できる。
lsコマンドでflag.txtがあることがわかるので、cat flag.txtでフラグが得られる。最終的なコードは以下の通り。

import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('macsh.chal.pwning.xxx', 64791))

data = recvuntil(s, '|$|> ')
print data

cmd_head = 'a<|>tag '

#### ls exec ####
cmd1 = 'echo 0123456789;' * 128 + 'echo'
cmd = cmd_head + cmd1
print cmd
s.sendall(cmd + '\n')
data = recvuntil(s, '|$|> ')
print data
mac1 = data.split('\n')[0]

cmd2 = 'echo 0123456789;' * 128 + 'ls .'
cmd = cmd_head + cmd2
print cmd
s.sendall(cmd + '\n')
data = recvuntil(s, '|$|> ')
print data
mac2 = data.split('\n')[0]

cmd3 = 'echo'
cmd = cmd_head + cmd3
print cmd
s.sendall(cmd + '\n')
data = recvuntil(s, '|$|> ')
print data
mac3 = data.split('\n')[0]

mac_ls = '%032x' % (int(mac1, 16) ^ int(mac2, 16) ^ int(mac3, 16))
cmd_ls = mac_ls + '<|>ls .'
print cmd_ls
s.sendall(cmd_ls + '\n')
data = recvuntil(s, '|$|> ')
print data

#### cat exec ####
cmd1 = 'echo 0123456789;' * 128 + 'echo 0123456'
cmd = cmd_head + cmd1
print cmd
s.sendall(cmd + '\n')
data = recvuntil(s, '|$|> ')
print data
mac1 = data.split('\n')[0]

cmd2 = 'echo 0123456789;' * 128 + 'cat flag.txt'
cmd = cmd_head + cmd2
print cmd
s.sendall(cmd + '\n')
data = recvuntil(s, '|$|> ')
print data
mac2 = data.split('\n')[0]

cmd3 = 'echo 0123456'
cmd = cmd_head + cmd3
print cmd
s.sendall(cmd + '\n')
data = recvuntil(s, '|$|> ')
print data
mac3 = data.split('\n')[0]

mac_cat = '%032x' % (int(mac1, 16) ^ int(mac2, 16) ^ int(mac3, 16))
cmd_cat = mac_cat + '<|>cat flag.txt'
print cmd_cat
s.sendall(cmd_cat + '\n')
data = recvuntil(s, '|$|> ')
print data

実行結果は以下の通り。

   :
|$|>
9daabce6ee4c110034527e8a432abd26<|>ls .
__pycache__
flag.txt
fmac.py
macsh.py
.bashrc
.bash_logout
.profile
   :
|$|>
e04241a0f78ab714e517439cd7f8dccb<|>cat flag.txt
PCTF{fmac_is_busted_use_PMAC_instead}
|$|>
PCTF{fmac_is_busted_use_PMAC_instead}

ASIS CTF Quals 2018 Writeup

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

Welcome (Trivia)

Twitterで@ASISCTFがフラグをツイートしている。

ASIS{Welcome_to_ASISCTF_Let's_increase_the_world's_entropy}

The Early School (Crypto)

暗号の処理は以下のイメージ。

FLAGを2進数にする。

FLAGの2進数の長さが79の場合
msg[0] + msg[1] + msg[0] ^ msg[min(1, 78)]
msg[2] + msg[3] + msg[2] ^ msg[min(3, 78)]
msg[4] + msg[5] + msg[4] ^ msg[min(5, 78)]
         :
msg[78] + msg[78] ^ msg[min(79, 78)]

roundの回数だけ繰り返す。

元の平文も含まれているので、復号はそれほど難しくない。

from Crypto.Util.number import *

def decrypt(msg):
    dec = ''
    for i in range(0, len(msg), 3):
        if (i + 2) < len(msg):
            assert int(msg[i]) ^ int(msg[i+1]) == int(msg[i+2])
            dec += msg[i:i+2]
        else:
            assert int(msg[i+1]) == 0
            dec += msg[i:i+1]
    return dec

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

ENC = bin(bytes_to_long(data))[2:]

i = 1
while True:
    try:
        print 'Round %d' % i
        ENC = decrypt(ENC)
        i += 1
    except:
        break

while True:
    if len(ENC) % 8 == 0:
        break
    ENC = '0' + ENC

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

print FLAG
ASIS{50_S1mPl3_CryptO__4__warmup____}

Blaze CTF 2018 参戦

この大会は2018/4/21 5:20(JST)~2018/4/26 1:20(JST)に開催されました。
今回もチームで参戦。結果は2100点で166チーム中39位でした。
今回は自分が得点した問題は1問もありませんでした。
Ethereum関連の問題もあり、いろいろ調べながらでも何とか解きたかったのですが、残念です。

NoNameCon CTF Quals 2018 Writeup

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

Subdomain (50)

nonameconctf2018.xyzのサブドメインを調査する問題。

>dnsrecon.py -d nonameconctf2018.xyz
[*] Performing General Enumeration of Domain: nonameconctf2018.xyz
[-] DNSSEC is not configured for nonameconctf2018.xyz
[*]      SOA dns1.registrar-servers.com 216.87.155.33
[*]      NS dns1.registrar-servers.com 216.87.155.33
[*]      NS dns1.registrar-servers.com 2620:74:19::33
[*]      NS dns2.registrar-servers.com 216.87.152.33
[*]      NS dns2.registrar-servers.com 2001:502:cbe4::33
[*]      MX eforward4.registrar-servers.com 162.255.118.62
[*]      MX eforward5.registrar-servers.com 162.255.118.62
[*]      MX eforward3.registrar-servers.com 162.255.118.61
[*]      MX eforward2.registrar-servers.com 162.255.118.62
[*]      MX eforward1.registrar-servers.com 162.255.118.61
[*]      TXT nonameconctf2018.xyz v=spf1 include:spf.efwd.registrar-servers.com ~all
[*] Enumerating SRV Records
[-] No SRV Records Found for nonameconctf2018.xyz
[+] 0 Records Found

>dnsrecon.py -d nonameconctf2018.xyz -t brt
[*] No file was specified with domains to check.
[*] Using file provided with tool: C:\CTF\Tool\dnsrecon\namelist.txt
[*]      A chat.nonameconctf2018.xyz 188.166.99.124
[*]      A hidden.nonameconctf2018.xyz 188.166.102.232
[*]      A info.nonameconctf2018.xyz 188.166.126.109
[*]      A www.nonameconctf2018.xyz 188.166.46.238
[+] 4 Records Found

http://hidden.nonameconctf2018.xyz/にアクセスすると、フラグが表示された。

That was easy. Flag for 50 points: nnc{ef62e674aa6585412e1ee529b4bd9090}
nnc{ef62e674aa6585412e1ee529b4bd9090}

*ctf 2018 参戦

この大会は2018/4/21 10:00(JST)~2018/4/23 10:00(JST)に開催されました。
今回もチームで参戦。結果は444点で146チーム中70位でした。
今回は自分が得点した問題は1問もありませんでした。
1問くらいは解きたかったのですが、残念です。