De1CTF 2019 Writeup

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

We1come (Misc)

Telegramに入ると、フラグが書いてあった。

de1ctf{We1c0me_2_De1CTF}

xorz (Crypto)

以下のような条件になっているXOR暗号の問題。

keyの長さ38未満
saltの長さ14
keyとsaltのxorがXORの鍵となっている。
→全体の鍵の長さは14の倍数
encの長さは600

鍵の先頭4文字を総当たりで復号し、printableなものを出力してみる。
以下は調査のためのコード。最終的にはencの長さからもj=15の時が怪しく、その場合に絞っている。

from itertools import *
from string import *

def is_letters(s, head):
    chars = letters + ' '
    for c in s:
        if c not in chars:
            return False
    if head:
        #if s[0] not in uppercase:
        if s[0] != 'T':
            return False
    return True

def decrypt4(ct, salt, key):
    pt = ''
    for i in range(4):
        code = ord(ct[i]) ^ ord(salt[i]) ^ ord(key[i])
        pt += chr(code)
    return pt

enc = '49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'.decode('hex')

salt = 'WeAreDe1taTeam'

for c in product(printable, repeat=4):
    key = ''.join(c)
    pt_head = decrypt4(enc[0:4], salt, key)
    if is_letters(pt_head, True):
        #for j in range(1, 76):
        for j in range(15, 16):
            found = True
            pt_parts = []
            for k in range(0, len(enc), 14 * j):
                pt_part = decrypt4(enc[k:k+4], salt, key)
                pt_parts.append(pt_part)
                if is_letters(pt_part, False) == False:
                    found = False
                    break
            if found:
                print key, pt_parts, j

この結果でもまだかなり多くのパターンがあるが、一つ一つ見たときに以下の復号結果が正解の可能性が高いと推測した。

W3lc ['In f', 'ne d', 'ng t'] 15

この結果をもとに、keyは30バイトで、W3lcの次はomeのリート文字が来ると推測し、試しながら復号する。
W3lc0m3まで指定したときの復号結果は以下の通りとなる。

In fait***********************th mine***********************a thous***********************s my he***********************y despi***********************ew is p***********************mine ea***********************ne deli***********************g to ba***********************ste, no***********************nvitedT***********************h thee *********************** nor my***********************e one f***********************ng thee***********************e liken***********************heart`s***********************h to be***********************r I cou***********************t makes***********************

keyの長さは合っていそうなので、このまま継続して、英単語を推測しながら復号する。さらに途中まで復号すると、ここの文章になることがわかるので、参考にする。
www.poetryfoundation.org
最終的なコードは以下の通りとなる。

ct = '49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'.decode('hex')

salt = 'WeAreDe1taTeam'
#key = 'W3lc0m3***********************'
key = 'W3lc0m3tOjo1nu55un1ojOt3m0cl3W'

pt = ''
for i in range(len(ct)):
    if key[i%len(key)] == '*':
        pt += '*'
    else:
        code = ord(ct[i]) ^ ord(salt[i%len(salt)]) ^ ord(key[i%len(key)])
        pt += chr(code)

print pt

flag = 'de1ctf{%s}' % key
print flag

これで復号でき、keyもわかり、フラグとなる。実行結果は以下の通り。

In faith I do not love thee with mine eyes,For they in thee a thousand errors note;But `tis my heart that loves what they despise,Who in despite of view is pleased to dote.Nor are mine ears with thy tongue`s tune delighted;Nor tender feeling to base touches prone,Nor taste, nor smell, desire to be invitedTo any sensual feast with thee alone.But my five wits, nor my five senses canDissuade one foolish heart from serving thee,Who leaves unswayed the likeness of a man,Thy proud heart`s slave and vassal wretch to be.Only my plague thus far I count my gain,That she that makes me sin awards me pain.
de1ctf{W3lc0m3tOjo1nu55un1ojOt3m0cl3W}
de1ctf{W3lc0m3tOjo1nu55un1ojOt3m0cl3W}

Baby Rsa (Crypto)

以下のような流れで復号できれば、全部の情報を使わなくても、フラグを求められる。

1. Hastad's broadcast Attackでpを求める。
2. Low Public-Exponent Attackでe2を求める。
3. e2がp*q2と互いに素にならないため、Low Public-Exponent Attackを使いながら、段階を経て復号する。

この流れをコードにすると、以下のようになる。

import functools
import gmpy
from Crypto.Util.number import *

def chinese_remainder(n, a):
    sum = 0
    prod = functools.reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod
 
def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a // b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

def inv_pow(c, e):
    low = -1
    high = c+1
    while low + 1 < high:
        m = (low + high) // 2
        p = pow(m, e)
        if p < c:
            low = m
        else:
            high = m
    m = high
    assert pow(m, e) == c
    return m

#### solve p ####
n =  [20129615352491765499340112943188317180548761597861300847305827141510465619670536844634558246439230371658836928103063432870245707180355907194284861510906071265352409579441048101084995923962148527097370705452070577098780246282820065573711015664291991372085157016901209114191068574208680397710042842835940428451949500607613634682684113208766694028789275748528254287705759528498986306494267817198340658241873024800336013946294891687591013414935237821291805123285905335762719823771647853378892868896078424572232934360940672962436849523915563328779942134504499568866135266628078485232098208237036724121481835035731201383423L, 31221650155627849964466413749414700613823841060149524451234901677160009099014018926581094879840097248543411980533066831976617023676225625067854003317018794041723612556008471579060428898117790587991055681380408263382761841625714415879087478072771968160384909919958010983669368360788505288855946124159513118847747998656422521414980295212646675850690937883764000571667574381419144372824211798018586804674824564606122592483286575800685232128273820087791811663878057827386379787882962763290066072231248814920468264741654086011072638211075445447843691049847262485759393290853117072868406861840793895816215956869523289231421L, 29944537515397953361520922774124192605524711306753835303703478890414163510777460559798334313021216389356251874917792007638299225821018849648520673813786772452822809546571129816310207232883239771324122884804993418958309460009406342872173189008449237959577469114158991202433476710581356243815713762802478454390273808377430685157110095496727966308001254107517967559384019734279861840997239176254236069001453544559786063915970071130087811123912044312219535513880663913831358790376650439083660611831156205113873793106880255882114422025746986403355066996567909581710647746463994280444700922867397754748628425967488232530303L, 25703437855600135215185778453583925446912731661604054184163883272265503323016295700357253105301146726667897497435532579974951478354570415554221401778536104737296154316056314039449116386494323668483749833147800557403368489542273169489080222009368903993658498263905567516798684211462607069796613434661148186901892016282065916190920443378756167250809872483501712225782004396969996983057423942607174314132598421269169722518224478248836881076484639837343079324636997145199835034833367743079935361276149990997875905313642775214486046381368619638551892292787783137622261433528915269333426768947358552919740901860982679180791L]
c =  [19131432661217908470262338421299691998526157790583544156741981238822158563988520225986915234570037383888112724408392918113942721994125505014727545946133307329781747600302829588248042922635714391033431930411180545085316438084317927348705241927570432757892985091396044950085462429575440060652967253845041398399648442340042970814415571904057667028157512971079384601724816308078631844480110201787343583073815186771790477712040051157180318804422120472007636722063989315320863580631330647116993819777750684150950416298085261478841177681677867236865666207391847046483954029213495373613490690687473081930148461830425717614569L, 15341898433226638235160072029875733826956799982958107910250055958334922460202554924743144122170018355117452459472017133614642242411479849369061482860570279863692425621526056862808425135267608544855833358314071200687340442512856575278712986641573012456729402660597339609443771145347181268285050728925993518704899005416187250003304581230701444705157412790787027926810710998646191467130550713600765898234392350153965811595060656753711278308005193370936296124790772689433773414703645703910742193898471800081321469055211709339846392500706523670145259024267858368216902176489814789679472227343363035428541915118378163012031L, 18715065071648040017967211297231106538139985087685358555650567057715550586464814763683688299037897182845007578571401359061213777645114414642903077003568155508465819628553747173244235936586812445440095450755154357646737087071605811984163416590278352605433362327949048243722556262979909488202442530307505819371594747936223835233586945423522256938701002370646382097846105014981763307729234675737702252155130837154876831885888669150418885088089324534892506199724486783446267336789872782137895552509353583305880144947714110009893134162185382309992604435664777436197587312317224862723813510974493087450281755452428746194446L, 2282284561224858293138480447463319262474918847630148770112472703128549032592187797289965592615199709857879008271766433462032328498580340968871260189669707518557157836592424973257334362931639831072584824103123486522582531666152363874396482744561758133655406410364442174983227005501860927820871260711861008830120617056883514525798709601744088135999465598338635794275123149165498933580159945032363880613524921913023341209439657145962332213468573402863796920571812418200814817086234262280338221161622789516829363805084715652121739036183264026120868756523770196284142271849879003202190966150390061195469351716819539183797L]
e = 4
a = chinese_remainder(n, c)
for val_n, val_c in zip(n, c):
    assert a % val_n == val_c
p = inv_pow(a, e)

f = lambda m, e, n, c: pow(m, e, n) == c
assert(sum(map(f, [p] * 4, [4] * 4, n ,c)) == 4)

print 'p =', p

#### solve e2 ####
ee2 = 3
ce2 = 13908468332333567158469136439932325992349696889129103935400760239319454409539725389747059213835238373047899198211128689374049729578146875309231962936554403287882999967840346216695208424582739777034261079550395918048421086843927009452479936045850799096750074359160775182238980989229190157551197830879877097703347301072427149474991803868325769967332356950863518504965486565464059770451458557744949735282131727956056279292800694203866167270268988437389945703117070604488999247750139568614939965885211276821987586882908159585863514561191905040244967655444219603287214405014887994238259270716355378069726760953320025828158
tmp = 864078778078609835167779565982540757684070450697854309005171742813414963447462554999012718960925081621571487444725528982424037419052194840720949809891134854871222612682162490991065015935449289960707882463387
n = 15911581555796798614711625288508309704791837516232122410440958830726078821069050404012820896260071751380436992710638364294658173571101596931605797509712839622479368850251206419748090059752427303611760004621378226431226983665746837779056271530181865648115862947527212787824629516204832313026456390047768174765687040950636530480549014401279054346098030395100387004111574278813749630986724706263655166289586230453975953773791945408589484679371854113457758157492241225180907090235116325034822993748409011554673180494306003272836905082473475046277554085737627846557240367696214081276345071055578169299060706794192776825039

c2 = ce2
while True:
    m = gmpy.root(c2, ee2)[0]
    if pow(m, ee2, n) == ce2:
        break
    c2 += n

e2 = m - tmp
assert(pow(e2 + tmp, ee2, n) == ce2)

print 'e2 =', e2

#### solve flag ####
q2 = 114401188227479584680884046151299704656920536168767132916589182357583461053336386996123783294932566567773695426689447410311969456458574731187512974868297092638677515283584994416382872450167046416573472658841627690987228528798356894803559278308702635288537653192098514966089168123710854679638671424978221959513
c2 = 7395591129228876649030819616685821899204832684995757724924450812977470787822266387122334722132760470911599176362617225218345404468270014548817267727669872896838106451520392806497466576907063295603746660003188440170919490157250829308173310715318925771643105064882620746171266499859049038016902162599261409050907140823352990750298239508355767238575709803167676810456559665476121149766947851911064706646506705397091626648713684511780456955453552020460909638016134124590438425738826828694773960514221910109473941451471431637903182205738738109429736425025621308300895473186381826756650667842656050416299166317372707709596

c = c2 % q2
e = e2 / 2
phi = q2 - 1
d = inverse(e, phi)
m1 = pow(c, d, q2)

m0 = m1
c1 = m1
while True:
    m = gmpy.root(c1, 2)[0]
    if pow(m, 2, q2) == m0:
        break
    c1 += q2

flag = m
assert(c2 == pow(flag, e2, p * q2))

flag = long_to_bytes(flag)
print flag

実行結果は以下の通り。

p = 109935857933867829728985398563235455481120300859311421762540858762721955038310117609456763338082237907005937380873151279351831600225270995344096532750271070807051984097524900957809427861441436796934012393707770012556604479065826879107677002380580866325868240270494148512743861326447181476633546419262340100453
e2 = 381791429275130
de1ctf{9b10a98b-71bb-4bdf-a6ff-f319943de21f}
de1ctf{9b10a98b-71bb-4bdf-a6ff-f319943de21f}

CyBRICS CTF Quals 2019 Writeup

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

Mic Check (Cyber, Baby)

ruleのページにフラグが書いてある。

