ISITDTU CTF 2018 Quals Writeup

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

WELCOME !!! (Misc)

Telegram Channelにフラグが書かれていた。

ISITDTU{Y0u_4r3_0nl1n3!!_3nj0y_th3_CTF}

Somewhere (Misc)

ソースを見るとhidden.cssを読み込んでいることが分かる。https://ctf.isitdtu.com/themes/DuyTan/static/css/hidden.cssにアクセスすると、フラグが表示された。

ISITDTU{y4y_y0u_f0und_m3!!!}

Play with ... (Misc)

0xffとXORをとると、PNGファイルになる。
f:id:satou-y:20180802214956p:plain
too low と書いてあるので、画像の高さを高くしてみる。
f:id:satou-y:20180802215110p:plain
鏡文字になっているので左右反転し、文字に起こす。

KVXHA3DFMFZWC3TUEBXG64RAMRUW22LOOV2GS33OEBSXQY3FNRWGK3TDMUQGC4DBOJ2G2ZLOORZSA2LNOBZ
HKZDFNZRWKIDUNBSSA3LFOQQG4ZLXFYQEI4TBO4QHAYLSOQQHI2DFNUQGQZJAMFXCA5DPEBUGKIDSN5XWMIDPN
ZWHSLRAJV2XG2LDEBWGKYLWMUQHGYLZEBSG633SOMQGQ2LNFYQFI33SMUQGE4TFMQQGM33SNUQGSZRAONU
WO2BAMNQXGZJAMFZSAZDPFYQFG5DBPFUW4ZZANBSSA3TPEBWG633LNFXGOIDJMYQGI3ZAN5YGS3TJN5XC4ICTM
VXHI2LNMVXHI4ZAO5QXSIDVNZSGK4TTORXW6ZBAMVXGIIDQMFZHI2LBNRUXI6JAMFXGIIDINFZS4IAKBJKHK4TOMV
SCA2LUEB2XAIDTNBXXK3DEEBXG6IDWMFWGYZLZEBRW65LTNFXCA2DFFYQFG4DFMFVWS3THEBXHK3LFOJXXK4ZAM
FZWWIDENFSCA2DPOJZGSYTMMUQHAYLDNNQWOZLTEBZWK5BOEBAXG2DBNVSWIIDIMVZHGZLMMYQGQYLTEBSGS
43UMFXHIIDDMFXCA43UOVSGSZLEEBWXE4ZOEBGGKZBAORUGK4TFMZXXEZJANF2HGIDNNFSGI3DFORXW4IDQMVZ
HAZLUOVQWYIDGOVWGM2LMNRSWIIDQOJXXM2LTNFXW4IDGOJQW423OMVZXGLRAIZWGCZZANFZSASKTJFKEIVCV
PM2G4XZUNZTTG3C7GBTF6MLNGRTTGX3IGNZDGIL5FYQFG3LBNRWCA2DFEBSHEYLXNYQGCZTUMVZCAYLNN5XGOID
FOZSXE6JAORUHEZLFEBXG6LRAIFWGYIDIMF3GS3THEBRHK5BAPFXXKIDFMR3WC4TEEBTWK3TJOVZSA5DIN52WO2BA
OJSW2YLSNMQG63TFFYQAUCSJNZZWS4DJMRUXI6JAORUGKIDTOVTGM2LDNFSW45BAMRUXGY3SMV2GS33OEBUW
24DSOVSGK3TDMUQHEZLTN5WHK5DJN5XCA43JOIQGQ2LNEBSGKY3JONUXMZLMPEXCAUDSN5RWKZLEEBUG65ZAM
FXHSIDFNZTWCZ3FMQQHM2LTNF2G64ROEBCXQ4DMMFUW4ZLEEBYHE33QOJUWK5DZEBXWMZRAN52XIIDQMVZH
AZLUOVQWYIDINFZSA6LPOUXCARTFMVWCA43PNRSCA33GMYQGMZLMOQQG4YLZEBZG643FEBWWK5BAPFXXKLRAK
5SSA43PEBSW45DSMVQXI2LFOMQGG5LMORUXMYLUMVSCAYLTORXW42LTNBSWIIDJOMXCAV3BOMQHG2LTORSXE
IDGN5ZCAZTFO4QGY33OM5SXEIDNOJZSA43VMRSGK3RAORQWYZLOOQQGEZLDN5WWKLRAIRXW4ZJANVQXSIDCN5
ZGKIDROVUXIIDFOZUWYIDPNRSCA3LINRSS4ICJMYQGY2LLMVWHSIDBNUQG6ZRAMJSWC5LUPEQHIYLTORSXGLRA

BASE32文字のようなので、デコードする。

