TUCTF 2017 Writeup

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

I'm Playing (Misc 1)

Discordにログインしたら、ダイレクトメッセージにフラグがあった。

@XXXXXXXX, Welcome to TUCTF! Please read the rules before posting!
If you have a question about a challenge, please use @Challenge: ChallengeName to automatically tag the challenge creator!

And finally, here's your "I'm playing" flag: TUCTF{I’m_Mr_m33seek5_G1V3_M3_th3_fl4g!}

Thank you for playing TUCTF!
TUCTF{I’m_Mr_m33seek5_G1V3_M3_th3_fl4g!}

High Source (Web 25)

ソースを見ると、scripts/login.jsを参照していることがわかる。scripts/login.jsを見ると、次のコードが書いてある部分がある。

var password = "I4m4M4st3rC0d3rH4x0rsB3w43";

このパスワードを認証パスワードとして入れる。

Welcome! Here's the flag: TUCTF{H1gh_S0urc3_3qu4ls_L0ng_F4ll}
TUCTF{H1gh_S0urc3_3qu4ls_L0ng_F4ll}

Cookie Duty (Web 50)

ログインした後、クッキーnot_adminの値を0にするとフラグが表示された。

TUCTF{D0nt_Sk1p_C00k13_Duty}

The Never Ending Crypto (Crypto 50)

$ nc neverending.tuctf.com 12345
------------------------------------------
Welcome to The Neverending Crypto!
How fast can you solve it?
Round 1. Give me some text:abcdef
abcdef encrypted is 234567
What is H6=4@>6\P924<6C decrypted?

シーザー暗号なので、シフト数を計算して復号するプログラムにする。

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

def ceaser(s, rot):
    p = ''
    for i in range(len(s)):
        code = ord(s[i]) - rot
        if code < 32:
            code = code + 95
        p += chr(code)
    return p

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('neverending.tuctf.com', 12345))

for i in range(50):
    data = s.recv(256)
    print data + 'abcdefgh'
    s.sendall('abcdefgh\n')
    data = s.recv(256)
    pattern = 'encrypted is (.+)'
    m = re.search(pattern, data)
    test_enc = m.group(1)
    rot =  ord(test_enc[0]) - ord('a')
    if rot < 0:
        rot = rot + 95

    pattern = 'What is (.+) decrypted?'
    m = re.search(pattern, data)
    enc = m.group(1)
    dec = ceaser(enc, rot)
    print data + dec
    s.sendall(dec + '\n')

data = s.recv(256)
print data

50回正解したら、フラグが表示された。

TUCTF{wh0_w@s_her3_la5t_ye@r?!?}

Crypto Clock (Crypto 300)

添付ファイルはpcapngファイル。パケットNo.6とNo.7のData部分をデコードすると、サーバの処理コードが得られる。

{
    "n": 14558832750877392629213532071,
    "e": 3
}
#!/usr/bin/env python

import sys
import random
import json
import arrow

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


with open('keys') as f:
    keys = json.load(f)

#now to get some randomness in here!
with open('/dev/urandom', 'rb') as f:
    rand = f.read(8)

rand_int = int(rand.encode('hex'),16)

#now lets use something easier.
random.seed(rand_int)

offset = random.randint(-1511521829,1511521829)


while True:
    sys.stdout.write( '''Welcome to the ntp server
What would you like to do?
    1) get current time
    2) enter admin area
    3) exit
:''')
    sys.stdout.flush()
    response = raw_input('')
    if response == '1':
        time = arrow.utcnow().timestamp - offset
# to increase size for security!
        enc_time = pow(time,keys['e'],keys['n'])
        sys.stdout.write('HAHAHAHAHAHA, this NTP server has been taken over by hackers!!!\n')
        sys.stdout.write('here is the time encrypted with sweet RSA!\n')
        sys.stdout.write(str(enc_time))
        sys.stdout.write('\n')
        sys.stdout.flush()
    elif response == '2':
        # lets get even more random!
        time = arrow.utcnow().timestamp - offset
        random.seed(time)
        guessing_int = random.randint(0,999999999999)
        sys.stdout.write('''ACCESS IS ONLY FOR TRUE HACKERS!
to prove you are a true hacker, predict the future:''')
        sys.stdout.flush()
        response = raw_input('')
        if response == str(guessing_int):
            sys.stdout.write('''Wow, guess you are a hacker.\n''')
            sys.stdout.write(flag)
            sys.stdout.write('\n')
            break
        else:
            sys.stdout.write('''I knew you weren't a hacker''')
            sys.stdout.write('\n')
            break
    else:
        print 'Good by.'
        break

時間をおいて、2回暗号化すると、(n, e, mの暗号), (n, e, m+bの暗号) (bは小さい)の2組が得られる。Franklin-Reiter Related Message Attackで1回目の平文を復号できる。その結果から、offsetを取得できるので、それをseedにした乱数の1回目を答えればよい。

# crypto_clock.sage
import socket
import time, datetime
import random

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+diff)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('cryptoclock.tuctf.com', 1230))
data = s.recv(256)

print data + '1'
s.sendall('1\n')
data = s.recv(256)
c1 = int(data.split('\n')[2])
now1 = datetime.datetime.now()
cur1 = int(time.mktime(now1.timetuple()))

time.sleep(3)

print data + '1'
s.sendall('1\n')
data = s.recv(256)
c2 = int(data.split('\n')[2])
now2 = datetime.datetime.now()
cur2 = int(time.mktime(now2.timetuple()))

n = 14558832750877392629213532071
e = 3
diff = cur2 - cur1

m = related_message_attack(c1, c2, diff, e, n)
offset = cur1 - m

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

now3 = datetime.datetime.now()
time = int(time.mktime(now2.timetuple())) - offset
random.seed(time)
guessing_int = random.randint(0, 999999999999)
print guessing_int

s.sendall(str(guessing_int) + '\n')
data = s.recv(256)
print data
TUCTF{g00d_th1ng_th3_futur3_i5_r3lated!}