[阶段性总结] Crypto个人出题经历整理
发布时间:
目录
前言
AI时代确实很不适应,加上本身就菜,做不到像其他师傅那样可以抗着AI强度出大家都解决不了的0解题,简单总结一下自己一直以来在密码题上面的贡献,感觉也该找找新的地方做事了。
我很喜欢ctf这种特别的学习模式,不同于教科书一个原理接一个原理的死板学习法,通过实际调试代码能够快速掌握到相关知识点。因为没有人带着学习,自己打了很长一段时间的ctftime那种全国性质的赛事,大部分都是用ctfd起一个简单平台,题目也是线性提升,即使什么都不懂也能快速融入ctf成为不爆0的选手。
其中我很喜欢一类题目:整体处理得干净明了,没有太多前置知识,考点也不复杂化(俗称套娃),选手一眼能看出考点,然后学习并解出,看到选手们根据题目一点一点对上思路,然后解出对于我来说会感觉很欣慰(?),不过后期的AI时代大家不需要理解原理就能解出,甚至是wp也用AI自动生成,也许从这个时候开始对于我来说ctf的意义已经被AI吞噬干净了吧。
basectf2024
BaseCTF应该是我自认为开始比较有代表性的出题经历,前面像tpcup、vyctf等比赛只是单纯引用一些以前学到的题目,当时出了ez_math、mid_math、hard_math,这些算是开始按照自己的理解在出题, 其中hard_math基于LLL原理进行了简单的延伸,主要只是展示一下mid_math的出题思路,出题思路源于代数中的行列式:
mid_math-basectf2024
import numpy as np
from Crypto.Util.number import *
a, b, c, d = [getPrime(128) for _ in range(4)]
point1 = a * d
point2 = b * c
matrix2 = [[0, a, b], [0, c, d]]
flag = b"flag{test_flag}"
flag = bytes_to_long(flag)
def randomArray():
upper = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
low = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
for i in range(3):
for j in range(i+1, 3):
upper[i][j] = getPrime(128)
low[j][i] = getPrime(128)
result = np.array(upper) @ np.array(low)
return result
A = np.array([[flag, 0, 0]] + matrix2)
B = randomArray()
C = randomArray()
MAT = C @ A @ B
print(point1)
print(point2)
print(MAT)
'''
65540596822333029826884315503808996273733737079814345540607878287618419734231
45151244176940366132774311848077675849486332018843894072137609985463616792271
[[9259505595451159514948336330303511539525155092949382077995385373332083424570340733825203563332256599256361679775371565817159463557158551820090084800254999338417057682355404780422980119717238594927467956675771042145306399815569005775907169857728757334979422594358
3700462282298785820527479428312072678870010244861115107206951164684911761755437333209293039456840068340334559453608012512177623936248784897843503284633804083281388001236742261832974291349480314135560368365574114042082002559069958228523318326290833422846224288247
20791012146351643571145217310876690226642338279942557085580439219377325884045305279931904540467264182713135410067252835618936836675270813727053937054168296298149405902638242278868020381541490973458957704137657413376043351193]
[3802535350808074374431476757195874789213113083310705049856269457737583463559458126494122484246497049005001474007088865512110432486291568737501434666990689483191924384489484665070592656641925905986397402822195880143437724155134584374613878027218950975919679551229
1519642544380087919293814751485424198320747098741960781639133554268321708273309194651985562222274023623071346914239982055028526526058064787882720065775210796950963778381575914964024929110539407721461321785325399699126116201001806816030960662346173275101476487421
8538097185709421082644083672229287227818939415260987123718318427750267353075860559170390896769087600458156859498331152566368881938040799840806164389020986990994328370205184734637870147251004626759120887684269603636183629300]
[17987668490992083132878642797176089621188858356259455169173987325310681186627844776077058221612169421636403546746899152917309634315569997105261046388995579843528014810244648968375990949478033964619008761814039733347955609163
7188579142941521685422767412932555782658469950638690886255638896617687421517941457682493542615460990114218059246938237257830976937359020731335958068934235967457123039874441635435388736524907036941379695243043923900290273902
40388963560266769813551191613694768219344365780650048155838802242681775019274045964917142477325170274191702615504062392461666558731638338001971723737440974198823443420018559746335727687]]
'''
已知存在一个矩阵A:
\[A=\begin{bmatrix} flag & 0 & 0 \\ 0 & a & b \\ 0 & c & d \end{bmatrix}\]其中 a, b, c, d 为随机数, 我们明显可以将其看作一个分块矩阵:
\[A=\begin{bmatrix} flag & 0 \\ 0 & A_{abcd} \end{bmatrix}, \\ A_{abcd}=\begin{bmatrix} a & b \\ c & d \end{bmatrix}\]即对A求行列式有:
\[|A|=|flag|*|A_{abcd}|\]对于二阶矩阵我们可以计算:
\[|A_{abcd}|=a*d-b*c\]其中已给出point1, point2, 我们可以计算出矩阵B、C的行列式, 即只需要知道矩阵的行列式即可求出flag, 对于矩阵B有:
\[B = B_{upper} * B_{low} , C = C_{upper} * C_{low}\]其中upper为主对角线为1的上三角矩阵, low为主对角线为1的下三角矩阵, 易得知:
\[|B|=1 , |C|=1\]又有:
\[MAT = C * A * B\]故有:
\[|MAT| = |A|\]我们可以通过以下方式计算flag:
point1 = 65540596822333029826884315503808996273733737079814345540607878287618419734231
point2 = 45151244176940366132774311848077675849486332018843894072137609985463616792271
MAT = [[9259505595451159514948336330303511539525155092949382077995385373332083424570340733825203563332256599256361679775371565817159463557158551820090084800254999338417057682355404780422980119717238594927467956675771042145306399815569005775907169857728757334979422594358,
3700462282298785820527479428312072678870010244861115107206951164684911761755437333209293039456840068340334559453608012512177623936248784897843503284633804083281388001236742261832974291349480314135560368365574114042082002559069958228523318326290833422846224288247,
20791012146351643571145217310876690226642338279942557085580439219377325884045305279931904540467264182713135410067252835618936836675270813727053937054168296298149405902638242278868020381541490973458957704137657413376043351193],
[3802535350808074374431476757195874789213113083310705049856269457737583463559458126494122484246497049005001474007088865512110432486291568737501434666990689483191924384489484665070592656641925905986397402822195880143437724155134584374613878027218950975919679551229,
1519642544380087919293814751485424198320747098741960781639133554268321708273309194651985562222274023623071346914239982055028526526058064787882720065775210796950963778381575914964024929110539407721461321785325399699126116201001806816030960662346173275101476487421,
8538097185709421082644083672229287227818939415260987123718318427750267353075860559170390896769087600458156859498331152566368881938040799840806164389020986990994328370205184734637870147251004626759120887684269603636183629300],
[17987668490992083132878642797176089621188858356259455169173987325310681186627844776077058221612169421636403546746899152917309634315569997105261046388995579843528014810244648968375990949478033964619008761814039733347955609163,
7188579142941521685422767412932555782658469950638690886255638896617687421517941457682493542615460990114218059246938237257830976937359020731335958068934235967457123039874441635435388736524907036941379695243043923900290273902,
40388963560266769813551191613694768219344365780650048155838802242681775019274045964917142477325170274191702615504062392461666558731638338001971723737440974198823443420018559746335727687]]
from sage.all import *
from Crypto.Util.number import *
print(long_to_bytes(det(matrix(MAT)) // (point1 - point2)))
# b'BaseCTF{E439646E-1768-18B3-DC4B-483C40C5340C}'
litctf2025
这场比赛时当时生活正面临着极大的困境,出题思路阻塞严重,庆幸的是自己对密码算法不再是只能简单使用了,当时出了ez_math, new_bag两题,ez_math基于rsa原理将数扩展到了矩阵,new_bag则是一个简单的背包问题,我想考察BKZ的基本应用,不过读的论文太早了,自己在限制的时候也限制得比较小,大家采用了各种不同解法完成题目,让我也学习到了好多新的知识,主要只简单举一下ez_math:
ez_math
from sage.all import *
from Crypto.Util.number import *
from uuid import uuid4
flag = b'LitCTF{'+ str(uuid4()).encode() + b'}'
flag = bytes_to_long(flag)
len_flag = flag.bit_length()
e = 65537
p = getPrime(512)
P = GF(p)
A = [[flag, getPrime(len_flag)],
[getPrime(len_flag), getPrime(len_flag)]]
A = matrix(P, A)
B = A ** e
print(f"e = {e}")
print(f"p = {p}")
print(f"B = {list(B)}".replace('(', '[').replace(')', ']'))
# e = 65537
# p = 8147594556101158967571180945694180896742294483544853070485096002084187305007965554901340220135102394516080775084644243545680089670612459698730714507241869
# B = [[2155477851953408309667286450183162647077775173298899672730310990871751073331268840697064969968224381692698267285466913831393859280698670494293432275120170, 4113196339199671283644050914377933292797783829068402678379946926727565560805246629977929420627263995348168282358929186302526949449679561299204123214741547], [3652128051559825585352835887172797117251184204957364197630337114276860638429451378581133662832585442502338145987792778148110514594776496633267082169998598, 2475627430652911131017666156879485088601207383028954405788583206976605890994185119936790889665919339591067412273564551745588770370229650653217822472440992]]
rsa使用p,q计算n作为公钥,这里将p直接当作n使用,2×2 矩阵下的群会进化为一般线性群,有
\[|GL(2,p)|=(p^2−1)(p^2−p)\]可以直接计算:
from sage.all import *
from Crypto.Util.number import *
e = 65537
p = 8147594556101158967571180945694180896742294483544853070485096002084187305007965554901340220135102394516080775084644243545680089670612459698730714507241869
B = [[2155477851953408309667286450183162647077775173298899672730310990871751073331268840697064969968224381692698267285466913831393859280698670494293432275120170, 4113196339199671283644050914377933292797783829068402678379946926727565560805246629977929420627263995348168282358929186302526949449679561299204123214741547], [3652128051559825585352835887172797117251184204957364197630337114276860638429451378581133662832585442502338145987792778148110514594776496633267082169998598, 2475627430652911131017666156879485088601207383028954405788583206976605890994185119936790889665919339591067412273564551745588770370229650653217822472440992]]
phi= (p**2-1) * (p**2-p)
d=inverse(e,phi)
P = GF(p)
B = matrix(P, B)
B =B **d
print(long_to_bytes(int(B[0][0])))
#b'LitCTF{13dd217e-9a67-4093-8a1b-d2592c45ba82}'
考虑到叫ez_math,当时留了个口,即使只知道rsa也是可以通过p-1做出来的,因为(p^2-1)=(p+1)(p-1), 选择参数时直接选在了(p-1)上。
lilctf2025
这次出题差不多完成了自己一直以来的想法,共计ez_math,mid_math两题,同样的思路在ez_math中强调代数中的特征向量,然后将mid_math作为主要考题。
mid_math-lilctf2025
from sage.all import *
from Crypto.Util.number import *
from tqdm import tqdm
from random import randint
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
flag = b'LILCTF{test_flag}'
p = getPrime(64)
P = GF(p)
key = randint(2**62, p)
def mul(vector, c):
return [vector[0]*c, vector[1]*c, vector[2]*c, vector[3]*c, vector[4]*c]
v1 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v2 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v3 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v4 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v5 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
a, b, c, d, e = getPrime(64), getPrime(64), getPrime(64), getPrime(64), 0
A = matrix(P, [v1, v2, v3, v4, v5])
B = matrix(P, [mul(v1,a), mul(v2,b), mul(v3, c), mul(v4, d), mul(v5, e)])
C = A.inverse() * B
D = C**key
key = pad(long_to_bytes(key), 16)
aes = AES.new(key,AES.MODE_ECB)
msg = aes.encrypt(pad(flag, 64))
print(f"p = {p}")
print(f'C = {[i for i in C]}'.replace('(', '[').replace(')', ']'))
print(f'D = {[i for i in D]}'.replace('(', '[').replace(')', ']'))
print(f"msg = {msg}")
#p = 14668080038311483271
#C = [[11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212], [4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914], [12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670], [6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413], [1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707]]
#D = [[1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044], [10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372], [9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427], [4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551], [3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395]]
#msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"
特征向量对于我来说一直很具有诱惑性,不同于特征值,通过特征向量降低矩阵幂运算是代数中一个有效的应用方式,题目本质上提供了一个模p下的5x5随机矩阵A,1x5随机向量e,B=A*e, 现在构造A到B的变换C,有D=C^key, 需要求key用于解出flag。
\[A*C=A*e=B\]可以看出本题是一个简单的离散对数问题,只是扩展到矩阵上,一般情况下直接求key的话算力开销很高,因为A到B的变换C进行了降维,无法求取行列式直接求离散对数拿到结果,但是我们可以反过来思考,变换C的本质是B=Ae,可以对角化,理论上试图求CC, 我们可以通过变换转换为
\[PAP^(-1)=C\]再计算
\[D=PA*e*eP^(-1)\]参考以上思路,我们可以通过特征向量重新构建一组矩阵,对角化基下,原矩阵幂运算
\[D=C^{key}\]被简化为各特征值的标量幂运算:
from sage.all import *
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from tqdm import tqdm
p = 14668080038311483271
C = [[11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212], [4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914], [12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670], [6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413], [1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707]]
D = [[1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044], [10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372], [9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427], [4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551], [3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395]]
msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"
P = GF(p)
C = matrix(P, C)
D = matrix(P, D)
C_eigen = matrix(GF(p), [vector(v) for eig, vecs, alg_mult in C.eigenvectors_left() for v in vecs])
C_Basis = C_eigen*C*C_eigen.inverse()
D_Basis = C_eigen*D*C_eigen.inverse()
n = []
m = []
for i in tqdm(range(5)):
if D_Basis[i, i] != 0 and C_Basis[i, i] != 0:
m_flag = discrete_log(P(D_Basis[i, i]), P(C_Basis[i, i]))
m.append(m_flag)
n_order = P(C_Basis[i, i]).multiplicative_order()
n.append(n_order)
key = crt(m, n)
key = pad(long_to_bytes(key), 16)
aes = AES.new(key,AES.MODE_ECB)
flag = aes.decrypt(msg)
print(flag)
# b'LILCTF{Are_y0u_5till_4wake_que5t1on_m4ker!}\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15'
mcctf
当时我记得好像出了一个ez_adic和ez_lattice,有点忘记了当时为什么没有上ez_padic的题目,当时同时在赶几个比赛,时间比较紧凑,ez_adic简单考了p进数,可以通过中国剩余定理直接求出,ez_lattice算是问题比较大的一题,所以重新整理了一类题目,不过当时出现了一些严重的非预期,不需要LLL即可得到flag。
ez_lattice
from sage.all import *
from Crypto.Util.number import *
from uuid import uuid4
_flag = b'mcctf{'+ str(uuid4()).encode() + b'}'
v = 10
len_flag = len(_flag)//v
flag = [bytes_to_long( _flag[len_flag*i:len_flag*(i+1)] if i+1<v else _flag[len_flag*i:] ) for i in range(v)]
n = getPrime(1024)
k = getPrime(320)
h = [(pow(i, -1, n) * k) % n for i in flag]
print('n =', n)
print('h =', h)
#n = 101058037881577920580002375991533361443161573598172872401436929130908010655347634552777064174546568575027746607488492099154312267724394462943506690144947830040343315369562513570527692894685015573081207528621819747699733304080614793374057284273387848357708739112531677281801323793173090364390370736467003582917
#h = [65238694465386915975309840141683615649716012162296959248563569592374718661731701206400896806537952397410032261877378530686978759625292099657084619983375773255755144153781457417211674091516407534881571548771997217431814162001948536903510439216595988996202297986689400937900640116301788349788192250828531102128, 32068430839487144716294122362877372185693231094331598085462142396101367356886620469702732239532304931830205469549873073426114974524345753699104479003686428551918989837919692818874041699167857438504796702427222322750641837975840611428682636854000155270647980139296254355022602725831316373502464385756822509507, 48045538813319220884490922546018868575029946351799253696566675045038593914426407040185597260237676046539039366729571890778376978523204708113947916683959718708951574554301326506033685253589648114421528029323355988178699924863264060114597711724540199556277259170518549061045104668001222710528847166168767224549, 75123995948950968391603159591624639368414690146643442871589447787252191810497231696845253418339980365162413355185111520483921331823652266243697559776174240013810488917495610598469082493126712875223200656678886780170706766832861117452830161713449328056483280254149803408052080851148204725932967126493489495389, 42343824812526091206264281076069981510653655573870037741842182418474719039360760283698683250943728878536765635239317776400805788012233353293622608102030711446811052604409325460820893599261928948443614050545507085890105202716250838690843035633052646253790841787346488131868389877735682981371140434336342163669, 37628269352817676029356094741613805749594096808670358767615556155530757612666502454541938594622721603313749449559343049177509738095358796943332112708078541782706675648358891508089680020008921913219362413211208103447607363315907316039312691994345309495981824477958094292712571053294153865686883217285045072976, 73095252261793287281307332092278639250704283375416144614680921495193800381044097746018933097918509033128787939821334122567907138901258104948978871755972801343922472074421943147663482695952043375035475082008819785709339730299175470843123204596279944349658280821397882576553260815905563725407952447607089415633, 8361412049519617647068414326705006074844850312921172001754491320379019244806566558657382200324831784028297924464089518976736942901670220750313172374617027089856976338617784260689863433040095122712749142220670740521573738642943802961154307094774904173422138158314888817191233893356292405644600013350124816088, 62352524946552767010792198212753557220884107271554779577796506420796971189249469475111704305722210605467500390830564039781429821148651682920442985923243637249469873109032519540399258091710648539664160224041270628257896923843424376128648420038693104506093124335294407330397776591999820661062819804224881373090, 45213281711383478082421649210599256239528608897580799695344082282030442480159495245224471785682835728050988008519768665616995327124516724160052452164034157361068252085880379351444661551341271829125924017634986473455126215443151677035338594360564464453084451500811436155828884254112395862897223575590665873227]
题目算比较简单的格密码题型,稍微在上面做了点变式,将flag切分后在维度进行了扩展,已知将flag切分为10段,对每段进行:
\[h_i = flag_i^{-1} \times k \mod{n}\]现在已知各行的h与n,我们可以构造一个矩阵求格计算出k,然后反代出flag,但是我忘记了因为各段之间没有联系,其实也可以直接分别将flag各段格出后再拼出flag:
from sage.all import *
from Crypto.Util.number import *
n = 101058037881577920580002375991533361443161573598172872401436929130908010655347634552777064174546568575027746607488492099154312267724394462943506690144947830040343315369562513570527692894685015573081207528621819747699733304080614793374057284273387848357708739112531677281801323793173090364390370736467003582917
h = [
65238694465386915975309840141683615649716012162296959248563569592374718661731701206400896806537952397410032261877378530686978759625292099657084619983375773255755144153781457417211674091516407534881571548771997217431814162001948536903510439216595988996202297986689400937900640116301788349788192250828531102128,
32068430839487144716294122362877372185693231094331598085462142396101367356886620469702732239532304931830205469549873073426114974524345753699104479003686428551918989837919692818874041699167857438504796702427222322750641837975840611428682636854000155270647980139296254355022602725831316373502464385756822509507,
48045538813319220884490922546018868575029946351799253696566675045038593914426407040185597260237676046539039366729571890778376978523204708113947916683959718708951574554301326506033685253589648114421528029323355988178699924863264060114597711724540199556277259170518549061045104668001222710528847166168767224549,
75123995948950968391603159591624639368414690146643442871589447787252191810497231696845253418339980365162413355185111520483921331823652266243697559776174240013810488917495610598469082493126712875223200656678886780170706766832861117452830161713449328056483280254149803408052080851148204725932967126493489495389,
42343824812526091206264281076069981510653655573870037741842182418474719039360760283698683250943728878536765635239317776400805788012233353293622608102030711446811052604409325460820893599261928948443614050545507085890105202716250838690843035633052646253790841787346488131868389877735682981371140434336342163669,
37628269352817676029356094741613805749594096808670358767615556155530757612666502454541938594622721603313749449559343049177509738095358796943332112708078541782706675648358891508089680020008921913219362413211208103447607363315907316039312691994345309495981824477958094292712571053294153865686883217285045072976,
73095252261793287281307332092278639250704283375416144614680921495193800381044097746018933097918509033128787939821334122567907138901258104948978871755972801343922472074421943147663482695952043375035475082008819785709339730299175470843123204596279944349658280821397882576553260815905563725407952447607089415633,
8361412049519617647068414326705006074844850312921172001754491320379019244806566558657382200324831784028297924464089518976736942901670220750313172374617027089856976338617784260689863433040095122712749142220670740521573738642943802961154307094774904173422138158314888817191233893356292405644600013350124816088,
62352524946552767010792198212753557220884107271554779577796506420796971189249469475111704305722210605467500390830564039781429821148651682920442985923243637249469873109032519540399258091710648539664160224041270628257896923843424376128648420038693104506093124335294407330397776591999820661062819804224881373090,
45213281711383478082421649210599256239528608897580799695344082282030442480159495245224471785682835728050988008519768665616995327124516724160052452164034157361068252085880379351444661551341271829125924017634986473455126215443151677035338594360564464453084451500811436155828884254112395862897223575590665873227
]
flag = b""
for i in h:
M = matrix(
[[n, 0],
[i, 1]]
)
reduced = M.LLL()
flag += long_to_bytes(abs(reduced[0,1]))
# b'mcctf{f670d7a5-80db-4b7a-86f3-466a0e1e7daf}'
当然我还忘记一个更大的问题,flag切分以后存在其中一段可以被预测,可以预测然后反代出k,进而计算flag:
from Crypto.Util.number import *
n = 101058037881577920580002375991533361443161573598172872401436929130908010655347634552777064174546568575027746607488492099154312267724394462943506690144947830040343315369562513570527692894685015573081207528621819747699733304080614793374057284273387848357708739112531677281801323793173090364390370736467003582917
h = [
65238694465386915975309840141683615649716012162296959248563569592374718661731701206400896806537952397410032261877378530686978759625292099657084619983375773255755144153781457417211674091516407534881571548771997217431814162001948536903510439216595988996202297986689400937900640116301788349788192250828531102128,
32068430839487144716294122362877372185693231094331598085462142396101367356886620469702732239532304931830205469549873073426114974524345753699104479003686428551918989837919692818874041699167857438504796702427222322750641837975840611428682636854000155270647980139296254355022602725831316373502464385756822509507,
48045538813319220884490922546018868575029946351799253696566675045038593914426407040185597260237676046539039366729571890778376978523204708113947916683959718708951574554301326506033685253589648114421528029323355988178699924863264060114597711724540199556277259170518549061045104668001222710528847166168767224549,
75123995948950968391603159591624639368414690146643442871589447787252191810497231696845253418339980365162413355185111520483921331823652266243697559776174240013810488917495610598469082493126712875223200656678886780170706766832861117452830161713449328056483280254149803408052080851148204725932967126493489495389,
42343824812526091206264281076069981510653655573870037741842182418474719039360760283698683250943728878536765635239317776400805788012233353293622608102030711446811052604409325460820893599261928948443614050545507085890105202716250838690843035633052646253790841787346488131868389877735682981371140434336342163669,
37628269352817676029356094741613805749594096808670358767615556155530757612666502454541938594622721603313749449559343049177509738095358796943332112708078541782706675648358891508089680020008921913219362413211208103447607363315907316039312691994345309495981824477958094292712571053294153865686883217285045072976,
73095252261793287281307332092278639250704283375416144614680921495193800381044097746018933097918509033128787939821334122567907138901258104948978871755972801343922472074421943147663482695952043375035475082008819785709339730299175470843123204596279944349658280821397882576553260815905563725407952447607089415633,
8361412049519617647068414326705006074844850312921172001754491320379019244806566558657382200324831784028297924464089518976736942901670220750313172374617027089856976338617784260689863433040095122712749142220670740521573738642943802961154307094774904173422138158314888817191233893356292405644600013350124816088,
62352524946552767010792198212753557220884107271554779577796506420796971189249469475111704305722210605467500390830564039781429821148651682920442985923243637249469873109032519540399258091710648539664160224041270628257896923843424376128648420038693104506093124335294407330397776591999820661062819804224881373090,
45213281711383478082421649210599256239528608897580799695344082282030442480159495245224471785682835728050988008519768665616995327124516724160052452164034157361068252085880379351444661551341271829125924017634986473455126215443151677035338594360564464453084451500811436155828884254112395862897223575590665873227
]
m0 = bytes_to_long(b"mcct")
k = (h[0] * m0) % n
flag = b""
for i in h:
m_i = long_to_bytes((pow(i, -1, n) * k) % n)
flag += m_i
print(flag)
#b'mcctf{f670d7a5-80db-4b7a-86f3-466a0e1e7daf}'
(关键我还忘记最开始怎么准备的exp了,以后有时间试着做一做)
Ttpcup2026
好像也背了点锅,主要出了ClairautDH,后来发现存在重大bug又重新补了一道ClairautDH-Revenge,本来打算出两道题来着,后来确实燃尽了。ClairautDH确实应该算脑洞打开的一题,记得以前和其他师傅聊过AI冲击下很难出适合新生但能防范AI的题目,队伍进行选拔时很难区分选手的真实实力。刚好前段时间有人找我设计密码系统涉及到DH密钥交换,麻省理工的积分决赛也打完不久,之前看比赛时顺便自己顺便用AI简单求了一下积分,没求出来,于是有了将微积分应用于密钥交换的灵感。
clairautdh-revenge
# task.py
import hashlib
from sympy import symbols, diff, simplify, Integer
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import long_to_bytes, bytes_to_long
import os
import random
from secret import flag, f
# 共享函数和flag
x, y = symbols('x y')
'''
Bob and Alice share a function f(x, y). Whenever they need to generate a shared secret key, they can temporarily use the following method.
We provide a simple example.
'''
#f = x*y + x + y
def AES_enc(msg, key):
key_hash = hashlib.sha256(long_to_bytes(key)).digest()
cipher = AES.new(key_hash[:16], AES.MODE_ECB)
return cipher.encrypt(pad(msg, AES.block_size))
def AES_dec(msg, key):
key_hash = hashlib.sha256(long_to_bytes(key)).digest()
cipher = AES.new(key_hash[:16], AES.MODE_ECB)
return unpad(cipher.decrypt(msg), AES.block_size)
# --- 第一步:Alice ---
alice_x = Integer(random.getrandbits(128))
f_x = diff(f, x).subs(x, alice_x)
print(f"\\partial F/ \\partial x: {simplify(f_x)}")
# --- 第二步:Bob ---
bob_y = Integer(random.getrandbits(128))
print(diff(f, y))
f_y = diff(f, y).subs(y, bob_y)
print(f"\\partial F/ \\partial y: {simplify(f_y)}")
# --- 第三步:Alice ---
secret_scalar = abs(int(diff(f_y, x).subs(x, alice_x)))
secret = AES_enc(flag, secret_scalar)
print(f"secret: {bytes_to_long(secret)}")
# --- 第四步:Bob ---
secret_scalar_bob = abs(int(diff(f_x, y).subs(y, bob_y)))
decrypted_flag = AES_dec(secret, secret_scalar_bob)
# 验证
assert decrypted_flag == flag
print('Bob get flag.')
题目基于偏微分的克莱罗定理,对于连续可微函数的混合偏导数相等,密钥交换需要有一个正向容易逆向困难的计算方式保障个人生成的密钥安全,这里的体现是函数微分容易积分困难。
已知各偏导数表达式很容易还原方程,进而解出共享密钥,故在公开偏微分方程时进行降纬处理,原题目就变成了已知两个微分投影求出原始函数。
相比数学方法,求取积分更倾向于找感觉,我们可以通过偏导函数未进行微分的另一维度去猜测原始函数的大概轮廓,从而进行还原,例如如下输出:
'''
\partial F/ \partial x: 1091410879207163423527614718617914148888*y**3 + 81254019297725586260762901144707066777558658481431202628329889377193662772828317753114607046787850201807677669860192 - 1/y
\partial F/ \partial y: 677492876058731304548321124430382899202536309194603058276767059068971723406326*x**2 + x/112915479343121884091386854071730483200422718199100509712794509844828620567721 + 25706026840600906043088759983422158599436354716595565472703098295727374823141571327891394051094542563877396658316435605977006168485141809993256872220352031815049004360456983843517338282006837806
secret: 29541001074036648052457017922472446898167742481813256589556212172626042913295772830659172788048290288920622639932811
Bob get flag.
'''
通过\(\partial F/ \partial x\)可以大致窥见函数与\(my^3 - n/y\)有关,从\(\partial F/ \partial y\)可以猜测函数形似\(mx^2 + x/n\), 我们可以激进地猜测函数大致为
\[mx^2y^3 - nx/y\]直接求导回推, 也可以谨慎对已知信息排查密钥(毕竟未进行取模运算),有个神奇的地方是gemini能够直接精准地还原函数(PS. 至今不明白它怎么知道我为了简化难度没带系数?函数选择是尽量精简过的而不是求导过程将复杂函数变简单的?)
以\(\partial F/ \partial y\)为例,保守计算可以猜测\(x^2\)处倾向为一个复合函数的计算结果,即:
\[dy = f′(g(x))⋅g′(x)⋅dx\]结合x的偏导可以猜测系数与\(3(y^2)\)具有相关性,故有其中的EXP如下所示:
import hashlib
from sympy import symbols, diff, root
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import long_to_bytes, bytes_to_long
def AES_dec(msg, key):
key_hash = hashlib.sha256(long_to_bytes(key)).digest()
cipher = AES.new(key_hash[:16], AES.MODE_ECB)
return unpad(cipher.decrypt(msg), AES.block_size)
def find_y(_y):
n = 3
while True:
if _y % n == 0:
q = _y // n
y = root(q, 2)
if y == int(y):
return y
n += 1
y = symbols("y")
f_x = 1091410879207163423527614718617914148888*y**3 + 81254019297725586260762901144707066777558658481431202628329889377193662772828317753114607046787850201807677669860192 - 1/y
secret = 29541001074036648052457017922472446898167742481813256589556212172626042913295772830659172788048290288920622639932811
_y = 677492876058731304548321124430382899202536309194603058276767059068971723406326
result = find_y(_y)
secret_scalar_bob = abs(int(diff(f_x, y).subs(y, result)))
decrypted_flag = AES_dec(long_to_bytes(secret), secret_scalar_bob)
print(decrypted_flag)