2023MoeCTF

记录MoeCTF2023部分题解,主要是Crypto,还有一些其他方向的简单题

密码题难度有梯度。

稍微整理了一下对于笔者而言的难度梯度

Crypto

简单

baby_e

题目

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.number import getPrime,bytes_to_long

p,q = getPrime(2048),getPrime(2048)
e = 7
n = p*q
m = bytes_to_long(open('flag.txt','rb').read().strip())
c = pow(m,e,n)
print("c = ",c)
print("n = ",n)

# c = 147693154873835354725007152781732424355869776162377337823960431913672366269917723916891506269449726723757821517328874729037838600793748824028829185409932536014732765063216715033843955453706710187792772702199448156372644163429786386035008302836467605094954587157232829525150652611067567669525072625329634860065850520051628272535479197120008981979404760445193750864902244921407742155742716289495581989134730376783828846663464819337418977287363028738701414486788851136608957124505485242331701209645216580641917007780811842757125048746184068597664780265422321550909392419865169775282217442331295071069272774722564587602419768461231775480847018941840911357926330143045826277813722919121117172763493242590521245640828462665947672485094793188432098216701511715232654611338293295459889814699850788048985878279440740712956248569068077253790198036918598519191892836075254345518967666166925163908185663991353344555402397055977817370082929420443034626201745027965444069777059760865359310439815816749939498993014457995041394803598825093836045546578310632172636478575946653375857640993393714607308326474003446154152048840071034349831168612740218034679021240949747357214453636633636662650940968576792518622437627529244515229173
# n = 553409369582823237678532685244026647155180191225879439432235077135813123637186465008813830373646133388592395760175777499266561095087891764348044063111935877931069321764391883899483374576303169645488542398590564148654412004383012178107972880058460460806768779452529433458826925606225797078653905380530651390617109384086518728626571028089036812787671647095695947167204428442727185744172445701874820612799168887428075695751162763647868386879374037826876671079326544820609721731078985096813307183878793033824330869698508952853770794414757655681370862323768018291030331209143189638496644361618184164228294031490537429556439588954274708598530042700988138862000054458742762198052079867259365645914383561162796796952346445529346145323567650621600171442575319262718389389870407629339714751583360252884338116164466349449862781112019462555743429653595045695696967783338371470032332852204294900011651434678829104876529439166176589508898757122660322523937330848536715937381297551894198974459004139082562228022412335520195652419375915216074658463954339332593244483927157329404652516225481116614815221154229491846087288087715884363786672244655901308480290011237244562251084095684531716327141154558809471185132979704992609461470501119328696999713829

小明文攻击

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import *
import gmpy2

n =
e = 7
c =
k = 0

t = gmpy2.iroot(c,e)
if t[1]:
print(long_to_bytes(t[0]))
else:
while 1:
m = gmpy2.iroot(k*n+c,e)
if m[1]:
print(m[0])
print(long_to_bytes(m[0]))
break
else:
k += 1

bad_E

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
p = getPrime(512)
q = getPrime(512)
e = 65537

print(p) # 6853495238262155391975011057929314523706159020478084061020122347902601182448091015650787022962180599741651597328364289413042032923330906135304995252477571
print(q) # 11727544912613560398705401423145382428897876620077115390278679983274961030035884083100580422155496261311510530671232666801444557695190734596546855494472819

with open("flag.txt","r") as fs:
flag = fs.read().strip()

m = bytes_to_long(flag.encode())
c = pow(m,e,p*q)
print(c) # 63388263723813143290256836284084914544524440253054612802424934400854921660916379284754467427040180660945667733359330988361620691457570947823206385692232584893511398038141442606303536260023122774682805630913037113541880875125504376791939861734613177272270414287306054553288162010873808058776206524782351475805

常规思路发现,$e$和$phi$不互素,而且$e$能整除$phi$。试了其他方法,没解决

最后尝试AMM算法

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#sage

from Crypto.Util.number import *
import gmpy2
import time
import random
from tqdm import tqdm
e = 65537
p =
q =
c =
n = p * q

