CTF密码学RSA基础及常见题目类型解析

什么是RSA

在1977年,Ron Rivest, Adi Shami和Leonard Adleman这三个人开发了一个新的算法,并用他们三个名字的首字母来命名这个算法,这个算法名叫RSA

非对称加密与对称加密

RSA算法采用的是非对称加密,假如我有你的公钥,我想给你发一个加密的消息,我可以利用你的公钥来加密我的消息, 然后你就可以用你的私钥来解密

而对称加密则是要双方都拥有同一个私钥,才能进行解密

RSA

RSA是进行基本的数学运算,你要加密的消息越大,数字就越大,这就是为什么AES比RSA快

ABC --> \x41\x42\x43 --> 0x414243 --> 4276803
   ascii             hex        hex转dec

加密

https://en.wikipedia.org/wiki/RSA_(cryptosystem)
c:加密后的消息
e:公钥
m:需要加密的消息
n:公钥

RSA会对消息进行一个数学运算,这个公式很简单,跟高中数学差不多

((0x414243)^e %n) = c

解密

d:私钥

(c)^d %n = 0x414243

进行这两个运算非常依赖e和d,也就是公钥与私钥,如果我们e作为消息的幂运算并模n,然后再用d作为幂进行运算并模n,就会得到原消息

((0x414243)^e)^d %n = 0x414243

mod n

mod是什么意思呢,假如我们有一个钟表,时针指向的是3,那么12小时后指向的是几呢,结果还是3

12 + 3 =15
15 % 12 = 3

而这个12就是mod 12,取余数

在之前的运算中,如果不进行mod运算,我们会得到一个非常大的数字

((0x414243)^e)^d = 114514.........312331....313

进行mod运算后,就会变成很小的数字

((0x414243)^e)^d = 114514.........312331....313
mod n = 0x414243

RSA的密钥生成

首先看看RSA的密钥生成过程

如果要生成RSA密钥,首先需要两个大质数,p和q,这两个质数必须是随机的,然后使用p乘以q,得到n,也就是上面算术使用的mod n,n也是公钥的一部分,这也是为什么n是公开的,它只是两个数的乘积,并且我们也很难通过因数分解来得到原来的q和p,因为p和q都是随机的,并且是质数,而且非常大

现在得到了公钥n,接下来我们要看看私钥d部分

φ(n) = n - (p + q -1)
d ≡ e^−1 (mod φ(n))

这是公钥e的生成过程

e是一个大于1小于φ(n)的随机数,e通常设置为65537,因为当随机选择更大的数字时,加密效率会大大降低

RSA算法简单演示

加密

首先我们需要将字符转换为数字

B --> 2

然后对这个数字进行运算,这里需要e和n,假设e是5,n是14,方程式就是这样的

2^5 (mod 14) == 32 (mod 14) = 4

加密后的密文就为4

解密

解密我们需要私钥d以及公钥n

d ≡ e^−1 (mod φ(n))

这里d通过计算得到11,n还是14

4^11(mod 14) == 4194304(mod14)

最终运算结果为2,转换为字符就是B

这里拿一个题目来演示会更容易理解

给了你q和p值,叫我们求n值,并且还问我们n值能求出来吗

n = pq

q = 60413
p = 76753
n = p*q

print(n)
//4636878989

成功通过第一关,第二关给了我们p和n值,需要求q值

q = n // p

p = 54269
n = 5051846941
q = n // p

print(q)
//93089

第三关给了我们e和n值,叫我们求p和q的值,因为条件不够(没有t的值),这里选n

来到第四关,这里给了我们q和p的值,叫我们求toitent(n)的值

totient(n) = (p -- 1) * (q -- 1)

def totient(p, q):
    return (p - 1)*(q - 1)
 
q = 66347
p = 12611

print(totient(q, p))
//836623060

成功通过第四关,第五关给了我们plaintest和e,n的值,叫我们加密,直接加密即可

def encryption(plaintext, e, n):
    return (plaintext**e) % n
plaintext = 6357294171489311547190987615544575133581967886499484091352661406414044440475205342882841236357665973431462491355089413710392273380203038793241564304774271529108729717
e = 3
n = 29129463609326322559521123136222078780585451208149138547799121083622333250646678767769126248182207478527881025116332742616201890576280859777513414460842754045651093593251726785499360828237897586278068419875517543013545369871704159718105354690802726645710699029936754265654381929650494383622583174075805797766685192325859982797796060391271817578087472948205626257717479858369754502615173773514087437504532994142632207906501079835037052797306690891600559321673928943158514646572885986881016569647357891598545880304236145548059520898133142087545369179876065657214225826997676844000054327141666320553082128424707948750331