Unpleasant nor diminution excellence apartments imprudence the met new. Draw part them he an to he roof only. Music leave say doors him. Tore bred form if sigh case as do. Staying he no looking if do opinion. Sentiments way understood end partiality and his.

Turned it up should no valley cousin he. Speaking numerous ask did horrible packages set. Ashamed herself has distant can studied mrs. Led therefore its middleton perpetual fulfilled provision frankness. Flag is ISITDTU{4n_4ng3l_0f_1m4g3_h3r3!}. Small he drawn after among every three no. All having but you edward genius though remark one.

Insipidity the sufficient discretion imprudence resolution sir him decisively. Proceed how any engaged visitor. Explained propriety off out perpetual his you. Feel sold off felt nay rose met you. We so entreaties cultivated astonished is. Was sister for few longer mrs sudden talent become. Done may bore quit evil old mhle. If likely am of beauty tastes.

文中にフラグがあった。

ISITDTU{4n_4ng3l_0f_1m4g3_h3r3!}

XOR (Crypto)

XOR keyは10バイト。計算して少し複雑な順番でXORをしているが、フラグの最初がISITDTU{で最後が}になることを使って、XOR keyを求め、復号する。

ISITDTU{*el*ome_to_ISITDTUCT*_C*ntest!_Hav3_a_g0*d_*ay._Hope_y0u_w1l*_3*j0y_and_hav3_a_h*gh*rank_1n_0ur_F1rs*_C*f_C0nt3st._Thank*}

