Security Fest 2017 Writeup

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

#makeircgreatagain (Misc 10)

freenodeで#securityfest-ctfチャネルに入る。

21:01 *topic : DL server problems, WORKING ON IT! https://securityfest.ctf.rocks - SCTF{Welcome_to_SECURITYFEST_CTF_2017!} -  start: https://goo.gl/L9Ltwj
SCTF{Welcome_to_SECURITYFEST_CTF_2017!}

Empty (Misc 100)

何も書かれていない空のPDFファイルが与えられている。

$ strings empty.pdf
     :
/LastChar 25
/Widths[/Widths[83 67 84 70 123 115 116 114 52 110 103 51 95 111 98 106 51 99 116 95 99 104 114 95 49 110 95 112 108 52 49 110 95 115 49 116 51 125]
/FontDescriptor 12 0 R
     :

Widthsの値がASCIIコードの羅列に見える。文字に置き換えてみる。

codes_str = '83 67 84 70 123 115 116 114 52 110 103 51 95 111 98 106 51 99 116 95 99 104 114 95 49 110 95 112 108 52 49 110 95 115 49 116 51 125'
codes = codes_str.split(' ')

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

print flag

フラグの文字列だった。

SCTF{str4ng3_obj3ct_chr_1n_pl41n_s1t3}

Qr code madness (Misc 200)

QRコードの画像が複数ある。1つの画像で1文字を読め、なんとなくBase64の文字種別になっていることがわかる。
あとはどの順番で読むかを考える。更新日付順で最後に読めたのが「=」になっていることがわかったので、その順で並び替えればよそさう。
まずはタイムスタンプをファイル名にして、QRコードをテキストにする。

import os
import time
import datetime
from PIL import Image

BLACK = 'X'
WHITE = ' '
UNKNOWN = '?'

quiet_zone_size = 12
cell_size = 3

QR_DIR = 'qrcodemadness\\'
TXT_DIR = 'qrtext\\'

files = os.listdir(QR_DIR)
for file in files:
    img = Image.open(QR_DIR + file)
    width, height = img.size

    pixels = img.load()
    output = ""
    y = quiet_zone_size + 1

    qrtxt = ''
    while y < height - quiet_zone_size:
        x = quiet_zone_size + 1
        while x < width - quiet_zone_size:
            if pixels[x, y] == 0:
                qrtxt += BLACK
            else:
                qrtxt += WHITE
            x += cell_size
        qrtxt += '\n'
        y += cell_size

    utime = datetime.datetime.fromtimestamp(os.stat(QR_DIR + file).st_mtime)
    o_filename = TXT_DIR + str(int(time.mktime(utime.timetuple()))) + '.txt'
    with open(o_filename, 'w') as f:
        f.write(qrtxt)

次にQRコードを読み取り、最初の1文字を除いて、Base64デコードする。

import os
import subprocess

TXT_DIR = 'qrtext\\'
CMD_FORMAT = 'python sqrd.py qrtext\\%s'

files = os.listdir(TXT_DIR)

b64str = ''
for file in files:
    cmd = CMD_FORMAT % file
    ret = subprocess.check_output( cmd.split(" ") )
    ret = ret.replace('\r\n', '')
    ret = ret.replace('\r', '')
    ret = ret.replace('\n', '')
    b64str += ret

print b64str[1:].decode('base64')
SCTF{Th3s3_d4mn_QR_c0d3_k33p_p0p1ng_up}

I heart cats (Misc 50)

空白やタブが目立つHTMLが与えられている。
空白8バイトを0、タブを1として、2進数のコードにして読込む。

with open('index.html', 'r') as f:
    data = f.read()

data = data.replace('        ', ' ')

bin_code = ''
for i in range(len(data)):
    if data[i] == ' ':
        bin_code += '0'
    elif data[i] == '\t':
        bin_code += '1'

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

print flag
SCTF{Wh1735p4c35_4r3_84d_4u!}

Fair play! (Crypto 50)

問題のタイトルからもPlayfair暗号と推測。オンラインのツールなどで復号してみたが、うまくいかない。CryptoCrackを使って、ブルートフォースで復号する。
この結果、キーはCOKBVQARNDXYEPWSHMZILFTGU。復号結果は以下の通り。

THEFLAGISTHENEXTFORTYONECHARACTERSPLAYFAIRISAFUNCIPHERTOCRACKDONTYOUTHINKSOIHOPEYOUENIOYEDTHECHALXLENGESPORTSMANSHIPISANASPIRATIONORETHOSTHATASPORTORACTIVITYWILLBEXENIOYEDFORITSOWNSAKEWITHPROPERCONSIDERATIONFORFAIRNESXSETHICSRESPECTANDASENSEOFXFELXLOWSHIPWITHONESCOMPETITORSASORELOSERREFERSTOONEWHODOESNOTXTAKEDEFEATWELXLWHEREASAGOXODSPORTMEANSBEINGAGOODWINXNERASWELLASBEINGAGOXODLOSERSOMEONEWHOSHOWSCOURTESYTOWARDSANOTHERINASPORTSGAMESPORTSMANSHIPCANBECONCEPTUALIZEDASANENDURINGANDRELATIVELYSTABLECHARACTERISTICORDISPOSITIONSUCHTHATINDIVIDUALSDIFXFERINTHEWAYTHEYAREGENERALXLYEXPECTEDTOBEHAVEINSPORTSITUATIONSINGENERALSPORTSMANSHIPREFERSTOVIRTUESSUCHASFAIRNESXSXSELFCONTROLCOURAGEANDPERSISTENCEANDHASBEENASSOCIATEDWITHINTERPERSONALCONCEPTSOFTREATINGOTHERSANDBEINGTREATEDFAIRLYMAINTAININGSELFCONTROLIFDEALINGWITHOTHERSANDRESPECTFORBOTHAUTHORITYANDOPPONENTSXSPORTSMANSHIPISALSOLOXOKEDATASBEINGTHEWAYONEREACTSTOASPORTGAMEPLAYERTHEFOURELEMENTSOFSPORTSMANSHIPAREOFTENSHOWNBEINGGOODFORMTHEWILLTOWINEQUITYANDFAIRNESSALXLFOURELEMENTSARECRITICALANDABALANCEMUSTBEFOUNDAMONGALXLFOURFORTRUESPORTSMANSHIPTOBEILXLUSTRATEDTHESEELEMENTSMAYALSOCAUSECONFLICTASAPERSONMAYDESIRETOWINMORETHANPLAYINEQUITYANDFAIRNESXSANDTHUSRESULTINGINACLASHWITHINTHEASPECTSOFSPORTSMANSHIPTHISWILXLCAUSEPROBLEMSASTHEPERSONBELIEVESTHEYAREBEINGAGOODSPORTSMANBUTTHEYAREDEFEATINGTHEPURPOSEOFTHISIDEAASTHEYAREIGNORINGTWOKEYCOMPONENTSOFBEINGSPORTSMANLIKEWHENATHLETESBECOMETOXOSELFCENTREDTHEIDEAOFSPORTSMANSHIPISDISMISSEDTODAYSXSPORTINGCULTUREINPARTICULARTHEBASEOFELITESPORTPLACESGREATIMPORTANCEONTHEIDEAOFCOMPETITIONANDWINXNINGANDTHUSXSPORTSMANSHIPTAKESABACKSEATASARESULTINMOSTIFNOTALXLSPORTSXSPORTSMENATXTHEXELITELEVELMAKETHESTANDARDSONSPORTSMANSHIPANDNOMATXTERWHETHERTHEYLIKEITORNOTXTHEYARESEXENASLEADERSANDROLEMODELSINSOCIETYSINCEEVERYSPORTISRULEDRIVENTHEMOSTCOMMONOFXFENCEOFBADSPORTSMANSHIPISTHEACTOFCHEATINGORBREAKINGTHERULESTOGAINANUNFAIRADVANTAGEACOMPETITORWHOEXHIBITSPOORSPORTSMANSHIPAFTERLOSINGAGAMEORCONTESTISOFTENCALLEDASORELOSERTHOSEWHOSHOWPOXORSPORTSMANSHIPAFTERWINXNINGARETYPICALLYCALXLEDBADCHAMPSSORELOSERBEHAVIOURINCLUDESBLAMINGOTHERSFORTHELOSSNOTACCEPTINGRESPONSIBILITYFORPERSONALACTIONSTHATCONTRIBUTEDTOTHEDEFEATREACTINGTOTHELOSXSINANIMXMATUREORIMPROPERFASHIONMAKINGEXCUSESFORTHEDEFEATANDCITINGUNFAVOURABLECONDITIONSOROTHERPETXTYISSUESASREASONSFORTHEDEFEATABADWINNERACTSINASHALLOWFASHIONAFTERHISORHERVICTORYSUCHASBYGLOATINGABOUTHISORHERWINRUBXBINGTHEWININTHEFACESOFTHEOPXPONENTSANDLOWERINGTHEOPXPONENTSXSXSELFESTEEMBYCONSTANTLYREMINDINGTHEOPPONENTSOFPOXORPERFORMANCEINCOMPARISONEVENIFTHEOPPONENTSCOMPETEDWELLNOTSHOWINGRESPECTTOTHEOTHERTEAMISCONSIDEREDTOBEINGABADSPORTSMANANDCOULDLEADTODEMORALISINGEFFECTSASLESLIEHOWEDESCRIBESIFAPITCHERINBASEBALXLDECIDESTOPITCHNOTTOHISMAXIMUMABILITYSUGGESTTHATTHEBATTERISNOTATANADEQUATELEVELANDCOULDLEADTOTHEBATXTERTOHAVELOWSELFCONFIDENCEORWORTHTHEREARESIXDIFXFERENTCATEGORIESRELATINGTOSPORTSMANSHIPTHEELEMENTSOFSPORTSTHEXELEMENTSOFSPORTSMANSHIPCLARIFICATIONSCONFLICTSBALANCEANDIRREDUCIBILITYALLSIXOFTHESECHARACTERIZEAPERSONWITHGOODSPORTSMANSHIPEVENTHOUGHTHEREISSOMEAFFINITYBETWEXENSOMEOFTHECATEGORIESTHEYAREDISTINCTELEMENTSINESSENCEPLAYHASFORITSDIRECTEDANDIMXMEDIATEXENDIOYPLEASUREANDXDELIGHTSANDWHICHISDOMINATEDBYASPIRITOFMODERATIONANDGENEROSITYATHLETICSONTHEOTHERHANDISESSENTIALXLYACOMPETITIVEACTIVITYWHICHXHASFORITSENDVICTORYINTHECONTESTANDWHICHISCHARACTERIZEDOFDEDICATIONSACRIFICEANDINTENSITYFEXELEZZPPHENCETHEVIRTUESOFAPLAYERARERADICALXLYDIFXFERENTFROMTHEVIRTUESOFANATHLETEFEXELEZZPPWHENTALKINGABOUTMISUNDERSTANDINGSPORTSMANSHIPRUDXDANDSTOLLPROVIDEANEXAMPLEFROMAUSHIGHSCHOOLATHLETICLEAGUEBANXNEDTHEPOSTGAMEHANDSHAKETHATWASAPARTOFSPORTSXSUCHASFOOTBALXLANDBASKETBALXLTHEHANDSHAKINGWASBANXNEDBECAUSEOFFIGHTSTHATWEREENSUINGAFTERTHEHANDSHAKEPXPMOSTPLAYERSAREINFLUENCEDBYTHELEADERSAROUNDTHEMSUCHASCOACHESANDOLDERPLAYERSIFTHEREARECOACHESANDADMINISTRATORSWHODONTUNDERSTANDSPORTSMANSHIPTHENWHATABOUTTHEPLAYERS

スペースを入れて読みやすくすると、先頭部分にはこう書いてある。

THE FLAG IS THE NEXT FORTYONE CHARACTERS

次の41文字がフラグ。

PLAYFAIRISAFUNCIPHERTOCRACKDONTYOUTHINKSO

WhiteHat Contest 13 Writeup

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

Da Nang (Cryptography 200)

makefile.pycファイルとn, c1~c4の値が与えられている。
EasyPythonDecompilerでmakefile.pycをデコンパイルすると、以下のようなコードで暗号化していることがわかる。

# Embedded file name: /root/Desktop/ctf-2017/rade/makefile.py
from FLAG import *
from Crypto.Util.number import *
p = getPrime(1024)
q = getPrime(1024)
n = p * q
e1 = 17
e2 = 23
m1 = pow(flag, e1, n)
m2 = pow(flag, e2, n)
c1 = pow(m1 + 12345, e1, n)
c2 = pow(m1 + 56789, e1, n)
c3 = pow(m2 + 12345, e2, n)
c4 = pow(m2 + 56789, e2, n)

まず、Franklin-Reiter Related Message Attackでm1とm2を求める。

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

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

    return -gcd(g1, g2)[0]

n = 23992456508721632904544057999056395984840598682551527471151904763246778638836805311058576074076544471814362118450963752614808101805018226501728749753154687251596663180454809994272928278066817153483685668132338944992849111855590973474678454159123639456555256485147196164078477717189520027988101925788303558976065942012105491523422884349644306896086216721825961334666043695121350988761613913914655313225263251356627080558127266206207253980892813908459449433940028268804981985006285644508736389967109173883994849116220035021283230523958640143355551140077459561176550844398278642511471910168765368624465988996082307992457
c1 = 20373828497176435167633953604896280749084861536083536617122139289929021045377176745113967394233342868062933415554192920963948057887324013008257380015103546511196207444982065505097904316167335975881412955740451727799394577357107415462104942067847048630173382510001061068806873459709567970640047451719077689284250663779574472232440594570502056709748603086893912068708152949459839465110567129133452824619222918019880393381073202052633467737917229789195417254877277542398992729106705037414050409175283815504552780234997208287185820010853470529752628470226853313501197054312938111140137899570545855875031148908922559079288
c2 = 4360293477491071631082413589399984151934700148049881396354060511784164719699104502640364063839372357371432182276578283932647249629707984639755245731994004982047010862474568253438883825981211552726455647610616600152683140602219777227764342827766552684058977170125324805316413550680703753463380261978903747369677517090549750490497714726106808405188795507741435252228742048791254891796083781713724953974101730953449734214103419451007355326070889428042830527374106171411884042865409718813225704924308910578917985370161732700040769553579304781551439507813917217693054553136139858632877393572913681604198562756394542739717
c3 = 4345261358659519189634881349240932351623603440440060460588285817392306152201826610576491755629698765088135761317793967803519010356869269204503397215039512744495117601932620637450095192646479479541571697381615227131485964855439316795397514445542660502760126364064850197352955455820341422106740318181122113707487426652989875585231637716536188004952356846847687782788196021954904944243050038759261831381697585862182084930662578928342538660648403902072793943235882313263403348511364765676275877687747514403142706712995406077875195587485333137738286810493929324782535392363155178660765334428891921464976278013274433592459
c4 = 11163192691864487842504791707572292045010440994593022973064948393324184598662086767886111478358067883324039072186651857397731078118781186686459647088206129240315423019956385054334592265029781917246602461962626683725203283354094411740566113019140776580459728655850754495232498348144806199874330892136766891492831287879346899778473534258469040357585649045269842028785546606058644151580331160754603828262995696611984149905299109630697269974668616156412842759609194334129212734919638779629261074770920807952993453659743525451175541328430753432209789806184520128139906770561431423993324872179142853918272022140840732281811
e1 = 17
e2 = 23

diff = 56789 - 12345
m1 = related_message_attack(c1, c2, diff, e1, n) - 12345
print 'm1 =', m1
m2 = related_message_attack(c3, c4, diff, e2, n) - 12345
print 'm2 =', m2

以下の通り、m1とm2を算出することができた。

m1 = 19940708887759698510138918424450978321561069230659682994645881255326771243148984534641521276833375122703596885707253454427683279897828170878752612038114916127076079372073980486084679133895997151796999085785875836411210710632349923692939803228793799180641695640367708674225359974103741571982122048082035653192117956305119783207139142492158377892592169456609523577779421428356103367766774789680587969032302667151786683889761811812272622064633882550675704607685899902862193827181388950179113885788804816791465827265522578945146174815051717419478773041589332193216487854230882177980058898250512045181287339603392316697829
m2 = 2970132540488149339645289732236094260249062136537890152544022014880742014613667308336012839589875906076626365709335806489861391924524036985675821291443068706330596605582227620510725657401246096937011593605357908632053035718719770934145713092502591072370696481368118792860212738265793148704385419081990887592837829524490105986239282282260211257709359015566779069639346641105169115995195678357307965665965863966691547051261296196880300064216255207531200236387931460445195365540532178913046683294419159294042199243485508880751463979557947263693533936834072139357019316229736030987782295309538167642041408183532003721823

次にCommon Modules Attackでflagを求める。

import gmpy

def commom_modules_attack(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy.gcdext(e1, e2)
    if s1 < 0:
        s1 = -s1
        c1 = gmpy.invert(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = gmpy.invert(c2, n)
 
    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    x = (v*w) % n
    return x

n = 23992456508721632904544057999056395984840598682551527471151904763246778638836805311058576074076544471814362118450963752614808101805018226501728749753154687251596663180454809994272928278066817153483685668132338944992849111855590973474678454159123639456555256485147196164078477717189520027988101925788303558976065942012105491523422884349644306896086216721825961334666043695121350988761613913914655313225263251356627080558127266206207253980892813908459449433940028268804981985006285644508736389967109173883994849116220035021283230523958640143355551140077459561176550844398278642511471910168765368624465988996082307992457
e1 = 17
e2 = 23
m1 = 19940708887759698510138918424450978321561069230659682994645881255326771243148984534641521276833375122703596885707253454427683279897828170878752612038114916127076079372073980486084679133895997151796999085785875836411210710632349923692939803228793799180641695640367708674225359974103741571982122048082035653192117956305119783207139142492158377892592169456609523577779421428356103367766774789680587969032302667151786683889761811812272622064633882550675704607685899902862193827181388950179113885788804816791465827265522578945146174815051717419478773041589332193216487854230882177980058898250512045181287339603392316697829
m2 = 2970132540488149339645289732236094260249062136537890152544022014880742014613667308336012839589875906076626365709335806489861391924524036985675821291443068706330596605582227620510725657401246096937011593605357908632053035718719770934145713092502591072370696481368118792860212738265793148704385419081990887592837829524490105986239282282260211257709359015566779069639346641105169115995195678357307965665965863966691547051261296196880300064216255207531200236387931460445195365540532178913046683294419159294042199243485508880751463979557947263693533936834072139357019316229736030987782295309538167642041408183532003721823

flag = commom_modules_attack(m1, m2, e1, e2, n)
flag_str = ('%x' % flag).decode('hex')
print flag_str

この結果は以下の通り。

common_modulus_and_franklin_reiter_related_message
$ echo -n common_modulus_and_franklin_reiter_related_message | sha1sum
4111c011bc640806ef98a32a5caaf9d169a960a0  -
WhiteHat{4111c011bc640806ef98a32a5caaf9d169a960a0}

Ha Long bay (Cryptography 250)

以下のコードと、n, e, C1~C3の値が与えられている。

from Crypto.Util import number

from secret import M, flag

p = number.getPrime(512)
q = number.getPrime(512)
n = p * q
e = 3

assert (len(flag) < 18),"Fail!"

fsplit = len(flag)/3

m1 = M + chr(number.getRandomRange(32, 128)) + flag[:fsplit]
m2 = M + chr(number.getRandomRange(32, 128)) +  flag[fsplit:2*fsplit]
m3 = M + chr(number.getRandomRange(32, 128)) +  flag[2*fsplit:]

m1 = int(m1.encode('hex'),16)
m2 = int(m2.encode('hex'),16)
m3 = int(m3.encode('hex'),16)

C1 = pow(m1, e, n)
C2 = pow(m2, e, n)
C3 = pow(m3, e, n)

Coppersmith's Short Pad Attackで平文の差を求めながら、Franklin-Reiter Related Message Attackでm1~m3を求める。

# coppersmiths_short_pad-and-related_message_attack.sage
def short_pad_attack(c1, c2, e, n):
    PRxy.<x,y> = PolynomialRing(Zmod(n))
    PRx.<xn> = PolynomialRing(Zmod(n))
    PRZZ.<xz,yz> = PolynomialRing(Zmod(n))

    g1 = x^e - c1
    g2 = (x+y)^e - c2

    q1 = g1.change_ring(PRZZ)
    q2 = g2.change_ring(PRZZ)

    h = q2.resultant(q1)
    h = h.univariate_polynomial()
    h = h.change_ring(PRx).subs(y=xn)
    h = h.monic()

    kbits = n.nbits()//(2*e*e)
    diff = h.small_roots(X=2^kbits, beta=0.5)[0]  # find root < 2^kbits with factor >= n^0.5

    return diff

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

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

    return -gcd(g1, g2)[0]

n = 103812409689464886276475047044770609297885893720146571798654593885477457188425375786047017735691464865799751262776976174291152757506033948402223696021505470811665108707476725788296289255777914656800454451779117367605227364391483323666809650532860469176398816220707851639905500304208833022438234033264200398959
e = 3

C1 = 9317106922634505445328279149755295612464875825943780841886193649700131610057100771444165757592479299555845021862440454968502065319770337331674061960123779876709748994547507057835826773522733671492329400557540100987018908395372213360970889608191940631081957498661950679566048064234326337242245437800870328713
C2 = 9314352234311777268152500810432217195086739647228997430466360767070638972017377674642639833356580951663634114522574738957708645348438979256455824440476178459296267056062724307782801856615506577790281367843133061096735566462588088807072141910079117362448136849082448630141871675817981866725491776400151339928
C3 = 102298788718908831383454512674839558060033746080468660252243145800572734468521296602058538109452538907204027081338987796558296639830757629468491322438048912956916198524821066676689795698612028521062296901313097979278633376983280108123961097977187251599033620058543603046291262367128490175209233898073639549204

diff = short_pad_attack(C1, C2, e, n)
m1 = related_message_attack(C1, C2, diff, e, n)
msg1 = ('%x' % m1).decode('hex')
m2 = m1 + diff
msg2 = ('%x' % m2).decode('hex')

diff = short_pad_attack(C2, C3, e, n)
m3 = m2 + diff
msg3 = ('%x' % m3).decode('hex')
print msg1
print msg2
print msg3

この結果は以下の通り。

ab688786fce01806bea4f1812740c15703837b12_part of flag is !uozt_
ab688786fce01806bea4f1812740c15703837b12_part of flag is \ficm_
ab688786fce01806bea4f1812740c15703837b12_part of flag is l41t0r

このことから以下のことがわかる。

flag = 'uozt_ficm_41t0r'
$ echo -n uozt_ficm_41t0r | sha1sum
a1ac8b83a8c8495b94007c2745596206d431748f  -
WhiteHat{a1ac8b83a8c8495b94007c2745596206d431748f}

RCTF 2017 Writeup

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

Sign In (Misc)

freenodeで#rctf2017チャネルに入る。

09:03 *topic : RCTF{Welcome_To_RCTF_2017}
RCTF{Welcome_To_RCTF_2017}

RSA_sign1 (Crypto)

メッセージと対応するシグネチャがあっていればフラグが表示できそう。
何でもよいので、先にシグネチャを決める。それからメッセージを求め、メッセージから順に指定する。

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

with open('public.pem') as f:
    pub_pem = f.read()

pub_key = rsa.PublicKey.load_pkcs1(pub_pem)
sig = 'hogehoge'

blocksize = rsa.common.byte_size(pub_key.n)
encrypted = rsa.transform.bytes2int(sig)
decrypted = rsa.core.decrypt_int(encrypted, pub_key.e, pub_key.n)
clearsig = rsa.transform.int2bytes(decrypted, blocksize)
sep_idx = clearsig.index('\x00', 2)
msg = clearsig[sep_idx+1:]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('rsasign1.2017.teamrois.cn', 3000))

data = s.recv(256)
print data
print msg
s.sendall(msg + '\n')
data = s.recv(256)
print data
print sig.encode('hex')
s.sendall(sig.encode('hex') + '\n')
data = s.recv(256)
print data

このコードを実行したらフラグを得られた。

RCTF{your_fir5t_sig_test}

UIUCTF 2017 Writeup

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

babyrsa (crypto 200)

RSA暗号。nが大きく、eが小さいため、cのe乗根により復号する。

import gmpy

e = 5
c = 199037898049081148054548566008626493558290050160287889209057083223407180156125399899465196611255722303390874101982934954388936179424024104549780651688160499201410108321518752502957346260593418668796624999582838387982430520095732090601546001755571395014548912727418182188910950322763678024849076083148030838828924108260083080562081253547377722180347372948445614953503124471116393560745613311509380885545728947236076476736881439654048388176520444109172092029548244462475513941506675855751026925250160078913809995564374674278235553349778352067191820570404315381746499936539482369231372882062307188454140330786512148310245052484671692280269741146507675933518321695623680547732771867757371698350343979932499637752314262246864787150534170586075473209768119198889190503283212208200005176410488476529948013610803040328568552414972234514746292014601094331465138374210925373263573292609023829742634966280579621843784216908520325876171463017051928049668240295956697023793952538148945070686999838223927548227156965116574566365108818752174755077045394837234760506722554542515056441166987424547451245495248956829984641868331576895415337336145024631773347254905002735757

m = gmpy.root(c, e)[0]
flag = ('%x' % m).decode('hex')
print flag

実行結果は次の通り。

welcome to uiuctf!
your super secret flag is: flag{c4n_w3_get_s0m3b0dy_t0_sm1th_some_c0pper_pls}
flag{c4n_w3_get_s0m3b0dy_t0_sm1th_some_c0pper_pls}

WhiteHat Challenge 03 Writeup

この大会は2017/4/27 11:00(JST)~2017/4/27 19:00(JST)に開催されました。
今回は仕事の合間に個人で参戦。結果は70点で101チーム中42位でした。
自分で解けた問題をWriteupとして書いておきます。

Crypto001 (Cryptography 25)

単語ごとに反転して、11シフトする。

import string

al_U = string.uppercase

e = 'HJXAJY GPHTPR HPL P CPBDG APGTCTV SCP CPXRXIXADE DWL LTGWIGTKD TWI CPBDG RXAQJETG SCP STWHXAQPIHT TWI TAJG UD TWI HGDGTEBT. GPHTPR STHJ TWI HBTAQDGE SCP HEXWHSGPW UD TWI SDXGTE DI TIPTGR HXW CLD TBTGEJH APRXIXADE SCP NGPIXAXB GTLDE. VPAU HX TWI GPTN IPWI GPHPTR HPL CGDQ. CPBDG GDGTEBT HJXAJY GPHTPR HX STSGPVTG HP TCD UD TWI IHDB AJUGTLDE SCP AJUHHTRRJH HGTSPTA CX TWI NGDIHXW UD TWI SAGDL. HXW TUXA SCP HXW ICTADXK WIPTS TKPW CTTQ NATSXL STIPGQTATR CX TGJIPGTIXA SCP BAXU.'

words = e.split(' ')

m = ''
for i in range(len(words)):
    if words[i][-1:] == '.':
        word = words[i][::-1][1:] + '.'
    else:
        word = words[i][::-1]
    #print word
    for j in range(len(word)):
        if word[j] in al_U:
            index = al_U.index(word[j]) + 11
            if index > 25:
                index = index - 26
            m += al_U[index]
        else:
            m += word[j]

    if i != len(words) - 1:
        m += ' '

print m

実行結果は次の通り。

JULIUS CAESAR WAS A ROMAN GENERAL AND POLITICIAN WHO OVERTHREW THE ROMAN REPUBLIC AND ESTABLISHED THE RULE OF THE EMPERORS. CAESAR USED THE PROBLEMS AND HARDSHIPS OF THE PERIOD TO CREATE HIS OWN SUPREME POLITICAL AND MILITARY POWER. FLAG IS THE YEAR THAT CEASAR WAS BORN. ROMAN EMPEROR JULIUS CAESAR IS REGARDED AS ONE OF THE MOST POWERFUL AND SUCCESSFUL LEADERS IN THE HISTORY OF THE WORLD. HIS LIFE AND HIS VIOLENT DEATH HAVE BEEN WIDELY CELEBRATED IN LITERATURE AND FILM.

フラグはCeaserが生まれた年なので、紀元前100年。

$ echo -n 100 | sha1sum
310b86e0b62b828562fc91c7be5380a992b2786a  -
WhiteHat{310b86e0b62b828562fc91c7be5380a992b2786a}

Crypto002 (Cryptography 10)

n, p, q, e, cが与えられているので、そのままRSA暗号の復号を行う。

n = 132584825018166892036247821308754740533575984294912859418971244776769293548107490233355297115861255963076622070127865142205107181207673702587393494860613316425336008819081169031194442020498569601888048767385032364436395644961861833287918315793817245497784496450110641025476274978672298849300106831336352152221
p = 12476682960795779723419989287306239606331347310604553825605263028855086418051086300006278888049896375754096827163121306696417314531666670662341673511789487
q = 10626608485185909739187126602183513204215955466032868268570782358820047751795982373252873333276843831383542360202874683262678664975380865415068554992090483
e = 65537
c = 126028558760741438230925566962334702896791270808414391828894437291120207835997354509410869568173448979784329516392078311028442458946906210498176026685581176779209904039179175088984829699783861789249260322822491502790157863487861171337660785254139478229397872969136220001367017765809130698515063339265165085655

a = (p - 1) * (q - 1)

x = 0
while True:
    if (a * x + 1) % e == 0:
        d = (a * x + 1) / e
        break
    x = x + 1

m = pow(c, d, n)

flag = ('%x' % m).decode('hex')
print flag

復号結果は simple_rsa_decryption

$ echo -n simple_rsa_decryption | sha1sum
100be37579e0f27c314efcb68a773b31537b5118  -
WhiteHat{100be37579e0f27c314efcb68a773b31537b5118}

Web001 (Web Security 20)

ソースに次のように書いてある。

<!--test/test-->

testアカウントでログインすると、You are not admin.と表示される。クッキーのuserの値がtestになっているので、adminに書き換え、ページを更新するとフラグが表示される。

Flag: don't_believe_cookies_at_all
$ echo -n "don't_believe_cookies_at_all" | sha1sum
92b2bc2f657574ab3481ebcb6705c36079b3e6d7  -
WhiteHat{92b2bc2f657574ab3481ebcb6705c36079b3e6d7}

For002 (Forensics 15)

POST /accounts/login/ の箇所が数か所ある。No.505に対して302 Foundで返ってきている。No.505で投入しているパスワードは"@Bkav123#$challange3"

$ echo -n "@Bkav123#\$challange3" | sha1sum
0d712cbea97819fa1e1c0a605283b1b912bcf350  -
WhiteHat{0d712cbea97819fa1e1c0a605283b1b912bcf350}

TAMUctf 2017 Writeup

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

Howdy (Tutorial 5)

問題に書いてあるフラグを投入するだけ。

gigem{H0WDY!}

vinegar (crypto 50)

Vigenere暗号。https://www.guballa.de/vigenere-solverで復号する。

VIGENERESOUNDSALOTLIKEVINEGARWHICHGOESONSALADSSALADSAREHEALTHYFOODSBUTBURGERSAREBETTERITISASHAMETHATBURGERSARENOTHEALTHYFORYOUHEREISYOURFLAGSALADCDAF
SALADCDAF

Hashed md5 (crypto 50)

MD5が706dc2ee585fb5dcb18e3ac08da7ce0cになるものを答える。
Googleで調べるだけ。

it's

onion_duke (crypto 50)

1バイト右の文字はさらに1つずつシフトしていると考え、26パターン復号してみる。

import string

e = 'TSETJJBYDD'
upper = string.uppercase

for i in range(1, 26):
    p = ''
    for j in range(len(e)):
        index = (upper.index(e[j]) + i - j) % 26
        p += upper[index]
    print p

この結果は以下の通り。

USDRGFWSWV
VTESHGXTXW
WUFTIHYUYX
XVGUJIZVZY
YWHVKJAWAZ
ZXIWLKBXBA
AYJXMLCYCB
BZKYNMDZDC
CALZONEAED
DBMAPOFBFE
ECNBQPGCGF
FDOCRQHDHG
GEPDSRIEIH
HFQETSJFJI
IGRFUTKGKJ
JHSGVULHLK
KITHWVMIML
LJUIXWNJNM
MKVJYXOKON
NLWKZYPLPO
OMXLAZQMQP
PNYMBARNRQ
QOZNCBSOSR
RPAODCTPTS
SQBPEDUQUT

単語になっていそうな以下の文字列がフラグ。

CALZONEAED

hail caesar (crypto 50)

http://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
わかりにくいが、ROT1の以下の文字列がフラグだった。

DRESSNGFDC

dachshund (crypto 100)

RSA暗号。eが非常に大きいため、Wiener's attackで復号する。

from fractions import Fraction

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, r = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
        gcd = b
    return gcd, x, y

def decrypt(p, q, e, c):
    n = p * q
    phi = (p - 1) * (q - 1)
    gcd, a, b = egcd(e, phi)
    d = a
    pt = pow(c, d, n)
    return hex(pt)[2:-1].decode('hex')

def continued_fractions(n,e):
    cf = [0]
    while e != 0:
        cf.append(int(n/e))
        N = n
        n = e
        e = N%e
    return cf

def calcKD(cf):
    kd = list()
    for i in range(1,len(cf)+1):
        tmp = Fraction(0)
        for j in cf[1:i][::-1]:
            tmp = 1/(tmp+j)
        kd.append((tmp.numerator,tmp.denominator))
    return kd

def int_sqrt(n):
    def f(prev):
        while True:
            m = (prev + n/prev)/2
            if m >= prev:
                return prev
            prev = m
    return f(n)

def calcPQ(a,b):
    if a*a < 4*b or a < 0:
        return None
    c = int_sqrt(a*a-4*b)
    p = (a + c) /2
    q = (a - c) /2
    if p + q == a and p * q == b:
        return (p,q)
    else:
        return None

def wiener(n,e):
    kd = calcKD(continued_fractions(n,e))
    for (k,d) in kd:
        if k == 0:
            continue
        if (e*d-1) % k != 0:
            continue
        phin = (e*d-1) / k
        if phin >= n:
            continue
        ans = calcPQ(n-phin+1,n)
        if ans is None:
            continue
        return (ans[0],ans[1])

with open('9b00a4b6f2ba73b4', 'r') as f:
    lines = f.readlines()

c = int(lines[0].strip().split(': ')[1].decode('base64').encode('hex'), 16)
e = int(lines[1].strip().split(': ')[1].decode('base64'))
n = int(lines[2].strip().split(': ')[1].decode('base64'))

p, q = wiener(n, e)

flag = decrypt(p, q, e, c)
print flag
gigem{h0Tdogs_2053124388faee1d}

lowered_expectations (crypto 100)

RSA暗号。nが非常に大きく、eが小さいのでe乗根を取れば復号できる。

import gmpy

with open('b449eea770bd6f3b', 'r') as f:
    lines = f.readlines()

c = int(lines[0].strip().split(': ')[1].decode('base64').encode('hex'), 16)
e = int(lines[1].strip().split(': ')[1].decode('base64'))
n = int(lines[2].strip().split(': ')[1].decode('base64'))

print ('%x' % gmpy.root(c, e)[0]).decode('hex')
gigem{get_L0W__101d935de22a247e}

commonplace (crypto 200)

RSA暗号。nが共通で、e,cのペアが複数あるので、Common Modules Attackで復号する。

import gmpy

def commom_modules_attack(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy.gcdext(e1, e2)
    if s1 < 0:
        s1 = -s1
        c1 = gmpy.invert(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = gmpy.invert(c2, n)
 
    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    x = (v*w) % n
    return x

with open('e538338d7671181d', 'r') as f:
    lines = f.readlines()

c1 = int(lines[0].strip().split(': ')[1])
c2 = int(lines[2].strip().split(': ')[1])
e1 = int(lines[4].strip().split(': ')[1])
e2 = int(lines[6].strip().split(': ')[1])
n = int(lines[8].strip().split(': ')[1])
 
m = commom_modules_attack(c1, c2, e1, e2, n)
flag = ('%x' % m).decode('hex')
print flag
gigem{c0mm0nly_knOwn_AS__a8b575875b4594cf}

siamese (stego 50)

$ strings 083643f9a8f5e192
           :
083643f9a8f5e192.txtUT	
Z2lnZW17dGhlX2NhdF9nb2VzX21lb3dfMWE5MjU2MGVkOWYyMTFkOH0=PK
083643f9a8f5e192.txtUT
$ echo Z2lnZW17dGhlX2NhdF9nb2VzX21lb3dfMWE5MjU2MGVkOWYyMTFkOH0= | base64 -d
gigem{the_cat_goes_meow_1a92560ed9f211d8}
gigem{the_cat_goes_meow_1a92560ed9f211d8}

jpeg ocean (stego 100)

Stegsolveで開き、Red plane 0にフラグがある。
f:id:satou-y:20170504080715p:plain

gigem{water_w0rld_99bc48af5a15e348}

chunky (stego 100)

PNGマジックナンバーの前に以下の文字列が入っている。

Z2lnZW17dGhlX2ZsYWdfdGhhdF9lYXRzX2xpa2VfYV9tZWFsX2ViNWNjNGM5ZmQzNjJkZGN9
$ echo Z2lnZW17dGhlX2ZsYWdfdGhhdF9lYXRzX2xpa2VfYV9tZWFsX2ViNWNjNGM5ZmQzNjJkZGN9 | base64 -d
gigem{the_flag_that_eats_like_a_meal_eb5cc4c9fd362ddc}
gigem{the_flag_that_eats_like_a_meal_eb5cc4c9fd362ddc}

ghost busters (stego 100)

$ file d5d148ea132cf63d 
d5d148ea132cf63d: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 44100 Hz

Sonic Visualiserでスペクトログラムを見る。
f:id:satou-y:20170504081250p:plain

gigem{now_you_see_me_ed34a1a931347170}

least important (stego 100)

$ file 12bc35c08727e99d 
12bc35c08727e99d: PC bitmap, Windows 3.x format, 2988 x 1573 x 24

StegsolveでData Extractを確認し、RGBの0ビットのところだけフィルタリングすると、先頭部分にフラグが入っている。

58:gigem{this_is_pretty_significant_success_83d4d343df042166}
gigem{this_is_pretty_significant_success_83d4d343df042166}

What to call me? (forensics 10)

$ ping ctf.tamu.edu
PING ctf.tamu.edu (52.33.57.247) 56(84) bytes of data.
      :
52.33.57.247

Who do I trust? (forensics 15)

証明書を確認する。

USERTrust RSA Certification Authority

Where am I... (forensics 50)

EXIF情報を見る。

GPS Latitude                    : 30 deg 36' 44.51" N
GPS Longitude                   : 96 deg 20' 29.34" W
GPS Position                    : 30 deg 36' 44.51" N, 96 deg 20' 29.34" W

Google Mapで調べる。

Memorial Student Center

Web Detective on the Scene (web 10)

HTMLソースにコメントが入っている。

<!-- Key = GigEm{TAMUctf_Web_a8ba90d7d8efeaf298cda418a2ab7073} -->
GigEm{TAMUctf_Web_a8ba90d7d8efeaf298cda418a2ab7073}

Web Detective Obtains Some Clues (web 15)

HTMLソース内のコメントに次のように書いてある。

<!-- This part is a header and doesn't offer you any flag -->
<!-- There is no key here!  Not even Google can help you now! -->

http://detective2.ctf.tamu.edu/robots.txtにアクセスすると、次のように書いてある。

User-agent: *
Disallow: /192492397fd616d47c5ff84b2d12d1df0da57d992f5fe95d/

http://detective2.ctf.tamu.edu/192492397fd616d47c5ff84b2d12d1df0da57d992f5fe95d/にアクセスすると、インデックスが見えて、user_file.txtがある。この中に次のように書いてあった。

#
#	TAMUctf web exploit challenges
#	Name:	user_file.txt
#	Course:	CSCE 491
#	Prof:	Dr. Daniel Ragsdale
#

#	Username : Password
Alice : TexasAggies2016!
Bob : BeatTheHell0utta_t.u.
Eve	: *H0meOfThe12thMan*
Key	: gigem{TAMUctf_Web_b304dc25b50afc611541e7ae1cb77ed2}
Sully : Br!gadierGenera1C$A
Rudder : GreaterL0veH@athNoM@n
gigem{TAMUctf_Web_b304dc25b50afc611541e7ae1cb77ed2}

SQL: The Injectioning (web 25)

Usernameに ' or 1=1 # と入れてログインすると、ログイン後のメッセージにフラグが入っている。
Huh. We really should gigem{S@niTIz3_Th0se_InpUTs}!

gigem{S@niTIz3_Th0se_InpUTs}

threads (reversing 100)

$ strings 229a70e76d99687e | grep gigem
gigem{stringy_lasagna_ec5a1e530491638f}
gigem{stringy_lasagna_ec5a1e530491638f}

Who am I? (recon 50)

Googleの画像検索を使う。

Brad Brekke, FBI

PlaidCTF 2017 Writeup

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

sanity check (Misc 1)

問題に書いてあるフラグを投入するだけ。

PCTF{poop}