print(encryption(plaintext,e, n)) 
//256931246631782714357241556582441991993437399854161372646318659020994329843524306570818293602492485385337029697819837182169818816821461486018802894936801257629375428544752970630870631166355711254848465862207765051226282541748174535990314552471546936536330397892907207943448897073772015986097770443616540466471245438117157152783246654401668267323136450122287983612851171545784168132230208726238881861407976917850248110805724300421712827401063963117423718797887144760360749619552577176382615108244813

第六关给了我们c,n和e值,叫我们解密,我们不知道p和q值,这里选n

第七关给了我们q、p和e值,需要计算出d值

d ≡ e^−1 (mod φ(n))

from Crypto.Util.number import inverse

q = 92092076805892533739724722602668675840671093008520241548191914215399824020372076186460768206814914423802230398410980218741906960527104568970225804374404612617736579286959865287226538692911376507934256844456333236362669879347073756238894784951597211105734179388300051579994253565459304743059533646753003894559
p = 97846775312392801037224396977012615848433199640105786119757047098757998273009741128821931277074555731813289423891389911801250326299324018557072727051765547115514791337578758859803890173153277252326496062476389498019821358465433398338364421624871010292162533041884897182597065662521825095949253625730631876637
e = 65537

print(inverse(e, (p-1)*(q-1)))
//1405046269503207469140791548403639533127416416214210694972085079171787580463776820425965898174272870486015739516125786182821637006600742140682552321645503743280670839819078749092730110549881891271317396450158021688253989767145578723458252769465545504142139663476747479225923933192421405464414574786272963741656223941750084051228611576708609346787101088759062724389874160693008783334605903142528824559223515203978707969795087506678894006628296743079886244349469131831225757926844843554897638786146036869572653204735650843186722732736888918789379054050122205253165705085538743651258400390580971043144644984654914856729

第八关给了p,c,e和n的值,叫我们求明文的值,我们写个小脚本直接跑

from Crypto.Util.number import inverse
p = 153143042272527868798412612417204434156935146874282990942386694020462861918068684561281763577034706600608387699148071015194725533394126069826857182428660427818277378724977554365910231524827258160904493774748749088477328204812171935987088715261127321911849092207070653272176072509933245978935455542420691737433
ciphertext = 18031488536864379496089550017272599246134435121343229164236671388038630752847645738968455413067773166115234039247540029174331743781203512108626594601293283737392240326020888417252388602914051828980913478927759934805755030493894728974208520271926698905550119698686762813722190657005740866343113838228101687566611695952746931293926696289378849403873881699852860519784750763227733530168282209363348322874740823803639617797763626570478847423136936562441423318948695084910283653593619962163665200322516949205854709192890808315604698217238383629613355109164122397545332736734824591444665706810731112586202816816647839648399
e = 65537
n = 23952937352643527451379227516428377705004894508566304313177880191662177061878993798938496818120987817049538365206671401938265663712351239785237507341311858383628932183083145614696585411921662992078376103990806989257289472590902167457302888198293135333083734504191910953238278860923153746261500759411620299864395158783509535039259714359526738924736952759753503357614939203434092075676169179112452620687731670534906069845965633455748606649062394293289967059348143206600765820021392608270528856238306849191113241355842396325210132358046616312901337987464473799040762271876389031455051640937681745409057246190498795697239