def AMM(o, r, q):
start = time.time()
print('\n----------------------------------------------------------------------------------')
print('Start to run Adleman-Manders-Miller Root Extraction Method')
print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q))
g = GF(q)
o = g(o)
p = g(random.randint(1, q))
while p ^ ((q-1) // r) == 1:
p = g(random.randint(1, q))
print('[+] Find p:{}'.format(p))
t = 0
s = q - 1
while s % r == 0:
t += 1
s = s // r
print('[+] Find s:{}, t:{}'.format(s, t))
k = 1
while (k * s + 1) % r != 0:
k += 1
alp = (k * s + 1) // r
print('[+] Find alp:{}'.format(alp))
a = p ^ (r**(t-1) * s)
b = o ^ (r*alp - 1)
c = p ^ s
h = 1
for i in range(1, t):
d = b ^ (r^(t-1-i))
if d == 1:
j = 0
else:
print('[+] Calculating DLP...')
j = - discrete_log(d, a)
print('[+] Finish DLP...')
b = b * (c^r)^j
h = h * c^j
c = c^r
result = o^alp * h
end = time.time()
print("Finished in {} seconds.".format(end - start))
print('Find one solution: {}'.format(result))
return result

def onemod(p,r):
t=p-2
while pow(t,(p-1) // r,p)==1:
t -= 1
return pow(t,(p-1) // r,p)

def solution(p,root,e):
g = onemod(p,e)
may = set()
for i in range(e):
may.add(root * pow(g,i,p)%p)
return may

cp = c % p

mp = AMM(cp,e,p)

mps = solution(p,mp,e)

for i in mps:
flag = long_to_bytes(int(i))
if b'moectf' in flag:
print(flag)

factor_signin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from Crypto.Util.number import getPrime
from math import prod

with open("flag.txt","rb") as f:
flag = f.read().strip()
assert len(flag) == 72

m1 = int.from_bytes(flag[:36],"big")
m2 = int.from_bytes(flag[36:],"big")

e = 65537

p,q = getPrime(2048),getPrime(2048)
n1 = p*q
c1 = pow(m1,e,n1)
print("c1 = ",c1)
print("n1 = ",n1)

primes = [getPrime(64) for _ in range(32)]
n2 = prod(primes)
c2 = pow(m2,e,n2)
print("c2 = ",c2)
print("n2 = ",n2)

# c1 = 10004937130983861141937782436252502991050957330184611684406783226971057978666503675149401388381995491152372622456604317681236160071166819028679754762162125904637599991943368450200313304999566592294442696755822585022667008378021280392976010576970877334159755332946926433635584313137140987588847077645814987268595739733550220882135750267567373532603503399428451548677091911410732474324157868011686641243202218731844256789044721309478991918322850448456919991540932206923861653518190974620161055008847475600980152660468279765607319838003177639654115075183493029803981527882155542925959658123816315099271123470754815045214896642428657264709805029840253303446203030294879166242867850331945166255924821406218090304893024711068773287842075208409312312188560675094244318565148284432361706108491327014254387317744284876018328591380705408407853404828189643214087638328376675071962141118973835178054884474523241911240926274907256651801384433652425740230755811160476356172444327762497910600719286629420662696949923799255603628210458906831175806791599965316549386396788014703044837917283461862338269599464440202019922379625071512100821922879623930069349084917919100015782270736808388388006084027673781004085620817521378823838335749279055639005125
# n1 = 343504538870081878757729748260620800783581983635281373321527119223374418103340873199654926888439040391545101913132680017655039577253974802351999985470115474655124168592386965001556620077117966153475518658881140827499124290142523464795351995478153288872749817655925271395693435582010998996210909883510311066017237567799370371513462802547313382594409676803895262837061350017911885033133654781876923251129406855067993830824618637981136966134029212516871210627954762147349788788999116702635535406398258621926040887099782494271000823401788337120154104692934583729065189687995570122890809807661370008740283447636580308161498808092269041815719148127168137018600113465985504975054319601741498799761500526467431533990903047624407330243357514588557352746347337683868781554819821575385685459666842162355673947984514687068626166144076257334426612302554448774082488600083569900006274897032242821388126274957846236552373226099112200392102883351088570736254707966329366625911183721875374731791052229266503696334310835323523568132399330263642353927504971311717117370721838701629885670598853025212521537158141447625623337563164790788106598854822686494249848796441153496412236527242235888308435573209980270776407776277489669763803746640746378181948641
# c2 = 4948422459907576438725352912593232312182623872749480015295307088166392790756090961680588458629287353136729331282506869598853654959933189916541367579979613191505226006688017103736659670745715837820780269669982614187726024837483992949073998289744910800139692315475427811724840888983757813069849711652177078415791290894737059610056340691753379065563574279210755232749774749757141836708161854072798697882671844015773796030086898649043727563289757423417931359190238689436180953442515869613672008678717039516723747808793079592658069533269662834322438864456440701995249381880745586708718334052938634931936240736457181295
# n2 = 8582505375542551134698364096640878629785534004976071646505285128223700755811329156276289439920192196962008222418309136528180402357612976316670896973298407081310073283979903409463559102445223030866575563539261326076167685019121804961393115251287057504682389257841337573435085535013992761172452417731887700665115563173984357419855481847035192853387338980937451843809282267888616833734087813693242841580644645315837196205981207827105545437201799441352173638172133698491126291396194764373021523547130703629001683366722885529834956411976212381935354905525700646776572036418453784898084635925476199878640087165680193737

前半部分发现$n_1$可以分解,分解得到$p,q$

后半部分用yafu分解$n_2$,得到所有因子

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from Crypto.Util.number import *
import gmpy2

c1 =
n1 =
c2 =
n2 =
e = 65537

p1 =
q1 = n1 //p1
d1 = gmpy2.invert(e,(p1-1)*(q1-1))
m1 = pow(c1,d1,n1)
flag1 = long_to_bytes(int(m1))

p =
phi2 = 1

for i in p:
phi2 *= i-1
d2 = gmpy2.invert(e,phi2)
m2 = pow(c2,d2,n2)
flag2 = long_to_bytes(m2)

print(flag1+flag2)

n&n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *

p = getPrime(1024)
q = getPrime(1024)

with open("flag.txt","r") as f:
flag = f.read().strip().encode()

m = bytes_to_long(flag)
n = p * q
e1 = 0x114514
e2 = 19198101

c1 = pow(m,e1,n)
c2 = pow(m,e2,n)
print(c1)
print(c2)
print(n)

# 5776799746376051463605370130675046329799612910435315968508603116759552095183027263116443417343895252766060748671845650457077393391989018107887540639775168897954484319381180406512474784571389477212123123540984850033695748142755414954158933345476509573211496722528388574841686164433315356667366007165419697987147258498693175698918104120849579763098045116744389310549687579302444264316133642674648294049526615350011916160649448726069001139749604430982881450187865197137222762758538645387391379108182515717949428258503254717940765994927802512049427407583200118969062778415073135339774546277230281966880715506688898978925
# 4664955020023583143415931782261983177552050757537222070347847639906354901601382630034645762990079537901659753823666851165175187728532569040809797389706253282757017586285211791297567893874606446000074515260509831946210526182765808878824360460569061258723122198792244018463880052389205906620425625708718545628429086424549277715280217165880900037900983008637302744555649467104208348070638137050458275362152816916837534704113775562356277110844168173111385779258263874552283927767924979691542028126412133709129601685315027689094437957165812994784648540588277901241854031439324974562449032290219652206466731675967045633360
# 13612969130810965900902742090064423006385890357159609755971027204203418808937093492927060428980020085273603754747223030702684866992231913349067578014240319426522039068836171388168087260774376277346092066880984406890296520951318296354893551565670293486797637522297989653182109744864444697818991039473180752980752117041574628063002176339235126861152739066489620021077091941250365101779354009854706729448088217051728432010328667839532327286559570597994183126402340332924370812383312664419874352306052467284992411543921858024469098268800500500651896608097346389396273293747664441553194179933758992070398387066135330851531

共模攻击

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from Crypto.Util.number import *
import gmpy2

c1 =
c2 =
n =
e1 = 0x114514

e2 = 19198101

t = gmpy2.gcd(e1,e2)
if t == 1:
s,x,y = gmpy2.gcdext(e1,e2)
m = (pow(c1,x,n)*pow(c2,y,n))%n
print(long_to_bytes(m))
else:
s,x,y = gmpy2.gcdext(e1,e2)
k = 0
while 1:
m = gmpy2.iroot((pow(c1,x,n)*pow(c2,y,n)+k*n)%n,t)
if m[1]:
print(long_to_bytes(m[0]))
break
else:
k += 1

|p-q|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
with open("flag.txt","rb") as fs:
flag = fs.read().strip()
assert len(flag) == 72

m = int.from_bytes(flag,"big")

from Crypto.Util.number import getPrime, isPrime

def next_prime(p):
while True:
p += 2
if isPrime(p):
return p

p = getPrime(2048)
q = next_prime(p)
n = p * q
e = 65537
c = pow(m,e,n)
print("n =",n)
print("c =",c)

# n = 329960318345010350458589325571454799968957932130539403944044204698872359769449414256378111233592533561892402020955736786563103586897940757198920737583107357264433730515123570697570757034221232010688796344257587359198400915567115397034901247038275403825404094129637119512164953012131445747740645183682571690806238508035172474685818036517880994658466362305677430221344381425792427288500814551334928982040579744048907401043058567486871621293983772331951723963911377839286050368715384227640638031857101612517441295926821712605955984000617738833973829140899288164786111118033301974794123637285172303688427806450817155786233788027512244397952849209700013205803489334055814513866650854230478124920442832221946442593769555237909177172933634236392800414176981780444770542047378630756636857018730168151824307814244094763132088236333995807013617801783919113541391133267230410179444855465611792191833319172887852945902960736744468250550722314565805440432977225703650102517531531476188269635151281661081058374242768608270563131619806585194608795817118466680430500830137335634289617464844004904410907221482919453859885955054140320857757297655475489972268282336250384384926216818756762307686391740965586168590784252524275489515352125321398406426217
# c = 307746143297103281117512771170735061509547958991947416701685589829711285274762039205145422734327595082350457374530975854337055433998982493020603245187129916580627539476324521854057990929173492940833073106540441902619425074887573232779899379436737429823569006431370954961865581168635086246592539153824456681688944066925973182272443586463636373955966146029489121226571408532284480270826510961605206483011204059402338926815599691009406841471142048842308786000059979977645988396524814553253493672729395573658564825709547262230219183672493306100392069182994445509803952976016630731417479238769736432223194249245020320183199001774879893442186017555682902409661647546547835345461056900610391514595370600575845979413984555709077635397717741521573798309855584473259503981955303774208127361309229536010653615696850725905168242705387575720694946072789441481191449772933265705810128547553027708513478130258801233619669699177901566688737559102165508239876805822898509541232565766265491283807922473440397456701500524925191214292669986798631732639221198138026031561329502985577205314190565609214349344303324429408234237832110076900414483795318189628198913032900272406887003325858236057373096880675754802725017537119549989304878960436575670784578550

根据题目意思,$p,q$很相近,先把$n$开根,再求下一个素数得到$p$

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
import gmpy2

n =
c =
e = 65537

t = gmpy2.iroot(n,2)[0]

p = gmpy2.next_prime(t)
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))

rsa_signin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
with open("flag.txt","rb") as f:
flag = f.read().strip()

m = int.from_bytes(flag, "big")
e = 65537

from Crypto.Util.number import getPrime

for x in range(10):
p = getPrime(1024)
q = getPrime(1024)
n = p * q
c = pow(m, e, n)

print("n =", n)
print("c =", c)


'''
n = 17524722204224696445172535263975543817720644608816706978363749891469511686943372362091928951563219068859089058278944528021615923888948698587206920445508493551162845371086030869059282352535451058203615402089133135136481314666971507135484450966505425514285114192275051972496161810571035753943880190780759479521486741046704043699838021850105638224212696697865987677760179564370167062037563913329993433080123575434871852732981112883423565015771421868680113407260917902892944119552200927337996135278491046562185003012971570532979090484837684759828977460570826320870379601193678304983534424368152743368343335213808684523217
c = 6870605439714128574950893771863182370595667973241984289208050776870220326525943524507319708560433091378319367164606150977103661770065561661544375425887970907060665421562712515902428061727268441585629591525591001533188276465911918724808701356962871139957343861919730086334623932624184172272488406793955068827527130338853980609365042071290967556159598511667974987218999253443575482949258292953639729393456515185185102248985930422080581185292420347510600574229080211050520146551505605537486989306457793451086767402197128573781597156939709237045132856159368959981648969874765462190363842275826077556314448408825308218451
n = 24974121071274650888046048586598797033399902532613815354986756278905133499432183463847175542164798764762683121930786715931063152122056911933710481566265603626437742951648885379847799327315791800670175616973945640322985175516271373004547752061826574576722667907302681961850865961386200909397231865804894418194711076667760169256682834206788730947602211228930301853348503098156592000286467190760378847541148772869356389938999094673945092387627113807899212568399028514283219850734634544982646070106811651490010946670117927664594365986238107951837041859682547029079035013475238052160645871718246031144694712586073789250183
c = 10324627733161143472233272675096997859064721978612320424254305978486200326061730105384511258706433940176741256952824288120499229240005823611541292676234913505775165761543820764046537413943393325463602612485849366939102550336256797820440347815027443410399157963547486098366749815425187247171697678576246606105486928212486117878157055321965270364583625270716186820068538749425299073309429589410882809098930213978117176627031795312102177342499674234163614021182116065492884880492891668658240362567156235958605768725892407536211503981819707919444725863397622629226309480836486427388484176463279384813974310500625102568341
n = 14215826065753265334521416948225868542990756976323308408298887797364519400310818641526401662106853573185085731682502059761982246604277475488691297554851873224516934619888327644352138127883043558424300092247604877819821625587944308487310522092440517150600171819145803937177931473336108429889165189521078678397694303305705260759351843006130968234071638035667854938070597400634242396852782331461576526836227336952718230741560369621645218729592233657856104560425642219241082727756696967324334634822771842625681505869025740662258929200756109704988223034840699133778958569054445520305361142302393767439478256174414187983763
c = 415916446053083522663299405080903121619846594209033663622616979372099135281363175464579440520262612010099820951944229484417996994283898028928384268216113118778734726335389504987546718739928112684600918108591759061734340607527889972020273454098314620790710425294297542021830654957828983606433731988998097351888879368160881316237557097381718444193741788664735559392675419489952796677690968481917700683813252460912749931286739585465657312416977086336732056497161860235343155953578618273940135486362350057858779130960380833359506761436212727289297656191243565734621757889931250689354508999144817518599291078968866323093
n = 12221355905532691305226996552124162033756814028292708728711809229588190407700199452617060657420166395065565154239801465361510672853972152857415394695376825120759202857555325904640144375262531345320714166285999668052224661520834318497234299585219832943519644095197479639328120838919035625832361810964127485907587199925564724081163804724975965691571850962714258888527902920462746795712011579424322515292865504642938090200503979483095345893697972170153990274670257331483858538617460680462369680572833191232126527727222302641204529110948993583190295067970240051042000918629138767209918572311469915774910003970381965123241
c = 2248834602646305164283014556051672824689884721514190813323189875541899566338153534858709617544459297836048770439230174669883719627734394673012731609952869246171300132019334542245094425654362711870373095782083791160029789553806741967408922001051006100049326921742208757147339981269528740944842177729701945606827918253016001436218891580980192743564642120923356793292885805519110411357830040053435569937296612987581482128241218218550319154933831743819546558930918761162723110000328532730751591375727881221199739397698390594797621758011191224528339478784930214820615602510460640307707682865125229937141010351138099874025
n = 18152103454920389919231636321286527841833809319334215885641536161086810144890443857211776387914779781628740172079478910188540146498426564211851629962338413488555121865779016981727229209606498886170396500155102635962395243364899026418106378234307821492609778555173516000309435730752571818439328803899462791834490025768785383592935046996428331508608555503567191807692523852530836008436655164751054189301721070209363416058642811329040202582026786024825518381761299547703962502636888833428457116986351812252188468878701301184044948733274488264320930936362549028124581962244201377136969591119942276742760215403738913067567
c = 2797812094994121597295362327809389195134238119144547570610194659000554967367804835006774413888965325870488368112707535584687083342412367127561646136089638402907513075405746055834487062923240856950047936297155455745928810738711368950139327254040579266046642851362228893522740216519732851152162928545416236075387903789535000820423985522550638100049857678600662008021574841083416323980817348573062083159710189689337626277009675683473560325178417766400002763719953723259300977655801234386662217462862844994462505601804422871991694828697337752697234180117437785537788728412520613916334045368736691714704501962513954509705
n = 22877887459293720334652698748191453972019668578065068224653972884599636421200068659750242304040301306798039254241668648594556654589309801728248683586229288074709849246660525799452637187132633064172425677552176203292787732404537215347782229753837476655088638984496409603054524994383358547132112778403912563916886533181616856401929346567686400616307916690806467019665390260267596320840786982457521423178851498130935577260638269429250197050326097193841333205073650802709022947551398142692735680419453533128176592587955634333425401930362881423044363132586170013458300714163531162544301477356808388416864173949089028317961
c = 12271947322974809255127222556723394446467844330408506340843897575503534175121932185624776713618037572593449207329510171212097269297133492090526270770286000839978630002819714376964416081198925899119135271459404333829811516667576167576916805217016117373027245648473458331936273975110163065432285322832123169216976420362833557809289561705091817949915218278430834098156335989014645979633658818904753942786129126233956314517292746008579152368541316795082120147520597254020266752859205131887527661767589367756335766220841483940854397440079467053684289006956034944336788288196391829411432383541473132962783883758561108297747
n = 19844333358004073542783728196775487079202832688982038135532362073659058674903791697765527614270399097276261983744620537925712167578187109058145015032736796457938148615396547198728652435169126585595701228287449135664667959433491335769206692390262797325133960778920452511673878233190120432257482339068405290918739453464061987163074129048150451046315248186376609350095502130018696275764450248681787926130463463923862832714969425813770847493135627599129546112143050369344208092649256659330284904392961574494907186727388685504929586018639846040474616307662546605623294842316524163106100888851228858194942825157286544846177
c = 9531264751315473345056673937611382755236533664089452852716992791452558274873158812669513178040971923528201631609089069182049526587423864397527252061341857426422965190913745048414029690931254119437249218321954899956104589066479231204536856131403590472063496956452030342299863907499976917750846369802185896519725837163530049157920978007252920334447236842959033879772444475877613295594785710745889554296655932909212643500877218304116451889820444820534937901427158918411546484157737612926382420354101675658160847653151539420222526999426483473829341628599881460824765758346670633385844187252696874025582747177333702736465
n = 16956880944655068255446705024149899655327230949463546092744762226005904114738078692036960935391303255804754787864713189658290361949509917704853428701870609882427423574672772606814823959758208695540116440342488334213300943604780971422918744381486937517952553797134323570131582724393100092308466968491068503301604506186521656059375518680612292667310641047190088814753025794048591445267711939066523165042651430468971452726568222388482323097260496415484997546126185688914792795834046855221759289007609518312601640548469651358391745947588643697900883634533872314566389446271647587564348026861264979727062157272541149018781
c = 16110326928338602237561005337578085623028116490564329920738844771341250444164294693848130674347672763073995755532723894042946521372321947507527854966013459795492930736187058535665041545095683801386814190612817128504426590828954205050425979880047802547011117626354405687170961272200066258220699329112978151044633994329352673342582175349200008181837211288847301836681860817044391028992501763375849046751094019224570802498414368189170656992427042010362385494565216988561215657424755648213390551881450141899860811844684546992754530755092358644968088017107313907435586729574798046187046145596726569637758312033849476689378
n = 16472195897077185060734002588086375750797253422014472876266294484788862733424113898147596402056889527985731623940969291811284437034420929030659419753779530635563455664549165618528767491631867637613948406196511848103083967995689432928779805192695209899686072900265108597626632371718430059561807147486376536203800038054012500244392964187780217667805308512187849789773573138494622201856638931435423778275004491853486855300574479177472267767506041000072575623287557610576406578525902565241580838652860552046216587141709709405062150243990097835181557208274750462554811004137033087430556692966525170882625891516050207318491
c = 11867731823522211833301190385669833752050387304375114576570892885641949969365352586215693183003550684262313893105989683214739695968039039944442567581277252581988489020834299896625977474857889570528169919064941042132119301236852358823696947330423679033138054012027878783478922023431469564210485180679933264749281963405243082505688901662659030897104957499953192201440290084373968716271056483463909282407034181891901928790601973222643210525000717355062752079302291729448234374709852429885984987094307177760741403086538949190424454337896501402430653783597070178968921411867485584517214777073301007918941216316241784521708
n = 13890749889361612188368868998653029697326614782260719535555306236512452110708495623964530174188871342332417484996749651846510646453983388637377706674890018646246874688969342600780781646175634455109757266442675502522791531161284420286435654971819525519296719668701529481662071464145515727217108362496784024871976015116522898184301395037566514980846499856316532479656908169681719288258287756566886281183699239684997698487409138330229321935477734921670373632304542254938831218652340699024011371979519574576890581492623709896310465567043899767342676912434857372520308852745792360420376574037705943820090308501053778144141
c = 6250115196713939477947942995075509357173312813431601073354390451609559579925704891503987992181988654989477525811826607070378476102616752398280691012244301950194800995432882828020405062344160270290542566163969692748126314259624623341922057435728127596172871894887055305291345372720594481096374310285437492746765510292863238933163142677773310305789984897974266961231555124787205980411992251387207335655129551950825339766848166539671565212408741432649813058363660321480995187545006718837863674527475323414266732366507905974800565463011676462244368010182725161416783875646259625352308599198614681446394427674340328493047
n = 21457499145521259498911107987303777576783467581104197687610588208126845121702391694574491025398113729462454256070437978257494064504146718372095872819969887408622112906108590961892923178192792218161103488204912792358327748493857104191029765218471874759376809136402361582721860433355338373725980783308091544879562698835405262108188595630215081260699112737457564998798692048522706388318528370551365364702529068656665853097899157141017378975007689790000067275142731212069030175682911154288533716549782283859340452266837760560153014200605378914071410125895494331253564598702942990036163269043699029806343766286247742865671
c = 6269656777204332618433779865483197625538144405832409880710764183039800286008967127279281167109250083159801218370191973055663058165456565194979210256278526713608759141588082614531352489547674696723140599892318118960648862531538435596775798128845789504910467783731144808685373807716609662688064728614003904579841055786083326311313295311152563668422289435606771091246147867715987583149743032723028324394173498623642539175178996531881058274717907066845565199058931743481410454382746158558886667761300257488769795092777021292335562818583719708133179974425584610403335487082478848975656282384575767178925517257692365828720
'''

多组$n$中可能存在公因数$p$。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from Crypto.Util.number import *
import gmpy2

e = 65537
n0 =
c0 =

n1 =
c1 =

n2 =
c2 =

n3 =
c3 =

n4 =
c4 =

n5 =
c5 =

n6 =
c6 =

n7 =
c7 =

n8 =
c8 =

n9 =
c9 =

n10 =
c10 =

n = [n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,n10]
c = [c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10]

for i in range(len(n)):
for j in range(len(n)):
if (i!=j):
t = gmpy2.gcd(n[i],n[j])
if t != 1:
p = t
q = n[i] // p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c[i],d,n[i])
print(long_to_bytes(m))

中等

flag_exchange

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from Crypto.Util.number import isPrime
from random import getrandbits

with open("flag.txt","rb") as fs:
flag = fs.read().strip()

def diffie_hellman(p, flag):
alice_privKey = getrandbits(1024)
alice_pubKey = pow(7, alice_privKey, p)
bob_privKey = getrandbits(1024)
bob_pubKey = pow(7, bob_privKey, p)

superkey = pow(bob_pubKey, alice_privKey, p)
m = int.from_bytes(flag, 'big')
return (m * superkey) % p, alice_pubKey, bob_pubKey


from typing import Callable

def chall(input:Callable[[str],None], print:Callable[[str],None]):
p = int(input("P = "))
if isPrime(p) and p.bit_length() >= 1024:
c, alice_pubKey, bob_pubKey = diffie_hellman(p, flag)
print("Alice's public key: {}".format(alice_pubKey))
print("Bob's public key: {}".format(bob_pubKey))
print("Ciphertext: {}".format(c))
else:
print("Invalid P")

类似于DAS7月赛的EzDHKE

传一个光滑的$p$,这样可以很快求出私钥$a,b$。

求出私钥后,求出$superkey$

因为返回的密文$c$满足$c\equiv m ×superkey \mod p$,$\therefore m \equiv c × superkey^{-1} \mod p$

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#sage
import gmpy2
from Crypto.Util.number import *

# 构造一个很光滑的p,即p可以分解成许多小素数之积
p = 1
i = 2
while isPrime(p+1) ==False or p.bit_length()<1024 :
p *= i
i = gmpy2.next_prime(i)

#print(p+1)

g = 7
p = 20404068993016374194542464172774607695659797117423121913227131032339026169175929902244453757410468728842929862271605567818821685490676661985389839958622802465986881376139404138376153096103140834665563646740160279755212317501356863003638612390661668406235422311783742390510526587257026500302696834793248526734305801634165948702506367176701233298064616663553716975429048751575597150417381063934255689124486029492908966644747931
A = 20336091449271352050000450320351597189353592062279377142884247684088072216754371140723688503847322269500056016823378486967669507529408071986247421480538070427020298314420596325895121922786822444746298488308935254197730980462214370281102952073051791017954008911348128021296665124600273965642418702110947948173149809724203543773244607616601110670126605337319276524135448598308833199821995622969372761107826550795621362415457182
B = 3016038452071464751422492594184606815201979377859418872430468328543181786920301668755954977729921559697635702877324759319811639923934643618711315555467453055288843246623654939387741368313873206560664229687375070952935800932126812264287402142801364732994539469801252414404987968911552061069354407508916590343552775560281691608737262245571829068155383159065889848197300894149747651679644288337413872066182471443965426404626423
c = 8101058129734054038632640353434785588447342802920921913999474154497466343094694993166042668700504613189281291125022077749043805204765309971981658929422844998392130190493145455241084775180221325627765838099393607299071835294706132501949635214088146102721237571159346281928107966372652823580266088579392328383545323322931789769149936581855383571990955947284354676448353798975087410774586309247023597516943906734455833918792577

a = discrete_log(mod(A,p),mod(g,p))

b = discrete_log(mod(B,p),mod(g,p))

superkey = pow(B, a, p)

p = gmpy2.mpz(p)
superkey = gmpy2.mpz(superkey)

d = gmpy2.invert(superkey,p) % p
m = c*d % p
print(long_to_bytes(int(m)))

minipack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import random

with open("flag.txt", "rb") as fs:
flag = fs.read().strip()

assert len(flag) == 72
m = int.from_bytes(b"\xff" + flag + b"\xff", "big")


def long2bits(long):
bits = []
while long > 0:
bits.append(long & 1)
long >>= 1
return list(reversed(bits))


def genkey(len):
sum = 0
out = []
for i in range(len):
delta = random.randint(1, 10000)
x = sum + delta
out.append(x)
sum += x
return out


key = genkey(74 * 8)

with open("key.txt", "w") as fs:
fs.write(str(key))


def encrypt(m, keys):
data = long2bits(m)
assert len(data) == len(keys)
return sum((k if (p == 1) else 1) for p, k in zip(data, keys))


with open("ciphertext.txt", "w") as fs:
fs.write(str(encrypt(m, key)))

给的$key$实际上从第二项开始就是一个超递增序列了,利用这个性质,我们便可以恢复$m$的每一位。

需要注意的是,加密的$m$是把高位和$key$的小值相乘的。

我们恢复的时候是通过$key$恢复$m$的每一位,所以恢复的是$m$的低位,因此最后需要逆序

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#sage
from Crypto.Util.number import *

key = [...]
c = 44096612931024003148407895164090667174657344536623354666642108463093659898867859567157728084018394988840266329206836985537987081415020571845239234014695335928717091578810470705259929


m = ''
for i in reversed(key):
if c > i:
m += '1'
c -= i
else:
m += '0'
c -= 1

msg = ''
for i in reversed(m):
msg += i

flag = long_to_bytes(int(msg,2))
print(flag)

bad_random

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import random

with open("flag.txt", "r") as f:
flag = f.read().strip()


class LCG:
def set_params(self):
self.m = random.randint(10000, 20000)
self.a = random.randint(10000, 20000)
self.c = random.randint(1, self.a-1)
self.x = random.randint(0, self.m-1)

def get_all_output(self):
x0 = self.x
s = set()
while (t := self()) not in s:
s.add(t)
self.x = x0
return s

def __init__(self):
self.set_params()
while len(self.get_all_output()) < 10:
self.set_params()

def __call__(self):
self.x = (self.a * self.x + self.c) % self.m
return self.x

from typing import Callable

def chall(input:Callable[[str],None], print:Callable[[str],None]):
from hashlib import md5
from string import ascii_letters
s = "".join(random.choices(ascii_letters, k=16))
h = md5(s.encode()).hexdigest()
print(f"<!> md5(XXXXXXXX+{s[8:]}) == {h}")
i = input("Give me XXXXXXXX: ")
if md5((i + s[8:]).encode()).hexdigest() != h:
print("<!> ACCESS DENIED <!>")
return
inst = LCG()
print("Let's play a simple game! If you can guess the right number, I will give your the flag! You have 10 tries")
for tries in range(10):
i = input(f"Give me a number, you have failed for {tries} times: ")
if int(i) == (right := inst()):
print(f"Congurations! You win the game! Your flag is here: {flag}")
else:
print(f"Oh, you are wrong! The right number is {right}")

题目要求预测下一个随机数(笔者在做题的时候,花了很多时间预测第一轮的值,审题的重要性),一个lcg问题

大家可能会疑惑这都没有输出,怎么预测啊。

需要注意的是,题目给了10次预测机会,如果回答错误,就会给予我们正确答案,我们可以用9次机会获得数据,再根据LCG的原理进行预测

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from pwn import *
import string
from itertools import product
import hashlib
from Crypto.Util.number import *
import gmpy2
from collections import Counter


while 1:
table = string.ascii_letters + string.digits

host = 'cl.akarin.tk'
port = 10001

GeShi = b'Give me XXXX: '
sh = remote(host,port)
data = sh.recvuntil(GeShi).decode()
print(data)

proof = data.split('md5')[1]

Xnum = proof.split('+')[0].upper().count("X")
tail = proof.split('+')[1].split(')')[0]
hash = proof.split('+')[1].split(')')[1].split('==')[-1].split('\n')[0].strip()


print("未知数:",Xnum)
print("tail:",tail)
print("hash:",hash)
print("开始爆破")
for i in product(table,repeat=4):
head = ''.join(i)
t = hashlib.md5((head + tail).encode()).hexdigest()
if t == hash:
print('爆破成功!结果是:', end='')
print(head)
sh.send(head.encode())
break


print("以下是提交完XXXX之后的流程\n")
output = []
d = sh.recvline()
print(d.decode())
for _ in range(9):
sh.send("1".encode())
data = sh.recvline()
print(data.decode())
output.append(int(data.decode().split(' ')[-1]))

print(output)


t = []
for i in range(1,len(output)):
t.append(output[i]-output[i-1])

T = []
for i in range(1,len(t)-1):
T.append(t[i+1]*t[i-1] - t[i]**2)

m = []
for i in range(len(T)-1):
mm = gmpy2.gcd(T[i],T[i+1])
if isPrime(mm):
m.append(int(mm))
else:
for i in range(1,100):
if isPrime(mm // i):
mm = mm // i
m.append(int(mm))
break
print("m的可能取值",m)

for i in m:
global flag
flag = False

if 10000 < i < 20000 and isPrime(i):
print("m=",i)
a = gmpy2.invert(t[0],i) * t[1] % i
if 10000 < a < 20000:
print("a=",a)
b = output[1] - a*output[0] % i
a_ = gmpy2.invert(a,i)

seed = (a * output[-1] + b) % i
print("下一个值是:",seed)
sh.send(str(seed).encode())
data = sh.recvline()
print(data.decode())
flag = data.decode().split(":")[-1].strip()
break
else:
continue
else:
continue
if flag != False:
print(flag)
break

值得注意的是,$a$和$m$都是有范围的,而且在做题过程中,发现成功率并不是100%,需要多次尝试。

giant_e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Util.number import getPrime

with open("flag.txt","rb") as fs:
flag = fs.read().strip()

p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 0x609778981bfbb26bb93398cb6d96984616a6ab08ade090c1c0d4fedb00f44f0552a1555efec5cc66e7960b61e94e80e7483b9f906a6c8155a91cdc3e4917fa5347c58a2bc85bb160fcf7fe98e3645cfea8458ea209e565e4eb72ee7cbb232331a862d8a84d91a0ff6d74aa3c779b2b129c3d8148b090c4193234764f2e5d9b2170a9b4859501d07c0601cdd18616a0ab2cf713a7c785fd06f27d68dff24446d884644e08f31bd37ecf48750e4324f959a8d37c5bef25e1580851646d57b3d4f525bc04c7ddafdf146539a84703df2161a0da7a368675f473065d2cb661907d990ba4a8451b15e054bfc4dd73e134f3bf7d8fa4716125d8e21f946d16b7b0fc43
m = int.from_bytes(flag,"big")
c = pow(m,e,n)

print(n)
#0xbaa70ba4c29eb1e6bb3458827540fce84d40e1c966db73c0a39e4f9f40e975c42e02971dab385be27bd2b0687e2476894845cc46e55d9747a5be5ca9d925931ca82b0489e39724ea814800eb3c0ea40d89ebe7fe377f8d3f431a68d209e7a149851c06a4e67db7c99fcfd9ec19496f29d59bb186feb44a36fe344f11d047b9435a1c47fa2f8ed72f59403ebb0e439738fd550a7684247ab7da64311690f461e6dce03bf2fcd55345948a3b537087f07cd680d7461d326690bf21e39dff30268cb33f86eeceff412cd63a38f7110805d337dcad25e6f7e3728b53ca722b695b0d9db37361b5b63213af50dd69ee8b3cf2085f845d7932c08b27bf638e98497239
print(c)
#0x45a9ce4297c8afee693d3cce2525d3399c5251061ddd2462513a57f0fd69bdc74b71b519d3a2c23209d74fcfbcb6b196b5943838c2441cb34496c96e0f9fc9f0f80a2f6d5b49f220cb3e78e36a4a66595aa2dbe3ff6e814d84f07cb5442e2d5d08d08aa9ccde0294b39bfde79a6c6dcd2329e9820744c4deb34a039da7933ddf00b0a0469afb89cba87490a39783a9b2f8f0274f646ca242e78a326dda886c213bc8d03ac1a9150de4ba08c5936c3fe924c8646652ef85aa7ac0103485f472413427a0e9d9a4d416b99e24861ca8499500c693d7a07360158ffffa543480758cafff2a09a9f6628f92767764fa026d48a9dd899838505ae16e38910697f9de14

e很大,维纳攻击

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import gmpy2
import libnum


def continuedFra(x, y):
"""计算连分数
:param x: 分子
:param y: 分母
:return: 连分数列表
"""
cf = []
while y:
cf.append(x // y)
x, y = y, x % y
return cf


def gradualFra(cf):
"""计算传入列表最后的渐进分数
:param cf: 连分数列表
:return: 该列表最后的渐近分数
"""
numerator = 0
denominator = 1
for x in cf[::-1]:
# 这里的渐进分数分子分母要分开
numerator, denominator = denominator, x * denominator + numerator
return numerator, denominator


def solve_pq(a, b, c):
"""使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
:param a:x^2的系数
:param b:x的系数
:param c:pq
:return:p,q
"""
par = gmpy2.isqrt(b * b - 4 * a * c)
return (-b + par) // (2 * a), (-b - par) // (2 * a)


def getGradualFra(cf):
"""计算列表所有的渐近分数
:param cf: 连分数列表
:return: 该列表所有的渐近分数
"""
gf = []
for i in range(1, len(cf) + 1):
gf.append(gradualFra(cf[:i]))
return gf


def wienerAttack(e, n):
"""
:param e:
:param n:
:return: 私钥d
"""
cf = continuedFra(e, n)
gf = getGradualFra(cf)
for d, k in gf:
if k == 0: continue
if (e * d - 1) % k != 0:
continue
phi = (e * d - 1) // k
p, q = solve_pq(1, n - phi + 1, n)
if p * q == n:
return d


n=
e=
c=

d = wienerAttack(e, n)
print("d =",d)
m = pow(c, d, n)
print(libnum.n2s(m).decode())

factorize_me

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from Crypto.Util.number import getPrime
from math import prod
from sympy import nextprime
from random import choices

with open('flag.txt', 'rb') as fs:
flag = fs.read().strip()

primes = [getPrime(512) for _ in range(9)]
print(f"{prod(primes) = }")
print(f"{prod(p - 1 for p in primes) = }")

primes2 = [nextprime(p) for p in choices(primes, k=3)]
n = prod(primes2)
e = 65537
c = pow(int.from_bytes(flag, 'big'), e, n)

print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')

# 363364907814244019888662301376841344262476227242899756862391470731421569394957444030214887114615748277199649349781524749919652160244484352285668794188836866602305788131186220057989320357344904731322223310531945208433910803617954798258382169132907508787682006064930747033681966462568715421005454243255297306718356766130469885581576362173340673516476386201173298433892314145854649884922769732583885904512624543994675379894718657682146178638074984373206937523380103438050549181568015985546172618830480078894445808092527561363650503540062128543705172678754195578429520889784813733491180748361345720247750720179608752244490362713103319685024237941527268458213442611663415417005556439749055222361212059968254748751273361732365487788593341859760309778894350385339764442343374673786357175846291309425081492959910254127778240522152676060766139057453197528944251599979227271074508795482632471242983094008619339488744362509349734218480932255216087706001484182136783834973304870508270118505737767002256270427907341952256516206663258530300791364944105025764611810001781971638030661367630116818647252727909489405550104641122269772492252464714694507693447974171377200402508765841829763548525530878309985480248379655169722567051495205792089930014228403456098065971372039443284193603395249634283366194562380309469628114581468645669390610963076340643757972439104287127375438663839421605531570285615180251
# 363364907814244019888662301376841344262476227242899756862391470731421569394957444030214887114615748277199649349781524749919652160244484352285668794188836492373364350673588273863828369502073826782362255108313852264064760467561392054178047091483873483255491431451728274259516789065331176728192953741805933100379191778599394515981288225535175013258094287912195847642598436035132783919453991516358280321085873745330313812205910011387125778714795906023110368957596998222544234082487264006696812862179916726781327290284827659294751262185328816323311831349296593013038823107653943652771448719760448938995150646738377177532550757319539185878535087009904848382493668686831331474113789651777885239747000076063679062106375348803749466079052774597412239427050432901553466002731972993029311850718200685157193170716432600165476733200831046297530470544781309612128231925681374239849452623513538498417735984094919756374577623486416462101457492789215144166273775249387638107644634704270216130852885082174564648445147377239033930079759024399532146184753110240154062693457622208373371290126810856885343328090305620627668495081760346853701632815149478447405718664667978825807101325764916405446176183238866136433205933785973568759281210319422288153910340542098573782006262190181726245838857185687242960093445000287347616796984610291664809895901301187179157382169999966124177588884152267266994164841066291200
# n = 899081756851564072995842371038848265712822308942406479625157544735473115850983700580364485532298999127834142923262920189902691972009898741820291331257478170998867183390650298055916005944577877856728843264502218692432679062445730259562784479410120575777748292393321588239071577384218317338474855507210816917917699500763270490789679076190405915250953860114858086078092945282693720016414837231157788381144668395364877545151382171251673050910143023561541226464220441
# e = 65537
# c = 841335863342518623856757469220437045493934999201203757845757404101093751603513457430254875658199946020695655428637035628085973393246970440054477600379027466651143466332405520374224855994531411584946074861018245519106776529260649700756908093025092104292223745612991818151040610497258923925952531383407297026038305824754456660932812929344928080812670596607694776017112795053283695891798940700646874515366341575417161087304105309794441077774052357656529143940010140

已知大N和大N对应的$\phi(N)$,

通过$\phi(N)$和$N$可以分解N。

然后稍微爆破一下即可出flag

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from math import gcd
from math import isqrt
from random import randrange
from gmpy2 import is_prime
# from sage.all import is_prime



def factorize(N, phi):
"""
Recovers the prime factors from a modulus if Euler's totient is known.
This method only works for a modulus consisting of 2 primes!
:param N: the modulus
:param phi: Euler's totient, the order of the multiplicative group modulo N
:return: a tuple containing the prime factors, or None if the factors were not found
"""
s = N + 1 - phi
d = s ** 2 - 4 * N
p = int(s - isqrt(d)) // 2
q = int(s + isqrt(d)) // 2
return p, q


def factorize_multi_prime(N, phi):
"""
Recovers the prime factors from a modulus if Euler's totient is known.
This method works for a modulus consisting of any number of primes, but is considerably be slower than factorize.
More information: Hinek M. J., Low M. K., Teske E., "On Some Attacks on Multi-prime RSA" (Section 3)
:param N: the modulus
:param phi: Euler's totient, the order of the multiplicative group modulo N
:return: a tuple containing the prime factors
"""
prime_factors = set()
factors = [N]
while len(factors) > 0:
# Element to factorize.
N = factors[0]

w = randrange(2, N - 1)
i = 1
while phi % (2 ** i) == 0:
sqrt_1 = pow(w, phi // (2 ** i), N)
if sqrt_1 > 1 and sqrt_1 != N - 1:
# We can remove the element to factorize now, because we have a factorization.
factors = factors[1:]

p = gcd(N, sqrt_1 + 1)
q = N // p

if is_prime(p):
prime_factors.add(p)
elif p > 1:
factors.append(p)

if is_prime(q):
prime_factors.add(q)
elif q > 1:
factors.append(q)

# Continue in the outer loop
break

i += 1

return tuple(prime_factors)

N =
phi =
prime = factorize_multi_prime(N,phi)
#print(prime)

import gmpy2
from random import choices
from Crypto.Util.number import *

while 1:
primes2 = [gmpy2.next_prime(p) for p in choices(prime, k=3)]
n =
c =
if prod(primes2) == n:
phi = 1
for i in primes2:
phi *= (i-1)
d = gmpy2.invert(65537,phi)
m = pow(c,d,n)
flag = long_to_bytes(int(m))
if b'moectf' in flag:
print(flag)
break

xorrrrrrrrr

这道题算是长见识了,在lingfeng师傅指导下学了新东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
flag = open('flag.txt','rb').read()
assert flag.startswith(b'moectf{') and flag.endswith(b'}')
article = open('article.txt','rb').read()

import random

strxor = lambda x,y: bytes([a^b for a,b in zip(x,y)])

result = []

for i in range(100):
range_start = random.randint(0, len(article) - len(flag))
mask = article[range_start:range_start + len(flag)]
result.append(strxor(flag,mask))

with open("result.log","w") as fs:
fs.writelines([str(i)+"\n" for i in result])

根据题目,可以意识到,我们可以通过已知的部分明文恢复100个7位的article(甚至更多,最后一位也可以恢复)。

然后把每一个7位的article,遍历密文中每7位值进行异或,如果结果是可打印字符,那么就有可能是$flag$的值

然后再从中判断$flag$后6位和有可能的值的前6位是否相等,如果相等,那么第七位就有可能是$flag$的后一位。

再进行词频分析,选择可能性更大的那个字符

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from Crypto.Util.number import *
from tqdm import *
from collections import Counter

strxor = lambda x,y: bytes([a^b for a,b in zip(x,y)])

with open("result.log",'r') as f:
f = f.readlines()

cipher = [eval(i) for i in f]

head = b'moectf{'

article = []
for i in cipher:
article.append(bytes(strxor(head,i[:7])))

print(article)
re=[]
for x in tqdm(article):
for i in cipher:
for j in range(0,len(i)-7):
tmp=strxor(i[j:j+7],x)
if len(str(tmp))==10:
re.append(tmp.decode())

flag = "moectf{"
for _ in trange(72-8):
col=[]
for i in re:
if flag[-6:] ==i[:6]:
col.append(i[-1])
K=Counter(col)
flag=flag+K.most_common()[0][0]
print(flag+'}')

值得注意的是

这里的eavl(i)是把$i$从str变成byte,区别于.encode()

``K=Counter(col)统计col`中每个字符每个字符出现的次数,并且以元组的形式返回

K.most_common()作用同上,但是返回的是列表,这个列表由有序对组成

使用示例

1
2
3
4
5
6
col = [1,2,64,4,8,7,5,12,14]
K = Counter(col)
print(K)
#Counter({1: 1, 2: 1, 64: 1, 4: 1, 8: 1, 7: 1, 5: 1, 12: 1, 14: 1})
print(K.most_common())
#[(1, 1), (2, 1), (64, 1), (4, 1), (8, 1), (7, 1), (5, 1), (12, 1), (14, 1)]

ez_chian

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from Crypto.Util.number import *

with open("key.txt", "r") as fs:
key = int(fs.read().strip())
with open("flag.txt", "rb") as fs:
flag = fs.read().strip()
assert len(flag) == 72

m = bytes_to_long(flag)

base = bytes_to_long(b"koito")
iv = 3735927943

def blockize(long):
out = []
while long > 0:
out.append(long % base)
long //= base
return list(reversed(out))


blocks = blockize(m)


def encrypt_block_cbc(blocks, iv, key):
encrypted = [iv]
for i in range(len(blocks)):
encrypted.append(blocks[i] ^ encrypted[i] ^ key)
return encrypted[1:]


print(encrypt_block_cbc(blocks, iv, key))
# [8490961288, 122685644196, 349851982069, 319462619019, 74697733110, 43107579733, 465430019828, 178715374673, 425695308534, 164022852989, 435966065649, 222907886694, 420391941825, 173833246025, 329708930734]

blockize函数相当于是把传入的longbase进制表示,最后把out逆序返回,所以返回的列表中存放的是long的高位到低位(对于base进制而言的高位低位)。

然后是个CBC加密模式,给了iv,重点是求出key。我们是能知道部分的明文,所以是可以求key的。

已知flag格式为moectf{}

自己把flag填充为72位,再把它表示成base进制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import *
import random
import string


base = bytes_to_long(b"koito")
def blockize(long):
out = []
while long > 0:
out.append(long % base)
long //= base
return list(reversed(out))

for _ in range(10):
flag = 'moectf{' + ''.join(random.choices(string.ascii_letters,k=64)) + '}'

m = bytes_to_long(flag.encode())

print(blockize(m))

经过多组数据测试,可以判断第一块加密的明文的值是 5329712293

由此,便可以求得key,然后就是CBC解密了

需要注意的是,CBC解出来的是m每一位的值(对于base进制),而且是从高到低。

最后用进制恢复m即可求解

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from Crypto.Util.number import *

base = bytes_to_long(b"koito")
print(base)

iv = 3735927943
m0 = 5329712293
c = [8490961288, 122685644196, 349851982069, 319462619019, 74697733110, 43107579733, 465430019828, 178715374673, 425695308534, 164022852989, 435966065649, 222907886694, 420391941825, 173833246025, 329708930734]

key = c[0] ^ m0 ^ iv

out = [m0]
for i in range(1,len(c)):
msg = c[i] ^ c[i-1] ^ key
out.append(msg)
print(out)

out = out[::-1]

m = 0
for i in range(len(out)):
m += out[i] * base **i

print(long_to_bytes(m))

broken_hash

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def F(x, y, z):
return ((x & ~y) ^ (y & ~z) ^ (z & ~x)) ^ (
((x + y) * (y + z) + (x + z)) & 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
)


def _block_hash(a, b, c, d):
x, y, z, w = F(a, b, c), F(b, c, d), F(c, d, a), F(d, a, b)
return (a ^ b ^ c ^ d ^ x ^ y ^ z ^ w) ^ 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF


def _hash(blocks: list[int]):
length = len(blocks)
if length % 4 != 0:
padding = 4 - length % 4
blocks += [0] * padding
length += padding
if length == 4:
return _block_hash(*blocks)
else:
block_size = length // 4
h1 = _hash(blocks[:block_size])
h2 = _hash(blocks[block_size : block_size * 2])
h3 = _hash(blocks[block_size * 2 : block_size * 3])
h4 = _hash(blocks[block_size * 3 :])
return _block_hash(h1, h2, h3, h4)


def bytes2blocks(data: bytes, block_size=16):
if len(data) % block_size != 0:
data += b"\x00" * (block_size - len(data) % block_size)
return [
int.from_bytes(data[i : i + block_size], "little")
for i in range(0, len(data), block_size)
]


def hash(*data: list[bytes]):
return _hash(bytes2blocks(b"".join(data)))


from typing import Callable
from random import randbytes
from base64 import b64decode,b64encode
from hashlib import md5
from string import ascii_letters
from random import choices

with open("flag.txt", "r") as f:
flag = f.read().strip()

def chall(input: Callable[[str], None], print: Callable[[str], None]):
def proof_of_work():

s = "".join(choices(ascii_letters, k=16))
h = md5(s.encode()).hexdigest()
print(f"<!> md5(XXXXXXXX+{s[8:]}) == {h}")
i = input("Give me XXXXXXXX: ")
return md5((i + s[8:]).encode()).hexdigest() == h
if not proof_of_work():
print("<!> ACCESS DENIED <!>")
return

b = randbytes(256)
print(f"this is a random bytes: {b64encode(b).decode()}")
i = input("give me another bytes with the same hash: ")
try:
d = b64decode(i)
except:
print("invaild input")
if hash(b) == hash(d) and d!=b:
print(f"congurations! and your flag is {flag}")

先分析函数

F

1
2
3
4
def F(x, y, z):
return ((x & ~y) ^ (y & ~z) ^ (z & ~x)) ^ (
((x + y) * (y + z) + (x + z)) & 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
)

接收3个参数。观察到(x & ~y) ^ (y & ~z) ^ (z & ~x)这个表达式有个特征:把$x,y,z$三者进行交换,或者两者进行交换是不影响整个表达式最后的值。

$(x + y) × (y + z) + (x + z) = y^2 + (y+1)(x+z) + xz$这个表达式交换$x,z$不影响整个表达式的值

所以$F(x,y,z) = F(z,y,x)$

_block_hash

1
2
3
def _block_hash(a, b, c, d):
x, y, z, w = F(a, b, c), F(b, c, d), F(c, d, a), F(d, a, b)
return (a ^ b ^ c ^ d ^ x ^ y ^ z ^ w) ^ 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF

$\because x,y,z,w = F(a,b,c),F(b,c,d),F(c,d,a),F(d,a,b)$

等同于$x,y,z,w = F(c,b,a),F(d,c,b),F(a,d,c),F(b,a,d)$

而且不影响最后(a ^ b ^ c ^ d ^ x ^ y ^ z ^ w)的值

$\therefore$ _block_hash(a, b, c, d) = _block_hash(c, d, a, b)

_hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def _hash(blocks: list[int]):
length = len(blocks)
if length % 4 != 0:
padding = 4 - length % 4
blocks += [0] * padding
length += padding
if length == 4:
return _block_hash(*blocks)
else:
block_size = length // 4
h1 = _hash(blocks[:block_size])
h2 = _hash(blocks[block_size : block_size * 2])
h3 = _hash(blocks[block_size * 2 : block_size * 3])
h4 = _hash(blocks[block_size * 3 :])
return _block_hash(h1, h2, h3, h4)

接收一个装有整形数据的列表。如果列表刚好是4个数据,那就返回_broken_hash()的值,如果不是,就先分为4组,再返回_broken_hash()的值

bytes2blocks

1
2
3
4
5
6
def bytes2blocks(data: bytes, block_size=16):
if len(data) % block_size != 0:
data += b"\x00" * (block_size - len(data) % block_size)
return [
int.from_bytes(data[i : i + block_size], "little")
for i in range(0, len(data), block_size)

接受byte数据,把一串byte数据分为16字节的块,并以列表的形式返回。

hash

1
2
def hash(*data: list[bytes]):
return _hash(bytes2blocks(b"".join(data)))

接收一个装有byte数据的列表。

分析

靶机给出256长的字节,按照bytes2blocks,我们可以将其分为16块,也就是4×4

bytes2block函数作用后

变成$[C_{1,1},C_{1,2},C_{1,3},C_{1,4},C_{2,1},C_{2,2},C_{2,3},C_{2,4},C_{3,1},C_{3,2}, C_{3,3},C_{3,4},C_{4,1},C_{4,2},C_{4,3},C_{4,4}]$

根据_hash(a,b,c,d),先分别计算

$h_1 = _hash(C_{1,1},C_{1,2},C_{1,3},C_{1,4})$;

$h_2 = _hash(C_{2,1},C_{2,2},C_{2,3},C_{2,4})$;

$h_3 = _hash(C_{3,1},C_{3,2},C_{3,3},C_{3,4})$;

$h_4 = _hash(C_{4,1},C_{4,2},C_{4,3},C_{4,4})$。

再计算$h = _hash(h_1,h_2,h_3,h_4)$

根据算法的性质

$h = _hash(h_3,h_4,h_1,h_2)$

$h_1 = _hash(C_{1,3},C_{1,4},C_{1,1},C_{1,2})$;

$h_2 = _hash(C_{2,3},C_{2,4},C_{2,1},C_{2,2})$;

$h_3 = _hash(C_{3,3},C_{3,4},C_{3,1},C_{3,2})$;

$h_4 = _hash(C_{4,3},C_{4,4},C_{4,1},C_{4,2})$。

所以只需要把靶机给的值进行一些交换即可

即把$[C_{1,1},C_{1,2},C_{1,3},C_{1,4},C_{2,1},C_{2,2},C_{2,3},C_{2,4},C_{3,1},C_{3,2}, C_{3,3},C_{3,4},C_{4,1},C_{4,2},C_{4,3},C_{4,4}]$

改为$[C_{3,3},C_{3,4},C_{3,1},C_{3,2},C_{4,3},C_{4,4},C_{4,1},C_{4,2},C_{1,3},C_{1,4}, C_{1,1},C_{1,2},C_{2,3},C_{2,4},C_{2,1},C_{2,2}]$

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from Crypto.Util.number import *
import gmpy2
from pwn import *
import string
from itertools import product
import hashlib
import base64


table = string.ascii_letters + string.digits
host = 'cl.akarin.tk'
post = 10002
GeShi = b'Give me XXXX: ' # 改格式!

sh = remote(host,post)
data = sh.recvuntil(GeShi).decode()
print(data)

proof = data.split('md5')[1]

Xnum = proof.split('+')[0].upper().count("X") #要爆破的数量
tail = proof.split('+')[1].split(')')[0].strip()
hash = proof.split('+')[1].split(')')[1].split('==')[-1].split('\n')[0].strip()


print("未知数:",Xnum)
print("tail:",tail)
print("hash:",hash)
print("开始爆破")
for i in product(table,repeat=Xnum):
head = ''.join(i)
t = hashlib.md5((head + tail).encode()).hexdigest()
if t == hash:
print('爆破成功!结果是:', end='')
print(head)
sh.send(head.encode())
break


print("以下是提交完XXXX之后的流程\n")
data1 = sh.recvline().decode().strip('\n')
print(data1)
cipher_enc = data1.split(':')[-1].strip().encode() #本来是str加个encode变成byte
cipher = base64.b64decode(cipher_enc)

data2 = sh.recvuntil(b':').decode()
print(data2)
def trans(c):
length = len(c)
C = [c[i:i+length//4] for i in range(0,length,length//4)]
C[0],C[1],C[2],C[3] = C[2],C[3],C[0],C[1]

return b"".join(C)

tmp = trans(cipher)
length = len(tmp)
tmpp = [tmp[i:i+length//4] for i in range(0,length,length//4)]

anser = b""
for i in tmpp:
anser += trans(i)

print(anser)
sh.send(base64.b64encode(anser)) #要进行base64加密后再传给靶机
sh.interactive() #base64后就是byte数据,不需要encode

做题过程中注意下数据类型

feistel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from Crypto.Util.number import *

round = 2
flag = open("./secret", "rb").read().strip()


def f(m, key):
m = m ^ (m >> 4)
m = m ^ (m << 5)
m = m ^ (m >> 8)
m ^= key
m = (m * 1145 + 14) % 2**64
m = (m * 1919 + 810) % 2**64
m = (m * key) % 2**64
return m


def enc(m, key, round):
key = bytes_to_long(key)
left = bytes_to_long(m[:8])
right = bytes_to_long(m[8:])
for i in range(round):
left, right = right, f(right, key) ^ left
left, right = right, left
return long_to_bytes(left).rjust(8, b"\x00") + long_to_bytes(right).rjust(8, b"\x00")


def padding(m):
mlen = len(m)
pad = 16 - mlen % 16
return m + pad * bytes([pad])


def ecb_enc(m, key):
m = padding(m)
mlen = len(m)
c = b""
for i in range(mlen // 16):
c += enc(m[i * 16 : i * 16 + 16], key, round)
return c


print(ecb_enc(flag, b"wulidego"))

# b'\x0b\xa7\xc6J\xf6\x80T\xc6\xfbq\xaa\xd8\xcc\x95\xad[\x1e\'W5\xce\x92Y\xd3\xa0\x1fL\xe8\xe1"^\xad'

手推一下过程

题目给出key

和key丢入f函数,再和密文的左一半异或,即可得到明文的后一半。

得到明文后一半后,丢入f函数,再和密文的右一半异或,即可得到明文的前一半

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from Crypto.Util.number import *

def f(m, key):
m = m ^ (m >> 4)
m = m ^ (m << 5)
m = m ^ (m >> 8)
m ^= key
m = (m * 1145 + 14) % 2**64
m = (m * 1919 + 810) % 2**64
m = (m * key) % 2**64
return m

def dec(c,key):
key = bytes_to_long(key)
left = bytes_to_long(c[:8])
right = bytes_to_long(c[8:])
m_right = f(right,key) ^ left
m_left = f(m_right,key) ^ right
return long_to_bytes(m_left) + long_to_bytes(m_right)


def ecb_dec(c, key):
clen = len(c)
m = b""
for i in range(clen // 16):
m += dec(c[i * 16 : i * 16 + 16], key)
return m

c = b'\x0b\xa7\xc6J\xf6\x80T\xc6\xfbq\xaa\xd8\xcc\x95\xad[\x1e\'W5\xce\x92Y\xd3\xa0\x1fL\xe8\xe1"^\xad'
key = b"wulidego"
flag = ecb_dec(c,key)
print(flag)

feistel_promax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from Crypto.Util.number import *
from os import urandom

round = 2
flag = open("./secret", "rb").read().strip()

def f(m, key):
m = m ^ (m >> 4)
m = m ^ (m << 5)
m = m ^ (m >> 8)
m ^= key
m = (m * 1145 + 14) % 2**64
m = (m * 1919 + 810) % 2**64
m = (m * key) % 2**64
return m


def enc(m, key, round):
key = bytes_to_long(key)
left = bytes_to_long(m[:8])
right = bytes_to_long(m[8:])
for i in range(round):
left, right = right, f(right, key) ^ left
left, right = right, left
return long_to_bytes(left).rjust(8, b"\x00") + long_to_bytes(right).rjust(8, b"\x00")


def padding(m):
mlen = len(m)
pad = 16 - mlen % 16
return m + pad * bytes([pad])


def ecb_enc(m, key):
m = padding(m)
mlen = len(m)
c = b""
for i in range(mlen // 16):
c += enc(m[i * 16 : i * 16 + 16], key, round)
return c


key = urandom(8)

print(ecb_enc(padding(flag), key))
# b'B\xf5\xd8gy\x0f\xaf\xc7\xdf\xabn9\xbb\xd0\xe3\x1e0\x9eR\xa9\x1c\xb7\xad\xe5H\x8cC\x07\xd5w9Ms\x03\x06\xec\xb4\x8d\x80\xcb}\xa9\x8a\xcc\xd1W\x82[\xd3\xdc\xb4\x83P\xda5\xac\x9e\xb0)\x98R\x1c\xb3h'

和前一题不同之处在于ecb_enc(padding(flag), key),而且没给出key。

题目把2填充后的flag进行加密,说明实际加密的明文是进行了两次填充。

而且第二次填充的内容是b'\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'

还得根据这个f的特性,发现如果用部分的key加密,会使得加密后的结果与用完整key加密的结果在低位上有相同的地方,于是可以用爆破求出key。

自己试了从低位,爆破key。一位一位爆的太慢了,然后请教了一下其他师傅,每两位进行爆破,很快就出结果了。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from Crypto.Util.number import *

def f(m, key):
m = m ^ (m >> 4)
m = m ^ (m << 5)
m = m ^ (m >> 8)
m ^= key
m = (m * 1145 + 14) % 2**64
m = (m * 1919 + 810) % 2**64
m = (m * key) % 2**64
return m

def dec(c,key):
key = bytes_to_long(key)
left = bytes_to_long(c[:8])
right = bytes_to_long(c[8:])
m_right = f(right,key) ^ left
m_left = f(m_right,key) ^ right
return long_to_bytes(m_left) + long_to_bytes(m_right)


def ecb_dec(c, key):
clen = len(c)
m = b""
for i in range(clen // 16):
m += dec(c[i * 16 : i * 16 + 16], key)
return m

m = b'\x10\x10\x10\x10\x10\x10\x10\x10'
ct = b'B\xf5\xd8gy\x0f\xaf\xc7\xdf\xabn9\xbb\xd0\xe3\x1e0\x9eR\xa9\x1c\xb7\xad\xe5H\x8cC\x07\xd5w9Ms\x03\x06\xec\xb4\x8d\x80\xcb}\xa9\x8a\xcc\xd1W\x82[\xd3\xdc\xb4\x83P\xda5\xac\x9e\xb0)\x98R\x1c\xb3h'
c = bytes_to_long(ct[-8:]) ^ bytes_to_long(m)
bin_c = bin(c)[2:].rjust(64,'0')


k1 = [b'']
for i in range(1,5):
k2 = []
for KEY in k1:
for j in range(2**16):
key1 = long_to_bytes(j) + KEY
cc = f(bytes_to_long(m),bytes_to_long(key1))
bin_cc = bin(cc)[2:].rjust(64,'0')
if bin_cc[-16*i:] == bin_c[-16*i:]:
k2.append(key1)
k1 = k2
# print(k2)

key = [b'4t*zFD\xac\xb4', b'\xb4t*zFD\xac\xb4', b'\\q\x0f\x00w\xeb\xc1"', b'\xdcq\x0f\x00w\xeb\xc1"']
for KEY in key:
flag = ecb_dec(ct,KEY)
print(flag)

古典密码

ezrot

1
@64E7LC@Ecf0:D0;FDE020D:>!=60=6EE6C0DF3DE:EFE:@?04:!96C0tsAJdEA6d;F}%0N

rot47

1
oectf{rot47_is_just_a_simPle_letter_substitution_ciPher_EDpy5tpe5juNT_}

皇帝的新密码

1
tvljam{JhLzhL_JPwoLy_Pz_h_cLyF_zPtwPL_JPwoLy!_ZmUVUA40q5KbEQZAK5Ehag4Av}

移位7

1
moectf{CaEsaE_CIphEr_Is_a_vErY_sImpIE_CIphEr!_SfNONT40j5DuXJSTD5Xatz4To}

不是皇帝的新密码

1
2
scsfct{wOuSQNfF_IWdkNf_Jy_o_zLchmK_voumSs_zvoQ_loFyof_FRdiKf_4i4x4NLgDn}
md5 of flag (utf-8) `ea23f80270bdd96b5fcd213cae68eea5`

维吉尼亚

先对表$moectf \longrightarrow scsfct$,得到$key = goodjo$

解完发现不对,把$key$换成$goodjob$

1
moectf{vIgENErE_CIphEr_Is_a_lIttlE_hardEr_thaN_caEsar_CIphEr_4u4u4EXfXz}

可可的新围墙

1
mt3_hsTal3yGnM_p3jocfFn3cp3_hFs3c_3TrB__i3_uBro_lcsOp}e{ciri_hT_avn3Fa_j

W型栅栏

栅栏数3

1
moectf{F3nc3_ciph3r_shiFTs_3ach_l3TT3r_By_a_Giv3n_nuMB3r_oF_plac3s_Ojpj}

猫言喵语

1
喵喵? 喵喵喵喵喵喵喵喵喵喵喵喵 喵喵喵 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵?喵喵喵喵喵? 喵喵喵喵喵? 喵喵喵喵喵?喵喵? 喵喵喵喵喵? 喵喵喵喵喵喵 喵喵喵喵喵喵 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵?喵喵喵喵喵?喵喵喵 喵喵喵喵喵? 喵喵? 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵?喵喵喵喵喵喵喵喵喵 喵喵喵喵喵喵喵喵? 喵喵? 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵?喵喵喵喵喵喵喵喵喵 喵喵喵 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵?喵喵喵喵喵?喵喵喵 喵喵喵喵喵? 喵喵喵喵喵?喵喵喵喵喵喵 喵喵喵喵喵?喵喵喵喵喵喵 喵喵喵 喵喵?喵喵喵喵喵喵 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵?喵喵?喵喵喵 喵喵?喵喵?喵喵? 喵喵喵喵喵喵喵喵? 喵喵?喵喵?喵喵喵喵喵喵 喵喵喵喵喵喵 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵?喵喵喵喵喵喵喵喵喵 喵喵?喵喵喵喵喵?喵喵? 喵喵喵喵喵喵喵喵?喵喵?喵喵喵喵喵? 喵喵喵喵喵?喵喵喵 喵喵?喵喵喵喵喵喵喵喵?

Morse

喵喵?-喵喵喵.

1
THE_KAWAII_CAT_BUT_BE_CALLED_GOUZI_BY_RX

加上格式moectf{THE_KAWAII_CAT_BUT_BE_CALLED_GOUZI_BY_RX}

Jail

Jail level 0

1
2
3
4
print("Welcome to the MoeCTF2023 Jail challenge.It's time to work on this calc challenge.")
print("Enter your expression and I will evaluate it for you.")
user_input_data = input("> ")
print('calc Answer: {}'.format(eval(user_input_data)))

利用eval()函数的特性。输入print(open('flag','r').read())

这个文件名是猜的,尝试各种可能,总能弄出来。

Jail level 1

1
2
3
4
5
6
7
print("Welcome to the MoeCTF2023 Jail challenge level1.It's time to work on this calc challenge.")
print("Enter your expression and I will evaluate it for you.")
user_input_data = input("> ")
if len(user_input_data)>12:
print("Oh hacker! Bye~")
exit(0)
print('calc Answer: {}'.format(eval(user_input_data)))

题目限制了命令的长度不超过12

如果不超过13的话,可以用eval(input())

尝试通过help()函数,然后随机进入一个模块比如os,再输入!sh即可获得shell,然后就可以为所欲为了

但是我输入os之后还是停留在help()不懂是为啥。

思路来源:https://dummykitty.github.io/posts/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E5%8E%9F%E7%90%86/#help-%E5%87%BD%E6%95%B0

然后尝试用breakpoint(),使用breakpoint()之后就会进到Pdb里面。

这里摘抄一下Pdb为何物:

pdb 模块定义了一个交互式源代码调试器,用于 Python 程序。它支持在源码行间设置(有条件的)断点和单步执行,检视堆栈帧,列出源码列表,以及在任何堆栈帧的上下文中运行任意 Python 代码。它还支持事后调试,可以在程序控制下调用。

思路来源:[PyJail] python沙箱逃逸探究·上(HNCTF题解 - WEEK1) - 知乎 (zhihu.com)

Jail level 2

1
2
3
4
5
6
7
print("Welcome to the MoeCTF2023 Jail challenge level1.It's time to work on this calc challenge.")
print("Enter your expression and I will evaluate it for you.")
user_input_data = input("> ")
if len(user_input_data)>6:
print("Oh hacker! Bye~")
exit(0)
print('calc Answer: {}'.format(eval(user_input_data)))

长度限制在6,只能用help()

这题倒是成功了。不知道为啥

Jail level 3

1
2
3
4
5
6
7
8
9
10
11
12
import re
BANLIST = ['breakpoint']
BANLIST_WORDS = '|'.join(f'({WORD})' for WORD in BANLIST)
print("Welcome to the MoeCTF2023 Jail challenge.It's time to work on this calc challenge.")
print("Enter your expression and I will evaluate it for you.")
user_input_data = input("> ")
if len(user_input_data)>12:
print("Oh hacker! Bye~")
exit(0)
if re.findall(BANLIST_WORDS, user_input_data, re.I):
raise Exception('Blacklisted word detected! you are hacker!')
print('Answer result: {}'.format(eval(user_input_data)))

breakpoint办了

1
2
3
4
re.findall(BANLIST_WORDS, user_input_data, re.I) 执行了一个正则表达式的查找操作。其中:
BANLIST_WORDS 是一个正则表达式模式,表示黑名单中的词汇。
user_input_data 是用户输入的数据,需要在其中查找匹配的词汇。
re.I 是一个标志参数,表示忽略大小写进行匹配。

如果输入breakpointre.findall()会返回一个空列表

1
2
if re.findall(BANLIST_WORDS, user_input_data, re.I):
raise Exception('Blacklisted word detected! you are hacker!')

如果re.findall()返回空的列表,则会抛出异常

总的来说,这道题不能用breakpoint,除此之外还限制了payload的长度,用chr进行拼接也不行

试了help()也不行

在大佬指导下,通过把breakpoint半角转全角得breakpoint,可以绕过限制

在线全角半角转换—在线工具 (sojson.com)

Jail level 4

没给源码

题目要求用的python2.7

没明白这个报错,不知道这样算不算预期解

Leak level 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
fake_key_into_local_but_valid_key_into_remote = "moectfisbestctfhopeyoulikethat"
print("Hey Guys,Welcome to the moeleak challenge.Have fun!.")
print("| Options:
| [V]uln
| [B]ackdoor")
def func_filter(s):
not_allowed = set('vvvveeee')
return any(c in not_allowed for c in s)
while(1):
challenge_choice = input(">>> ").lower().strip()
if challenge_choice == 'v':
code = input("code >> ")
if(len(code)>9):
print("you're hacker!")
exit(0)
if func_filter(code):
print("Oh hacker! byte~")
exit(0)
print(eval(code))
elif challenge_choice == 'b':
print("Please enter the admin key")
key = input("key >> ")
if(key == fake_key_into_local_but_valid_key_into_remote):
print("Hey Admin,please input your code:")
code = input("backdoor >> ")
print(eval(code))
else:
print("You should select valid choice!")

v功能下的限制命令长度不超过9,而且不能出现v,e,因此不能通过help(),再经过__main__查看变量。

b功能,需要key进入

我们可以用globals()查看全局变量获得key,然后转到b功能下输入不受限的命令。

Leak level 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
fake_key_into_local_but_valid_key_into_remote = "moectfisbestctfhopeyoulikethat"
print("Hey Guys,Welcome to the moeleak challenge.Have fun!.")
def func_filter(s):
not_allowed = set('moe_dbt')
return any(c in not_allowed for c in s)
print("| Options:
| [V]uln
| [B]ackdoor")
while(1):
challenge_choice = input(">>> ").lower().strip()
if challenge_choice == 'v':
code = input("code >> ")
if(len(code)>6):
print("you're hacker!")
exit(0)
if func_filter(code):
print("Oh hacker! byte~")
exit(0)
print(eval(code))
elif challenge_choice == 'b':
print("Please enter the admin key")
key = input("key >> ")
if(key == fake_key_into_local_but_vailed_key_into_remote):
print("Hey Admin,please input your code:")
code = input("backdoor >> ")
print(eval(code))
else:
print("You should select valid choice!")

换了限制的字符,不能输入m,o,e,_,d,b,t,限制命令长度不超过6

半角转全角的思路,进入help(),通过__main__获得key

Leak level 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fake_key_into_local_but_valid_key_into_remote = "moectfisbestctfhopeyoulikethat"
print("Hey Guys,Welcome to the moeleak challenge.Have fun!.")
print("| Options:
| [V]uln
| [B]ackdoor")
def func_filter(s):
not_allowed = set('dbtaaaaaaaaa!')
return any(c in not_allowed for c in s)
while(1):
challenge_choice = input(">>> ").lower().strip()
if challenge_choice == 'v':
print("you need to ")
code = input("code >> ")
if(len(code)>6):
print("you're hacker!")
exit(0)
if func_filter(code):
print("Oh hacker! byte~")
exit(0)
if not code.isascii():
print("please use ascii only thanks!")
exit(0)
print(eval(code))
elif challenge_choice == 'b':
print("Please enter the admin key")
key = input("key >> ")
if(key == fake_key_into_local_but_vailed_key_into_remote):
print("Hey Admin,please input your code:")
code = input("backdoor> ")
print(eval(code))
else:
print("You should select valid choice!")

这道题不能出现d,b,t,a而且多了一个限制,必须是ascii字符,半角转全角的思路不可用了。

不过我们直接用help()即可

pwn

test_nc

先ls,发现没有flag文件,但是有个gift

查看提示,看来考点是查看隐藏文件

了解一下用ls -al查看隐藏文件

发现flag

fd

附件丢ida分析一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __cdecl main(int argc, const char **argv, const char **envp)
{
int input; // [rsp+4h] [rbp-6Ch] BYREF
int fd; // [rsp+8h] [rbp-68h]
int new_fd; // [rsp+Ch] [rbp-64h]
char flag[80]; // [rsp+10h] [rbp-60h] BYREF
unsigned __int64 v8; // [rsp+68h] [rbp-8h]

v8 = __readfsqword(0x28u);
input = 0;
init();
puts("Do you know fd?");
fd = open("./flag", 0, 0LL);
new_fd = (4 * fd) | 0x29A;
dup2(fd, new_fd);
close(fd);
puts("Which file do you want to read?");
puts("Please input its fd: ");
__isoc99_scanf("%d", &input);
read(input, flag, 0x50uLL);
puts(flag);
return 0;
}

让GPT分析了一下

1
2
3
  fd = open("./flag", 0, 0LL);
new_fd = (4 * fd) | 0x29A;
dup2(fd, new_fd);

以只读的形式打开名为./flag,并且将其文件描述符存储在fd变量中,则这个fd=1

然后计算new_fd = (fd × 4) | 0x29A ,然后将文件描述符fd复制到new_fd,使得new_fd也指向同一文件。

所以我们根据fd=1,计算出new_fd= 670,即可拿到flag

baby_calculator

判断100道加法是否正确即可获得flag

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pwn import *

host = ''
post =
GeShi = b'Now..start!' # 改格式!

sh = remote(host,post)

data = sh.recvuntil(GeShi).decode()

for i in range(100):
data1 = sh.recvuntil(b'=').decode()
print(data1)

a = data1.split(':')[-1].split('\n')[-1].split('=')[0]
b = sh.recvuntil(b'\n').decode()
print("a:",a)
print("b:",b.strip('\n'))

if eval(a) == int(b):
sh.sendline("BlackBird".encode())
sh.recvline()
else:
sh.sendline("WingS".encode())
sh.recvline()

print(sh.recvall())

int_overflow

1
2
3
4
Welcome to Moectf2023.
Do you know int overflow?
Can you make n == -114514 but no '-' when you input n.
Please input n:

整形数据范围在$-2147483648—2147483647$,根据溢出规则,超出最大值的数将从最小值开始往前

比如2147483648 = -2147483648,2147483649 = -2147483647

根据这个规则-114514 = 4294852782

我的计算方式是先计算$-114514$和$-2147483648$的差值x,再加上$2147483648$。

因为2147483649 = -2147483647,$-2147483647$和$-2147483648$差值为1,所以是加上$2147483648$

Reverse

base_64

找个网站把pyc文件反编译为py文件

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
from string import *
str1 = 'yD9oB3Inv3YAB19YynIuJnUaAGB0um0='
string1 = 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba0123456789+/'
string2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
flag = input('welcome to moectf\ninput your flag and I wiil check it:')
enc_flag = base64.b64encode(flag.encode()).decode()
enc_flag = enc_flag.translate(str.maketrans(string2, string1))
if enc_flag == str1:
print('good job!!!!')
else:
print('something wrong???')
exit(0)

给出了base64的原表和替换后的表

换标即可

exp:

1
2
3
4
5
6
7
8
9
10
import base64

cipher = "yD9oB3Inv3YAB19YynIuJnUaAGB0um0="

table1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" #这是ba64的表
table2 = "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba0123456789+/"

flag = base64.b64decode(cipher.translate(str.maketrans(table2,table1)))
print(flag)
#moectf{pYc_And_Base64~}

XOR

enc[i]0x39异或回去即可

以上是enc的值

exp:

1
2
3
4
5
6
7
8
enc = [0x54, 0x56, 0x5C, 0x5A, 0x4D, 0x5F, 0x42, 0x60, 0x56, 0x4C, 0x66, 0x52, 0x57, 0x9, 0x4E, 0x66, 0x51, 0x9, 0x4E, 0x66, 0x4D,0x9,0x66,0x61,0x9,0x6b,0x18,0x44]
flag = ""
for i in enc:
m = i ^ 0x39
flag += chr(m)

print(flag)
#moectf{You_kn0w_h0w_t0_X0R!}

ANDROID

JEB打开, 找到主函数,然后反编译

找到enckey,以及加密过程,异或回去即是明文

exp:

1
2
3
4
5
6
7
8
9
10
enc = ['\u0019', '\u0007', '\u0000', '\u000E', '\u001B', '\u0003', '\u0010', '/', '\u0018', '\u0002', '\t', ':', '\u0004', '\u0001', ':', '*', '\u000B', '\u001D', '\u0006', '\u0007', '\f', '\t', '0', 'T', '\u0018', ':', '\u001C', '\u0015', '\u001B', '\u001C', '\u0010']
key = ['t', 'h', 'e', 'm', 'o', 'e', 'k', 'e', 'y']
flag = ""

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

print(flag)
#moectf{Java_in_Android_1s_easy}

UPX!

先脱壳,丢入IDA发现没有主函数。然后打开Shift + F12,找到字符串

双击进去,找到关键函数

反编译

得到密文

Shift + e提取密文

0A08020413011C570F381E5712382C095710382F571038130838350211541514023832373F4646461A0000000000000000000000000000000000000000000000

加密是c[i] = m[i] ^ 0x67,异或回来即可

exp:

1
2
3
4
5
6
7
8
9
10
c = "0A08020413011C570F381E5712382C095710382F571038130838350211541514023832373F4646461A0000000000000000000000000000000000000000000000"
c = bytes.fromhex(c)

m = ""
for i in c:
mm = chr(i ^ 0x67)
m += mm

print(m)
#moectf{0h_y0u_Kn0w_H0w_to_Rev3rse_UPX!!!}
-------------已经到底啦!-------------