b00t2root '19 Writeup

この大会は2019/3/30 2:30(JST)~2019/3/31 2:30(JST)に開催されました。
今回もチームで参戦。結果は4155点で323チーム中5位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (misc)

slackに入ると、#generalチャネルにフラグが書いてあるメッセージがピン止めされている。

b00t2root{w3lc0me_h0pe_y0u_h4v3_fun}

Steve Rogers (linux)

添付のシェルスクリプトでログインする。プロセスを見ると、フラグを引数にして実行されているものがあった。

steve@2ad43eba9d74:~$ ls -la
total 20
drwxr-xr-x 2 steve steve 4096 Mar 29 15:30 .
drwxr-x--x 1 root  root  4096 Mar 29 15:30 ..
-rw-r--r-- 1 steve steve  220 Mar 29 15:30 .bash_logout
-rw-r--r-- 1 steve steve 3771 Mar 29 15:30 .bashrc
-rw-r--r-- 1 steve steve  807 Mar 29 15:30 .profile
steve@e7d4a74360d0:~$$ ps -ef | grep b00t2root | grep -v grep
root         1     0  0 22:03 pts/0    00:00:00 bash /tmp/42.sh b00t2root{Cmd_l1n3_fl4g5_4r3_0bv10u5}
b00t2root{Cmd_l1n3_fl4g5_4r3_0bv10u5}

cuz_rsa_is_lub (crypto)

nをFermat法で素因数分解して、復号する。

from Crypto.Util.number import *

def isqrt(n):
    x = n
    y = (x + n // x) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def fermat(n):
    x = isqrt(n) + 1
    y = isqrt(x * x - n)
    while True:
        w = x * x - n - y * y
        if w == 0:
            break
        elif w > 0:
            y += 1
        else:
            x += 1
    return x - y, x + y

n = 71641831546926719303369645296528546480083425905458247405279061196214424558100678947996271179659761521775290973790597533683668081173314940392098256721488468660504161994357
e = 65537
c = 63127079832500412362950100242549738176318170072331491750802716138621322974529994914407846448954487685068331564008936808539420562251661435790855422130443584773306161128156

p, q = fermat(n)

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
b00t2root{RSA_c4n_b3_vuln3r4bl3}

Scatter Me (crypto)

x:y:1というデータが;区切りで並んでいる。scatter(散布)というタイトルからも散布図を書けばフラグになりそう。

import matplotlib.pyplot as plt
import numpy as np

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

codes = data.split(';')
x = np.arange(len(codes), dtype = 'float')
y = np.arange(len(codes), dtype = 'float')
for i in range(len(codes)):
    x[i] = float(codes[i].split(':')[0])
    y[i] = float(codes[i].split(':')[1])

plt.scatter(x, y, label="flag")
plt.legend()
plt.show()

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

b00t2root{300728}

Genetics (crypto)

https://github.com/JohnHammond/ctf-katanaのDNA Codeの対応表を参考に復号する。

import string

dna_code_tbl = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 .'

enc = 'ACCAGTAAAACGTTGAGACAGTTGAATATCAAACTACACCGAATTCATATGTCACAGCGGCCGACACAGATGATAACA'

enc = enc.replace('A', '00')
enc = enc.replace('C', '01')
enc = enc.replace('G', '10')
enc = enc.replace('T', '11')

dec = ''
for i in range(0, len(enc), 6):
    idx = int(enc[i:i+6], 2)
    dec += dna_code_tbl[idx]

print dec

実行結果は以下の通り。

flag is dnaCrypto1sAwesome
b00t2root{dnaCrypto1sAwesome}

Let's Travel (crypto)

異なる2つの文字列を与えて、同じHash値になればよい。頭に\x00をつけてもHash値は変わらないので、そのような2つを送信する。

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(('18.188.225.135', 2001))

place1 = 'Angkor'
place2 = '\x00' + place1
data = recvuntil(s, '1: \n').strip()
print data + place1
s.sendall(place1 + '\n')
data = recvuntil(s, '2: \n').strip()
print data + place2
s.sendall(place2 + '\n')
data = recvuntil(s, '\n').strip()
data += recvuntil(s, '\n').strip()
data += recvuntil(s, '\n').strip()
print data
b00t2root{C0ngr4tulati0n5!_y0u_c4n_n0w_go_4nywh3re}

Strange Oracle (crypto)

RSA LSB Oracle Attackで攻めたいが、アクセス回数制限があるため、普通のやり方だと攻撃しきれない。何回か試したところ、最初の方のアクセスでは、lsbは0になることが分かっている。そのため、制限ぎりぎりアクセスできるよう決め打ちで最初はlsbは0として、途中からアクセスして判断する方式にする。

import socket
from fractions import Fraction
from Crypto.Util.number import long_to_bytes

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

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 lsb_oracle(s, enc):
    data = recvuntil(s, ': ')
    print data
    print '2'
    s.sendall('2\n')
    data = recvuntil(s, ': ')
    print data + enc
    s.sendall(enc + '\n')
    data = recvuntil(s, '\n').strip()
    print data.encode('hex')
    print data
    if data == 'I like it!':
        return 0
    else:
        return 1

LIMIT = 450

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('18.188.225.135', 2002))

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

#### calculate n ####
try_rsa_enc = []
for m in [2, 4, 16]:
    print '1'
    s.sendall('1\n')
    data = recvuntil(s, ': ')
    print data + chr(m)
    s.sendall(chr(m) + '\n')
    data = recvuntil(s, '\n').strip()
    print data
    enc = data[12:]
    try_rsa_enc.append(int(enc, 16))
    data = recvuntil(s, ': ')
    print data

diff1 = (try_rsa_enc[0]) ** 2 - try_rsa_enc[1]
diff2 = (try_rsa_enc[1]) ** 2 - try_rsa_enc[2]

n, _, _ = egcd(diff1, diff2)
e = 65537

for i in range(2, 10):
    if n % i == 0:
        n = n / i
        break

#### get encrypted flag ####
print '3'
s.sendall('3\n')
enc_flag = recvuntil(s, '\n').strip()
print enc_flag

#### get flag ####
c = int(enc_flag, 16)
bounds = [0, Fraction(n)]

i = 0
while True:
    print 'Round %d' % (i+1)
    i += 1

    c2 = (c * pow(2, e, n)) % n
    h2 = '%x' % c2
    if len(h2) % 2 == 1:
        h2 = '0' + h2
    if i > (1024 - LIMIT + 4):
        lsb = lsb_oracle(s, h2)
    else:
        lsb = 0
    if lsb == 1:
        bounds[0] = sum(bounds)/2
    else:
        bounds[1] = sum(bounds)/2
    diff = bounds[1] - bounds[0]
    diff = diff.numerator / diff.denominator

    if diff == 0:
        m = bounds[1].numerator / bounds[1].denominator
        break
    c = c2

flag = long_to_bytes(m)
print flag
b00t2root{dr_s7r4ng3_1s_4_m4rv3l0u5_0r4cl3}