1文字だけkeyがわからないが、{の後はWが来ると予想できることから復号する。

enc_flag = '1d14273b1c27274b1f10273b05380c295f5f0b03015e301b1b5a293d063c62333e383a20213439162e0037243a72731c22311c2d261727172d5c050b131c433113706b6047556b6b6b6b5f72045c371727173c2b1602503c3c0d3702241f6a78247b253d7a393f143e3224321b1d14090c03185e437a7a607b52566c6c5b6c034047'

m = []
for i in range(0, len(enc_flag), 2):
    m.append(int(enc_flag[i:i+2], 16))

idxes = []
for a in range(10):
    i = a
    for b in range(len(m)/10):
        if b % 2 != 0:
            idxes.append(i)
        else:
            idxes.append(i+10-(a+1+a))
        i += 10

FLAG_HEAD = 'ISITDTU{'
FLAG_TAIL = '}'

key = [-1] * 10
for i in range(len(FLAG_HEAD)):
    idx = idxes.index(i)
    key[9-i] = ord(FLAG_HEAD[i]) ^ m[idx]

print key

idx = idxes.index(len(m) - 1)
key[0] = ord(FLAG_TAIL) ^ m[idx]

FLAG_EXPECTED_FIRST = 'W'
idx = idxes.index(len(FLAG_HEAD))
key[1] = ord(FLAG_EXPECTED_FIRST) ^ m[idx]

flag = ''
for i in range(len(m)):
    idx = idxes.index(i)
    k = key[idx//(len(m)/10)]
    if k == -1:
        flag += '*'
    else:
        flag += chr(m[idx] ^ k)

print flag
ISITDTU{Welcome_to_ISITDTUCTF_C0ntest!_Hav3_a_g00d_day._Hope_y0u_w1ll_3nj0y_and_hav3_a_h1gh_rank_1n_0ur_F1rst_Ctf_C0nt3st._Thank5}

Love CryptoGraphy (Crypto)

mesに"ISITDTU"が含まれていることがわかっている。同じ文字は同じ数字になるので、該当箇所を探す。

I: 1470896290937720121923671834268680644293311486759008609851306976
S: 998119569566922793772397587105095499481847516084421474175736111
I: 1470896290937720121923671834268680644293311486759008609851306976
T: 2108440654988370562048343461399852810852299068780806568036885800
D: 549685894064591284908235658839357390847445522332458370260385633
T: 2108440654988370562048343461399852810852299068780806568036885800
U: 903564225292763328142142737672378470519554721949504047040621938

このことからm, c, nを求める。それから逆算すれば、復号できる。

from Crypto.Util.number import inverse

enc_nums = [360575205516272353647725959973923332922859934062623515990157287L, 147121471099487603100103942210642238821212368870164629959708554L, 973776523698456974485285268207248463304785539161797442415515724L, 973776523698456974485285268207248463304785539161797442415515724L, 1989542264845745277130976293069288745712944297723265109141551240L, 2251682090999461666252822948628263912168715816760972058692440747L, 147121471099487603100103942210642238821212368870164629959708554L, 500999802327659646334011021043663318493321568487210306739944859L, 147121471099487603100103942210642238821212368870164629959708554L, 690110490875978577594520719909097376417907156757045161010173205L, 1516765543474947948979702045905703600901480327048677973465980375L, 1446553245069254302636559515370833608116249509836384578091086589L, 1989542264845745277130976293069288745712944297723265109141551240L, 1351997900795094837006304665938116579153956715701467150955972416L, 1516765543474947948979702045905703600901480327048677973465980375L, 1684350025354504872471293852031961738394959051951467495881755709L, 2251682090999461666252822948628263912168715816760972058692440747L, 595555146601819111964265870476380347455614362622127733875059032L, 1162887212246775905745794967072682521229371127431632296685744070L, 2273208297668223674021740841367439832599822680128017390546893759L, 1800431576297426345870466594203854687788358709453430254871322894L, 2251682090999461666252822948628263912168715816760972058692440747L, 2273208297668223674021740841367439832599822680128017390546893759L, 1800431576297426345870466594203854687788358709453430254871322894L, 2251682090999461666252822948628263912168715816760972058692440747L, 1516765543474947948979702045905703600901480327048677973465980375L, 1989542264845745277130976293069288745712944297723265109141551240L, 1705876232023266880240211744771137658826065915318512827736208721L, 690110490875978577594520719909097376417907156757045161010173205L, 2251682090999461666252822948628263912168715816760972058692440747L, 1257442556520935371376049816505399550191663921566549723820858243L, 973776523698456974485285268207248463304785539161797442415515724L, 336232159647806534360613641076076296745797957139999484229936900L, 52566126825328137469849092777925209858919574735247202824594381L, 1022462615435388613059509906002942535658909493007045505935956498L, 2251682090999461666252822948628263912168715816760972058692440747L, 1470896290937720121923671834268680644293311486759008609851306976L, 998119569566922793772397587105095499481847516084421474175736111L, 1470896290937720121923671834268680644293311486759008609851306976L, 2108440654988370562048343461399852810852299068780806568036885800L, 549685894064591284908235658839357390847445522332458370260385633L, 2108440654988370562048343461399852810852299068780806568036885800L, 903564225292763328142142737672378470519554721949504047040621938L, 1422210199200788483349447196472986571939187532913760546330866202L, 1446553245069254302636559515370833608116249509836384578091086589L, 690110490875978577594520719909097376417907156757045161010173205L, 147121471099487603100103942210642238821212368870164629959708554L, 336232159647806534360613641076076296745797957139999484229936900L, 2178652953394064208391485991934722803637529885993099963411779586L, 430787503921965999990868490508793325708090751274916911365051073L, 171464516967953422387216261108489274998274345792788661719928941L, 1754562323760198518814436382566831731180189869163760891256649495L, 1565451635211879587553926683701397673255604280893926036986421149L, 430787503921965999990868490508793325708090751274916911365051073L, 1705876232023266880240211744771137658826065915318512827736208721L, 879221179424297508855030418774531434342492745026880015280401551L, 2178652953394064208391485991934722803637529885993099963411779586L, 879221179424297508855030418774531434342492745026880015280401551L, 1989542264845745277130976293069288745712944297723265109141551240L, 1611320887749107414609956895338420629863773121183595400601094548L, 879221179424297508855030418774531434342492745026880015280401551L, 430787503921965999990868490508793325708090751274916911365051073L, 336232159647806534360613641076076296745797957139999484229936900L, 973776523698456974485285268207248463304785539161797442415515724L, 973776523698456974485285268207248463304785539161797442415515724L, 1327654854926629017719192347040269542976894738778843119195752029L]

enc_S = enc_nums[37]
enc_T = enc_nums[39]
enc_U = enc_nums[42]

m = enc_T - enc_S
n = m - (enc_U - enc_T)
c = (enc_S - m * ord('S')) % n

mes = ''
for enc_num in enc_nums:
    code = ((enc_num - c) * inverse(m, n)) % n
    mes += chr(code)

print mes

実行結果は以下の通り。

Hello everybody, this is your flag: ISITDTU{break_LCG_unknown_all}
ISITDTU{break_LCG_unknown_all}

Baby (Crypto)

numerを指定。flagの値とのorのsha512を返す。各ビットの1とXORを取り変わらなければ1、変われば0になることを使って、1ビットずつフラグを割り出す。

import socket
from hashlib import *

def checkFlag(flag_bit, h):
    f = int(flag_bit, 2)
    if sha512(str(f)).digest().encode('hex') == h:
        return True
    else:
        return False

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('35.185.178.212', 33337))

data = s.recv(1024)
data += s.recv(1024)
print data + '0'
s.sendall('0\n')
data = s.recv(1024)
data += s.recv(1024)
print data
h_flag = data.split('\n')[1]
print 'h_flag =', h_flag

bit = 0
flag_bit = ''
while True:
    number = 2**bit
    print number
    s.sendall(str(number) + '\n')
    data = s.recv(1024)
    data += s.recv(1024)
    print data
    h = data.split('\n')[1]
    if h == h_flag:
        flag_bit = '1' + flag_bit
    else:
        flag_bit = '0' + flag_bit

    if len(flag_bit) % 8 == 0:
        flag = convFlag(flag_bit)
        print flag
        if checkFlag(flag_bit, h_flag):
            break

    bit += 1

flag = convFlag(flag_bit)
print flag
ISITDTU{bit_flipping_is_fun}

Simple RSA (Crypto)

コードの以下の部分からおおよそpはNを10**6で割った数値の4乗根。

p = next_prime(p)
p1 = next_prime(p*10)
p2 = next_prime(p1*10)
p3 = next_prime(p2*10)
N = p*p1*p2*p3

その値から少しずつ調整し、各要素の乗算がNになるものを探す。素因数分解できれば、あとはMulti-prime RSAの復号の解き方で復号できる。

import gmpy
from Crypto.Util.number import *

def rev_next_prime(n):
    num = n - 1
    while True:
        if isPrime(num):
            return num
        num += 1

def chinese_remainder(n, a):
    sum = 0
    prod = reduce(lambda a, b: a*b, n)

    for n_i, a_i in zip(n, a):
        p = prod / n_i
        sum += a_i * gmpy.invert(p, n_i) * p
    return sum % prod

N = 603040899191765499692105412408128039799635285914243838639458755491385487537245112353139626673905393100145421529413079339538777058510964724244525164265239307111938912323072543529589488452173312928447289267651249034509309453696972053651162310797873759227949341560295688041964008368596191262760564685226946006231
e = 65537
c = 153348390614662968396805701018941225929976855876673665545678808357493865670852637784454103632206407687489178974011663144922612614936251484669376715328818177626125051048699207156003563901638883835345344061093282392322541967439067639713967480376436913225761668591305793224841429397848597912616509823391639856132

tmp_p = gmpy.root(N // (10**6), 4)[0]

while True:
    p = rev_next_prime(tmp_p)
    p1 = rev_next_prime(p*10)
    p2 = rev_next_prime(p1*10)
    p3 = rev_next_prime(p2*10)
    if p*p1*p2*p3 == N:
        break
    tmp_p -= 1

primes = []
primes.append(p)
primes.append(p1)
primes.append(p2)
primes.append(p3)

n_ary = []
a_ary = []
for p in primes:
    phi = p - 1
    d = gmpy.invert(e, phi)
    mk = pow(c, d, p)
    n_ary.append(p)
    a_ary.append(mk)

m = chinese_remainder(n_ary, a_ary)
flag = ('%x' % m).decode('hex')
print flag
ISITDTU{f6b2b7472273aacf803ecfe93607a914}

aes_cnv (Crypto)

$ nc 35.185.111.53 13337
******************************************************************
* Challenge created by chung96vn                                 *
* My blog: https://chung96vn.blogspot.com                        *
* From: AceBear Team                                             *
******************************************************************


Give me content of your request you want to encrypt: aaa
Your encypted: kO/CNrsWq0FJ7jA86Jjcb3zpu/AoSRxt4gXXpWlgOeYwqjZGfbXZZ8BKNaSvFbup
Give me encrypted of your request: kO/CNrsWq0FJ7jA86Jjcb3zpu/AoSRxt4gXXpWlgOeYwqjZGfbXZZ8BKNaSvFbup
Your request: aaa
Bye~~

以下、処理のメモ。

req: 暗号化する文字(Give me the flagで始まるものはダメ)
game.encode(req)を表示
enc: 暗号化文字
game.decode(enc)を表示
Give me the flagで始まるものの場合、フラグを表示

Give me the flagは16文字なので、1ブロック目しか気にする必要はない。以下のように調整すれば、1ブロック目の暗号化は同じになり、フラグが得られるはず。

aaaaaaaaaaaaaaa ^ IV1 =  Give me the flag ^ IV2
import socket
import re
from base64 import b64decode
from base64 import b64encode

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('35.185.111.53', 13337))

data = s.recv(1024)
data += s.recv(1024)
plain1 = 'a' * 16
print data + plain1

s.sendall(plain1 + '\n')
data = s.recv(1024)
data += s.recv(1024)
print data[:-1],

pattern = 'Your encypted: (.+)'
m = re.search(pattern, data)
b64_enc = m.group(1)
enc = b64decode(b64_enc)
iv1 = enc[:16]
enc_body = enc[16:]

plain2 = 'Give me the flag'
iv2 = str_xor(str_xor(plain1, iv1), plain2)
enc = iv2 + enc_body
b64_enc = b64encode(enc)
print b64_enc

s.sendall(b64_enc + '\n')
data = s.recv(1024)
data += s.recv(1024)
print data
ISITDTU{chung96vn_i5_v3ry_h4nds0m3}