After I written the web writeups for it, it’s time to have some cryptography~ LoTuXCTF.com
Write Up
Story Teller
It’s a system which you have to netcat to the machine and receive some strings of the story, a tricky point, the option 2 to show the entire story aren’t availible, which took me some time to get the part containing the flag XD. After receiving some strings, I guessed that was subtituition cipher, and I threw it into online solver :) Solver link Payload:
His long and arduous quest had come to an end,His heart raced as he rushed forward,Who had a deep fascination with flags.Across vast mountains and treacherous rivers,Months turned into years, and still, he persisted, And there it was, fluttering in the wind, the Golden Flag.By the way, the Golden flag is LoTuZ{Cr4cK_TraNspOs1tion_c1ph3r}
Ah… A little bit spelling mistake but I could fix that! Flag:LoTuX{Cr4cK_TraNspOs1tion_c1ph3r}
from Crypto.Util.number import getPrime, bytes_to_long, isPrime
flag = bytes_to_long(open('flag', 'rb').read())
base = getPrime(1024) + 1 p = base + getPrime(32) whilenot isPrime(p): p = base + getPrime(32) q = base + getPrime(32) whilenot isPrime(q): q = base + getPrime(32)
n : 18237670125629885217430444493412839683095316338154144657680055976957974511816887698014416778742458617727487825434215559521989763244658964742225298092775063375018442448500053641936340518366158207747898446464520032586461919330382037485266254993555571157687695056078760857442635607952038570214754973168928089799842679683858910621891986110243891564040587804656833234367273023439077360826316365299615019347351708635126105543987138042781032074548061385679276733178303852236701205308618104580479348575448991707466053009501662110431355315670739380731676506390117135831446230889312521145538861000127173891904075977932619433701 e : 65537 c : 2794914051908911497221655143209424203922373183519333986384370150540081118988520470638422240640871503705252562916755361442937060309400197458850988987533804149120361867582722612196413981932620461095660678707865772870903786875488203232110180545108071103351865212866486810405978838144245014127614439626237547452221513936043858002978206734391643932326225256517137555670052456674982930815715697146571529593744303003652598514891831529852333422746758214908655326413375875929526048928440304071556256481865754300879231890488410605859312056395813613983737119590091264580408250111053950716284781610855403529161875851373036573381
We can observe that the difference between p and q is low, so it’s a good way to enumeration through the difference.
1 2 3
let p=a+b q=a-b n=a^2-b^2, while b is small, we can make a estimation that a is almost equal to n.
from Crypto.Util.number import * from gmpy2 import * n=18237670125629885217430444493412839683095316338154144657680055976957974511816887698014416778742458617727487825434215559521989763244658964742225298092775063375018442448500053641936340518366158207747898446464520032586461919330382037485266254993555571157687695056078760857442635607952038570214754973168928089799842679683858910621891986110243891564040587804656833234367273023439077360826316365299615019347351708635126105543987138042781032074548061385679276733178303852236701205308618104580479348575448991707466053009501662110431355315670739380731676506390117135831446230889312521145538861000127173891904075977932619433701 e=65537 c=2794914051908911497221655143209424203922373183519333986384370150540081118988520470638422240640871503705252562916755361442937060309400197458850988987533804149120361867582722612196413981932620461095660678707865772870903786875488203232110180545108071103351865212866486810405978838144245014127614439626237547452221513936043858002978206734391643932326225256517137555670052456674982930815715697146571529593744303003652598514891831529852333422746758214908655326413375875929526048928440304071556256481865754300879231890488410605859312056395813613983737119590091264580408250111053950716284781610855403529161875851373036573381 ''' # brute forcing the base base=int(iroot(n, 2)[0]) i=0 while True: i=i+1 if iroot((base+i)**2-n, 1)[1]==True: print(base+i) break ''' base=135046918238180782328924971921958639261591204333628236588208930075562324277197936991698876778956199104793720358740299658687466966551023519180829878597741679344340717369554900909298947887554903073150791778577280508711415653004575837190385463599852964397715671365204087723325984507408797902938259367674214992035 pad=int(iroot(base**2-n, 2)[0]) p=base+pad q=base-pad d=inverse(e, (p-1)*(q-1)) flag=long_to_bytes(pow(c, d, n)) print(flag)
and finally, I got the flag:LoTuX{4sYmm3tR1c}
Bad Privacy
The source:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
from Crypto.Util.number import getPrime, bytes_to_long from flag import flag import os
p, q = getPrime(1024), getPrime(1024) n = p * q d = getPrime(500) e = pow(d, -1, (p - 1) * (q - 1))
flag += b'\x00' + os.urandom(n.bit_length() // 8 - len(flag) - 1) c = pow(bytes_to_long(flag), e, n)
print(f'{n, e = }') print(f'{c = }')
ciphertext:
1 2 3
n, e = (17931953129698545860672038166028641954211279868347010542121381522667183633654326153957901012736767926583895074075364454732415371988672711250552233364148619238124205992079987287032753603740879208227974019911770407580002458086483457941143722287017418948106156662337731905493480955268532227886732623142880169309291516456509754017328833523667904784256091479734388109422882591425800752647369349484968205543256347464275671725136701166089617502616428600867096627161749301161622412955242636638883206530169334556183645604728422783310878651509098626849351203005652884326862518439282339266587358921695257337994346530294379799713, 6429612413476205915435062664586221987266728476324429759478636846601841457248411300522968107483913162956560510606687683087570538068744774753786338439784578451887065650935242982338955084352549776989544241687047148672460229410005415566973404149241388633539038484420213725248760668987344228735725963502285593544916442673326026828057883738757861159402439164322495097760924015952567105010877972853876504056471966751308006181695402303033833156301078335468253261591674289896625773475364950380799836993215091688800787918071824307845038843678471739904411406065925753148271164925604607165382477612342803025945280286782801081709) c = 5808824944844149727771265099593085156170782038346130466697544160912957964694001898321313061748692927503448308895020346102848151119516478731215714116386757105053889318916251229295825483854561411436075680376250409732167265792475265936667433509075434517970352484245084493797741546494778583083258894741352496183649534791939546039484141394521931063022600705304609657072860338412264090017971488965745739106188918871960930640977339294906315812462625918308606782358708347513890899250818772613434856885849072024549071522932681241209502561201893189085521782173799276971060267532081898267672091645231297285377428516846987643669
Since the d is too small, the Wiener’s Attack would work!(Maybe there would be a post in future talking about Wiener’s Attack) Wiener’s Attack on Python Online Solver and the CRYING FLAG: LoTuX{Oh_n0!_W1en3r_4t7@ck_M3!} LMAO
Johnny
First of all, the shadow file in Linux will store all the passwords’ hash+salt and passwd file would record users and its home file location In this challenge, I already got the passwd file + shadow file, so it’s time to unshadow it:
1
unshadow passwd shadow > john
after this step, I would like to use john + rockyou.txt to bruteforce the hash:
1
john --format=crypt john --wordlist=~/rockyou.txt
and the password revealed:
1
passw0rd
nc to the server and got the flag:
1
LoTuX{RRRRRRR_start_a_livestream}
How is your math
I was saccrafied because I didn’t know AES well at that time, but after a brain storming, I realized that its just a simple XOR XD After connected to the server, some data would be retrieved
cipher : 0d6beaad8daba34786cf9ba261eab7c3129a455629eae4111cc66957e27198d219c549c854df275c5f889df6e97d44ae last block of plain : c588b65dba94c71918dfaa27b4990e80 let the last block of plain change to : 488fc668a2664a640a2e012f45cdc851
Since we can use XOR to cauculate new cipher text, is easy to write a solve script down below
which gave me the most parts of p, it allows an LLL attack… text:
1 2 3
n = 49317379804500209796069154838279294954734740260396969034362244628150502249525013341446737093977379039171277765668408135551429857961821092446119100418821374122189863172040513586589131824390453447510139315735819628529302309979625386802390332631395130113472540866945551795771627526235891533041190420454741961703 p = 7146471852062807277569830669618365331180167544805326080561292157538715086180460637539592780330363947718000143009999132925845868128087197314427832652781728 c = 34947545889759223661585220387725459637867459013983415649668150556071818987266451435085470915014102268810099737752533693324005084812311826831142921631853103134471247181887924060648234847407394744849343861064479563108491457911737123039686631953466948411009164374963649430994606479666126787716286750789879631629
solve script with sage math:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
defknown_high_bits_of_p(n: int, p0: int, epsilon=None): """ - input : `n (int)`, `p0 (int)`, `epsilon (default=None)` , `0 < epsilon <= 0.5/7` - output : `(p, q) (int, int)` """ P = PolynomialRing(Zmod(n), implementation='NTL', names=('x',)) x = P._first_ngens(1)[0] f = p0 + x small_roots = f.small_roots(beta=0.5, epsilon=epsilon) iflen(small_roots) > 0: p = p0 + int(small_roots[0]) q = n // p assert p * q == n return p, q else: return -1 known_high_bits_of_p(49317379804500209796069154838279294954734740260396969034362244628150502249525013341446737093977379039171277765668408135551429857961821092446119100418821374122189863172040513586589131824390453447510139315735819628529302309979625386802390332631395130113472540866945551795771627526235891533041190420454741961703, 7146471852062807277569830669618365331180167544805326080561292157538715086180460637539592780330363947718000143009900000000000000000000000000000000000000000, 0.5/7)