この大会は2021/3/20 3:00(JST)~2021/3/22 3:00(JST)に開催されました。
今回もチームで参戦。結果は2347点で324チーム中23位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome to UDCTF (Misc)
Discordに入ると、#welcomeチャネルのメッセージにフラグがあった。
UDCTF{g00d-LuCk-3v3ry0n3}
Transforms (Misc)
$ nc challenges.ctfd.io 30008 you have solved 0/100 convert hexdigest to string: cbcf93f83286e075 @@@@@ > $ nc challenges.ctfd.io 30008 you have solved 0/100 convert bytearray to hexdigest: [97, 215, 52, 93, 185, 94, 149, 138] @@@@@ > $ nc challenges.ctfd.io 30008 you have solved 0/100 convert string to integer: ���:�� @@@@@ >
いくつか変換の問題が出るので、スクリプトにして答える。
import socket import re import binascii from Crypto.Util.number import * 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(('challenges.ctfd.io', 30008)) for i in range(100): data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '> ') print data[:-1], pattern = '(convert .+)\: (.+) @@@@@' m = re.search(pattern, data, re.DOTALL) q_key = m.group(1) q_val = m.group(2) #### hexdigest -> ? #### if q_key == 'convert hexdigest to bytearray': ans = str([int(q_val[i:i+2], 16) for i in range(0, len(q_val), 2)]) elif q_key == 'convert hexdigest to string': ans = q_val.decode('hex') elif q_key == 'convert hexdigest to integer': ans = str(int(q_val, 16)) #### bytearray -> ? #### elif q_key == 'convert bytearray to hexdigest': q_val = eval(q_val) ans = ''.join([binascii.hexlify(chr(v)) for v in q_val]) elif q_key == 'convert bytearray to string': q_val = eval(q_val) ans = ''.join([chr(v) for v in q_val]) elif q_key == 'convert bytearray to integer': q_val = eval(q_val) ans = str(bytes_to_long(''.join([chr(v) for v in q_val]))) #### string -> ? #### elif q_key == 'convert string to bytearray': ans = str([ord(v) for v in q_val]) elif q_key == 'convert string to hexdigest': ans = binascii.hexlify(q_val) elif q_key == 'convert string to integer': ans = str(bytes_to_long(q_val)) #### integer -> ? #### elif q_key == 'convert integer to hexdigest': ans = binascii.hexlify(long_to_bytes(q_val)) elif q_key == 'convert integer to bytearray': b = long_to_bytes(int(q_val)) ans = str([ord(v) for v in b]) elif q_key == 'convert integer to string': q_val = int(q_val) ans = long_to_bytes(q_val) print ans s.sendall(ans + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
: you have solved 95/100 convert integer to hexdigest: 9956280526821745813 @@@@@ > 8a2bd06375596495 great you have solved 96/100 convert integer to bytearray: 939509236931364368 @@@@@ > [13, 9, 206, 172, 90, 126, 6, 16] great you have solved 97/100 convert bytearray to hexdigest: [211, 219, 56, 79, 245, 3, 242, 82] @@@@@ > d3db384ff503f252 great you have solved 98/100 convert bytearray to integer: [241, 150, 175, 119, 172, 154, 13, 205] @@@@@ > 17408294338178977229 great you have solved 99/100 convert bytearray to integer: [91, 83, 120, 30, 110, 87, 208, 12] @@@@@ > 6580735552614027276 great UDCTF{r0b075_1N_d15gu153}
UDCTF{r0b075_1N_d15gu153}
PHEnomenal (Crypto)
$ nc challenges.ctfd.io 30004 HEllo Pascal! Can you hold on to this for me? 9401696427901823179425938612277559249331574262532464295046824245382364018745231738575982969553544987594404297605813475997925866335673494370550221471865620616488976938340522230965539774358455753772834025830308454646634578935062692257126419871099402646207953512716237839934967790084562794412205441615393142365189792829617628664746618204819731840933188287806838329752042038570194539813884197233892382767553374417069324242153780932983354405599555489739657095780569087453259418346102459939811318079615339586952810931131125330412546579493324346375440985341938785968313623135218985485391173371270978828038621740048422775173004731329694426966984097954107178345438735093859366884282690067805991478699428642315377555162702730247252415862225131631318438295595806992588913614612153113002364979049305962923576002304196001857094325571356769235128539829930014624443643845336236462505426407769026892053035561446403728481656413411560702779701630030500914960829835116198915654551134955125553532514334893016350861535169974509858170988825850467528098220479301044348487028684723703783995627594861929042441215921405542452886210123808860298958064055339098929791702367327488226117417530515533234034764388398871573802186727761836167535559351958402698411401087653102996338249901467968019498793973030703040624693859687135581781124470332810414636748564334299791035414378769633542008284482034650798292476947381842136089712406697363760382139263151426933551067409948792414935630080509767405826876541117004371718271940941997756163716687690595887755355537742631234238027698122137428597944442394644937563401825433354599706202655070368078707310633917124527614676616112777449176016983714316779490514965104296294716488133723902673282647606908899471693138508468510887689967901713523209128682835007726958177822251628480238693326583471855136776412486367859346446560585832704077345121216646 I want to keep it somewhere safe. It's an encryption of my lucky number 61 ! Oh, I think you may need N^2 as well: 11524758657623999982442658301236796480466459977520300597557767528123954032057862449679708951179328168372176050989981442876023409285804582602964508661899216132642272355130795098923056785895175377095862957831524247471228639125488315700367645226595931645231369986758606729300686193373144247294329400597194500032362693063296602021233169957029367930879220364642471772623413217464725693670469627165119041661071660491278696527533731691772920121347155156311849597253498482506850159221549910350587148027534199681620229730720462951207562976847473097670840167610505869476369016727546246598162381358835578402733011530049522459378101342195007732259463382272290990335363266494048034530071459593426176748991344795820073413617789924902597715156974863072095225673279635178302788041064929206697981189697539836011746648332117502460578304035905452491608471299210310715546650614793213722779906137412446237796073255369431031282227236288730574992060167990277597164697649278738357574815560023826092588842108786838111748972310851424670910093027251750380185001876929453214876811498252925138854297116864500047933812972400236445779857499147169385946219223424743590703646161257211891341931725866510263914588320124190478553090668581904952215993379975007331084742439024510348158356619949608739017786634860907110262635879680412787178706149856661968839258727912616366392978478557392339002918613194059148943308757990769198090688279963475792270735876629539220760212988594934694776932004909113593048293991812807361945230529704506674434774936353585928562307413577354173181452785736766106622419787881107623221517121569905885828790269252032914115523389584809493934039073449660229698441093587938825063040071240240184405450811925449698902077190488801007615688258319805371436103808116812081023523007303957555489015611827516684607641295132231862670645295583845862874057967705603054442480921089 Uh oh, I need some help! Can you give me the encryption of 49105?
タイトルと合わせて考えると、Paillier暗号のようだ。
c = (pow(g, m) * pow(r, n)) % N^2 m = 61の場合 c = 94016964279....
上記の場合49105は61の倍数になっている。何回かアクセスしてみたが、数値は変わるが必ず倍数になっている。Pailler暗号の加法的準同型性を利用する。
平文aとbの暗号化C(a), C(b)について、以下のようになる。
C(a + b) = C(a) * C(b)
このことから49105の暗号を算出できる。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('challenges.ctfd.io', 30004)) for _ in range(2): data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data c1 = int(data.split(' ')[-1]) data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data m1 = int(data.split(' ')[-2]) data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data N2 = int(data.split(' ')[-1]) data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '? ') m2 = int(data.split(' ')[-2][:-1]) assert m2 % m1 == 0 c2 = pow(c1, m2 // m1, N2) % N2 print data + str(c2) s.sendall(str(c2) + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
HEllo Pascal! Can you hold on to this for me? 2856202253840126425607479940562414686504060276541544326077121841542971533651022158043463327671150037558862024897338868586689732353681779160368549315968342584841433855961811846725781989836304258950434432266659558881778012148366451140654051231787441009277320957853301913739979638335779344175939775015834163308908721757246260402066112375842551184345806029863429190659219854909567681301498851297476125188051701821059659829732928801957682954371875538625918534485887999113707789888651538807091140474653513737813631265591446381867297953367553732961397606553380788544640659266658468517080998402206744148331276475802684842389911306112367919101523214094361615757663980901281815833116716947971931573402937123686893538743246375034672576129603388657474488037226100391947658622281816195053698705583018301362675846412877237993060313322952293980843345053313359910040061735611869045029888974582487463713682138586739453798331127079824651009112125807698208883266591963976240223590817727248131339687143997651296366349926901450441515361432017463527903621952997893100895442341279639999638454033573179650363865987216042069376664679311600505353327182644673409066528072850790401539816585484599499660357052452567023693377115052337587157358210464872250514254601436849364735401207910022922960810435125931005829786682985667607275359012000772663491351577065201856160449119190579237415143171524796483165896608527301636615039457469489517590014367878764318064588418785460652567271381245063066985199235402203195483700803965212406799754694045293566057103034364395552206059278842864855985283190690503259918635716629735110608317294569457756091972029874802539135474078296554443265107941565309036524417676158373489328931112418671531345263997755565468492401227124143997203628251727713060260268820074043161232487210073265595614430500535011732593339026309181727275165329889953628040240114403 I want to keep it somewhere safe. It's an encryption of my lucky number 39 ! Oh, I think you may need N^2 as well: 9462443801404504174539993065515652478061032522288659760464062752045490903620143876820156413591103069000866494184722675104687633244484653513266138373423732461376250375945555879146920913895666632034205043574773727395899188343148229094638900893938227922985993102317695063859116480622688867405786428632431883415654523756514758495829626766788964757085348300511123904285859925967527288204732558160933085095987551545933913422033536350322762118527232624509690981264658965867169282331307028150124243992546072534148836887631184117053448015649640730687537088097590425131324433423625561003715685221817364561449734050214711351737617134501381461915460970690187048253995619498114159738502051963307165283899998273208493437676544979234847265462331069187147234308910769003352240892224941749958600945061074033067457060402165043750002270287110079523561553178471215339681869957788106500666389826015762921025104773613534580002970992429077474029580863032489958295743510689141597015134660457072404473120409763524617177785943610323199636219471269421292956289984725052078127150980198556207789707504162310532999113155598635057605794704133247062673594724037142620826968566626519114747433758034075969843527428627997717556067129640981731844105558435905851041701153499022067345816462073005280240921091873198043608803184739656567040861946403476345080529101459625330913943722133997981914796097958520433622865246661498512272934465893714295273860819337851339979419050618688232927204732618796276752148741202731941165938650035796483979506790958693491061115423884746894510607344696002672026900736162407913311216535908545933892552437236846030560174275086193301423414548590790705311401669752793422443854632039954293291091336729055022470248724894640872954908548735427700795322820803213742928774293915059246343431403542602500211258802321632876660940567192890048090636428727782678833507279049 Uh oh, I need some help! Can you give me the encryption of 21294? 9109398162485919474689826547424353875793679926603336599817986914336873127152774782738370079124609227332954517148390421616587718752860268037847126668821937574811992316476547767043015279747784041562189644043518023699083715255673796263579953520365230157608854062521755520978637475953592385938315359624717155912735694609013113366553903755954284276064721507296067832332400732415660037175896480040135931434827478462093090964041334349567243612082779736203480693670851323724561683561531447716854680501511833034907724311619491119234434445215611861598580113994237572784742284518394418528163390803203913503654851246946653287244700394141886783617111109854047688482202462147328914348538012173610510180289349495931090729667509857676802878994668659862898945201528389738936885664548870706553980546868443625604110233469408971645321980409260401127065229894252179636018820091593227406524899363724630838250703945686813381325633045662591202887645041450849745286148761192322436595146250349689211955716277741238356187356701678851866685322407654539188381257459999011520401595434054058138204649049703944387574370803456120219878525578395868287415735996451845116868415830937390451817969118210072534235233109418751516242409332736403842508559011430547480173812954191728534743296100772294257187214667554992110385390900317759554476388800677347058938156728427172209111812689681692153867723876468995214127647264566197750779163265930459111954164553029938370336146049496615119365367140818448740760489373263048716202363258616157435276691389131987300663402312802313536277977471265190764359445535286681125195709203294476354341694372749331083132047249313010736124156654849789487962202957496835966202918900965742313791157846691807988669843470739293668732937003449775799935310223779695668889980889108977224037595349940393532575118881021004232243177975833405260728876185217965392222536195269 UDCTF{P41ll13R_one_oh_one}
UDCTF{P41ll13R_one_oh_one}
OTP1 (Crypto)
2つのテキスト文がflagをキーにXOR暗号されている。テキスト文はアルファベット大文字のみで構成されている。
flagの先頭 "UDCTF{" で条件を満たす箇所を調べる。
[+] 000 THUSCA WEMUST [+] 025 PRESSI NDNOTS [+] 050 OSTEXT USMYDE [+] 075 NCONST HEPLAY [+] 100 SEVERY TUNEDO [+] 125 RUMPET DPEOPL [+] 150 STONIS RWHATT [+] 175 RSTHER UNDRED [+] 200 ESOCOM EJOICE [+] 225 THEWOR EYREJO [+] 250 ERFALL PEOPLE [+] 275 AUSETH GAINOR [+] 300 ONOFTH NAGREE [+] 325 ATSUCH EORMER [+] 350 LANDST EVERKN [+] 375 MBWITH BOUTIT [+] 400 AXIMOF SLAINA [+] 425 EXCLUD ORDNEE [+] 450 EVERRE OKEEPI [+] 475 BLEMUS ECANEN [+] 500 REGRET SIDETH [+] 525 FROMTH SUFFER [+] 550 FORWAR HEREIS [+] 575 SJUSTB PEECHM [+] flag length: 6 [*] flag: UDCTF{
この結果からflagは25バイトと推測して良さそうだ。
あとはこの結果から文となるようキーを推測していく。
from Crypto.Util.strxor import strxor from string import uppercase def is_uppercase(s): for c in s: if not c.isupper(): return False return True ct = '010c1607053a24763f2c76242b1521630b7936371e2d30292d051606071532387d2d2d74393c0d357e1169343c1b373c23301a1717111e2f25723a22772c2b0b3679187f212006222623341b070c1a152f367d382c732e300d267511733033122c3a21280601151114223372352a6424360b3c751c652626102e20292907110e04032f317c3e377820301c3c67107e31370d223a223c06100c1a0f283f7e292d642c310b3c751c783c3719223732320717171c0329327a2230753931163a771e643137192a35283e10170c170936277f293775213c0b3c7510603c3c162c3a293b010c060309293b772e3664392d1a267516633b3d1831312729101605150a373670352a6424360f26751c792637133a36233e141110111233326a282c73222b0c217c0b643d3710333d28341a0a0c121233327a3e2c6723291620641375223d0d2f303235141010010533237b252d773e311e3f750f7c34311a2220273119050d10152f257a27266425201826751e64223d0d2f3022281806141d1233367e2d397520201120790b792633112c382230141c0a19093d3a7a22266425240b23781a7e2c3d0a2b353038101c0018133f3277382b7524280f3b630c79373e1a343c272910120606143e3a72252d63252a0831661a623c3f0f313b243c170806191328237129377828310d216417673021172c212a3907010406032f38663e2e793e311e3f750c713b36132635343313160c191233327e2e366423200931621c71272006373c2330130b11030729337a22377f392d1a32650b652737082a202e28060e1607123925762d3778242b183d631164393b092a3a21@@02010e01152f3a7629376228331a26631a63373d1327383f3c1b000d1b122822752a2662392d1a39641076273b182b20233300170e0d023e36613b267d38360b38751e623b2610223732291d0113180722386638347520300c207c1666303f163032292f01110d110234207d38317f393c1a207e1064343a0a2d3034381114061b1637327a2237782c311d35640b7c3039112623203207130b15122f3f7635257f382217207f0d673d2b112c202735000a0706033f3875382b75242b1c3b7e0c7931370d2220232f100e0c1d053e2560252d642520093d730b7f272b082b2d3235101d11110c343e7029277e223117357c19713d27112726233905010c040a3e20763e266425201d31640b7527341031202e3812050a1a09293b7c3f307e223117357c1971313d05263a2b381b050406033e237c382b793e2d102162107e213a1a2035332e100b111903293e673f227e292b10367f1b693c3c0c2b3b3429101206060d3532642d2d69392d163a771b792626162d37323c170b16000f2f35663837782828102162117527211025202e380608021d083a3a7a22277e28201b2772107f3e211e3035352a1a16071a033e33602d347828310c207f11753c3416373d35291a0f06111632236029277728281627761062212711262729331007021a033533663e2664252006377f12753320102e3b3329060d07111233326a2d31752c261c3d741a7e21211d36203232061105120329317c3e2c7e283610237e1971203e0b30352e291d0111110f28237b293064242b183b761379333719223d342e050106170e36366a242a742824193b65137830330d37' ct1 = ct.split('@@')[0].decode('hex') ct2 = ct.split('@@')[1].decode('hex') assert len(ct1) > len(ct2) flag = 'UDCTF{' #### guess (add the conditions) #### pt2_131 = 'E' flag += strxor(pt2_131, ct2[131]) pt1_232 = 'D' flag += strxor(pt1_232, ct1[232]) pt1_108 = 'Y' flag += strxor(pt1_108, ct1[108]) pt1_359 = 'E' flag += strxor(pt1_359, ct1[359]) pt1_210 = 'E' flag += strxor(pt1_210, ct1[210]) pt2_586 = 'E' flag += strxor(pt2_586, ct2[586]) pt2_087 = 'UST' flag += strxor(pt2_087, ct2[87:90]) pt1_015 = 'ST' flag += strxor(pt1_015, ct1[15:17]) pt2_267 = 'ER' flag += strxor(pt2_267, ct2[267:269]) pt1_444 = 'LE' flag += strxor(pt1_444, ct1[444:446]) pt2_296 = 'EN' flag += strxor(pt2_296, ct2[296:298]) pt2_098 = 'OR' flag += strxor(pt2_098, ct2[98:100]) ################################### for i in range(len(ct2) - len(flag)): try_pt1 = strxor(ct1[i:i+len(flag)], flag) if not is_uppercase(try_pt1): continue try_pt2 = strxor(ct2[i:i+len(flag)], flag) if not is_uppercase(try_pt2): continue print '[+] %03d' % i, try_pt1, try_pt2 print '[+] flag length:', len(flag) print '[*] flag:', flag
最終的なコードで実行した結果は以下の通り。
[+] 000 THUSCASESOFINJUSTICEANDOP WEMUSTMEETREVERSESBOLDLYA [+] 025 PRESSIONANDTYRANNYANDTHEM NDNOTSUFFERTHEMTOFRIGHTEN [+] 050 OSTEXTRAVAGANTBIGOTRYAREI USMYDEARWEMUSTLEARNTOACTT [+] 075 NCONSTANTOCCURRENCEAMONGU HEPLAYOUTWEMUSTLIVEMISFOR [+] 100 SEVERYDAYITISTHECUSTOMTOT TUNEDOWNTROTYETNOTAHUNDRE [+] 125 RUMPETFORTHMUCHWONDERANDA DPEOPLEINTHATBATTLEKNEWFO [+] 150 STONISHMENTATTHECHIEFACTO RWHATTHEYFOUGHTORWHYNOTAH [+] 175 RSTHEREINSETTINGATDEFIANC UNDREDOFTHEINCONSIDERATER [+] 200 ESOCOMPLETELYTHEOPINIONOF EJOICERSINTHEVICTORYWHYTH [+] 225 THEWORLDBUTTHEREISNOGREAT EYREJOICEDNOTHALFAHUNDRED [+] 250 ERFALLACYITISPRECISELYBEC PEOPLEWERETHEBETTERFORTHE [+] 275 AUSETHEYDOCONSULTTHEOPINI GAINORLOSSNOTHALFADOZENME [+] 300 ONOFTHEIROWNLITTLEWORLDTH NAGREETOTHISHOURONTHECAUS [+] 325 ATSUCHTHINGSTAKEPLACEATAL EORMERITSANDNOBODYINSHORT [+] 350 LANDSTRIKETHEGREATWORLDDU EVERKNEWANYTHINGDISTINCTA [+] 375 MBWITHAMAZEMENTITISANOLDM BOUTITBUTTHEMOURNERSOFTHE [+] 400 AXIMOFMINETHATWHENYOUHAVE SLAINAMINDNEEDSBOOKSASASW [+] 425 EXCLUDEDTHEIMPOSSIBLEWHAT ORDNEEDSAWHETSTONEIFITIST [+] 450 EVERREMAINSHOWEVERIMPROBA OKEEPITSEDGEMISFORTUNESON [+] 475 BLEMUSTBETHETRUTHWESHOULD ECANENDURETHEYCOMEFROMOUT [+] 500 REGRETOURMISTAKESANDLEARN SIDETHEYAREACCIDENTSBUTTO [+] 525 FROMTHEMBUTNEVERCARRYTHEM SUFFERFORONESOWNFAULTSAHT [+] 550 FORWARDINTOTHEFUTUREWITHU HEREISTHESTINGOFLIFEFAIRS [+] flag length: 25 [*] flag: UDCTF{w3lc0me_t0_0ur_ctf}
UDCTF{w3lc0me_t0_0ur_ctf}
hot_diggity_dog (Crypto)
eが大きいので、Wiener 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]) ct = 1445158457387990092729868574235690883328476381078437687117878228610678310947334902928151958126564073831321511131120493936116572980844392339059695082676944986711787958020467838355715919844010417357214590554232283621365683356875746321589009098953624328174730340745378234041481603493747258963262531884670382042229428718330510592290008758111173719368374299854731022071324105897071753892331177288853041690780740815531486651595502580196838941532727366305459058746061588746640490687182589242546152875181973797724210940606061367176901019489599206915050389787568489542789560060345579688651817178939492459114225606406771235955 e = 5330937005006880093598805190457883063630518250745326049791291310524191688770900677498253541876968725608988780011516913878357798140003135027183366863417959569121844853154112633643536091957118974799216641940813816076761892236211162375350644432689995305078796388972317016831896068476526526240493710949951454131783438007369494808004238449351949310021365138218085659500266511659278324660907726047634205954578349090143258122339575130486086737348129446819710953982137783348510587816619070115876002869074901265649919320219852616110355922199696052682562769419854041433691634833396620026960716117376716599744619445906636482175 N = 23556978386989862035227152665942267051448371189104346192996652142451495705784925966782823083699578017218099068429270687003013513402446285863364337355914497014903287391640470691911301379324025739991132998846569160231096469746500676982968388236346330604875221880999292763567973167457083433522181668657217912177241888729758963460176309486324449898397985561906686499104935496010357883868875083629704557203354175080409863639147851384555705571167351576323781959338290250693193849433909988970284946272004099242515681352168706645027456876652910731084445006361925449302087580591425037548670961363916624298931229939048540585477 p, q = wiener(N, e) flag = decrypt(p, q, e, ct) print flag
UDCTF{5t1ck_t0_65537}
OTP2 (Crypto)
2つのテキスト文が[ランダム文字列]+[flag]+[ランダム文字列]をキーにXOR暗号されている。テキスト文はアルファベット大文字のみで構成されている。OTP1との違いはキーの長さが128バイトと決まっていて、途中の一部でのみフラグが鍵になっていること。
flagの先頭 "UDCTF{" で条件を満たす箇所を調べる。
[+] 005 INGONE THINGS
この結果からflagは0~127のインデックスで5からフラグが鍵になっているとわかる。あとはこの結果から文となるようキーを推測していく。
from Crypto.Util.strxor import strxor from string import uppercase def is_uppercase(s): for c in s: if not c.isupper(): return False return True ct = 'de1aaff3510601061a092e2a7b307e3e23100f643a1e27203a763c22763a1a35783a38a73de82037856d0e68a62d5fd23cc78e88c4c88ef8a307cd96d8d26d461ab0d4fa35c675befe312308972f1f1e7d0b9f536f5ba7c1ea43f5eebfb7a3ac756e4efd9053fd0b42df31c56cc175cd5c26afbfbc116a31b5d80c7c869a1617df12acef59061d0c01092924642b643929171966370b3b3b27742633763c192f652429b137e8202f9f781d6bbc3b4ec82bc78a95c0d490e2be1dc48ed4d9765b0db0cafa30dc63adf6272412942f1906640b9a4c7c52b4d0e45ae5e7b2b0b8ab767d5bfe9c52f5005fd020c063db75c45d35bda6ac0f6621b9cc1e789b9a100cd91aa0e8411d051511023423762d65212210167f26173a3a35702129633a1624622338a42bf5242e95630868bf2856d52eda8689c9cf99e8a41bce97d4d966521ba1cbeb31d464b5eb3738168c34131962058153665bbec1e346efe3a7b1b0ab76654af49c45fe175ed132dd6cc166ca5c35aca6ab0a6e38b1d20776899d1502c21caaed561c170e1b1233286137702c3e0c1075301a3f3d37652b34763b17277b313cbf37ef3b2891641363a73b42dc37ca9980d7df8ee2a21dc294c8d361461aa5d9fc3cd470ade8352b158e261d046416885e6f43b3cbf95bfbffaeb1a4a5727d5bee9152e80559da36c660d775cd5a3fada8bb0c7720a7c8057e9c9b180fde0fbbe65d1c0a041b083e22753079302016167f20093a2626662b34602016257f2038b337f4262f97680d72a22852ce31db8689c9c797e8a21ac596d4d1674701adddf627c168a9fc302f0797371e14790d8e566a54@@ce1eb6ea5701050b01083f3f76206130220f14753b11273c33672c26633c13237c3938a03ef53b2c9e6a0873a33f4edd30da888dd1c991fab80ac595c9d66a4606a4cafa30da66b8f32723088028181e6300884d6a4eb0d6ee45ede6a5a1a5b07e654af49c41f20744d137d07ada78d15b34b3bbbc096d3db4d80f73879a1102db1da3ec4d1b001111022b287c347d303a1a0a752617363637673a22652e1034633f38b039f3273484671374b83458cf37ce8383c4c28cf7b51dc69fd3d665410da5ccf020dd69bff32d3f148c2902056f078c4a785fbad6e64af0e6b2b7b6ad736551fe9653e20d5ecd2dc67fc664d35623a1a7bc14633aaec9037486891d0ac40fabea5b0105011b132f246726642139171d7d3d0a213a37613d28713c1723643b3cbe36ee213a9a671166a03f5fd232c38e90c2ce82e3a41bce88d8c46c5c1fb4d9ec33da6fa8fd2d380f8f2b1002660f8c4c675ba0c3e346ece6b5a9b8b77f6e4cef984ee81758db27cc61db64d35622abbab1026e32b6d504689a9d1e0cd81faee54d120c06020329347e2b633b24110d74310a21313370262667290c31762430b633f32729936a187ead3545cf26df8790d6c086fbb501df92d8d674561aa1dffa39d46ea8f42c3e0a8a2c1319780b985d675fb4caef4be3e1a1a1a5ad786951f88054fa0a43ce2ac061d36dcc5534a7b0bd066326a3d50a699b80160dc41eacf75d010c0a1a01282572346130231d0d64251a313b30663e32793f1a2a743830b22ce83c2f9e781d75ae3458cf2fc09f90c9c791e7a500df98cfd2634700a9d6f83dc66eb8f72b3c0f8d20' ct1 = ct.split('@@')[0].decode('hex') ct2 = ct.split('@@')[1].decode('hex') assert len(ct1) > len(ct2) flag = 'UDCTF{' #### guess (add the conditions) #### pt2_011 = 'RED' flag += strxor(pt2_011, ct2[11:14]) pt1_526 = 'HE' flag += strxor(pt1_526, ct1[526:528]) pt2_016 = 'OPLE' flag += strxor(pt2_016, ct2[16:20]) pt1_148 = 'E' flag += strxor(pt1_148, ct1[148]) pt1_021 = 'AT' flag += strxor(pt1_021, ct1[21:23]) pt2_023 = 'HAT' flag += strxor(pt2_023, ct2[23:26]) pt2_154 = 'TER' flag += strxor(pt2_154, ct2[154:157]) pt2_029 = 'TLE' flag += strxor(pt2_029, ct2[29:32]) pt1_288 = 'UTE' flag += strxor(pt1_288, ct1[288:291]) #################################### for i in range(128 - len(flag)): found = True for j in range(i, len(ct2), 128): try_pt1 = strxor(ct1[j:j+len(flag)], flag) if not is_uppercase(try_pt1): found = False break try_pt2 = strxor(ct2[j:j+len(flag)], flag) if not is_uppercase(try_pt2): found = False break if found: print '[+] %03d' % i, try_pt1, try_pt2 print '**********************************************************************' for i in range(5, len(ct2), 128): try_pt1 = strxor(ct1[i:i+len(flag)], flag) try_pt2 = strxor(ct2[i:i+len(flag)], flag) print '[+] %03d' % i, try_pt1, try_pt2 print '[+] flag length:', len(flag) print '[*] flag:', flag
最終的なコードで実行した結果は以下の通り。
[+] 005 INGONEOFTHEMINORVIRTUESWHICHWE THINGSHAPPENBUTWEBOBUPUNWELCOM ********************************************************************** [+] 005 SEENOUGHTOKNOWTHATTHEREARESOME TAHUNDREDPEOPLEINTHATBATTLEKNE [+] 133 SYOUORIWOULDHAVETHOUGHTATFIRST NDREDPEOPLEWERETHEBETTERFORTHE [+] 261 HAVEDONEITTOONOTHINGCONTRIBUTE TABOUTITBUTTHEMOURNERSOFTHESLA [+] 389 ISMOTHERSAYSSHEBELIEVESASHALFA GHEVERYMORNINUDCUREACHAPASWASM [+] 517 INGONEOFTHEMINORVIRTUESWHICHWE THINGSHAPPENBUTWEBOBUPUNWELCOM [+] flag length: 30 [*] flag: UDCTF{m3d1um_X0r_str3ng7h_f7w}
UDCTF{m3d1um_X0r_str3ng7h_f7w}