cybrics{W3lc0M3_t0_t3h_G4M#}

Warmup (Web, Baby)

http://45.32.148.106/にアクセスすると、http://45.32.148.106/final.htmlにリダイレクトされる。curlで直接のレスポンスを見てみる。

$ curl http://45.32.148.106/
    :
She gave me with their long manes, thick legs, and slow even pace, drawing along a perfect right to kill him as strange and shocking sight.<br/>
Here is your base64-encoded flag: Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0=
</p></body></html>
$ echo Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0= | base64 -d
cybrics{4b646c7985fec6189dadf8822955b034}
cybrics{4b646c7985fec6189dadf8822955b034}

Zakukozh (Cyber, Baby)

Affine暗号のようなので、バイナリデータが1バイトごとに以下のような形式で暗号化されている。

(a * x + b) mod 256

復号するときも同様で、PNGから試したところ、あるa, bのときにヘッダがPNGになることがわかり、そのまま復号してみる。

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

PNG_HEAD = '\x89PNG'

found = False
for a in range(256):
    for b in range(256):
        d0 = (ord(enc[0]) * a + b) % 256
        d1 = (ord(enc[1]) * a + b) % 256
        d2 = (ord(enc[2]) * a + b) % 256
        d3 = (ord(enc[3]) * a + b) % 256
        if d0 == ord(PNG_HEAD[0]) and d1 == ord(PNG_HEAD[1]) \
             and d2 == ord(PNG_HEAD[2]) and d3 == ord(PNG_HEAD[3]):
             found = True
             break
    if found:
        break

flag = ''
for e in enc:
    code = (ord(e) * a + b) % 256
    flag += chr(code)

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

復号したPNG画像にフラグが書かれていた。
f:id:satou-y:20190726185353p:plain

cybrics{W311_C0M3_2_CY13R1C5}

Big RAM (rebyC, Easy)

換字式暗号だが、原則2文字ずつセットで置換している。奇数長の場合の最後の文字の置換については、1文字の置換表を別に持つ。
https://downhousesoftware.wordpress.com/2014/09/08/cryptography/を参考に、頻度分析をしながら、英語の文章になるよう置換していく。

ybn -> the
adybowz -> nothing
btktybowz -> something
btktsnbyn -> somewhere

さらに2066行目にフラグの形式がある。

xanjtou -> cybrics

置換して復号して確認し、調整することを繰り返す。最終的にはフラグの部分だけでも復号できればよい。フラグが判明した時点での復号コードは以下の通り。

import re

map1 = {'n': 'e', 'z': 'g', 'p': 'i', 'v': 'k', 'u': 's', 's': 't', 'h': 'h',
    'y': 'o', 'a': 'r', 'l': 'n', 'q': 'u', 'f': 'y', 'd': 'a', 'x': 'd',
    'g': 'l', 'b': 'p', 'i': 'f', 'e': 'm', 't': 'c', 'w': 'w'
}
map2 = {'yb': 'th', 'ow': 'in', 'ad': 'no', 'bt': 'so', 'kt': 'me', 'cz': 'on',
    'xa': 'cy', 'nj': 'br', 'to': 'ic', 'od': 'is', 'sn': 'wh', 'by': 'er',
    'xq': 'at', 'aq': 'wi', 'pr': 'pe', 'jy': 're', 'cm': 'ss', 'au': 'ag',
    'ac': 'ou', 'dl': 'gh', 'oy': 'ki', 'mw': 'ng', 'hd': 'mb', 'hj': 'ai',
    'ws': 'll', 'zv': 'he', 'uq': 'es', 'cd': 'ed', 'ov': 'ma', 'zs': 'tc',
    'wr': 'an', 'ij': 'yo', 'mu': 'ot', 'ol': 'en', 'em': 'do', 'hn': 'lo',
    'mq': 'ne', 'lv': 'ge', 'oe': 'pr', 'rl': 'si', 'of': 'ar', 'nu': 'as',
    'qy': 'ev', 'md': 'iv', 'vp': 'io', 'qb': 'na', 'pk': 'xt', 'ha': 'mi',
    'hy': 'sw', 'jo': 'to', 'tb': 'ci', 'sb': 'al', 'yh': 'ly', 'sg': 'mo',
    'jd': 'st', 'iq': 'ad', 'wa': 'di', 'dq': 'da', 'hz': 'ak', 'zd': 'yz',
    'qc': 'la', 'zm': 'rl', 'dk': 'ct', 'yx': 'dn', 'sd': 'of', 'tj': 'id',
    'px': 'yt', 'ez': 'hi', 'sq': 'ti', 'sw': 'ok', 'lo': 'rs', 'dg': 'im',
    'th': 'el', 'ex': 've', 'qn': 'li', 'ew': 'ra', 'fb': 'gl', 'ke': 'us',
    'af': 'uk', 'ao': 'nd', 'nw': 'sa', 'xg': 'op', 'uf': 'ri', 'in': 'ec',
    'bq': 'tw', 'bv': 'or', 'da': 'fi', 'uo': 'le', 'sj': 'hu', 'sv': 'sh',
    'qo': 'tt', 'qs': 'un', 'hc': 'ce', 'sr': 'ni', 'qt': 'ze', 'qj': 'sn',
    'fz': 'bu', 'bf': 'pa', 'ef': 'rt', 'xp': 'ke', 'zj': 'tr', 'gm': 'sl',
    'ms': 'nt', 'zo': 'te', 'un': 'de', 'ld': 'ta', 'bh': 'af', 'kc': 'og',
    'te': 'se', 'zc': 'wn', 'zk': 'ea', 'tk': 'vo', 'ru': 'ur', 'bj': 'ca',
    'gu': 'go', 'xh': 'co', 'kg': 'ha', 'aj': 'ex', 'yt': 'nc', 'ea': 'ns',
    'kx': 'ue', 'eh': 'ab', 'xw': 'ua', 'ba': 'tl', 'bx': 'il', 'fi': 'ba',
    'hs': 'ro', 'fn': 'ob', 'nl': 'ul', 'xu': 'ld', 're': 'vi', 'ej': 'be',
    'rq': 'su', 'uc': 'ch', 'sp': 'wo', 'cp': 'rd', 'ji': 'mm', 'ie': 'gs',
    'mr': 'ol', 'ro': 'nu', 'nc': 'fo', 'fd': 'od', 'rh': 'mp', 'bb': 'lf',
    'ry': 'sy', 'kj': 'em', 'ya': 'ho', 'lq': 'ff', 'ki': 'wa', 'rc': 'uc',
    'om': 'we', 'kh': 'nv', 'fk': 'ie', 'gi': 'ac', 'wk': 'cu', 'ty': 'sf',
    'lk': 'ms', 'xc': 'ty', 'mg': 'ee', 'qg': 'ys', 'gn': 'am', 'ev': 'pl',
    'dp': 'eq', 'yo': 'rr', 'di': 'ry', 'ot': 'bl', 'tw': 'gu', 'wn': 'ir',
    'pz': 'ye', 'dw': 'ts', 'if': 'rv', 'ls': 'po', 'xx': 'om', 've': 'ut',
    'nd': 'pt', 'hv': 'oc', 'si': 'dd', 'wd': 'nf', 'tc': 'sc', 'ft': 'qu',
    'gz': 'ks', 'ka': 'yb', 'zy': 'oa', 'ln': 'pi', 'ho': 'ck', 'mz': 'gr',
    'wf': 'kl', 'ay': 'rm', 'ny': 'fe', 'pv': 'ib', 'ip': 'ds', 'fg': 'ef',
    'zw': 'it', 'gc': 'lu'
}

def decrypt(s):
    s = s.group(0)
    res = ''
    for i in range(0, len(s), 2):
        chunk = s[i:i+2]
        if len(chunk) == 2:
            if chunk.lower() in map2:
                plain = map2[chunk.lower()]
                if chunk[0] == chunk[0].upper():
                    plain = plain[0].upper() + plain[1]
                if chunk[1] == chunk[1].upper():
                    plain = plain[0] + plain[1].upper()
            else:
                plain = '**'
        elif len(chunk) == 1:
            if chunk.lower() in map1:
                plain = map1[chunk.lower()]
                if chunk == chunk.upper():
                    plain = plain.upper()
            else:
                plain = '*'
        res += plain
    return res

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

text = re.sub(r'[a-z]+', decrypt, enc, 0, re.I)
print text

このコードを実行した復号結果は以下の通り。

         :
There was a trill of a mo**le **one. Rinat replied:

- Yes. Good. I understood. How **ch time is there? Good. Got it.

He **t the **one on the table and said quietly:

- Worm called. Th** already **** that we wo**. Not yet traced the si**al, but looking.

“Let them search,” grunted the old man. - A **ag in th**r hands (this one: cybrics{ok_i_will_probably_read_it_later_thanksalot}) and a locomotive me**. Look here.

Rinat bent down.

- Same…

- Yes! - the old man **iled, and **shed the keyboard to Rinat. - All kird** server. **t the sn**fer!
         :
cybrics{ok_i_will_probably_read_it_later_thanksalot}

ISITDTU CTF 2019 Quals Writeup

この大会は2019/6/29 20:00(JST)~2019/6/30 21:00(JST)に開催されました。
今回もチームで参戦。結果は4356点で327チーム中26位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (Welcome)

Discordに入り、ピン止めされたメッセージを見ると、フラグが書いてあった。

ISITDTU{Welcome_everyone_to_ISITDTUCTF}

Old story (Crypto)

各要素の値は2の累乗になっているようだったので、2の何乗かを出してみる。

[19, 22, 14, 10, 22, 5, 18, 21, 22, 24, 46, 51, 13, 23, 13, 52, 24, 55, 14, 41, 26, 20, 21, 54, 25, 36, 2, 34, 29, 39, 18, 32, 14, 7, 58, 37, 24, 55, 10, 52, 14, 36, 18, 62]

64以下の値で構成されている。

$ echo 'ISITDTU' | base64
SVNJVERUVQo=

Base64のインデックスになっているように見えるので、それを前提にデコードする。

import string

enc = [524288, 4194304, 16384, 1024, 4194304, 32, 262144, 2097152, 4194304, 16777216, 70368744177664, 2251799813685248, 8192, 8388608, 8192, 4503599627370496, 16777216, 36028797018963968, 16384, 2199023255552, 67108864, 1048576, 2097152, 18014398509481984, 33554432, 68719476736, 4, 17179869184, 536870912, 549755813888, 262144, 4294967296, 16384, 128, 288230376151711744, 137438953472, 16777216, 36028797018963968, 1024, 4503599627370496, 16384, 68719476736, 262144, 4611686018427387904]

log = {}
for i in range(64):
    log[2**i] = i

codes = []
for e in enc:
    codes.append(log[e])

b64tbl = string.uppercase + string.lowercase + string.digits + '+/'

b64enc = ''
for code in codes:
    b64enc += b64tbl[code - 1]

flag = b64enc.decode('base64')
print flag
ISITDTU{r1c3_che55b0ard_4nd_bs64}

Easy RSA 1 (Crypto)

eが非常に大きいので、Wiener's Attackで復号する。

# wiener_attack.sage
def wiener(e, n):
    m = 12345
    c = pow(m, e, n)
    q0 = 1

    list1 = continued_fraction(Integer(e)/Integer(n))
    conv = list1.convergents()
    for i in conv:
        k = i.numerator()
        q1 = i.denominator()

        for r in range(30):
            for s in range(30):
                d = r*q1 + s*q0
                m1 = pow(c, d, n)
                if m1 == m:
                    return d
        q0 = q1
    return None

n = 137709853388874260067664060306224801065880567280896538344708662296491187670268243771581008615887329285442080267493172576517227603581814922784005894668293664250883858847270414696279127204843462302026428548409668060539273696154323753582118715764251826181439387752900169898505518594134423187783089073450520527969
e = 85605944479801539190292577400294315989703364683792339737453887619652946907819548388390076584246192710348319524405250340541547224543819349494488596679810310694697763188201375896240755682682520811114377302206659716598003967368992125832908158096663258971132148617761060030524535044970322206572134828548118438019
c = 22066874366908131522623165919721031154048372827959879486746669804284618882326621205522887800063326155288431192110879403482210420111408801477749097399368616494107231276644736023482237804800287810317122448198973027350599454646050497733117161743355379588761228932055556016302337519814538743706899009493572298224

d = wiener(e, n)
m = pow(c, d, n)

flag = ('%x' % m).decode('hex')
print flag
ISITDTU{Thank5_f0r_4tt3nd1ng_0ur_C0nt3st}

Chaos (Crypto)

$ nc 104.154.120.223 8085
Your cipher key: 88/cc/,,/oo 00/xx/UU/II 33/cc/OO/DD 88/mm/~~/rr 00/uu/KK/$$/,, 88/pp/DD/LL 22/ii/UU/SS 77/ee/CC/__/$$ 11/yy/KK/DD 88/77/tt 33/xx/KK/OO 44/mm/||/ii 55/ff/**/pp 55/ii/GG/??/%% 11/88/zz 77/88/oo 88/ww/^^/dd 66/kk/((/gg 33/qq/DD/<</,, 99/rr/,,/gg 33/ll/EE/VV 44/xx/__/hh 88/nn/KK/&&/++ 44/cc/CC/II 88/mm/LL/++/__ 77/oo/``/oo 11/66/ww 66/hh/((/ww 77/pp/__/vv 11/ee/RR/((/## 55/mm/II/MM 44/uu/ZZ/ZZ 55/cc/++/pp 99/pp/PP/../%% 22/vv/CC/$$/<< 22/ss/AA/,,/?? 66/11/ff 99/qq/<</jj 22/88/ww 99/mm/PP/MM 88/dd/))/hh 22/vv/PP/NN 99/66/uu 44/tt/HH/!!/== 22/cc/CC/``/++ 77/bb/ZZ/MM 33/oo/SS/BB 22/vv/OO/HH 88/ss/HH/RR 11/aa/%%/rr 22/qq/??/mm 99/nn/%%/ll 22/kk/CC/OO 77/vv/@@/ee 33/ff/YY/DD 66/ee/XX/BB 99/bb/RR/@@/(( 77/rr/$$/hh 99/dd/DD/##/## 77/ii/JJ/WW 44/hh/EE/XX 44/ff/__/tt 33/00/gg 55/pp/HH/LL
WELCOME TO CHAOS TOOL: 
Description: This is a tool which helps you hide the content of the message
Notes:
- Message cannot contain whitespace characters
- Message can use all characters including punctuation marks and number
- Decrypt the above key to get the flag, len(key) = 64
- All punctuation marks use in plain key: ~`!@#$%^&*()_-+=<,>.?|
- Key is not a meaningful sentence
- Find the rule in this tool
**FEATURES**
<1> Encrypt message 
<2> Get the flag 
Your choice: 1
Enter your message: 123

Here is your cipher: 11/00/jj 22/77/aa 33/00/aa
**FEATURES**
<1> Encrypt message 
<2> Get the flag 
Your choice: 2
Please enter the key to get flag: 123
WRONG KEY

何回か試してみると、この暗号の特徴は以下のようになっていることがわかった。

"/"区切りで3つの場合は数字で、平文は"/"区切りの1番目の文字
"/"区切りで4つの場合で、4つ目が英小文字の場合は英小文字で、平文は"/"区切りの2番目の文字
"/"区切りで4つの場合で、4つ目が英大文字の場合は英大文字で、平文は"/"区切りの3番目の文字
"/"区切りで5つの場合は記号で、平文は"/"区切りの5番目の文字

これを元に復号して、キーを指定する。

import socket
import string

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(('104.154.120.223', 8085))

data = recvuntil(s, 'Your choice: ')
print data + '1'
s.sendall('1\n')

ct = data.split('\n')[0].split(': ')[1]
ct = ct.split(' ')

chars = string.letters + string.digits + '~`!@#$%^&*()_-+=<,>.?|'
data = recvuntil(s, ': ')
print data + chars
s.sendall(chars + '\n')

pt = ''
for c in ct:
    elems = c.split('/')
    if len(elems) == 3:
        pt += elems[0][0]
    elif len(elems) == 4:
        if elems[3][0] in string.lowercase:
            pt += elems[1][0]
        else:
            pt += elems[2][0]
    else:
        pt += elems[4][0]

data = recvuntil(s, 'Your choice: ')
print data + '2'
s.sendall('2\n')

data = recvuntil(s, ': ')
print data + pt
s.sendall(pt + '\n')

data = s.recv(8192)
print data

実行結果は以下の通り。

Your cipher key: 44/66/yy 44/qq/HH/../&& 00/rr/~~/zz 55/ww/--/gg 77/ll/LL/JJ 55/rr/QQ/||/.. 77/dd/GG/II 55/uu/LL/YY 66/nn/II/==/## 55/44/kk 99/99/ww 22/ee/``/yy 00/tt/))/rr 11/yy/ZZ/LL 77/dd/../uu 33/rr/GG/II 33/cc/**/tt 88/11/xx 88/77/zz 77/qq/QQ/((/?? 55/xx/SS/DD 66/aa/@@/dd 00/hh/||/vv 22/33/bb 33/oo/ZZ/**/!! 66/44/bb 44/bb/__/zz 99/mm/__/mm 55/88/ww 00/rr/EE/BB 99/ii/PP/||/)) 22/rr/<</ss 00/ll/BB/XX 77/ll/++/qq 99/tt/KK/GG 55/ww/&&/yy 99/bb/DD/LL 44/jj/>>/uu 22/dd/UU/TT 44/vv/NN/??/~~ 55/aa/JJ/--/|| 77/pp/II/FF 99/cc/HH/EE 22/jj/RR/MM 55/22/rr 33/yy/MM/AA 33/bb/KK/AA 33/qq/>>/ll 88/ff/++/ee 22/oo/TT/||/** 55/88/ii 22/aa/CC/KK 11/vv/UU/JJ 44/bb/SS/||/== 66/ll/ZZ/__/^^ 33/bb/EE/NN 11/cc/SS/,,/%% 55/ii/HH/++/.. 22/ff/))/pp 99/yy/>>/yy 99/uu/KK/QQ 22/qq/~~/ee 22/uu/++/zz 00/ww/PP/<</++
WELCOME TO CHAOS TOOL:
Description: This is a tool which helps you hide the content of the message
Notes:
- Message cannot contain whitespace characters
- Message can use all characters including punctuation marks and number
- Decrypt the above key to get the flag, len(key) = 64
- All punctuation marks use in plain key: ~`!@#$%^&*()_-+=<,>.?|
- Key is not a meaningful sentence
- Find the rule in this tool
**FEATURES**
<1> Encrypt message
<2> Get the flag
Your choice: 1
Enter your message: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()_-+=<,>.?|

Here is your cipher: 77/aa/``/vv 55/bb/~~/ii 44/cc/@@/oo 00/dd/$$/qq 99/ee/,,/ll 44/ff/--/vv 44/gg/++/zz 66/hh/||/uu 00/ii/##/ii 00/jj/__/kk 77/kk/))/ee 44/ll/!!/dd 11/mm/,,/ff 44/nn/##/tt 88/oo/!!/vv 44/pp/>>/gg 77/qq/%%/qq 55/rr/../cc 66/ss/@@/ee 66/tt/``/ii 88/uu/%%/ll 11/vv/##/yy 33/ww/,,/ff 77/xx/??/uu 11/yy/##/ee 33/zz/&&/vv 66/cc/AA/WW 77/gg/BB/LL 55/ii/CC/KK 33/cc/DD/CC 22/gg/EE/HH 66/hh/FF/XX 77/dd/GG/OO 88/pp/HH/MM 22/xx/II/PP 55/zz/JJ/TT 11/kk/KK/OO 00/hh/LL/EE 22/tt/MM/II 88/yy/NN/LL 11/qq/OO/GG 11/tt/PP/TT 55/nn/QQ/LL 55/uu/RR/GG 66/uu/SS/AA 55/ww/TT/TT 77/oo/UU/YY 33/gg/VV/VV 44/mm/WW/WW 55/yy/XX/KK 55/ss/YY/PP 55/gg/ZZ/YY 00/99/jj 11/77/xx 22/22/uu 33/22/xx 44/88/ee 55/44/aa 66/77/yy 77/22/uu 88/11/ll 99/77/qq 00/ll/KK/++/~~ 44/cc/UU/**/`` 11/cc/FF/**/!! 77/bb/WW/>>/@@ 22/aa/FF/--/## 77/nn/GG/##/$$ 99/qq/JJ/__/%% 99/hh/RR/((/^^ 11/ii/QQ/++/&& 44/aa/EE/~~/** 44/hh/EE/,,/(( 88/ww/OO/%%/)) 88/ww/MM/~~/__ 44/gg/ZZ/$$/-- 99/gg/CC/@@/++ 77/qq/PP/==/== 99/xx/JJ/--/<< 44/uu/XX/~~/,, 66/uu/RR/--/>> 11/zz/OO/++/.. 33/ff/TT/&&/?? 00/qq/KK/``/||
**FEATURES**
<1> Encrypt message
<2> Get the flag
Your choice: 2
Please enter the key to get flag: 4&rwL.GL#59etZdGc88?Sah2!6bm5E)rBlKwDjU~|IHR5MKqf*5CU=^E%.fyKqu+
Good job! Here is your flag: ISITDTU{Hav3_y0u_had_a_h3adach3??_Forgive_me!^^}
ISITDTU{Hav3_y0u_had_a_h3adach3??_Forgive_me!^^}

decrypt to me (Crypto)

prgの生成はフラグのbit長にのみ依存する。暗号化のbit長がわかるので、それから+8bit以内で正しいprgが得られるので、逆算してフラグを導く。

from Crypto.Util.number import *

def generate_prg_bit(n):
    state = n
    while True:
        last_bit = state & 1
        yield last_bit
        middle_bit = state >> len(bin(n)[2:])//2 & 1
        state = (state >> 1) | ((last_bit ^ middle_bit) << (len(bin(n)[2:])-1))

enc = 'OKQI+f9R+tHEJJGcfko7Ahy2AuL9c8hgtYT2k9Ig0QyXUvsj1B9VIGUZVPAP2EVD8VmJBZbF9e17'
n = bytes_to_long(enc.decode('base64'))
enc_bin_text = bin(n)[2:]

prg_n = len(enc_bin_text)
tmp_enc_bin_text = enc_bin_text
while True:
    prg = generate_prg_bit(prg_n)
    flag_bits = []
    for i in range(prg_n):
        flag_bits.append(int(tmp_enc_bin_text[i]) ^ next(prg))
    plaintext = '0b' + ''.join(map(str, flag_bits))
    n = int(plaintext, 2)
    flag = long_to_bytes(n)
    if 'ISITDTU{' in flag:
        print flag
        break

    tmp_enc_bin_text = '0' + tmp_enc_bin_text
    prg_n += 1
ISITDTU{Encrypt_X0r_N0t_Us3_Pseud0_Rand0m_Generat0r!!!!!}

Do you like math? (Programming)

$ nc 104.154.120.223 8083

  #    #####        #         #            
 ##   #     #   #   #    #   ##            
# #         #   #   #    #  # #      ##### 
  #    #####  ##### #    #    #            
  #         #   #   #######   #      ##### 
  #   #     #   #        #    #            
#####  #####             #  #####          
                                           
>>>

ASCIIアートで算数の問題が出題される。
全文字のパターンを見てみる。

$ nc 104.154.120.223 8083

  #   #######        #####   #####           
 ##   #             #     # #     #          
# #   #                   #       #    ##### 
  #   ######  #####  #####   #####           
  #         #             # #          ##### 
  #   #     #       #     # #                
#####  #####         #####  #######          
                                             
>>>

$ nc 104.154.120.223 8083

#######   ###            #####    ###            
#        #   #   #   #  #     #  #   #           
#       #     #   # #         # #     #    ##### 
######  #     # #######  #####  #     #          
      # #     #   # #         # #     #    ##### 
#     #  #   #   #   #  #     #  #   #           
 #####    ###            #####    ###            
                                                 
>>>

$ nc 104.154.120.223 8083

 #####          #        #####           
#     #  #   #  #    #  #     #          
#     #   # #   #    #  #          ##### 
 #####  ####### #    #  ######           
#     #   # #   ####### #     #    ##### 
#     #  #   #       #  #     #          
 #####               #   #####           
                                         
>>>

$ nc 104.154.120.223 8083

#       #######         #        #####           
#    #  #    #   #   #  #    #  #     #          
#    #      #     # #   #    #        #    ##### 
#    #     #    ####### #    #   #####           
#######   #       # #   ####### #          ##### 
     #    #      #   #       #  #                
     #    #                  #  #######          
                                                 
>>>

$ nc 104.154.120.223 8083

 #####   #####          #     #            
#     # #     #   #    ##    ##            
      # #     #   #   # #   # #      ##### 
 #####   ###### #####   #     #            
      #       #   #     #     #      ##### 
#     # #     #   #     #     #            
 #####   #####        ##### #####          
                                           
>>>

縦にスペースが並ぶ列を区切りとして、各数字と記号のパターンを比較し、数式をパースする。あとは計算して答えていく。

import socket

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

def get_char(s):
    if s == '  ###   #   # #     ##     ##     # #   #   ###  ':
        return '0'
    elif s == '  #   # #    ########      #      #':
        return '1'
    elif s == ' #  ####  #  ##  #  ##  #  ##  #  ##  #  # ##   #':
        return '2'
    elif s == ' #   # #  #  ##  #  ##  #  ##  #  ##  #  # ## ## ':
        return '3'
    elif s == '#####      #      #      #      #   ######    #  ':
        return '4'
    elif s == '#### # #  #  ##  #  ##  #  ##  #  ##  #  ##   ## ':
        return '5'
    elif s == ' ##### #  #  ##  #  ##  #  ##  #  ##  #  # #  ## ':
        return '6'
    elif s == '##     #      #   ####  #   # #    ##     #      ':
        return '7'
    elif s == ' ## ## #  #  ##  #  ##  #  ##  #  ##  #  # ## ## ':
        return '8'
    elif s == ' ##  # #  #  ##  #  ##  #  ##  #  ##  #  # ##### ':
        return '9'
    elif s == '   #      #    #####    #      #   ':
        return '+'
    elif s == '   #      #      #      #      #   ':
        return '-'
    elif s == '   #    # # #   ###     #     ###   # # #    #   ':
        return '*'
    elif s == '  # #    # #    # #    # #    # #  ':
        return '='
    else:
        return ''

def get_formula(lines):
    formula = ''
    char_str = ''
    for x in range(len(lines[0])):
        sp_flg = True
        tmp_char = ''
        for y in range(len(lines)):
            tmp_char += lines[y][x]
            if lines[y][x] != ' ':
                sp_flg = False
        if sp_flg:
           formula += get_char(char_str)
           char_str = ''
        else:
           char_str += tmp_char
    return formula

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('104.154.120.223', 8083))

for i in range(100):
    print '\nRound %d' % (i+1)
    data = ''
    for j in range(9):
        data += recvuntil(s, '\n')
    print data
    d = data.split('\n')[1:-2]
    formula = get_formula(d)
    ans = str(eval(formula.rstrip('=')))

    data = recvuntil(s, '>>> ')
    print data + ans
    s.sendall(ans + '\n')

data = s.recv(8192)
print data

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

ISITDTU{sub5cr1b3_b4_t4n_vl0g_4nd_p3wd13p13}

Pytecode (Reverse)

Pythonアセンブラが添付されている。チェック処理を上から見ていく。

  9           6 LOAD_GLOBAL              0 (ord)
              9 LOAD_FAST                0 (flag)
             12 LOAD_CONST               1 (0)
             15 BINARY_SUBSCR       
             16 CALL_FUNCTION            1
             19 LOAD_CONST               2 (52)
             22 BINARY_ADD          
             23 LOAD_GLOBAL              0 (ord)
             26 LOAD_FAST                0 (flag)
             29 LOAD_CONST               3 (-1)
             32 BINARY_SUBSCR       
             33 CALL_FUNCTION            1
             36 COMPARE_OP               3 (!=)
             39 POP_JUMP_IF_TRUE        78
             42 LOAD_GLOBAL              0 (ord)
             45 LOAD_FAST                0 (flag)
             48 LOAD_CONST               3 (-1)
             51 BINARY_SUBSCR       
             52 CALL_FUNCTION            1
             55 LOAD_CONST               4 (2)
             58 BINARY_SUBTRACT     
             59 LOAD_GLOBAL              0 (ord)
             62 LOAD_FAST                0 (flag)
             65 LOAD_CONST               5 (7)
             68 BINARY_SUBSCR       
             69 CALL_FUNCTION            1
             72 COMPARE_OP               3 (!=)
             75 POP_JUMP_IF_FALSE       88

ord(flag[0]) + 52 = ord(flag[-1])
ord(flag[-1]) - 2 = ord(flag[7])
★まだ具体的な文字はわからないので保留(1)

 11     >>   88 LOAD_FAST                0 (flag)
             91 LOAD_CONST               5 (7)
             94 SLICE+2             
             95 LOAD_CONST               6 ('ISITDTU')
             98 COMPARE_OP               3 (!=)
            101 POP_JUMP_IF_FALSE      120

flag[:7] = 'ISITDTU'

ここで保留(1)を考える。
・flag[-1] == chr(73 + 52) == '}'
・flag[7] = chr(73 + 52 - 2) == '{'

          11111111112222222222
012345678901234567890123456789
ISITDTU{                     }

 13     >>  120 LOAD_FAST                0 (flag)
            123 LOAD_CONST               7 (9)
            126 BINARY_SUBSCR       
            127 LOAD_FAST                0 (flag)
            130 LOAD_CONST               8 (14)
            133 BINARY_SUBSCR       
            134 COMPARE_OP               3 (!=)
            137 POP_JUMP_IF_TRUE       180
            140 LOAD_FAST                0 (flag)
            143 LOAD_CONST               8 (14)
            146 BINARY_SUBSCR       
            147 LOAD_FAST                0 (flag)
            150 LOAD_CONST               9 (19)
            153 BINARY_SUBSCR       
            154 COMPARE_OP               3 (!=)
            157 POP_JUMP_IF_TRUE       180
            160 LOAD_FAST                0 (flag)
            163 LOAD_CONST               9 (19)
            166 BINARY_SUBSCR       
            167 LOAD_FAST                0 (flag)
            170 LOAD_CONST              10 (24)
            173 BINARY_SUBSCR       
            174 COMPARE_OP               3 (!=)
            177 POP_JUMP_IF_FALSE      193

flag[9] == flag[14]
flag[14] == flag[19]
flag[19] == flag[24]
→"_"のことを言っているのか?

          11111111112222222222
012345678901234567890123456789
ISITDTU{ _    _    _    _    }

 15     >>  193 LOAD_GLOBAL              0 (ord)
            196 LOAD_FAST                0 (flag)
            199 LOAD_CONST              12 (8)
            202 BINARY_SUBSCR       
            203 CALL_FUNCTION            1
            206 LOAD_CONST              13 (49)
            209 COMPARE_OP               3 (!=)
            212 POP_JUMP_IF_TRUE       235
            215 LOAD_FAST                0 (flag)
            218 LOAD_CONST              12 (8)
            221 BINARY_SUBSCR       
            222 LOAD_FAST                0 (flag)
            225 LOAD_CONST              14 (16)
            228 BINARY_SUBSCR       
            229 COMPARE_OP               3 (!=)
            232 POP_JUMP_IF_FALSE      245

ord(flag[8]) == 49
flag[8] == flag[16]
→flag[8] == flag[16] == '1'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_    _ 1  _    _    }


 17     >>  245 LOAD_FAST                0 (flag)
            248 LOAD_CONST              15 (10)
            251 LOAD_CONST               8 (14)
            254 SLICE+3             
            255 LOAD_CONST              16 ('d0nT')
            258 COMPARE_OP               3 (!=)
            261 POP_JUMP_IF_FALSE      277

flag[10:14] == 'd0nT'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_ 1  _    _    }

 19     >>  277 LOAD_GLOBAL              4 (int)
            280 LOAD_FAST                0 (flag)
            283 LOAD_CONST              17 (18)
            286 BINARY_SUBSCR       
            287 CALL_FUNCTION            1
            290 LOAD_GLOBAL              4 (int)
            293 LOAD_FAST                0 (flag)
            296 LOAD_CONST              18 (23)
            299 BINARY_SUBSCR       
            300 CALL_FUNCTION            1
            303 BINARY_ADD          
            304 LOAD_GLOBAL              4 (int)
            307 LOAD_FAST                0 (flag)
            310 LOAD_CONST              19 (28)
            313 BINARY_SUBSCR       
            314 CALL_FUNCTION            1
            317 BINARY_ADD          
            318 LOAD_CONST               7 (9)
            321 COMPARE_OP               3 (!=)
            324 POP_JUMP_IF_TRUE       347
            327 LOAD_FAST                0 (flag)
            330 LOAD_CONST              17 (18)
            333 BINARY_SUBSCR       
            334 LOAD_FAST                0 (flag)
            337 LOAD_CONST              19 (28)
            340 BINARY_SUBSCR       
            341 COMPARE_OP               3 (!=)
            344 POP_JUMP_IF_FALSE      357

int(flag[18]) + int(flag[23]) + int(flag[28]) == 9
flag[18] == flag[28]
★まだ具体的な文字はわからないので保留(2)

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_ 1  _    _    }


 21     >>  357 LOAD_FAST                0 (flag)
            360 LOAD_CONST              20 (15)
            363 BINARY_SUBSCR       
            364 LOAD_CONST              21 ('L')
            367 COMPARE_OP               3 (!=)
            370 POP_JUMP_IF_FALSE      386

flag[15] == 'L'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1  _    _    }


 23     >>  386 LOAD_GLOBAL              0 (ord)
            389 LOAD_FAST                0 (flag)
            392 LOAD_CONST              22 (17)
            395 BINARY_SUBSCR       
            396 CALL_FUNCTION            1
            399 LOAD_CONST              23 (-10)
            402 BINARY_XOR          
            403 LOAD_CONST              24 (-99)
            406 COMPARE_OP               3 (!=)
            409 POP_JUMP_IF_FALSE      422

ord(flag[17]) == (-10) ^ (-99)
→flag[17] == chr(107) == 'k'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1k _    _    }

 25     >>  422 LOAD_GLOBAL              0 (ord)
            425 LOAD_FAST                0 (flag)
            428 LOAD_CONST              25 (20)
            431 BINARY_SUBSCR       
            432 CALL_FUNCTION            1
            435 LOAD_CONST               4 (2)
            438 BINARY_ADD          
            439 LOAD_GLOBAL              0 (ord)
            442 LOAD_FAST                0 (flag)
            445 LOAD_CONST              26 (27)
            448 BINARY_SUBSCR       
            449 CALL_FUNCTION            1
            452 COMPARE_OP               3 (!=)
            455 POP_JUMP_IF_TRUE       502
            458 LOAD_GLOBAL              0 (ord)
            461 LOAD_FAST                0 (flag)
            464 LOAD_CONST              26 (27)
            467 BINARY_SUBSCR       
            468 CALL_FUNCTION            1
            471 LOAD_CONST              27 (123)
            474 COMPARE_OP               4 (>)
            477 POP_JUMP_IF_TRUE       502
            480 LOAD_GLOBAL              0 (ord)
            483 LOAD_FAST                0 (flag)
            486 LOAD_CONST              25 (20)
            489 BINARY_SUBSCR       
            490 CALL_FUNCTION            1
            493 LOAD_CONST              28 (97)
            496 COMPARE_OP               0 (<)
            499 POP_JUMP_IF_FALSE      515

ord(flag[20]) + 2 == ord(flag[27])
ord(flag[27]) <= 123
ord(flag[20]) >= 97
★まだ具体的な文字はわからないので保留(3)

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1k _    _    }

 27     >>  515 LOAD_GLOBAL              0 (ord)
            518 LOAD_FAST                0 (flag)
            521 LOAD_CONST              26 (27)
            524 BINARY_SUBSCR       
            525 CALL_FUNCTION            1
            528 LOAD_CONST              29 (100)
            531 BINARY_MODULO       
            532 LOAD_CONST               1 (0)
            535 COMPARE_OP               3 (!=)
            538 POP_JUMP_IF_FALSE      551

ord(flag[27]) % 100 == 0
→ord(flag[27]) == 100
→flag[27] == 'd'

ここで保留(3)を考える。
flag[20] == 'b'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1k _b   _  d }

 29     >>  551 LOAD_FAST                0 (flag)
            554 LOAD_CONST              30 (25)
            557 BINARY_SUBSCR       
            558 LOAD_CONST              31 ('C')
            561 COMPARE_OP               3 (!=)
            564 POP_JUMP_IF_FALSE      580

flag[25] = 'C'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1k _b   _C d }

 31     >>  580 LOAD_GLOBAL              0 (ord)
            583 LOAD_FAST                0 (flag)
            586 LOAD_CONST              32 (26)
            589 BINARY_SUBSCR       
            590 CALL_FUNCTION            1
            593 LOAD_CONST               4 (2)
            596 BINARY_MODULO       
            597 LOAD_CONST               1 (0)
            600 COMPARE_OP               3 (!=)
            603 POP_JUMP_IF_TRUE       675
            606 LOAD_GLOBAL              0 (ord)
            609 LOAD_FAST                0 (flag)
            612 LOAD_CONST              32 (26)
            615 BINARY_SUBSCR       
            616 CALL_FUNCTION            1
            619 LOAD_CONST              33 (3)
            622 BINARY_MODULO       
            623 LOAD_CONST               1 (0)
            626 COMPARE_OP               3 (!=)
            629 POP_JUMP_IF_TRUE       675
            632 LOAD_GLOBAL              0 (ord)
            635 LOAD_FAST                0 (flag)
            638 LOAD_CONST              32 (26)
            641 BINARY_SUBSCR       
            642 CALL_FUNCTION            1
            645 LOAD_CONST              34 (4)
            648 BINARY_MODULO       
            649 LOAD_CONST               1 (0)
            652 COMPARE_OP               3 (!=)
            655 POP_JUMP_IF_TRUE       675
            658 LOAD_FAST                0 (flag)
            661 LOAD_CONST              32 (26)
            664 BINARY_SUBSCR       
            665 LOAD_ATTR                5 (isdigit)
            668 CALL_FUNCTION            0
            671 UNARY_NOT           
            672 POP_JUMP_IF_FALSE      685

ord(flag[26]) % 2 == 0
ord(flag[26]) % 3 == 0
ord(flag[26]) % 4 == 0
flag[26]は数値
→flag[26]は'0'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1k _b   _C0d }

 33     >>  685 LOAD_GLOBAL              4 (int)
            688 LOAD_FAST                0 (flag)
            691 LOAD_CONST              18 (23)
            694 BINARY_SUBSCR       
            695 CALL_FUNCTION            1
            698 LOAD_CONST              33 (3)
            701 COMPARE_OP               3 (!=)
            704 POP_JUMP_IF_FALSE      720

int(flag[23]) == 3

ここで保留(2)を考える。
int(flag[18]) + int(flag[28]) == 9 - 3
flag[18] == flag[28]
→ flag[18] == flag[28] == '3'

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1k3_b  3_C0d3}

 35     >>  720 LOAD_FAST                0 (flag)
            723 LOAD_CONST              35 (22)
            726 BINARY_SUBSCR       
            727 LOAD_FAST                0 (flag)
            730 LOAD_CONST              36 (13)
            733 BINARY_SUBSCR       
            734 LOAD_ATTR                6 (lower)
            737 CALL_FUNCTION            0
            740 COMPARE_OP               3 (!=)
            743 POP_JUMP_IF_FALSE      756

flag[22] == flag[13].lower()

          11111111112222222222
012345678901234567890123456789
ISITDTU{1_d0nT_L1k3_b t3_C0d3}

 42         791 LOAD_FAST                2 (tmp)
            794 LOAD_GLOBAL              0 (ord)
            797 LOAD_FAST                3 (i)
            800 CALL_FUNCTION            1
            803 INPLACE_ADD         
            804 STORE_FAST               2 (tmp)
            807 JUMP_ABSOLUTE          785
        >>  810 POP_BLOCK           

 43     >>  811 LOAD_FAST                2 (tmp)
            814 LOAD_CONST              37 (2441)
            817 COMPARE_OP               3 (!=)
            820 POP_JUMP_IF_FALSE      833

フラグ全体のASCIIコードの合計は2441。
このことから残りの文字のASCIIコードを算出し、文字を割り出す。
tmp_flag = 'ISITDTU{1_d0nT_L1k3_b\x00t3_C0d3}'

sum_code = 0
for c in tmp_flag:
    sum_code += ord(c)

code = 2441 - sum_code
flag = tmp_flag.replace('\x00', chr(code))
print flag
ISITDTU{1_d0nT_L1k3_b:t3_C0d3}

Survey (Thank you)

アンケートに答えたら、フラグが表示された。

ISITDTU{thank_you_for_your_feedback}

Google Capture The Flag 2019 (Quals) Writeup

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

Reverse a cellular automata (CRYPTO)

Wolfram rule 126というアルゴリズムの問題。rule 126は以下のような変換をするアルゴリズムで、逆変換は複数の値が候補に挙がる。
2進数の先頭からその前(先頭の場合は末尾)とその後(末尾の場合は先頭)を含め、以下のパターンで変換する。

変換前 変換後
111 0
110 1
101 1
100 1
011 1
010 1
001 1
000 0
例えば、011.......111の場合、
先頭の文字は101→1
次の文字は011→1
   :
最後から2番目の文字は111→0
最後の文字は110→1

このことを前提に66de3c1bf87fdfcfになる元のhexデータの候補を見つける。

zero = ['000', '111']
one = ['110', '101', '100', '011', '010', '001']

step = bin(int('66de3c1bf87fdfcf', 16))[2:].zfill(64)

dec = []
for i in range(64):
    if i == 0:
        if step[i] == '0':
            for b in zero:
                dec.append(b)
        else:
            for b in one:
                dec.append(b)
    else:
        tmp = []
        for d in dec:
           for zo in ['0', '1']:
               text = d[i] + d[i+1] + zo
               if step[i] == '0' and text in zero:
                   tmp.append(d + zo)
               elif step[i] == '1' and text in one:
                   tmp.append(d + zo)
        dec = tmp

rev = []
for b in dec:
    if b[0] == b[-2] and b[1] == b[-1]:
        rev.append(b[1:-1])

for r in rev:
    print '%x' % int(r, 2)

結果、10752通りの候補が挙がる。あとはブルートフォースで復号し、フラグ文字が入っているものを探す。

import subprocess

def exec_cmd(cmd):
    proc = subprocess.Popen(cmd, shell=True, stdin  = subprocess.PIPE,
        stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    stdout_data, stderr_data = proc.communicate()
    return stdout_data

CMD_FORMAT1 = 'echo \"%s\" > /tmp/plain.key;' \
            + ' xxd -r -p /tmp/plain.key > /tmp/enc.key'
CMD_FORMAT2 = 'echo \"%s\" | openssl enc -d -aes-256-cbc -pbkdf2 -md sha1' \
            + ' -base64 --pass file:/tmp/enc.key'
enc_flag = 'U2FsdGVkX1/andRK+WVfKqJILMVdx/69xjAzW4KUqsjr98GqzFR793lfNHrw1Blc8UZHWOBrRhtLx3SM38R1MpRegLTHgHzf0EAa3oUeWcQ='

with open('result.txt', 'r') as f:
    keys = f.readlines()

for key in keys:
    cmd = CMD_FORMAT1 % key.rstrip()
    exec_cmd(cmd)

    cmd = CMD_FORMAT2 % enc_flag
    ret = exec_cmd(cmd)
    if 'CTF' in ret:
        print 'key  =', key.rstrip()
        print 'flag =', ret
        break

実行結果は以下の通り。

key  = 3c73e7f12fcd767a
flag = CTF{reversing_cellular_automatas_can_be_done_bit_by_bit}
CTF{reversing_cellular_automatas_can_be_done_bit_by_bit}

BCACTF I Writeup

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

hello-world (0: welcome 50)

問題にフラグが書いてあった。

bcactf{hello!}

net-cat (0: welcome 50)

ncで接続するだけ。

$ nc challenges.ctfd.io 30126
bcactf{5urf1n_7h3_n37c47_c2VydmVyc2lkZQ}
bcactf{5urf1n_7h3_n37c47_c2VydmVyc2lkZQ}

wuphf (0: welcome 50)

BCACTFのDiscord, Twitter, Instagramにアクセスして、確認すると、フラグの断片があった。

[Discord]
Thanks for joining the Discord! The Discord flag-ment is bcactf{h17

[Twitter]
Thanks for checking out our twitter! Why don't you drop us a follow?
Flag-ment: _u5_uP_d3

[Instagram]
A high school-made, middle to high school-focused CTF. Follow for the latest news and updates.
Flag-ment: VwaGYuY29t}

見つけたフラグの断片を結合すると、フラグになる。

bcactf{h17_u5_uP_d3VwaGYuY29t}

executable (binary-exploitation 150)

$ ./executable-ubuntu
Welcome to the lottery!
So now we're going to pick a ginormous number!
If it's 1, you win!
Your number is 1804289383!
Try again next time!

Ghidraでアセンブリを見る。

    :
        004006a4 83 fb 01        CMP        EBX,0x1
        004006a7 75 7b           JNZ        LAB_00400724
        004006a9 bf 70 08        MOV        EDI=>s_Congratulations,_you're_our_luck_004008   = "Congratulations, you're our l
                 40 00
        004006ae e8 1d fe        CALL       puts                                             int puts(char * __s)
                 ff ff
    :

ebxが1のときに正解する。面倒なので、75 7bを90でつぶして何でも正解するようにして実行する。

$ ./executable-ubuntu_mod
Welcome to the lottery!
So now we're going to pick a ginormous number!
If it's 1, you win!
Your number is 1804289383!
Congratulations, you're our lucky winner!
-
-
[
-
-
-
-
-
>
+
<
]
>
-
-
-
-
.
+
.
-
-
.
+
+
.
-
[
-
-
-
>
+
<
]
>
-
-
.
+
+
+
[
-
>
+
+
+
<
]
>
+
.
+
[
-
-
-
-
-
>
+
<
]
>
.
>
-
[
-
-
-
-
-
>
+
<
]
>
.
+
[
-
-
-
>
+
+
<
]
>
.
[
+
+
>
-
-
-
<
]
>
-
.
-
[
-
>
+
+
<
]
>
-
.
-
[
-
-
-
>
+
<
]
>
-
.
-
.
>
-
[
-
-
-
-
-
>
+
<
]
>
+
.
-
-
-
[
-
>
+
+
<
]
>
.
+
+
+
+
+
+
+
+
+
+
.
[
-
-
>
+
<
]
>
-
-
-
.
-
-
[
-
-
-
>
+
+
<
]
>
-
-
-
.
+
+
[
-
>
+
+
+
<
]
>
.
[
-
-
-
>
+
<
]
>
-
-
-
.
+
+
+
[
-
>
+
+
+
<
]
>
.
+
+
+
+
+
+
+
.
-
[
-
-
-
>
+
<
]
>
-
-
.
-
-
-
-
-
-
-
.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.
+
[
-
-
>
+
<
]
>
+
.
+
.
+
+
.
+
[
-
>
+
+
<
]
>
.
-
-
.
-
-
-
.
+
+
+
+
+
+
+
+
+
+
+
+
+
.
-
-
[
-
>
+
+
+
+
+
<
]
>
.
+
+
+
+
+
+
+
+
.
+
.
-
-
-
-
-
-
-
.
+
+
.
+
.
>
-
-
[
-
-
>
+
+
+
<
]
>
.

そのまま結合すると、Brainfuck言語になっている。

--[----->+<]>----.+.--.++.-[--->+<]>--.+++[->+++<]>+.+[----->+<]>.>-[----->+<]>.+[--->++<]>.[++>---<]>-.-[->++<]>-.-[--->+<]>-.-.>-[----->+<]>+.---[->++<]>.++++++++++.[-->+<]>---.--[--->++<]>---.++[->+++<]>.[--->+<]>---.+++[->+++<]>.+++++++.-[--->+<]>--.-------.---------------.+[-->+<]>+.+.++.+[->++<]>.--.---.+++++++++++++.--[->+++++<]>.++++++++.+.-------.++.+.>--[-->+++<]>.

https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行すると、フラグが表示される。

bcactf{3x3cut4bl3s_r_fun_124jher089245}

basic-numbers (crypto 50)

2進数をASCIIコードとして、文字にしていく。

codes = '01100010 00110001 01101110 01100001 01110010 01111001 01011111 01110011 00110000 01101100 01110110 00110011 01100100 01011111 01100111 00110000 00110000 01100100 01011111 01110111 00110000 01110010 01101011'
codes = codes.split(' ')

flag = ''
for code in codes:
    flag += chr(int(code, 2))

flag = 'bcactf{%s}' % flag
print flag
bcactf{b1nary_s0lv3d_g00d_w0rk}

cracking-the-cipher (crypto 50)

シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 2:
the password is learning_caesar_ciphers_is_fun!
bcactf{learning_caesar_ciphers_is_fun!}

three-step-program (crypto 125)

最初の暗号はBase64。デコードする。

>>> base64.b64decode('MzIgLSAgfDMgVGltZXMgQSBDaGFybXwgLSAzMg==')
'32 -  |3 Times A Charm| - 32'

次の暗号のヒントになっているようだ。Base32と推測して、3回デコードする。

>>> import base64
>>> enc = 'JJGTEVSLKNBVISSGINCU2VCTGJFVETCWKNGVGTKLKJEEKQ2VJNEUSNC2KZKVCS2OJFNE4RKPKNFUUSKSJNKTITSDKJFEERKUI5GTETKJLJGVMQ2RJNLEWUSLIZAVES2DJRFE2RKDK5JU2SKKJBCTEVKLJBDUSWSUI5KTETSLKZEVKS2TLJKEWUSFIU2FKU2WJRBEIVCFKFJVASKWIFKU2USLIRDUUR2FGJJEWQ2LKJGFMR2TJNCUYSSIIRFU2U2UJFCTEVKJKZJUMSKKJNKU6VK2KRFVES2VGZKEWUSKIJCVIR2XKNBEUNKGIZDVMMSEJRFEERKDKRJVOR2SJJKUGV2TJVFDKR2VGRLVGSKLJUZEKSKWJNHEWWSKKVDVCSSUJFJEERJUK5JVKTCCIZKEKVCDIVFFUQKWKFITEQSJJZEVKV2SGJDEYQSCKVBVMSSTJFFEMRSFKMZEISKFLJCVSTKTIZEUUTCGJ5JVUV2KJJAVKNSVKNMUWTSBKZKU2MSUJJLEYRCFKEZEETCKJNDECVCSKZFU4QSVI5ITEU2LJZCEMU2VJNDEYRSOIVKVCS2OJRFE4RKPKFNFIS2SINCTEUSTKZGEERCVKNJEGRKKGVDEISKXINBEOVSDIVGVES2DJM2UIVKXKNFUKSS2I5LE2VSLLBGEKWSVJFJFGUCLLJHEKQ2QJI2UQVJWKE6T2PJ5'
>>> for i in range(3):
...     enc = base64.b32decode(enc)
...
>>> enc
'Why english so ard to tok. \nNo speak more English. \nAil gi you tu hints to read my encrypted languich. \n\n1. SALT iz key to gret food!\n2. Le francais crypte le meilleur'

次の暗号のヒントになっているはず。SALTが鍵になっている。暗号から考えるとシフト暗号。シフト数が変わると推測し、復号する。

import string

enc = '''lhlm oad lamaew eyhmgs. lg i sxsro rgu ntee qhj a qesg? dbfcp rgu stne xtve tm lhtl xac, b'dl rh wadr gn jhm ayw zayw at zowr. 
mvscey{bu57_j0n_o4i7_kgbhmffhlqe} bfm, te htjnpw, feim lixx at hhf't mx ko dbepwx...'''

key = 'SALT'.lower()

flag = ''
i = 0
for c in enc:
    if c in string.lowercase:
        idx = string.lowercase.index(c)
        idx = idx - string.lowercase.index(key[i%len(key)])
        if idx < 0:
            idx += 26
        flag += string.lowercase[idx]
        i += 1
    else:
        flag += c

print flag

復号結果は以下の通り。

that was simple enough. so i heard you came for a flag? since you have made it this far, i'll go easy on you and hand it over.
bcactf{ju57_y0u_w4i7_znjhbmnhaxm} but, be warned, next time it won't be so simple...
bcactf{ju57_y0u_w4i7_znjhbmnhaxm}

tupperware (crypto 175)

いろいろ巨大な数字になることを推測して、組み立ててみたがうまくいかない。
タイトルにヒントがあるかと考え、調べていたら、「タッパーの自己言及式」というものがあることがわかった。
en.wikipedia.org
これを使うのであれば、数字3桁部分のみ情報があればよいので、数字3桁部分のみ抜き出す。

from word2number import w2n

with open('k', 'r') as f:
    data = f.read().split('\n')[14]

data = data.replace('<span class="Apple-converted-space">\xc2\xa0 </span>', ' ')
nums = data[31:-12].split(', ')

num_str = ''
for i in range(len(nums)):
    if i != len(nums) - 1:
        num_al = ' '.join(nums[i].split(' ')[:-1])
        num = str(w2n.word_to_num(num_al)).zfill(3)
        num_str += num + ' '
    else:
        num = str(w2n.word_to_num(nums[i])).zfill(3)
        num_str += num

print num_str

実行結果は以下の通り。

004 858 487 703 217 654 168 507 377 105 634 002 647 731 128 499 307 244 851 441 090 357 865 711 033 443 419 793 531 999 883 261 579 086 703 285 988 550 325 970 512 343 063 312 965 327 580 978 241 840 458 705 778 582 504 607 655 879 151 828 149 694 589 867 198 840 959 598 497 597 989 866 084 694 560 490 675 801 786 347 730 851 438 931 275 584 908 138 190 409 481 880 911 319 491 978 644 808 036 817 873 514 056 590 135 331 578 105 862 821 454 454 617 375 919 870 245 363 440 066 372 700 053 263 362 863 694 129 133 765 441 926 533 609 503 994 018 565 319 382 913 454 161 429 058 505 655 350 780 911 229 203 432 392 967 236 137 647 780 308 966 936 691 439 636 980 058 266 693 498 568 235 464 310 907 628 785 433 105 091 526 639

https://takitamblog.tk/skrypty/Tupper/でこの結果を入力し、グラフに表示させる。さらにGraph options and functionsで、Flip verticallyとFlip horizontallyを使って向きを変えると、フラグが表示された。
f:id:satou-y:20190616200532p:plain

bcactf{a11_0ccur}

runescape (crypto 180)

換字式暗号と推測し、ASCII文字に1:1で適当に変換する。

ABCBDE{EFGHIGJBKCJCLKMNMNMOGFKPFGCDQRGJMSLONJPCJJSKNJPTSJSCLURCAGDNBMIAMDNDIDNSJBNURGFM_CABVGEPRNWXLTJSUHFMDIOQYKZ}

https://quipqiup.com/で復号する。

BCACTF{ FREQUENCY ANALYSIS IS VERY GREAT WHEN SOLVING ANNOYING MONO ALPHABETIC SUBSTITUTION CIPHERS_ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
bcactf{frequencyanalysisisverygreatwhensolvingannoyingmonoalphabeticsubstitutionciphers_abcdefghijklmnopqrstuvwxyz}

a-major-problem (crypto 200)

https://major-system.info/en/ で各単語の数値(何行目か)を得る。

Pave 98
Pop  99
Poke 97
Pop  99
Dutch 116
Dozen 102
Denim 123
Deism 103
Loot   51
Thatch 116
Pal 95
Atheism 103
Rough 47
Ditch 116
Tonal 125

ASCIIコードとして文字にする。

bcactf{g3t_g/t}

これはフラグとして通らなかった。Roughの行数がずれたと考え、48にして考えると、フラグとして通った。

bcactf{g3t_g0t}

split-the-red-sea (forensics 100)

Stegsolveで開き、Red plane 1を見ると、フラグが書いてある。
f:id:satou-y:20190616201452p:plain

bcactf{7w0_r3d5_sdf3wqa}

bca-craft (forensics 125)

datapacks\bcacraft\data\bca\functions\flag.mcfunctionを見ると、フラグ文字列が入っている。

tellraw @a ["Hello ", {"selector": "@p", "color": "yellow"}, "! The flag is: ", "b", "c", "a", "c", "t", "f", "{", {"text": "m1n3cr4f7_b347s_f0rtn1t3", "color": "blue", "bold": true, "obfuscated": true, "hoverEvent": {"action": "show_text", "value": {"text": "Good luck! ", "extra": [{"text": "Hint: Where does Minecraft store its worlds?", "color": "dark_gray", "italic": true}]}}}, "}"]
bcactf{m1n3cr4f7_b347s_f0rtn1t3}

file-head (forensics 125)

pngのファイルヘッダが壊れている。修正すると、画像を見ることができ、フラグが書かれている。
f:id:satou-y:20190616205022p:plain

bcactf{f1l3_h3ad3rs_r_c001}

open-docs (forensics 150)

解凍すると、word/secrets.xmlBase64文字列が入っている。

PHNlY3JldCBmbGFnPSJiY2FjdGZ7ME94TWxfMXNfNG00ejFOZ30iIC8+
$ echo PHNlY3JldCBmbGFnPSJiY2FjdGZ7ME94TWxfMXNfNG00ejFOZ30iIC8+ | base64 -d
<secret flag="bcactf{0OxMl_1s_4m4z1Ng}" />
bcactf{0OxMl_1s_4m4z1Ng}

wavey (forensics 150)

wavファイルが添付されている。Audacityで開き、スペクトグラムを見る。
f:id:satou-y:20190616205314p:plain

bcactf{f331in_7h3_vib3z}

the-flag-is (forensics 200)

pdfファイルが添付されている。PDFStreamDumperで開き、0x172-0x1D2を見ると、フラグが入っていた。

BT /F1 16 Tf 100 700 Td (bcactf{d0n7_4g3t_4b0u7_1nCr3Men74l_uPd473s}) Tj ET
bcactf{d0n7_4g3t_4b0u7_1nCr3Men74l_uPd473s}

large-pass (reversing 100)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long in_FS_OFFSET;
  long local_20;
  long local_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_18 = 0x4cf7983ffb7741c8;
  __printf_chk(1,"Password: ");
  __isoc99_scanf(&DAT_0010098f,&local_20);
  if (local_18 == local_20) {
    puts("Correct password.");
  }
  else {
    puts("Incorrect Password.");
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

入力した文字を数値として扱い、0x4cf7983ffb7741c8と比較している。

>>> 0x4cf7983ffb7741c8
5546068866699313608L
5546068866699313608

survey (0: welcome 25)

簡単にコメントして、Submitしたら得点となった。

HSCTF 6 Writeup

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

Discord (Miscellaneous)

Discordに入り、#announcementsチャネルでメッセージを見ると、フラグが書いてあった。

hsctf{hi_welcome_to_discord}

Hidden Flag (Miscellaneous)

pngファイルの最後にkey is invisibleと書いてある。"invisible"をXOR鍵として復号する。

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

key = 'invisible'
enc = enc[:-len('key is invisible')-1]

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

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

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

hsctf{n0t_1nv1s1bl3_an5m0r3?-39547632}

64+word (Miscellaneous)

Base64文字が100×100に敷き詰められている。フラグがBase64で縦横斜めのどれかに隠れていると推測できる。検索しても横には無かったので、とりあえず縦上からと斜め左上からで検索するスクリプトを組む。

import base64

with open('64word.txt', 'r') as f:
    rows = f.read().split('\n')

def disp_lu_rd(rows, x, y):
    s = ''
    m = max(x, y)
    for i in range(len(rows) - m):
        s += rows[y+i][x+i]
        if len(s) % 4 == 0:
            if '}' in base64.b64decode(s):
                break
    return s

target = base64.b64encode('hsctf{')

## Vertical ##
for y in range(len(rows)-len(target)):
    for x in range(len(rows[0])):
        s = ''
        for i in range(len(target)):
            s += rows[y+i][x]
        if s == target:
            print x, y

## Left Up to Right Down ##
for y in range(len(rows)-len(target)):
    for x in range(len(rows[0])-len(target)):
        s = ''
        for i in range(len(target)):
            s += rows[y+i][x+i]
        if s == target:
            b64 = disp_lu_rd(rows, x, y)
            flag = b64.decode('base64')
            flag = flag[:flag.index('}')+1]
            print flag

左上から斜めに右下方向に隠されていた。

hsctf{b4s3_64_w0rd_s3arch3s_ar3_fu9?}

Inspect Me (Web)

HTMLソースを見ると、コメントにフラグの一部がある。

<!-- The first part of the flag is: hsctf{that_was_ -->

style.cssを見ると、コメントにフラグの一部がある。

body {
    font-family: Arial, Helvetica, sans-serif;
    background-color: #000;
}

main {
    max-width: 70ch;
    padding: 2ch;
    margin: auto;
}

/* The second part of the flag is: pretty_easy_ */

script.jsを見ると、コメントにフラグの一部がある。

document.addEventListener('contextmenu', function(e) {
    e.preventDefault();
});

// The last part of the flag is: right}

全部結合すると、フラグになる。

hsctf{that_was_pretty_easy_right}

Agent Keith (Web)

HTMLソースを見ると、コメントにこう書いてある。

<!-- DEBUG (remove me!!): NCSA_Mosaic/2.0 (Windows 3.1) -->

UserAgentをこのデータにしてアクセスする。

$ curl  -H "User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)" https://agent-keith.web.chal.hsctf.com/
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
        <title>agent-keith</title>
        <link rel="stylesheet" href="http://localhost:8002/static/style.css">
    </head>
    <body>
        <main>
            <h2>If you're not Keith, you won't get the flag!</h2>
            <p><b>Your agent is:</b> NCSA_Mosaic/2.0 (Windows 3.1)</p>
            <p><b>Flag:</b> hsctf{wow_you_are_agent_keith_now}</p>
            <!-- DEBUG (remove me!!): NCSA_Mosaic/2.0 (Windows 3.1) -->
        </main>
    </body>
</html>

レスポンスにフラグが入っていた。

hsctf{wow_you_are_agent_keith_now}

S-Q-L (Web)

問題はSign-inのぺージ。SQLインジェクションをしてみる。

Username: ' or 1=1 #
Password: a

上記入力してサインインすると、フラグが表示された。

Hello Keith!
The flag is hsctf{mysql_real_escape_string}
hsctf{mysql_real_escape_string}

Cool Image (Forensics)

pdf拡張子のファイルが添付されている。

$ file cool.pdf
cool.pdf: PNG image data, 1326 x 89, 8-bit/color RGBA, non-interlaced

pdfではなかったようだ。拡張子をpngにして開く。
f:id:satou-y:20190608112036p:plain

hsctf{who_uses_extensions_anyways}

Cool Image 2 (Forensics)

PNGファイルのシグネチャの前に以下のゴミが入っている。

I found this cool file. Its really cool!

このゴミを取り除き、ファイルを開く。
f:id:satou-y:20190608112429p:plain

hsctf{sorry_about_the_extra_bytes}

Logo Sucks Bad (Forensics)

PNGファイルが添付されている。StegsolveのData ExtractでRGBのLSBをテキストで見る。

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non velit rutrum, porttitor est a, porttitor nisi. Aliquam placerat nibh ut diam faucibus, ut auctor felis sodales. Suspendisse egestas tempus libero, efficitur finibus orci congue sit amet. Sed accumsan mi sit amet porttitor pellentesque. Morbi et porta lacus. Nulla ligula justo, pulvinar imperdiet porta quis, accumsan et massa. In viverra varius eleifend. Ut congue feugiat leo a ultrices.

Ut risus ipsum, dictum id euismod nec, mattis eu dolor. In aliquam viverra congue. Mauris lacinia lectus quis erat porttitor, vitae iaculis mauris ultrices. Donec quis imperdiet mi, et fermentum purus. Mauris rhoncus sit amet ex quis gravida. In tempor, libero vel finibus tristique, velit est vestibulum est, non semper leo mauris vel enim. Nulla non orci pharetra, bibendum quam a, pharetra felis. Morbi tincidunt, mauris nec aliquam maximus, eros justo rutrum odio, in dapibus sem arcu blandit nunc. Mauris dapibus sem lorem, quis lacinia nunc consectetur pulvinar. Donec sapien erat, pulvinar non fermentum tempor, auctor pellentesque tortor.

Suspendisse id vehicula enim. Cras ut enim sollicitudin, aliquam mauris eget, vehicula arcu. Morbi convallis sed nulla et pellentesque. Cras risus justo, fermentum eget ex ac, dictum dignissim magna. Nullam nec velit vel nulla varius gravida. Aliquam ac lorem tempor, venenatis nibh sed, ultricies urna. In fringilla hendrerit purus, tristique aliquam ipsum molestie vitae. Sed efficitur auctor lacus ac luctus.

Donec id viverra augue. Vivamus nullhsctf{th4_l3est_s3gnific3nt_bbbbbbbbbbbbb}a neque, iaculis quis urna eget, gravida commodo quam. Vestibulum porttitor justo in suscipit rutrum. Sed id tristique ipsum. Nulla vel porta nisl. Quisque leo quam, placerat id neque eu, ullamcorper facilisis lacus. Maecenas magna eros, sollicitudin id est a, fermentum elementum leo. Vestibulum porttitor urna eget bibendum interdum. Mauris eget consequat est. Aenean hendrerit eleifend finibus. Sed eu luctus nulla, non tristique nunc. Cras aliquet vehicula tincidunt. Maecenas nec semper ipsum.

Proin pulvinar lacus id malesuada bibendum. Mauris ac sapien eros. Sed non neque id ante porta finibus eget eget enim. Pellentesque placerat, neque sit amet dictum eleifend, tortor dolor porttitor ex, in vestibulum lacus tortor id purus. Phasellus varius nulla sed magna finibus aliquet. Proin eros metus, sodales vel enim eu, imperdiet pulvinar erat. Nunc quis iaculis dui. In cursus a urna in dapibus. Sed eu elementum quam. Vivamus ornare convallis leo sed mollis. Aenean sit amet nulla vel leo cursus dictum ac nec sem. Morbi nec ultrices felis.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

この長文の中にフラグが含まれていた。

hsctf{th4_l3est_s3gnific3nt_bbbbbbbbbbbbb}

Reverse Search Algorithm (Cryptography)

RSA暗号。factordbでnを素因数分解する。

p = 29
q = 19378812610208711050554891591368513578428260883630885898953907471497427917962675301070084754463193723428901453

あとはそのまま復号する。

from Crypto.Util.number import *

n = 561985565696052620466091856149686893774419565625295691069663316673425409620917583731032457879432617979438142137
e = 65537
c = 328055279212128616898203809983039708787490384650725890748576927208883055381430000756624369636820903704775835777

p = 29
q = 19378812610208711050554891591368513578428260883630885898953907471497427917962675301070084754463193723428901453

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

flag = long_to_bytes(m)
print flag
hsctf{y3s_rsa_1s_s0lved_10823704961253}

Super Secure System (Crypto)

$ nc crypto.hsctf.com 8111
* * * SUPER SECURE SYSTEM * * *
My encryption system is impossible to crack if used once!
You can use this system to encrypt any of your messages with my super special key!!!
Here is my super secret message: 09321f276a3f475b137455033254233e281740131b3f024701711c2c496c030f6d5e686536496539487d692b3706715d2f5d550609

Enter the message you want to encrypt: aaa

Encrypted: 00201d

Enter the message you want to encrypt: bbb

Encrypted: 03231e

XORが固定になっているように見える。XORキーを求め、復号する。

import socket

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

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(('crypto.hsctf.com', 8111))

data = recvuntil(s, 'message: ')
enc_flag = recvuntil(s, '\n').rstrip()
print data + enc_flag

data = recvuntil(s, 'encrypt: ')
try_text = 'a' * (len(enc_flag) / 2)
print data + try_text
s.sendall(try_text + '\n')

data = recvuntil(s, 'Encrypted: ')
enc = recvuntil(s, '\n').rstrip()
print data + enc

key = str_xor(enc.decode('hex'), try_text)
flag = str_xor(enc_flag.decode('hex'), key)
print flag

実行結果は以下の通り。

* * * SUPER SECURE SYSTEM * * *
My encryption system is impossible to crack if used once!
You can use this system to encrypt any of your messages with my super special key!!!
Here is my super secret message: 470f1d53247123393b6a2a5347551a7b5b3c5d064b4630552d7a047d6573005f48564e7252534e01596b5a364a6b6b091305396177

Enter the message you want to encrypt: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Encrypted: 4e1d1f46236b2a682d542f01426b02294f0258021944234d1c2f3a686c213e4d18471c416c411c034d58080846393f5d135d3d3f6b
hsctf{h0w_d3d_y3u_de3cryP4_th3_s1p3R_s3cuR3_m355a9e?}
hsctf{h0w_d3d_y3u_de3cryP4_th3_s1p3R_s3cuR3_m355a9e?}

A Lost Cause (Crypto)

文字列の後ろに行くにつれ、シフト数が減っていくとのことで、26パターン総当たりする。

def decrypt(s, n):
    dec = ''
    for i in range(len(s)):
        code = ord(s[i]) - n + i
        while True:
            if code < ord('A'):
                code += 26
            elif code > ord('Z'):
                code -= 26
            else:
                break
        dec += chr(code)
    return dec

enc = 'CGULKVIPFRGDOOCSJTRRVMORCQDZG'

for i in range(26):
    dec = decrypt(enc, i)
    print dec

実行結果は以下の通り。

CHWOOAOWNAQOABQHZKJKPHKOAPDAI
BGVNNZNVMZPNZAPGYJIJOGJNZOCZH
AFUMMYMULYOMYZOFXIHINFIMYNBYG
ZETLLXLTKXNLXYNEWHGHMEHLXMAXF
YDSKKWKSJWMKWXMDVGFGLDGKWLZWE
XCRJJVJRIVLJVWLCUFEFKCFJVKYVD
WBQIIUIQHUKIUVKBTEDEJBEIUJXUC
VAPHHTHPGTJHTUJASDCDIADHTIWTB
UZOGGSGOFSIGSTIZRCBCHZCGSHVSA
TYNFFRFNERHFRSHYQBABGYBFRGURZ
SXMEEQEMDQGEQRGXPAZAFXAEQFTQY
RWLDDPDLCPFDPQFWOZYZEWZDPESPX
QVKCCOCKBOECOPEVNYXYDVYCODROW
PUJBBNBJANDBNODUMXWXCUXBNCQNV
OTIAAMAIZMCAMNCTLWVWBTWAMBPMU
NSHZZLZHYLBZLMBSKVUVASVZLAOLT
MRGYYKYGXKAYKLARJUTUZRUYKZNKS
LQFXXJXFWJZXJKZQITSTYQTXJYMJR
KPEWWIWEVIYWIJYPHSRSXPSWIXLIQ
JODVVHVDUHXVHIXOGRQRWORVHWKHP
INCUUGUCTGWUGHWNFQPQVNQUGVJGO
HMBTTFTBSFVTFGVMEPOPUMPTFUIFN
GLASSESAREUSEFULDONOTLOSETHEM★
FKZRRDRZQDTRDETKCNMNSKNRDSGDL
EJYQQCQYPCSQCDSJBMLMRJMQCRFCK
DIXPPBPXOBRPBCRIALKLQILPBQEBJ

英文になりそうなものがフラグになる。

hsctf{GLASSESAREUSEFULDONOTLOSETHEM}

Massive RSA (Crypto)

nを素因数分解しようとしたら、n自身が素数であるとわかる。このため phi = n - 1 になる。あとはそのまま復号する。

from Crypto.Util.number import *

n = 950687172821200540428729809153981241192606941085199889710006512529799315561656564788637203101376144614649190146776378362001933636271697777317137481911233025291081331157135314582760768668046936978951230131371278628451555794052066356238840168982528971519323334381994143826200392654688774136120844941887558297071490087973944885778003973836311019785751636542119444349041852180595146239058424861988708991060298944680661305392492285898022705075814390941667822309754536610263449507491311215196067928669134842614154655850281748314529232542980764185554607592605321212081871630106290126123668106453941684604069442637972979374182617204123679546880646955063471680804611387541602675808433185504968764805413712115090234016146947180827040328391684056285942239977920347896230959546196177226139807640271414022569186565510341302134143539867133746492544472279859740722443892721076576952182274117616122050429733446090321598356954337536610713395670667775788540830077914016236382546944507664840405622352934380411525395863579062612404875578114927946272686172750421522119335879522375883064090902859635110578120928185659759792150776022992518497479844711483878613494426215867980856381040745252296584054718251345106582780587533445417441424957999212662923937862802426711722066998062574441680275377501049078991123518677027512513302350533057609106549686502083785061647562269181863107725160293272971931807381453849850066056697913028167183570392948696346480930400320904644898839942228059188904225142187444604612121676565893284697317106343998167640380023972222033520190994951064491572372368101650142992876761420785551386138148283615194775971673577063363049929945959258097086463812469068598955485574579363616634109593903116561526921965491646400040600138481505369027344295330767163087489333402201631708610718911106905154471963379233672543874307197342217544783263700843246351822145605839955798639016346308363889766574606793652730311687899415585873892778899179927359964882217066947566799298173326850382334054179474389651499891117938361854701587568363867264590395711833275763832842002504433841816245069655064326325306033334336469743800464944131049874472540605264250854258280373869113420817955012823462838351481855289027030577957168468047751024562853260494808998446682723835213272609799649864902376137320638444968430858790173696935815430513690803796736064125183005539073920032869713201073105497655763097638587404309062750746064609677994654409535743453776560694719663801069746654445359756195253816544699551
e = 65537
c = 358031506752691557002311547479988375196982422041486602674622689505841503255891193495423484852537391230787811575487947331018616578066891850752360030033666964406349205662189685086812466246139857474435922486026421639388596443953295273675167564381889788905773472245885677132773617051291379731995063989611049809121305468803148551770792609803351375571069366930457307762595216806633327492195442616272627113423143562166655122764898972565860928147259322712805600875994388377208017608434714747741249858321487547543201109467214209112271771033615033493406609653861223917338109193262445432032609161395100024272041503554476490575517100959892951805088735483927048625195799936311280172779052715645263075391841840633949032397082918665057115947698884582406130793211266028238396814146117158924884049679536261009188784571232730683037831940224049822081316216826346444136538278601803972530054219050666898301540575647763640218206611889707353810593843233814867745903144987805142815936160730054575462147126944741419094810558325854901931279755547624294325463528887326262902481099025253153222985717157272371423956465138892784879439141174797253720403065191378958340033965895823856879711180993895832306970105743588207727415495184380531676665121800713201192348940665501790550763379781627493441276077597720109700408848080221149485596419299548121287851605588246207568970548444975309457244824469026820421430723018384050095117420646392648577894835705672984626936461419833136418809219064810002991383584690376016818146065548853387107821627387061145659169570667682815001659475702299150425968489723185023734605402721950322618778361500790860436305553373620345189103147000675410970964950319723908599010461359668359916257252524290941929329344189971893558606572573665758188839754783710992996790764297302297263058216442742649741478512564068171266181773137060969745593802381540073397960444915230200708170859754559500051431883110028690791716906470624666328560717322458030544811229295722551849062570074938188113143167107247887066194761639893865268761243061406701905009155852073538976526544132556878584303616835564050808296190660548444328286965504238451837563164333849009829715536534194161169283679744857703254399005457897171205489516009277290637116063165415762387507832317759826809621649619867791323227812339615334304473447955432417706078131565118376536807024099950882628684498106652639816295352225305807407640318163257501701063937626962730520365319344478183221104445194534512033852645130826246778909064441514943

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

flag = long_to_bytes(m)
print flag
hsctf{forg0t_t0_mult1ply_prim3s}

Really Secure Algorithm (Crypto)

nを素因数分解しようとしたら、n は平方数(=p*p)であることがわかる。このため phi = p * (p -1) になる。あとはそのまま復号する。

from Crypto.Util.number import *

n = 263267198123727104271550205341958556303174876064032565857792727663848160746900434003334094378461840454433227578735680279553650400052510227283214433685655389241738968354222022240447121539162931116186488081274412377377863765060659624492965287622808692749117314129201849562443565726131685574812838404826685772784018356022327187718875291322282817197153362298286311745185044256353269081114504160345675620425507611498834298188117790948858958927324322729589237022927318641658527526339949064156992164883005731437748282518738478979873117409239854040895815331355928887403604759009882738848259473325879750260720986636810762489517585226347851473734040531823667025962249586099400648241100437388872231055432689235806576775408121773865595903729724074502829922897576209606754695074134609
e = 65537
c = 63730750663034420186054203696069279764587723426304400672168802689236894414173435574483861036285304923175308990970626739416195244195549995430401827434818046984872271300851807150225874311165602381589988405416304964847452307525883351225541615576599793984531868515708574409281711313769662949003103013799762173274319885217020434609677019589956037159254692138098542595148862209162217974360672409463898048108702225525424962923062427384889851578644031591358064552906800570492514371562100724091169894418230725012261656940082835040737854122792213175137748786146901908965502442703781479786905292956846018910885453170712237452652785768243138215686333746130607279614237568018186440315574405008206846139370637386144872550749882260458201528561992116159466686768832642982965722508678847

p = 16225510719965861964299051658340559066224635411075742500953901749924501886090804067406052688894869028683583501052917637552385089084807531319036985272636554557876754514524927502408114799014949174520357440885167280739363628642463479075654764698947461583766215118582826142179234382923872619079721726020446020581078274482268162477580369246821166693123724514271177264591824616458410293414647
assert n == p * p

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

flag = long_to_bytes(m)
print flag
hsctf{square_number_time}

Spooky ECC (Crypto)

スクリプトの概要は以下の通り。

myPubKey = g * myPrivKey
bobPubKey * myPrivKey がAES共通鍵

SSSA AttackでmyPrivKeyを求めることができれば、AES共通鍵がわかる。あとはそのまま復号すればよい。

from ecpy import *
from hashlib import sha256
from Crypto.Cipher import AES
import binascii

def computeSharedSecret(pubkey, privkey):
    return pubkey * privkey

p = 0xb09700d3d1c7123f0b0336474c18c3f3f60002d480a4bce33f007c08b498197ed832687c47c2bc76b7eb199d3a420fcf77d3e5a32389fefb1032744bb473a4bd
A = 0x1b2a886d1cfcaecd03954657956cd03df56ec7709fbb0de738fb073ed20b92b6fa3d72f771618c5e2060a23c33b586a6046993894fd4950db2c12776e77fdbd1
B = 0x303bde5e945d46949b8c9986519a9a1f0301f61ff043b3bf2785fd85e365e4caa163c64ad307db8dbbac0087fd8562273ee61aac095815030cc73c7495b46ddb
g_x = 0x9f12acd5b74cc67e03506be8f904087863ce7fd8ed1de6404f26e8e96bea3761fca1f5b21def5298e7adbbf8787ea431a43d241fda6bc9fbaddeaff35ab4f7c3
g_y = 0x51f33f0e5c36e1bf91ac78b04c7e4f819bfad8db291fc2e20c10ee00e98525927719ecf0e8b96c5e62e3f48a38b94e72dddee1109bfdad9c7dfd3f566da69eb4
h_x = 1177058043549358413014554258002815119079001682731148396776662750875463733619059415667987598866208023692880799135159888362631239206873676420277546691755222
h_y = 6042132606876152754155047441818131810928517366269481359146510190883638121779596002132009344517568983680414721512960291321687246617263491498797986759689315
flag_enc = 'd5cb4f93aa95af738bbcf5cbc1d4f1b66c9c9f84b4257035cf19e3ee41e2b79384fed7ef7d9fb58f6dfb86fefc95429b9f87b5b8a330aa082681fd140b8156bd'

F = FiniteField(p)
E = EllipticCurve(F, A, B)

G = E(g_x, g_y)
H = E(h_x, h_y)

myPrivKey = SSSA_Attack(F, E, G, H)

bob_pub_x = 0x993cf91c25dd287e30cb8f6a0d4fa70e89e90ac0953e7ee876b1ef190a6a442235479162b5ac61beb1d1a5aca03313ff5c53c2e3c81df2fbedf3b0add0b20d18
bob_pub_y = 0x4e75b39de8d5daa3f5f489c02b8fa2cce6f2cfb406bb4a5a0d75d29a3021dcd61df697ef485743e7f8a1b9cc60879bc808e74f9c909b2f0cecb1df0a03c771f5

bobPubKey = E(bob_pub_x, bob_pub_y)

DH_secret = computeSharedSecret(bobPubKey, myPrivKey)
DH_secret_str = '(%d, %d)' % (DH_secret.x, DH_secret.y)
AES_secret = sha256(DH_secret_str).digest()
obj = AES.new(AES_secret, AES.MODE_ECB)
ciphertext = binascii.unhexlify(flag_enc)
plaintext = obj.decrypt(ciphertext)
flag = plaintext.rstrip(' ')
print flag
hsctf{Anomalous curves, m0ar like anom00se curves}

Marginally More Spooky ECC (Crypto)

スクリプトの概要は以下の通り。

myPubKey = g * myPrivKey
bobPubKey * myPrivKey がAES共通鍵

「Spooky ECC」と同様にSSSA AttackでmyPrivKeyを求めることができれば、AES共通鍵がわかる。あとはそのまま復号すればよい。

from ecpy import *
from hashlib import sha256
from Crypto.Cipher import AES
import binascii

def computeSharedSecret(pubkey, privkey):
    return pubkey * privkey

N = 0x5f455eda519035fc8968cd1827931584553b826d33d6aa28553df93381878cc0f0d9ff60184251aa2624e10f79aa2a952a4e5ef06f03b5439af62db1cbc4832024757a21d1bdc0ceb6ac996a6c8258b2f28979f48b989bd90c30aa14a59adec286e2019bccdcae6e92fefcd7e9734b4c2f2d41489f771e58e0903e85f327c930e479c628281a5da73fd18e203f96c60a0738182ce0a137ba6c7a603f66f30e26ef37ddefd14b2787679daf8b040241fa971037b3eff67fe2f021abf62c5e765e1a8d44c62225c7768a8baa602eb06c03d2b79c7c25dc68cd2392432533bb780ba2d4b822b9098d45d28ee83e05e85fc965ef2ca6169c51b5da77a80207a7cfd4ea8471b42ae3093b29702bf62ee9fd16d97928e4da46b9f7564682ce684eab3183b232a2bf64fa9bb39a8e3e45378730432d51f67be725a7cd874c2d065dc5d0b5e8e3fd8d2aeeeb78abb837a08ca7a019e1856e43caf310176c72690bbcd20933fc48f3e3c90793a5c0ec1a5082c366524ffd30430e350079844c0588cf2497
A = 0x22f8e75e46ae5938f376ede629ae4b0c409e95df47addb399d0b7976d9127cecc21690737ff65821f6265418586861137e05c9ac1b5bab058de197b2871a8658b78d725e2a2ed195679748b8d3a01702f823a840ed8b8ab687faf75c3b25e617fd1fb746232380310a402e35ca9b3f5f261cfd4220219a7c8f8e1d8f1df1ee8172ab4096ad673179a06525e824ae63e3ebee83c86371050e805ecb919657deb1bebbf2ab786c75f65473b4c19a2a96aa5b5803eeb9883a46e97bf98d23045853fb628eb76c07936f23733e8661c080a5b09176d9d8696410a8347b4a52e5fb839db70780eecdd4453112e7310624b49ec3240c8b2a280276b409beab0765e1f220158e13e81614c927e982bf265c2626f0b8cc7300071c1b7c6ba6d09533a53c736b0890872b081759d8717964f1d7c659025f3c6b3a1bacb9dfc0847e971aa14c82d174376ddaffa598f3a54d4e2b75c07e80c6f448fbd0cdce5f52bad11adf4245b36664d9ea5a6644679ba9c17453bb7008a57a64b2e0a8527ddb81848cb1
B = 0x1fcb5c6d1ce86a8b6a23bf62b97ba31d9a5944ecc7fdc14de93b4abc62db794b7e1550ed9c2dc3501f2e6256d20e4c96fee5e3ae93069d35acee3644ec75ace6f6626f6d1ab8f48cfb8a05f6cb7409cddac6afe0762732beb1f2ad06f92738790bb805c710a574eb64ce6da4ac304a240fb783290bb30947aed2dab52344f9093067d53c83f800d25a49d263c8a20d389a850b11a4518a9d4fad16f011278617df63304a55f5d9d4c8c5866ace275faf0fcbe2003bf7ff3ae40338c889ac8bf6334ba924ce1b4e146a159453aa7a5bbb85e19932072ca23ee2680e857a713e572c9de3bf675d7f1cf3e4d9838e619558b41373b715e151f759ce721f32beb6e1f0e0b6696989370a9990d2a7653d06dc7b65af2aa90adf3ed68ce74c414066a0ed5a163b0a76e5db4259cc2d28a8de96e43fd939fb53f2a882da9e3ec983860a76607636b9885e00a6a8fb0beba28cada7882a979c5cd2c4577edd4ff5abc93d2ab53b73df724539d1c89449c98f34ee2ad33da21b97625f683ed8a33d5ac6b0
g_x = 0x3f66670d68519cbe0057246a150b34c579e34ec8880bbc4863a9e3cb4794f90ab5efaf00a9ddd323eeb3eb654e1d508be16f5ba3411ab929553fbb0b1c76e9e897055ec094d8747d5cdd47161296547442d8fd63a798d385490ea57aaa3af4af8a75a2495aeda31da47bc1a890abe142784f8743a71861a8c1aab547999acae9e6f72f320b6f550bca5222936b3cc1dfbd657c9cd655121d2a0c83047621f20e65fdba41ec098c6b51269ab8fe22938c292ae199ff49ad9afcc6e99f8d462ee0499c7e58eae6861d593426790fb6d747695e0f93dd3e00e29bb37911e88c13543467444a3ba90d7c9a4eb30aa2b7fd37fb209e034a15ff40b08ea4916311b22e54015fa1772b12dae675780a87546b8cda2bdc324be8b83a1b99a5c151339445a66a2828554b611a4257026d9c20c90617412a85f2786a45fb590c444a3c88d6b8ff38fc2e8972ebc1492c248add99c48f2ec70db4d2460da32d064d58aeeb895cf4e9a20a2b8ce12e426b3f62795adee331b1b61afa2e43884e35818ebe80d6
g_y = 0x38ba31ae0253a3a4070ef7e4380f40684f5a061f49d6591661ad5b4ff57deda46923bdc8185fcda5942f404030516ca5d1c7cf7ec9b43e4cbcdeed0296906988fa054a18d5a7f70f06692a140cf69e669884889bc778d72bd9a1e5c1dcdfba16121461a221dcdac7287ad5b6d3710c00fd100ef89c038baf23c621766e470501aa86de194539fb087dddc3ad5c0f3c78a79d0f4a981850af7c3cb86f392286fe075b10f5761d923cdfcd92cc1017bdee4e83ac64692ac76c40057735122813b2003b19422dc3adaf0f502eb254685ae88161fbde92027d8f419f4f939c988ee7ec34a05736e105d647baf2b84304caf006f39f85096419a11801f2402e02791f3d9813bdd50d8a0a7aaeade853d7b17e75f68612467cc59f7ec3e63ddb3551a697ba02f772671c4569acd5edec0e1e8e6f73fcb032d99789cefb572deb8b6ff330ddd48a2bcf77b47862925a155670679fc9acfd7900d4250a20821eb52a86819daec526f31bfd27cc26d46dfe71da188efde1cbdb115b2cacd910f3f4fb0c63
h_x = 437071643139846376174570991796635821556515973629140637974881910713809347474907708565664978550023080253628563436981461136240187164218943955473906962427621588751133571536263233128379933305722755097642938222821729992112928292343709506067984904756673958537023071586963031303778873355502404560858525640218412720622373369824098745839367743073545069145335781750997022711031566712394148013463290094808515199272554914216404105860245097476566116111286301652851468677715923578954629435631529176601375057076486686319987081515732244810707537343009005070717978425223526163538923468401194292605591320411183706696317193017854195733313461440688247150235910596818920488351049708819811858751684500436391333903440323475318106920865373152352052176746206454328646354342334742517809741676585846785744248199296743482652484393544744906275391431743950732816017334306485823146135060569081401399868784435172595669863476450094990358575115579475893309208
h_y = 1129749802565185627834158659553807502912216175360213456254075674481088988433443479576096556714622648959392819034084284852916959478876546195923213995294956323268527231581766047828610029859113248478558662868743044337554958806463167785068464444239137312628465183766940740668064934990938102926858847015457403169279134366635216151257462869638958624971531184853170344683049422552732473598742118914767190141609028327074078277290367090610634101108300855280065814455390630761148497086654130386459756495733281280347492148461347415812198075975619307315113752254659557316385115639576216222522363256407482949179689085285199590385738252375535519944626298197399005839337752193084119312960497435211490324941382907224728563381320712225158253541488668785632485406927982563839147149734593198947641619638737854288142722191014592471804685884172858025758753635315661266804801339190058215957655776800114139349071970424218949196308458641913657267781
flag_enc = 'f84a130d288fd200e5564253a7ea34e7dda3ccd28f18440fa06c92a3076ccc6746b8b95734c87177e2cc8759e0b00365'

F = FiniteField(N)
E = EllipticCurve(F, A, B)

G = E(g_x, g_y)
H = E(h_x, h_y)

myPrivKey = SSSA_Attack(F, E, G, H)

bob_pub_x = 0x35c7220105e55ae41a7098f528d4b25a4fb9840bae5e1489645be43d3d76853160ddc9afe04a53aef2b400f85b91272e71941c192cf49cbe2b4f73ce8e715da1986e40253eea400ef7b0e6c60aee5cd6984d29bcdd32a53cec2f47fd0f30d4fdf2f46c59a46d2d54c7766558c318230d4265d25ee72b6d8eff54403f36db684236a80dca118d464bd31eff3cb3a30fdd30c6924100960b8fbda2f6b64e7a04332b2ff481788e9ef26516d21d4b10afbbb59519c0bab71d84ff2df56cf29756e169a61a54800761ac4f9ef562f685ec004ff5c26917e28d914c8f3274f9effbc388c8151e46b490256325d9843d1d09f17059bcad949e75ffaa30027d13fcf169689c84f67693d0f12d8ec46a4bda3c9f8e0f61edd1c1525e0b55bf879ec6b3c53c45bc6f76a705de7554a40717e36150ba44ec1a52ae98587055129a02d211da8d18c74f0b69edb020d89325c1a41cdacdc4a7609527b6791407e2a2e79b8f0eb8f581f3317c47eb6a1afda3f8888b3580e4552ed5ab0030487b77b3e87db495
bob_pub_y = 0x17ab49ada83cbac774f8e9d48a9190ce2cde27d20981894526a4f7a151863a78c791d3cabcbe2d720fe9ae75549b685d5d068d61cfc31725ea3e1ee62417e977024e069d5f559c0e706ddeec7609a0ded60b694f638911996b872715ef0aca5b6d0b6652991bd37163885a60842e009794c03a56de47f107ae1b9c3de750d7e479c30836bdbc72f206991388812ace1dc11f03b5e9f96d5f3b80464389d9f137b62ea6d063b866424a4b04605fa2e7c9e2816fe19f98cbafed371711fc0923a234c194996004cf1fae0934a96d54f3dde1aa6d6fe68e84cb1ad2795b969b36b333cae0ac82c4010c12ffcedffc670859135ad0058ffff60f511946e44bc8f5295e85798c0466b2fc89a252797d65a338b37151385355b89adf70f2014667f1374c1508f30165e8b16bf9fd9f789499438db5fa725769a73844306afe2a99344c9fac0954bf3355318b0ba8283c9b45a29714968ee6b05cb381ecf6282d5ee27a4e57a9a646b87661fe3f605228d2326903051a32a3470515a40463d5084b614

bobPubKey = E(bob_pub_x, bob_pub_y)

DH_secret = computeSharedSecret(bobPubKey, myPrivKey)
DH_secret_str = '(%d, %d)' % (DH_secret.x, DH_secret.y)
AES_secret = sha256(DH_secret_str).digest()
obj = AES.new(AES_secret, AES.MODE_ECB)
ciphertext = binascii.unhexlify(flag_enc)
plaintext = obj.decrypt(ciphertext)
flag = plaintext.rstrip(' ')
print flag
hsctf{Y_does_4lice_have_such_weird_cuRV3s?}

Tux's Kitchen (Crypto)

$ nc crypto.hsctf.com 8112

				TUX's KITCHEN
                    ..- - .              
                   '        `.           
                  '.- .  .--. .          
                 |: _ | :  _ :|          
                 |`(@)--`.(@) |          
                 : .'     `-, :          
                 :(_____.-'.' `          
                 : `-.__.-'   :          
                 `  _.    _.   .         
                /  /  `_ '  \    .       
               .  :          \   \      
              .  : _      __  .\   .     
             .  /             : `.  \    
            :  /      '        : `.  .   
           '  `      :          : :  `.  
         .`_ :       :          / '   |  
         :' \ .      :           '__  :  
      .--'   \`-._    .      .' :    `).  
    ..|       \   )          :   '._.'  : 
   ;           \-'.        ..:         / 
   '.           \  - ....-   |        '  
      -.         :   _____   |      .'   
        ` -.    .'--       --`.   .'     
            `--                --    
[94502238958180, 3239939113188L, 36316791069768L, 74074574741772L, 66168572136316L, 80623467427418L, 75026654356284L, 50623013930716L, 14521993191832L, 1043830470864L, 9198516838638L, 12614129459328L, 19390428682606L, 50551489441974L, 23163695523340L, 32930787907352L, 64273717132804L, 2308552454540L, 23927252424676L, 4912213340334L, 48608335665180L, 17825781568022L, 59492604464762L, 61049515860332L, 28121911611052L, 70718923063032L, 6188788875442L, 50884233066568L, 50785141535028L, 38001261855484L, 9087790989292L, 10639550205734L, 6534967064988L, 25391516747560L, 33768565198062L, 16229074586954L, 8835287258924L, 10506577178492L, 9979169202152L, 14220693695642L, 41247903800840L, 41507250686812L, 1601286609988L, 70083909145228L, 31814590623406L, 50377900745900L, 14912013039884L, 35580271413072L, 11077521806466L, 53051496283116L, 18090007827476L, 38902288369748L, 24119603003596L, 26321069617516L, 27262655778060L, 29165495157296L, 28788848037938L, 31099248532660L, 5497383893232L, 48470151532048L, 25946791922962L, 25460577127462L, 24691906525312L, 21699741615156L, 30529196924104L, 5142675176780L, 56902352320640L, 24695196850078L, 16999993798690L, 65447783457532L]

スクリプトの処理概要は以下のようになっている。

■key = bake_it()
・10000以上99999以下の回数だけ以下実行
 s = random.randint(100000000000,999999999999)
・s -= random.randint(232,24895235)
・random.randint(100000000000,999999999999)を返却

■final_baking(flag,key)
・baked = rand0m_mess(food,key)
 mess = [key]
 (foodの長さ-1)回以下実行
 ・a=(a*b+c)%d
 ・messにaをappend
 messを返却
・treasureにflagの長さだけ以下をappend
 ord(flag[i])*baked[i]
・treasure = prepare(treasure)
・treasureを表示

ランダム値は何になるかはわからないが、prepare前のtreasureは算出できる。また、prepare前のtreasureの各要素はord(flag[i])*baked[i]になっているため、必ずord(flag[i])の倍数になる。この値を取り、最大公約数を取れば、フラグの各文字のASCIIコードになる。

import socket

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

def rev_prepare(food):
    org_food = food
    for i in range(len(org_food)):
        org_food[-1] -= MY_LUCKY_NUMBER
    for i in range(len(org_food)):
        org_food[i] = org_food[i] ^ MY_LUCKY_NUMBER
    return org_food

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

MY_LUCKY_NUMBER = 29486316

codes = []
for i in range(10):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('crypto.hsctf.com', 8112))
    data = recvuntil(s, '--    \n').rstrip()
    print data
    data = recvuntil(s, ']')
    print data
    treasure = rev_prepare(eval(data))
    if i == 0:
        codes = treasure
    else:
        for j in range(len(codes)):
            gcd, _, _ = egcd(codes[j], treasure[j])
            codes[j] = gcd

flag = ''
for code in codes:
    flag += chr(code)

print flag

実行結果は以下の通り。

    :
                                TUX's KITCHEN
                    ..- - .
                   '        `.
                  '.- .  .--. .
                 |: _ | :  _ :|
                 |`(@)--`.(@) |
                 : .'     `-, :
                 :(_____.-'.' `
                 : `-.__.-'   :
                 `  _.    _.   .
                /  /  `_ '  \    .
               .  :          \   \
              .  : _      __  .\   .
             .  /             : `.  \
            :  /      '        : `.  .
           '  `      :          : :  `.
         .`_ :       :          / '   |
         :' \ .      :           '__  :
      .--'   \`-._    .      .' :    `).
    ..|       \   )          :   '._.'  :
   ;           \-'.        ..:         /
   '.           \  - ....-   |        '
      -.         :   _____   |      .'
        ` -.    .'--       --`.   .'
            `--                --
[81118554797092, 7547416817262L, 13924726641214L, 24608708806052L, 20427372177504L, 4752740762539L, 25685977718996L, 6587661423732L, 6387324995677L, 2779701188569L, 1996595265884L, 10244439233919L, 22440974883832L, 17437990453970L, 4906410834476L, 4633172239142L, 10569490399219L, 6343329221262L, 14609369031062L, 4042647881186L, 23115014471738L, 8643276090734L, 14351992012611L, 20538335765384L, 5654620504828L, 22864776681506L, 9808285250767L, 21882865717333L, 8401998681094L, 20393585938440L, 4493085708140L, 12732130156075L, 18738761835793L, 18689672713854L, 6832347691292L, 12320314719526L, 5600302004409L, 12692625054340L, 20271109605100L, 9486494930594L, 14858167076081L, 10711897536764L, 6740004248652L, 7064890768756L, 10955238662235L, 9420096213164L, 1695027316220L, 8815883981882L, 3359624516498L, 3210261582694L, 4838126713825L, 5415903477176L, 1430472167156L, 16972867365502L, 14937265628996L, 353527285145L, 3861324095989L, 5791399138428L, 16645079816928L, 5130270927668L, 1659285419958L, 1903779832800L, 15454714302078L, 2270784256068L, 11695285242083L, 648658065248L, 8724789270003L, 1406023999045L, 2169124259302L, 1943044899939L]
hsctf{th1s_1s_0ne_v3ry_l0ng_fl@g_b3ca8s3_t5x_l0v3z_vveR9_LOn9_flaGs7!}
hsctf{th1s_1s_0ne_v3ry_l0ng_fl@g_b3ca8s3_t5x_l0v3z_vveR9_LOn9_flaGs7!}

Survey (Miscellaneous)

アンケートに答えたら、フラグが表示された。

hsctf{come_back_next_year!}

Facebook CTF 2019 Writeup

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

irc (misc)

freenodeで#fbctf-2019チャネルに入る。

09:14 *topic : CTF has started. Event ends June 3rd 00:00:00 UTC. fb{move_fast_and_hack_things}
fb{move_fast_and_hack_things}

easter egg (misc)

https://www.fbctf.com/careersのHTMLソースを見ると、HTMLタグの間に1文字ずつフラグの断片が入っている。

fb{we're_hiring}

keybaseish (crypto)

login画面やregister画面そのものには特に何も情報はない。次にlogin画面にリンクされているパスワードリカバリ画面を開く。
f:id:satou-y:20190608082742p:plain
ここではアクセスするたびに変わるpin番号が書いてあり、signature生成のスクリプトへのリンクがある。
スクリプトhttp://challenges.fbctf.com:8080/downloadは以下のようになっている。

from Crypto.PublicKey import RSA
from Crypto import Random

def print_twitter(sig):
    sig_str = str(sig)
    n = (len(sig_str) / 255) + 1
    chunks, chunk_size = len(sig_str), int(len(sig_str)/n) + 1
    tweets = [ sig_str[i:i+chunk_size] for i in range(0, chunks, chunk_size) ]
    print("Please post these signature strings as public twitter posts from your accout:")
    for ndx in range(len(tweets)):
        print ('  "PRF{}/{}:{}"'.format(ndx+1, len(tweets), tweets[ndx]))

def main():
    rng = Random.new().read
    print('Enter challenge pin from site: ')
    pin = input()
    print('Signing "{}" with a new RSA key....'.format(pin))
    RSAkey = RSA.generate(1024, rng)
    signature = RSAkey.sign(int(pin), rng)
    key_params = RSAkey.__getstate__()
    print_twitter(signature[0])
    print('\\n\\nPlease input your public key on the web form:')
    print('  "{}:{}"'.format(key_params['e'], key_params['n']))
    print('\\n\\n')

if __name__ == '__main__':
    main()

スクリプトRSAの通常の署名アルゴリズムになっていて、以下の式が成り立つ。

pow(signature, e, n) == pin

またパスワードリカバリ画面には、管理者のアカウントの情報として、以下のURLが記載されている。

https://twitter.com/baseishcoinfou1

ここにアクセスすると、signatureに関する情報が記載されている。
f:id:satou-y:20190608083033p:plain
スクリプトからsignatureは分断されているだけということがわかるので、1/2に書かれている数値と2/2に書かれている数値を結合したものがsignatureになる。
あとはeを適当に決めて、nを算出してe:nの形式にできれば、フラグにつながる情報が得られる。pinには900194が表示されたので、それを前提にスクリプトを組む。

sig1 = '43522081190908620239526125376626925272670879862906206214798620592212761409287968319160030205818706732092664958217053982767385296720310547463903001181881966'
sig2 = '554081621263332073144333148831108871059921677679366681345909190184917461295644569942753755984548017839561073991169528773602380297241266112083733072690367'
sig = int(sig1 + sig2)

e = 5
pin = 900194

n = pow(sig, e) - pin

print str(e) + ':' + str(n)

実行結果は以下の通り。

5:156152259934610603327242777109298638373934572320003018946780705593035129444427250712903196953268692654576940252842426080729553952653677882004392693966587497895771126063209172520687408155983845138814448218643812756870429001677159139697312185528286445629870220439486395991895413533025617686453330864683812555753179156400433858871091471963735884941049381029699284926525734780530683371637232542486580601832498002442370205259218637335066255536340402863614960635863279257288993952331758568563539255171752333659803630559976542479997459049186400474006649599261061239561731085004017823362429679461455596473090496147014713630612514413037251048846131962952608974559597665037989589516588758915639505296076818565509671460227096392858914958248349719519761080165615153613390888909577250254133864459387364823840496709605537195438529272540877498372727188018102166924786597538072319044503910010189326796934513137869684765977644563798377464844285519970335294217160696344254550102370879793030817756422968722131250375494110628633064924047523515038128770714301615363185342011455425978139447595336835379107137242896370138133354739730815895341370949542102301040465958437918589288157598747503502123011375289921007339937318514618737528870366815796019608309972031321180211231027906387141262192775078672105433479871010768651433527003348326639168952600370384527925162635252188830536959099871687232387447443824170080109823399109258223217519865552564954330502656132579024379460503613264612232039663908389480448494284442261160415871697947245392343456421606992172540056413

ハンドルネームはtwitterのハンドルネームを指定して、このスクリプトの実行結果を公開鍵として指定する。

twitter handle: baseishcoinfou1
public key: スクリプト実行結果

送信すると、画面遷移し、以下の情報が表示された。

Temporary Password
Account @admin Recovered:

Use this password to login: RWTFK1Z3zx5dsBV

login画面で以下を指定して送信すると、フラグが表示された。

Twitter handle: baseishcoinfou1
Password: RWTFK1Z3zx5dsBV

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

flag{6F4EF3C06D00C14731B424069225CC6CAE96CD869E03C1B45A5D94A08F679DA2}