SCTF2024-crypto
因为解出了所有密码,故发一篇wp吗,都是脚本,没讲原理。(还有一题问卷,其他都没做(出来)
(虽然都是简单题,虽然都是能找到脚本的题,虽然我都不会,虽然我不会格,但你就说我解没解没解出来吧)
(感谢鸡块师傅,太崇拜了)
Signin
xyctf中的factor3
(只能说,xyctf的含金量还在提高)
鸡块师傅的博客:2024-XYCTF-wp-crypto | 糖醋小鸡块的blog (tangcuxiaojikuai.xyz)
能够得(猜)到如下式子:
\(ed=1+k(p^2+p+1)(q^2+q+1)\)
\(ed=1+k*(N^2+p^2q+p^2+pq^2+N+p+q^2+q+1)\\\)
合并同类型得到:
\(ed=1+k[n^2+n+1+n(p+q)+(p+q)^2+(p+q)^2-2n+p+q]\)
最后得到:
\(ed=1+k[n^2-n+1+(n+1)(p+q)+(p+q)^2]\)
令x=k,y=p+q,得到模e下的多项式
\(f=1+x[n^2-n+1+(n+1)y+y^2]\)
"""from sage.all import *
from Crypto.Util.number import *
from hashlib import md5
class RSA():
def __init__(self, nbits):
self.nbits = nbits
self.p, self.q = self.getPrimes()
self.n = self.p*self.q
self.Gift = self.Gift()
self.priv, self.pub = self.keyGen()
def getPrimes(self):
nbits = self.nbits
p = random_prime(2^(nbits-1),lbound=2^(nbits-2))
q = random_prime(2^(nbits-1),lbound=2^(nbits-2))
while p == q:
q = random_prime(2^(nbits-1),lbound=2^(nbits-2))
return p,q
def Gift(self):
p,q = self.p, self.q
return (p^2 + p + 1)*(q^2 + q + 1)
def keyGen(self):
nbits = self.nbits
while True:
d = randint(2^(nbits//4),2^(nbits//2))
if gcd(d,self.Gift) != 1:
d = randint(2^(nbits//4),2^(nbits//2))
e = pow(d,-1,self.phi)
return (self.p,self.q,self.n,e,d),(self.n,e)
RRR = RSA(512)
bp = long_to_bytes(int(RRR.p))
FLAG = 'SCTF{'+md5(bp).hexdigest()+'}'
print(f'N = {RRR.n}')
print(f'e = {RRR.pub[1]}')"""
from Crypto.Util.number import *
from gmpy2 import *
import itertools
def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i in range(m + 1):
base = N ^ (m - i) * f ^ i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)
B, monomials = G.coefficient_matrix()
monomials = vector(monomials)
factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1 / factor)
H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
n = 32261421478213846055712670966502489204755328170115455046538351164751104619671102517649635534043658087736634695616391757439732095084483689790126957681118278054587893972547230081514687941476504846573346232349396528794022902849402462140720882761797608629678538971832857107919821058604542569600500431547986211951
e = 334450817132213889699916301332076676907807495738301743367532551341259554597455532787632746522806063413194057583998858669641413549469205803510032623432057274574904024415310727712701532706683404590321555542304471243731711502894688623443411522742837178384157350652336133957839779184278283984964616921311020965540513988059163842300284809747927188585982778365798558959611785248767075169464495691092816641600277394649073668575637386621433598176627864284154484501969887686377152288296838258930293614942020655916701799531971307171423974651394156780269830631029915305188230547099840604668445612429756706738202411074392821840
PR.<x,y>=PolynomialRing(Zmod(e))
f = 1 + x*(n^2-n+1 + (n+1)*y + y^2)
res = small_roots(f, (2^256,2^512), m=2, d=3)
pplusq = int(res[0][1])
pminusq = iroot(pplusq^2-4*n,2)[0]
p = (pplusq + pminusq) // 2
q = n // p
from hashlib import md5
print('SCTF{'+md5(long_to_bytes(int(p))).hexdigest()+'}') #SCTF{12899cda850fc484de8bce978839620d}
print('SCTF{'+md5(long_to_bytes(int(q))).hexdigest()+'}')
不完全阻塞干扰
一部分加密代码
msg = bytes_to_long(FLAG)
n = p^5*q^2
phi = p^4*(p-1)*q*(q-1)
e = 65537
d = inverse(d,phi)
c = pow(m,e,n)
证书分析,拿有效数据解base64转hex,中间一大段A就是d缺失的数据
-----BEGIN RSA PRIVATE KEY-----
MIIIFQKCA4AGfwqk6XSmOh/+jVwj5dPEMWU65BzHRvMF9iqfGT8iSGy37xsnVjSB
j0bQdSpROeGZGCcfoNfSe8Zg0rckFNCOpSyIN/lJx7rswwKboxcn7zvxINmSbALX
QS8YfpjcVt0HuYfSzBka1WFkoUTyiy9woV0QVYik8n+7KJH8UnvWiQpfeVtcSEdq
a/nftnt+Hrx7Gwhs0otYxolVv99E7M4R/6zfZUVRsVm3gyBAzCjujr6kj4Zy1T49
6I/Pu1+ydrUDiA3TTVmTM13fjMuWwbTXn1AtchBHZa2cKxhYoXrz1b5E+jy/S47r
lCqjlCo4cdLGWscCiRI/wun5sly/y9eEEJYGD6UEw6B7WRSTxkyI0LtFKFqFtffV
nbmPqgDCzT+7Y9pZkgXxyrDfUs97QxoO5KfjVpZUbOnQPvWV7O6S0hQskul9J0ST
lwNFW0xw3sJ8Mh7GuDwCliLoOp4NVdCyWNldTmEpGGXdp23GGfzpV3mQQpxud+nU
B4HjsvRJcBuD6LDGxm6zgPlkc+XUIu/uiysOiLcWsAp5ydUUyjrZ0t7lJmCf+VQX
MqQZjRG52/uy5Vwk2A6lItB4bjNV8jYGpdOKct5O78i2v8SCJIooYstp2ODj0xZZ
fanYCCi+hQVPrxX8NpyqyvuBXGlzwXGUBoPVahoZZ7Cbf/o/vlsuCGmXWdhNcWA/
UWRHaWuycyKmnzn2yiU+ANyVVdX5cygHDEZ/NmPMSJqtEw8oxC81v4jFcZIKuSrL
j3XQPjWnUQPFvZbwYclr0Cr24dGRsN0WS8chN3AD7b9dPvZaXpBGOFNWtSFiO+43
8WSFCgp6+w7U5+i9mv4SmPfVMrya2UGBLTMq7OddHMyx/2n9QrMfJIrledng1qFL
BUbnhLqUDjK9AcOV34/0WEBARitUefoHM21QPcMy5w/AbZRjKX/AQrYj1W+H76pS
WptYDjFNkNEhGJPtQHomUI3qoKE8nujJArnhw6Av6aUUUsAu573MhcDv9jiR4kcD
vSZdnJ2/RW4q+UCVOLzg/sx+urICZqqrBsdmw+ps2py5ul4dAkt9w9c+dvajMxl7
rYfE+zTVZaABSqxygl5Brc/q2tyHrO9ArYS3xVaRq61WG+BVDqCpiEcMQnQyrLj+
srnS0lmPsgibuRu9nLGZ6JLTYWTYvz7NVFdqlxNAR6EtqEIHSFu05QIDAQABAoID
gAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoGBAIBj0KIYduXOHiEB
wgAVUpBm7Zl2iC0QAqKe/g8v38wnQ/yaS1tlHMlxCGmeyi+x89kxdbrjQ+fJLkpB
xy0F5XAZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAoGBAOTw/kn5rhSSwJegqYj6cYdmJf5PzgWw
IE8f30PsZLTaxpnSjhZu/fx1YtGeWMNJPZEANlzyhAtGwPbujZZIBxcP8sE8TrgB
LsqzeGKjkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA
30
82 0815
n
02 82 0380
067f0aa4e974a63a1ffe8d5c23e5d3c431653ae41cc746f305f62a9f193f22486cb7ef1b275634818f46d0752a5139e19918271fa0d7d27bc660d2b72414d08ea52c8837f949c7baecc3029ba31727ef3bf120d9926c02d7412f187e98dc56dd07b987d2cc191ad56164a144f28b2f70a15d105588a4f27fbb2891fc527bd6890a5f795b5c48476a6bf9dfb67b7e1ebc7b1b086cd28b58c68955bfdf44ecce11ffacdf654551b159b7832040cc28ee8ebea48f8672d53e3de88fcfbb5fb276b503880dd34d5993335ddf8ccb96c1b4d79f502d72104765ad9c2b1858a17af3d5be44fa3cbf4b8eeb942aa3942a3871d2c65ac70289123fc2e9f9b25cbfcbd7841096060fa504c3a07b591493c64c88d0bb45285a85b5f7d59db98faa00c2cd3fbb63da599205f1cab0df52cf7b431a0ee4a7e35696546ce9d03ef595ecee92d2142c92e97d2744939703455b4c70dec27c321ec6b83c029622e83a9e0d55d0b258d95d4e61291865dda76dc619fce9577990429c6e77e9d40781e3b2f449701b83e8b0c6c66eb380f96473e5d422efee8b2b0e88b716b00a79c9d514ca3ad9d2dee526609ff9541732a4198d11b9dbfbb2e55c24d80ea522d0786e3355f23606a5d38a72de4eefc8b6bfc482248a2862cb69d8e0e3d316597da9d80828be85054faf15fc369caacafb815c6973c171940683d56a1a1967b09b7ffa3fbe5b2e08699759d84d71603f516447696bb27322a69f39f6ca253e00dc9555d5f97328070c467f3663cc489aad130f28c42f35bf88c571920ab92acb8f75d03e35a75103c5bd96f061c96bd02af6e1d191b0dd164bc721377003edbf5d3ef65a5e9046385356b521623bee37f164850a0a7afb0ed4e7e8bd9afe1298f7d532bc9ad941812d332aece75d1cccb1ff69fd42b31f248ae579d9e0d6a14b0546e784ba940e32bd01c395df8ff4584040462b5479fa07336d503dc332e70fc06d9463297fc042b623d56f87efaa525a9b580e314d90d1211893ed407a26508deaa0a13c9ee8c902b9e1c3a02fe9a51452c02ee7bdcc85c0eff63891e24703bd265d9c9dbf456e2af9409538bce0fecc7ebab20266aaab06c766c3ea6cda9cb9ba5e1d024b7dc3d73e76f6a333197bad87c4fb34d565a0014aac72825e41adcfeadadc87acef40ad84b7c55691abad561be0550ea0a988470c427432acb8feb2b9d2d2598fb2089bb91bbd9cb199e892d36164d8bf3ecd54576a97134047a12da84207485bb4e5
e
02 03
010001
d....
p
AoGBAIBj0KIYduXOHiEBwgAVUpBm7Zl2iC0QAqKe/g8v38wnQ/yaS1tlHMlxCGmeyi+x89kxdbrjQ+fJLkpBxy0F5XAZQ
02 8181
008063d0a21876e5ce1e2101c20015529066ed9976882d1002a29efe0f2fdfcc2743fc9a4b5b651cc97108699eca2fb1f3d93175bae343e7c92e4a41c72d05e57019
q
AoGBAOTw/kn5rhSSwJegqYj6cYdmJf5PzgWwIE8f30PsZLTaxpnSjhZu/fx1YtGeWMNJPZEANlzyhAtGwPbujZZIBxcP8sE8TrgBLsqzeGKjk
02 8181
00e4f0fe49f9ae1492c097a0a988fa71876625fe4fce05b0204f1fdf43ec64b4dac699d28e166efdfc7562d19e58c3493d9100365cf2840b46c0f6ee8d964807170ff2c13c4eb8012ecab37862a3
分析出来之后就是p高位和q高位,二元copper就行了
from Crypto.Util.number import *
from gmpy2 import *
import itertools
def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i in range(m + 1):
base = N ^ (m - i) * f ^ i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)
B, monomials = G.coefficient_matrix()
monomials = vector(monomials)
factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1 / factor)
H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
n=0x067f0aa4e974a63a1ffe8d5c23e5d3c431653ae41cc746f305f62a9f193f22486cb7ef1b275634818f46d0752a5139e19918271fa0d7d27bc660d2b72414d08ea52c8837f949c7baecc3029ba31727ef3bf120d9926c02d7412f187e98dc56dd07b987d2cc191ad56164a144f28b2f70a15d105588a4f27fbb2891fc527bd6890a5f795b5c48476a6bf9dfb67b7e1ebc7b1b086cd28b58c68955bfdf44ecce11ffacdf654551b159b7832040cc28ee8ebea48f8672d53e3de88fcfbb5fb276b503880dd34d5993335ddf8ccb96c1b4d79f502d72104765ad9c2b1858a17af3d5be44fa3cbf4b8eeb942aa3942a3871d2c65ac70289123fc2e9f9b25cbfcbd7841096060fa504c3a07b591493c64c88d0bb45285a85b5f7d59db98faa00c2cd3fbb63da599205f1cab0df52cf7b431a0ee4a7e35696546ce9d03ef595ecee92d2142c92e97d2744939703455b4c70dec27c321ec6b83c029622e83a9e0d55d0b258d95d4e61291865dda76dc619fce9577990429c6e77e9d40781e3b2f449701b83e8b0c6c66eb380f96473e5d422efee8b2b0e88b716b00a79c9d514ca3ad9d2dee526609ff9541732a4198d11b9dbfbb2e55c24d80ea522d0786e3355f23606a5d38a72de4eefc8b6bfc482248a2862cb69d8e0e3d316597da9d80828be85054faf15fc369caacafb815c6973c171940683d56a1a1967b09b7ffa3fbe5b2e08699759d84d71603f516447696bb27322a69f39f6ca253e00dc9555d5f97328070c467f3663cc489aad130f28c42f35bf88c571920ab92acb8f75d03e35a75103c5bd96f061c96bd02af6e1d191b0dd164bc721377003edbf5d3ef65a5e9046385356b521623bee37f164850a0a7afb0ed4e7e8bd9afe1298f7d532bc9ad941812d332aece75d1cccb1ff69fd42b31f248ae579d9e0d6a14b0546e784ba940e32bd01c395df8ff4584040462b5479fa07336d503dc332e70fc06d9463297fc042b623d56f87efaa525a9b580e314d90d1211893ed407a26508deaa0a13c9ee8c902b9e1c3a02fe9a51452c02ee7bdcc85c0eff63891e24703bd265d9c9dbf456e2af9409538bce0fecc7ebab20266aaab06c766c3ea6cda9cb9ba5e1d024b7dc3d73e76f6a333197bad87c4fb34d565a0014aac72825e41adcfeadadc87acef40ad84b7c55691abad561be0550ea0a988470c427432acb8feb2b9d2d2598fb2089bb91bbd9cb199e892d36164d8bf3ecd54576a97134047a12da84207485bb4e5
e=0x10001
c=145554802564989933772666853449758467748433820771006616874558211691441588216921262672588167631397770260815821197485462873358280668164496459053150659240485200305314288108259163251006446515109018138298662011636423264380170119025895000021651886702521266669653335874489612060473962259596489445807308673497717101487224092493721535129391781431853820808463529747944795809850314965769365750993208968116864575686200409653590102945619744853690854644813177444995458528447525184291487005845375945194236352007426925987404637468097524735905540030962884807790630389799495153548300450435815577962308635103143187386444035094151992129110267595908492217520416633466787688326809639286703608138336958958449724993250735997663382433125872982238289419769011271925043792124263306262445811864346081207309546599603914842331643196984128658943528999381048833301951569809038023921101787071345517702911344900151843968213911899353962451480195808768038035044446206153179737023140055693141790385662942050774439391111437140968754546526191031278186881116757268998843581015398070043778631790328583529667194481319953424389090869226474999123124532354330671462280959215310810005231660418399403337476289138527331553267291013945347058144254374287422377547369897793812634181778309679601143245890494670013019155942690562552431527149178906855998534415120428884098317318129659099377634006938812654262148522236268027388683027513663867042278407716812565374141362015467076472409873946275500942547114202939578755575249750674734066843408758067001891408572444119999801055605577737379889503505649865554353749621313679734666376467890526136184241450593948838055612677564667946098308716892133196862716086041690426537245252116765796203427832657608512488619438752378624483485364908432609100523022628791451171084583484294929190998796485805496852608557456380717623462846198636093701726099310737244471075079541022111303662778829695340275795782631315412134758717966727565043332335558077486037869874106819581519353856396937832498623662166446395755447101393825864584024239951058366713573567250863658531585064635727070458886746791722270803893438211751165831616861912569513431821959562450032831904268205845224077709362068478
hp=0x008063d0a21876e5ce1e2101c20015529066ed9976882d1002a29efe0f2fdfcc2743fc9a4b5b651cc97108699eca2fb1f3d93175bae343e7c92e4a41c72d05e57019
hq=0x00e4f0fe49f9ae1492c097a0a988fa71876625fe4fce05b0204f1fdf43ec64b4dac699d28e166efdfc7562d19e58c3493d9100365cf2840b46c0f6ee8d964807170ff2c13c4eb8012ecab37862a3
hp=hp<<504
hq=hq<<408
PR.<x,y>=PolynomialRing(Zmod(n))
f = (hp+x)^5*(hq+y)^2-n
res = small_roots(f, (2^504,2^408), m=2, d=3)
p=int(hp+res[0][0])
q=int(hq+res[0][1])
print(long_to_bytes(int(pow(c,invert(e,p^4*(p-1)*q*(q-1)),n))))
#SCTF{0ne_4rgum3nt_1s_r0tt3n_0r4ng3s,_th3_wh0le_cert1fic4t3_1s_r0tt3n_0r4ng3s:XD}
Whisper
cert1.pem
-----BEGIN PUBLIC KEY-----
MIIBHjANBgkqhkiG9w0BAQEFAAOCAQsAMIIBBgKBgBtdT+CqZ4LiddTOEqbVdWLv
u+fbb1J3JVuJFym/oqGNPttJhD15iaN7lRa+LfjKk5BY5l9ktfsgcb6k9fjROSiV
syvwN32Z9PeZeRJeXbAc21CAocLWZcmsMbWCMCVJnJUTJ3uuXnqEbNJxxDluK6IZ
Ag5YqQVcsYoo02oAv3F7AoGAB59czGZXZ7SiV+XB/1bpgD3y5WUDAtqtQgEF/mck
R3Q70/C+ocRqSYeTLpqIbKh6ev13lqvx5WKcSYb+TyLonNznq7BmJEZRRqLitsqa
sxls6rdGeXTB3EVgiiAEEbKR/a+Z99gNzk2zVm9Kni5XTGIkzQfYBjjSj3ggvPS0
kUM=
-----END PUBLIC KEY-----
cert2.pem
-----BEGIN PUBLIC KEY-----
MIIBHjANBgkqhkiG9w0BAQEFAAOCAQsAMIIBBgKBgAccMk6HaUkxh8FfctXMaVcp
tISI7j+9AdsA1cR48Ix88yCTumF0UFHT6dFpUjqpFDgYH0dnmv9e3SKVD3Sh6xRD
MgqqXZf1wegbXvmj5pumaavExsS0BfUIimA6dPm874iCO0UjV0EUyBBgCDhygZb4
5eDUru7qt53YaDpy88AXAoGAB59czGZXZ7SiV+XB/1bpgD3y5WUDAtqtQgEF/mck
R3Q70/C+ocRqSYeTLpqIbKh6ev13lqvx5WKcSYb+TyLonNznq7BmJEZRRqLitsqa
sxls6rdGeXTB3EVgiiAEEbKR/a+Z99gNzk2zVm9Kni5XTGIkzQfYBjjSj3ggvPS0
kUM=
-----END PUBLIC KEY-----
题目描述
Two public key certificates were monitored. And Mr. Dual intercepted a ciphertext. Just when he was in the rough, a Careless Whisper told that the length of a key parameter is carelessly set to 345 bits.
密文的16进制数据
15aadf434f05fc47e06f6997a554c1de7cecdad1faf40a74bc7c6d7c4c5fa533fe2c605bcde6acaa0ee3576f607be6503831b33d54928eaad1f8d64187cff8f4f1a67ff6d846715bfc4795b22e216ec7ec92106db6a1fdeb9d649968a81cbb10a3e5c828167af2fd0e81534f11198b63caad0ed8e9f844db8403027ba3eb1a56
是dual rsa,我没见过的东西,所以搜索引擎启动。
我自己能想到连分数处理的,但是写不出来,找了一篇博客https://hasegawaazusa.github.io/dual-rsa-note.html,但也解不出来,然后就开始,找脚本啊找脚本,找到一个好脚本。
参数什么的没动,n1,n2,e可以手工读公钥文件提取(因为我n2无法直接读)
(贴给gpt让它将py2转成py3就行了)
from sage.all import *
import math
from Crypto.Util.number import *
# display matrix picture with 0 and X
def matrix_overview(BB):
for ii in range(BB.dimensions()[0]):
a = '{:02d} '.format(ii)
for jj in range(BB.dimensions()[1]):
a += ' ' if BB[ii, jj] == 0 else 'X'
if BB.dimensions()[0] < 60:
a += ' '
print(a)
def dual_rsa_liqiang_et_al(e, n1, n2, delta, mm, tt):
N = (n1 + n2) // 2
A = ZZ(math.floor(N**0.5))
_XX = ZZ(math.floor(N**delta))
_YY = ZZ(math.floor(N**0.5))
_ZZ = ZZ(math.floor(N**(delta - 1./4)))
_UU = _XX * _YY + 1
M = Matrix(ZZ, [[A, e], [0, n1]])
B = M.LLL()
l11, l12 = B[0]
l21, l22 = B[1]
l_11 = ZZ(l11 / A)
l_21 = ZZ(l21 / A)
modulo = e * l_21
F = Zmod(modulo)
PR = PolynomialRing(F, 'u, x, y, z')
u, x, y, z = PR.gens()
PK = PolynomialRing(ZZ, 'uk, xk, yk, zk')
uk, xk, yk, zk = PK.gens()
PQ = PK.quo(xk * yk + 1 - uk)
f = PK(x * (n2 + y) - e * l_11 * z + 1)
fbar = PQ(f).lift()
gijk = {}
for k in range(mm + 1):
for i in range(mm - k + 1):
for j in range(mm - k - i + 1):
gijk[i, j, k] = PQ(xk**i * zk**j * PK(fbar)**k * modulo**(mm - k)).lift()
hjkl = {}
for j in range(1, tt + 1):
for k in range(math.floor(mm / tt) * j, mm + 1):
for l in range(k + 1):
hjkl[j, k, l] = PQ(yk**j * zk**(k - l) * PK(fbar)**l * modulo**(mm - l)).lift()
monomials = []
for k in gijk.keys():
monomials += gijk[k].monomials()
for k in hjkl.keys():
monomials += hjkl[k].monomials()
monomials = sorted(set(monomials), reverse=True)
assert len(monomials) == len(gijk) + len(hjkl)
dim = len(monomials)
M = Matrix(ZZ, dim)
row = 0
for k in gijk.keys():
for i, monomial in enumerate(monomials):
M[row, i] = gijk[k].monomial_coefficient(monomial) * monomial.subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ)
row += 1
for k in hjkl.keys():
for i, monomial in enumerate(monomials):
M[row, i] = hjkl[k].monomial_coefficient(monomial) * monomial.subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ)
row += 1
matrix_overview(M)
print('=' * 128)
B = M.LLL()
matrix_overview(B)
H = [(i, 0) for i in range(dim)]
H = dict(H)
for j in range(dim):
for i in range(dim):
H[i] += PK((monomials[j] * B[i, j]) / monomials[j].subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ))
H = list(H.values())
PQ = PolynomialRing(QQ, 'uq, xq, yq, zq')
uq, xq, yq, zq = PQ.gens()
for i in range(dim):
H[i] = PQ(H[i].subs(uk=xk * yk + 1))
I = Ideal(*H[1:20])
g = I.groebner_basis('giac')[::-1]
mon = [t.monomials() for t in g]
PX = PolynomialRing(ZZ, 'xs')
xs = PX.gen()
x_pol = y_pol = z_pol = None
for i in range(len(g)):
if mon[i] == [xq, 1]:
print(g[i] / g[i].lc())
x_pol = g[i] / g[i].lc()
elif mon[i] == [yq, 1]:
print(g[i] / g[i].lc())
y_pol = g[i] / g[i].lc()
elif mon[i] == [zq, 1]:
print(g[i] / g[i].lc())
z_pol = g[i] / g[i].lc()
if x_pol is None or y_pol is None or z_pol is None:
print('[-] Failed: we cannot get a solution...')
return
x0 = x_pol.subs(xq=xs).roots()[0][0]
y0 = y_pol.subs(yq=xs).roots()[0][0]
z0 = z_pol.subs(zq=xs).roots()[0][0]
assert f(x0 * y0 + 1, x0, y0, z0) % modulo == 0
a0 = z0
a1 = (x0 * (n2 + y0) + 1 - e * l_11 * z0) / (e * l_21)
d = a0 * l_11 + a1 * l_21
return d
if __name__ == '__main__':
delta = 0.334
mm = 4
tt = 2
n1=0x1b5d4fe0aa6782e275d4ce12a6d57562efbbe7db6f5277255b891729bfa2a18d3edb49843d7989a37b9516be2df8ca939058e65f64b5fb2071bea4f5f8d1392895b32bf0377d99f4f79979125e5db01cdb5080a1c2d665c9ac31b5823025499c9513277bae5e7a846cd271c4396e2ba219020e58a9055cb18a28d36a00bf717b
n2=0x071c324e8769493187c15f72d5cc695729b48488ee3fbd01db00d5c478f08c7cf32093ba61745051d3e9d169523aa91438181f47679aff5edd22950f74a1eb1443320aaa5d97f5c1e81b5ef9a3e69ba669abc4c6c4b405f5088a603a74f9bcef88823b4523574114c810600838728196f8e5e0d4aeeeeab79dd8683a72f3c017
e=0x079f5ccc665767b4a257e5c1ff56e9803df2e5650302daad420105fe672447743bd3f0bea1c46a4987932e9a886ca87a7afd7796abf1e5629c4986fe4f22e89cdce7abb06624465146a2e2b6ca9ab3196ceab7467974c1dc45608a200411b291fdaf99f7d80dce4db3566f4a9e2e574c6224cd07d80638d28f7820bcf4b49143
d1 = dual_rsa_liqiang_et_al(e, n1, n2, delta, mm, tt)
c=open('ciphertext.txt','rb').read()
c=bytes_to_long(c)
flag=long_to_bytes(int(pow(c,int(d1),n1)))
if b"SCTF" in flag:
print(flag)
# https://elliptic-shiho.github.io/ctf-writeups/#!ctf/2017/0CTF%20Finals/cr1000-AuthenticationSecrecy/README.md
# b'SCTF{Ju5t_3njoy_th3_Du4l_4nd_Copper5m1th_m3thod_w1th_Ur_0wn_1mplem3nt4t10n}'
LinearARTs
from random import choices
import json
from sage.all import *
from Crypto.Util.number import *
from sage.groups.perm_gps.permgroup_named import SymmetricGroup
def Young(FLAG):
f = int.from_bytes(FLAG, "big")
q = 65537
s = []
while f:
s.append(f % q)
f //= q
s = vector(GF(q), s)
n, m = len(s), len(s) ** 2
A = Matrix(GF(q), m, n, lambda i, j: randint(0, q - 1))
e = vector(choices(range(2^8), k=m), GF(q))*Matrix(ZZ,PermutationGroupElement(SymmetricGroup(m).random_element()).matrix())
b = (A*s) + e
return A,b
def Old(m, nbits):
Sn = SymmetricGroup(m)
p = [getPrime(360) for i in range(m)]
N = sorted([getRandomNBitInteger(nbits) for _ in range(m)])
S = []
for i in range(m):
r = [N[_] % p[i] for _ in range(m)]
r = vector(ZZ,r)
Per = Sn.random_element()
P = PermutationGroupElement(Per)
Pm = Matrix(ZZ,P.matrix())
r *= Pm
S.append(r)
S = matrix(ZZ,S)
with open('Old.matrix','w') as f:
json.dump({"S": str(list(S)),"p": str(p)}, f)
return N
def chall(nn):
# The challenge lasted nn rounds
# Young_level * virtue >= Old_level , where virtue = nn + 1
h = []
HP = []
MP = []
Old_level = 625*2*2
Young_level = 25*5*5*2
M = getPrime(Old_level)
XP = getRandomRange(1,M)
for _ in range(nn):
a = getRandomRange(1,M)
b = a*XP % M
HP.append(a)
MP.append(b)
delta_level = Old_level - Young_level
h.append(b >> delta_level << delta_level)
return XP,M,HP,MP,h
mm = 16
nn = 9
nbits = 3840
A,b = Young(FLAG)
N = Old(mm, nbits)
XP,M,HP,MP,h = chall(nn)
# MP is useful ,I can use him to cast five lightning spells
D = diagonal_matrix(GF(0x10001),N+MP)
Sn = SymmetricGroup(5*5)
Per = Sn.random_element()
P = PermutationGroupElement(Per)
PM = Matrix(GF(0x10001),P.matrix())
AA = A*D*PM
with open('D.matrix','w') as f:
json.dump({"D": str(list(D))}, f)
# OK! Find your martial arts, and then you can get the flag.
with open("output.txt", "w") as f:
json.dump({"AA": str(list(AA)), "b": str(b)}, f)
print(f'My SymmetricGroup is {Sn}, and my element is {Per}')
print(f'M = {M}')
print(f'h = {h}')
print(f'HP = {HP}')
'''
My SymmetricGroup is Symmetric group of order 25! as a permutation group, and my element is (1,23,2,13,3,16,15,6,22,18,14,4,25,11,20,24,21,9,5,17,7,19,10,12,8)
'''
只需要用到如下数据,其他数据都没用。
关键点在于\(AA = A*D*PM\)
PM可以自己测试一下就可以自己生成了,D也直接给了,AA也有了
那A就可以直接算出来了,其他数据都是杂鱼。
之后看Young中就是一个lwe问题,我不会,所有搜索引擎再次启动。
再次感谢鸡块师傅,最后又是找他的脚本出了
https://tangcuxiaojikuai.xyz/post/758dd33a.html
import json
from Crypto.Util.number import *
with open('D.matrix', 'r') as file:
data = json.load(file)
D=eval(data['D'])
D=Matrix(GF(0x10001),D)
with open('output.txt', 'r') as file:
data = json.load(file)
AA=eval(data['AA'])
AA=Matrix(GF(65537),AA)
b=list(eval(data['b']))
##### test
if 0:
Sn = SymmetricGroup(5*5)
Per = Sn.random_element()
P = PermutationGroupElement(Per)
PM = Matrix(GF(0x10001),P.matrix())
print(Per)
print(P)
print(PM)
#My SymmetricGroup is Symmetric group of order 25! as a permutation group, and my element is (1,23,2,13,3,16,15,6,22,18,14,4,25,11,20,24,21,9,5,17,7,19,10,12,8)
P=(1,23,2,13,3,16,15,6,22,18,14,4,25,11,20,24,21,9,5,17,7,19,10,12,8)
PM=[]
for i in range(len(P)):
PM.append([0]*25)
for i in range(len(P)):
PM[P[i]-1][P[(i+1)%len(P)]-1]=1
PM=Matrix(GF(0x10001),PM)
AD=PM.solve_left(AA)
A=D.solve_left(AD)
# https://tangcuxiaojikuai.xyz/post/758dd33a.html
def primal_attack2(A,b,m,n,p,esz):
L = block_matrix(
[
[matrix(Zmod(p), A).T.echelon_form().change_ring(ZZ), 0],
[matrix.zero(m - n, n).augment(matrix.identity(m - n) * p), 0],
[matrix(ZZ, b), 1],
]
)
#print(L.dimensions())
Q = diagonal_matrix([1]*m + [esz])
L *= Q
L = L.LLL()
L /= Q
res = L[0]
if(res[-1] == 1):
e = vector(GF(p), res[:m])
elif(res[-1] == -1):
e = -vector(GF(p), res[:m])
s = matrix(Zmod(p), A).solve_right((vector(Zmod(p), b)-e))
return s
f=primal_attack2(A,b,625,25,65537,2^8)
print(f)
m=0
for i in range(len(f)):
m=m+65537^i*int(f[i])
print(long_to_bytes(int(m)))
#b'SCTF{HunYu4n_TaiChi-5tyl3_P3rmut4t10nProup_m4st3r}'