r = (p-1)*(n//p-1)
d = inverse(e,r)
m = pow(ciphertext, d, n)

print(m)
//14311663942709674867122208214901970650496788151239520971623411712977120586163535880168563325

然后它说,这个明文就是flag,我们转换即可

m = 14311663942709674867122208214901970650496788151239520971623411712977120586163535880168563325
print(bytearray.fromhex(format(m, 'x')).decode())

得到flag

CTF RSA常见题目解析

环境安装

apt install python3 pip3
pip3 install pycryptodome
pip3 install factordb-pycli
pip3 install gmpy2

知道n,c,e,并且n的值不大

n:n是两个大质数p和q的乘积,即n=pq。n的长度就是密钥长度。
在实际应用中,RSA密钥一般是1024位,重要场合则为2048位。n是公开的,任何人都可以获取。
但是,只有将n因数分解,才能算出p和q,这是一件非常困难的事情,因此RSA算法的安全性主要依赖于大整数的因数分解的难度

e:e也称为加密指数,是一个与φ(n)=(p-1)(q-1)互质的整数,且1< e < φ(n)。在实际应用中,e常常是65537。e是公开的,任何人都可以获取

c:c是密文,是明文m经过加密后的结果

现在我们写一个小脚本来破解

from factordb.factordb import FactorDB
import gmpy2

c = 240986837130071017759137533082982207147971245672412893755780400885108149004760496
n = 831416828080417866340504968188990032810316193533653516022175784399720141076262857
e = 65537
//导入c,n,e的值

f = FactorDB(n)   //因为n不大,这里直接分解q和p的值
f.connect()
p, q = f.get_factor_list()  //获取因素列表
ph = (p-1)*(q-1)  //获取φ(n)值,之后计算私钥值
d = gmpy2.invert(e, ph) //有了e值和φ(n)值,可以计算私钥值
plaintext = pow(c, d, n)  //解密
print(bytearray.fromhex(format(plaintext, 'x')).decode())  //将十六进制字符串转换为ascii码

运行脚本,得到flag值

低加密指数攻击,e的值特别小

通常情况下,解密指数 d 的选择是基于安全性的考虑。较大的解密指数 d 能够提供更高的安全性,因为攻击者难以通过尝试所有可能的私钥值来破解密文。

然而,当使用较小的解密指数时,可能会存在低解密指数攻击的风险。低解密指数攻击的基本思想是,如果使用的解密指数 d 很小,那么攻击者可以通过计算 c^d modn的多次幂运算来尝试还原明文。如果d很小,那么尝试的次数相对较少,使得攻击者有可能通过枚举法或其他攻击手段成功还原出明文

n:n是两个大质数p和q的乘积,即n=pq。n的长度就是密钥长度。
在实际应用中,RSA密钥一般是1024位,重要场合则为2048位。n是公开的,任何人都可以获取。
但是,只有将n因数分解,才能算出p和q,这是一件非常困难的事情,因此RSA算法的安全性主要依赖于大整数的因数分解的难度

e:e也称为加密指数,是一个与φ(n)=(p-1)(q-1)互质的整数,且1< e < φ(n)。在实际应用中,e常常是65537。e是公开的,任何人都可以获取

c:c是密文,是明文m经过加密后的结果

这里有三个值,n,e和c值,且M ^ e刚比N大,意思是:

M^3 mod n = c

t值可以写成:

M^3 = tn + c

根据以上推理,可以得出:

M = iroot(tn+c, 3) //iroot是gmpy2模块的一个函数,作用是给a开b次方

现在我们写一个小脚本来获得flag

import gmpy2

n = 1615765684321463054078226051959887884233678317734892901740763321135213636796075462401950274602405095138589898087428337758445013281488966866073355710771864671726991918706558071231266976427184673800225254531695928541272546385146495736420261815693810544589811104967829354461491178200126099661909654163542661541699404839644035177445092988952614918424317082380174383819025585076206641993479326576180793544321194357018916215113009742654408597083724508169216182008449693917227497813165444372201517541788989925461711067825681947947471001390843774746442699739386923285801022685451221261010798837646928092277556198145662924691803032880040492762442561497760689933601781401617086600593482127465655390841361154025890679757514060456103104199255917164678161972735858939464790960448345988941481499050248673128656508055285037090026439683847266536283160142071643015434813473463469733112182328678706702116054036618277506997666534567846763938692335069955755244438415377933440029498378955355877502743215305768814857864433151287
e = 3
c = 1220012318588871886132524757898884422174534558055593713309088304910273991073554732659977133980685370899257850121970812405700793710546674062154237544840177616746805668666317481140872605653768484867292138139949076102907399831998827567645230986345455915692863094364797526497302082734955903755050638155202890599808154521995312832362835648711819155169679435239286935784452613518014043549023137530689967601174246864606495200453313556091158637122956278811935858649498244722557014003601909465057421728834883411992999408157828996722087360414577252630186866387785481057649036414986099181831292644783916873710123009473008639825720434282893177856511819939659625989092206115515005188455003918918879483234969164887705505900695379846159901322053253156096586139847768297521166448931631916220211254417971683366167719596219422776768895460908015773369743067718890024592505393221967098308653507944367482969331133726958321767736855857529350486000867434567743580745186277999637935034821461543527421831665171525793988229518569050
//导入n,e,c的值

for i in range(10000):   //遍历循环,找到m值
    m, t = gmpy2.iroot(i*n + c, e)
    if t:
        print(bytearray.fromhex(format(m, 'x')).decode()) //将十六进制字符串转换为字节后解密
        break

运行脚本,获得flag

Wiener's attack RSA攻击

"Weiner's attack" 指的是一种用于攻击 RSA 加密算法的特定方法。这个攻击是由数学家和密码学专家Michael J. Wiener在1990年提出的。

攻击的核心思想是利用 RSA 私钥的一个较小的分数近似于其公钥的倒数。当私钥的指数较小且满足一定条件时,Wiener's attack 可以有效地恢复出私钥

wiki介绍:

https://en.wikipedia.org/wiki/Wiener%27s_attack

得到了e,n和c的值

https://en.wikipedia.org/wiki/Wiener%27s_attack

直接用rsactftool脚本跑

https://github.com/RsaCtfTool/RsaCtfTool

python3 RsaCtfTool.py -n 73858245204393269120598772974257218874289179816258533748548954836353808000542909731398449660121424717248079724923923284922221800385870164798744402737063530114487159042350959711492581286948377310386675916734743955708759421607349585475823504183537132290212235481788134354234151518170214048887523664730897405647 -e 40185337488228965483088768689361237580655397969352079774015797079626751211817603607155962600219370112069853693506378621129705581863959239984871073674637310005220323695488701830477574210150173980203084059519439551150923764323395592278383658383525721471839777239932693638775164411928659371101425091253226312809 --uncipher 11279019002658132246395680583012680197641520463055015905954159065596263829466315684813964601637222796742820040947867106250604831640789683230174556440623073026811900236014477245382133062014821631152893829897692611106964558955134460480794092202226246351625569961925929573208935964302390747204250124554416009841 --attack wiener

得到flag

知道n,c,e,dp

n:n是两个大质数p和q的乘积,即n=pq。n的长度就是密钥长度。
在实际应用中,RSA密钥一般是1024位,重要场合则为2048位。n是公开的,任何人都可以获取。
但是,只有将n因数分解,才能算出p和q,这是一件非常困难的事情,因此RSA算法的安全性主要依赖于大整数的因数分解的难度

e:e也称为加密指数,是一个与φ(n)=(p-1)(q-1)互质的整数,且1< e < φ(n)。在实际应用中,e常常是65537。e是公开的,任何人都可以获取

c:c是密文,是明文m经过加密后的结果

dp: dp 是私钥中的指数,它满足 d * e ≡ 1 (mod p-1)。它用于加速 RSA 私钥的运算。

知道了e,n,c,dp,直接脚本运算即可
import gmpy2
from Crypto.Util.number import long_to_bytes
 
e = 65537
n = 12560945051080017158493499528974892180587722877115036753534197026090566088332985480712244911820684263887207667295414816331897571850721713798879301869899324045522734630476708240245009826264375506895615320038454402046687508807162501585118986842931362187606673072056471104479227651196794092064986511504154527262620989872948930084592891474710045243835563151518533397249183217231188332522146735105831257627853494431757700274528844720476874656763505456702216513258175393773027845394337647018718342122699241723786907959599821382393120389165169580881765481963538024971275497837029169708864713882861984019483842545848634444141
dp = 36302261382609013262047475941987267586775337866293058723897811686007051448114091924714278319431674216361514661901212473496243050240435008893097637649992237368934811237034909018690652463592988599205032019025052186318713297420200767796819265085971841695500067948907582568294148615074938439103021055978269964297
 
c = 481276874705898312803558995209125982254509211391874407382766686849699272120795145472927768361473473084128938701315637963972180935484971883683765843292157174626912821208631347384624209240874816472425432007260841591533188703845143836453156674747281763256117497394797728555592385683071532545246090745826127503006520474805352353596870069941373282166775026558370345676826215060794137541810380972329196005365070159805408347257613177031073686404818971161389159260159362034290780843441143423265275416134011539654571302523700183763316561375199470262290989098534518429904466417110355689124891054656161549790214206574584154648
 
for i in range(1, e): #循环遍历从 1 到 e-1 的整数
    if (dp * e - 1) % i == 0: #检查 (dp * e - 1) 是否可以被 i 整除
        if n % (((dp * e - 1) // i) + 1) == 0: #检查 n 是否可以被 (((dp * e - 1) // i) + 1) 整除。如果可以,表示找到了两个素数 p 和 q,它们的乘积等于 n
            p = ((dp * e - 1) // i) + 1 #计算得到两个素数 p 和 q
            q = n // (((dp * e - 1) // i) + 1)  #计算得到两个素数 p 和 q
            d = gmpy2.invert(e, (p-1)*(q-1)) #使用 gmpy2 库计算 RSA 私钥 d
            m = pow(c, d, n) #计算获得明文消息 m

print(long_to_bytes(m)) #将得到的明文消息 m 转换成字节并输出

知道p,q,c,dp,dq

知道p,q,c,dp,dq,这个时候我们不要e值也可以解密

p 和 q:这是两个大素数,它们的乘积(n = p * q)用作RSA的模数

c:这是RSA算法中的密文,表示经过加密后的消息。

dp 和 dq:这是与私钥相关的两个指数,dp 表示 d 模(p-1)的逆,dq 表示 d 模(q-1)的逆

其中 d 是私钥的指数。这两个参数有助于加速解密操作

import gmpy2
from Crypto.Util.number import long_to_bytes
p = 8637633767257008567099653486541091171320491509433615447539162437911244175885667806398411790524083553445158113502227745206205327690939504032994699902053229
q = 12640674973996472769176047937170883420927050821480010581593137135372473880595613737337630629752577346147039284030082593490776630572584959954205336880228469
dp = 6500795702216834621109042351193261530650043841056252930930949663358625016881832840728066026150264693076109354874099841380454881716097778307268116910582929
dq = 783472263673553449019532580386470672380574033551303889137911760438881683674556098098256795673512201963002175438762767516968043599582527539160811120550041
c = 24722305403887382073567316467649080662631552905960229399079107995602154418176056335800638887527614164073530437657085079676157350205351945222989351316076486573599576041978339872265925062764318536089007310270278526159678937431903862892400747915525118983959970607934142974736675784325993445942031372107342103852
I = gmpy2.invert(q,p) #这里使用了 gmpy2.invert 函数计算了 q 在模 p 下的乘法逆元
m1 = pow(c,dp,p) #这里计算了密文 c 在模 p 下使用 dp 指数解密得到的值
m2 = pow(c,dq,q) #这里计算了密文 c 在模 p 下使用 dq 指数解密得到的值
m = (((m1-m2)*I)%p)*q+m2 #这个公式涉及到了使用 中国剩余定理来组合模 p 和模 q 下的解密结果以得到最终的明文消息
print(long_to_bytes(m)) #将得到的明文消息 m 转换成字节并输出

共模攻击,知道多组c,e,n

共模攻击是一种RSA加密算法的攻击方法,其中攻击者在同一个模数 n 下使用多组不同的公钥来加密相同的明文,然后通过观察多个密文之间的关系来推断出密钥信息

在RSA加密算法中,公钥由模数n和指数e组成,私钥由模数n和指数d组成。当使用相同的模数n进行加密时,攻击者可以利用共模攻击来推导出私钥

假设攻击者拥有两个不同的公钥:(n, e1)和(n, e2),并且对同一消息进行了加密。攻击者可以使用扩展欧几里得算法来计算出e1和e2的最大公约数gcd,如果gcd为1,则无法进行共模攻击。然而,如果gcd不为1,则攻击者可以使用扩展欧几里得算法进一步计算出s和t,使得s * e1 + t * e2 = gcd。然后,攻击者可以使用模反演算法来计算出模反演系数r,其中r ≡ s^(-1) (mod gcd),最终可以使用r来恢复私钥d

import gmpy2
from Crypto.Util.number import getPrime,long_to_bytes
 
e1 = 2767
e2 = 3659
n = 21058339337354287847534107544613605305015441090508924094198816691219103399526800112802416383088995253908857460266726925615826895303377801614829364034624475195859997943146305588315939130777450485196290766249612340054354622516207681542973756257677388091926549655162490873849955783768663029138647079874278240867932127196686258800146911620730706734103611833179733264096475286491988063990431085380499075005629807702406676707841324660971173253100956362528346684752959937473852630145893796056675793646430793578265418255919376323796044588559726703858429311784705245069845938316802681575653653770883615525735690306674635167111
 
c1 = 20152490165522401747723193966902181151098731763998057421967155300933719378216342043730801302534978403741086887969040721959533190058342762057359432663717825826365444996915469039056428416166173920958243044831404924113442512617599426876141184212121677500371236937127571802891321706587610393639446868836987170301813018218408886968263882123084155607494076330256934285171370758586535415136162861138898728910585138378884530819857478609791126971308624318454905992919405355751492789110009313138417265126117273710813843923143381276204802515910527468883224274829962479636527422350190210717694762908096944600267033351813929448599
c2 = 11298697323140988812057735324285908480504721454145796535014418738959035245600679947297874517818928181509081545027056523790022598233918011261011973196386395689371526774785582326121959186195586069851592467637819366624044133661016373360885158956955263645614345881350494012328275215821306955212788282617812686548883151066866149060363482958708364726982908798340182288702101023393839781427386537230459436512613047311585875068008210818996941460156589314135010438362447522428206884944952639826677247819066812706835773107059567082822312300721049827013660418610265189288840247186598145741724084351633508492707755206886202876227
 
_,s1, s2 = gmpy2.gcdext(e1, e2)
m = pow(c1, s1, n) * pow(c2, s2, n) % n
print(long_to_bytes(m))

_,s1, s2 = gmpy2.gcdext(e1, e2)

gmpy2.gcdext(a, b)函数返回一个包含三个元素的元组,第一个元素是a和b的最大公约数,第二个和第三个元素则是满足as + bt = gcd(a, b)的s和t,_ 是一个占位符,表示我们不关心最大公约数的具体值,只关心 s1和s2

m = pow(c1, s1, n) * pow(c2, s2, n) % n

使用得到的s1和s2,计算出c1^s1 * c2^s2 mod n,这是共模攻击的核心部分

低解密指数攻击,e的值非常大

通常情况下,解密指数 d 的选择是基于安全性的考虑。较大的解密指数 d 能够提供更高的安全性,因为攻击者难以通过尝试所有可能的私钥值来破解密文。

然而,当使用较小的解密指数时,可能会存在低解密指数攻击的风险。低解密指数攻击的基本思想是,如果使用的解密指数 d 很小,那么攻击者可以通过计算 c^d modn的多次幂运算来尝试还原明文。如果d很小,那么尝试的次数相对较少,使得攻击者有可能通过枚举法或其他攻击手段成功还原出明文

当e很大时,相对的d就会很小,这里我们可以使用这个脚本

https://github.com/pablocelayes/rsa-wiener-attack

下载后我们要进入这个文件夹写脚本,因为我们要调用第三方脚本

import hashlib
import RSAwienerHacker
N = 101991809777553253470276751399264740131157682329252673501792154507006158434432009141995367241962525705950046253400188884658262496534706438791515071885860897552736656899566915731297225817250639873643376310103992170646906557242832893914902053581087502512787303322747780420210884852166586717636559058152544979471
e = 46731919563265721307105180410302518676676135509737992912625092976849075262192092549323082367518264378630543338219025744820916471913696072050291990620486581719410354385121760761374229374847695148230596005409978383369740305816082770283909611956355972181848077519920922059268376958811713365106925235218265173085
d = RSAwienerHacker.hack_RSA(e,N) #调用RSAwienerHacker脚本,找到d的值
flag = "flag{" + hashlib.md5(hex(d)).hexdigest() + "}" #创建一个字符串,该字符串以 "flag{" 开始,然后是私钥 d 的 MD5 哈希值
print(flag)

这里要用python2跑,用python3跑会报错

总结

最好要深入理解rsa的加解密方式,多刷题,了解各种攻击方式,这样才能成为大佬

相关推荐
TazmiDev1 天前
[极客大挑战 2019]BabySQL 1
服务器·数据库·安全·web安全·网络安全·密码学·安全威胁分析
吾即是光3 天前
[HNCTF 2022 Week1]你想学密码吗?
ctf
吾即是光3 天前
[NSSCTF 2022 Spring Recruit]factor
ctf
吾即是光3 天前
[LitCTF 2023]easy_math (中级)
ctf
看星猩的柴狗3 天前
密码学原理技术-第九章-椭圆曲线加密算法
密码学
吾即是光4 天前
[HNCTF 2022 Week1]baby_rsa
ctf
TazmiDev4 天前
【长城杯】Web题 hello_web 解题思路
安全·web安全·网络安全·密码学·php
白帽黑客26594 天前
入门网络安全工程师要学习哪些内容【2025年寒假最新学习计划】
linux·python·web安全·网络安全·系统安全·密码学·ddos