2024-HECTF-wp-crypto

2024-HECTF-wp-Crypto

简单比赛简单讲。

seven more

amm板子题,关于amm的原理已经在之前的数信杯半决赛中提到,在此不做展开。

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
from Crypto.Util.number import *
import os
from gmpy2 import *

def getMyPrime(nbits, multiplier):
while True:
n = 2 * multiplier * getPrime(nbits // 2) * getPrime(nbits // 2)
if is_prime(n + 1):
return n + 1

def generate_keys(nbits):
p = getMyPrime(nbits, 1009 * 7)
q = getMyPrime(nbits, 1009)
n = p * q
e = 1009 * 7
return p, q, n, e

p, q, n, e = generate_keys(700)

flag = b'HECTF{######}' + os.urandom(101)
m = bytes_to_long(flag)
assert m.bit_length() < n.bit_length()
assert m.bit_length() > q.bit_length()
assert m.bit_length() > p.bit_length()
c = pow(m, e, n)

print(f"n = {n}")
print(f"c = {c}")
print(f"p = {p}")
print(f"q = {q}")

#n = 211174039496861685759253930135194075344490160159278597570478160714793843648384778026214533259531963057737358092962077790023796805017455012885781079402008604439036453706912819711606916173828620000813663524065796636039272173716362247511054616756763830945978879273812551204996912252317081836281439680223663883250992957309172746671265758427396929152878633033380299036765665530677963287445843653357154379447802151146728382517702550201
#c = 191928992610587693825282781627928404831411364407297375816921425636703444790996279718679090695773598752804431891678976685083991392082287393228730341768083530729456781668626228660243400914135691435374881498580469432290771039798758412160073826112909167507868640830965603769520664582121780979767127925146139051005022993085473836213944491149411881673257628267851773377966008999511673741955131386600993547529438576918914852633139878066
#p = 31160882390461311665815471693453819123352546432384109928704874241292707178454748381602275005604671000436222741183159072136366212086549437801626015758789167455043851748560416003501637268653712148286072544482747238223
#q = 6776895366785389188349778634427547683984792095011326393872759455291221057085426285502176493658280343252730331506803173791893339840460125807960788857396637337440004750209164671124188980183308151635629356496128717687

在这里我们把模n的同余式转移到模p和模q,分别寻找他们的amm特解,然后再做crt组合。

模p比较明显,这里的模q应该要

$c^7 = m^{1009} mod(n)$

然后对c的七次方求amm得到m的amm特解,然后找到m的1009个模q的解,另一边能找到m的1009*7个模p的解,然后组合大概是七百万个,单线程是30分钟,我多开跑了7分钟出了,比较简单的一道题。

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
from Crypto.Util.number import *
import random
import math
import gmpy2
def AMM(residue,e,p):
# 计算t,s
t = 0
s = p - 1
while s % e == 0:
t += 1
s = s // e

d=gmpy2.invert(e,s)

if t==1:
return pow(residue,d,p)

noresidue = random.randint(1, p)
while pow(noresidue, (p - 1) // e, p) == 1:
noresidue = random.randint(1, p)
a=pow( noresidue , ( e ** ( t-1 )) * s, p )
b=pow( residue , e * d - 1, p )
c=pow( noresidue , s , p )
h=1
for i in range(1,t):
d=pow( b , e** ( t-1-i ) , p )
if d==1:
j=0
else:
j=-math.log(d,a)
b=pow( pow ( c , e , p ) , j , p ) * b % p
h=pow( c , j , p ) * h % p
c=pow( c , e , p )
return pow( residue , d , p ) * h % p

n = 211174039496861685759253930135194075344490160159278597570478160714793843648384778026214533259531963057737358092962077790023796805017455012885781079402008604439036453706912819711606916173828620000813663524065796636039272173716362247511054616756763830945978879273812551204996912252317081836281439680223663883250992957309172746671265758427396929152878633033380299036765665530677963287445843653357154379447802151146728382517702550201
c = 191928992610587693825282781627928404831411364407297375816921425636703444790996279718679090695773598752804431891678976685083991392082287393228730341768083530729456781668626228660243400914135691435374881498580469432290771039798758412160073826112909167507868640830965603769520664582121780979767127925146139051005022993085473836213944491149411881673257628267851773377966008999511673741955131386600993547529438576918914852633139878066
p = 31160882390461311665815471693453819123352546432384109928704874241292707178454748381602275005604671000436222741183159072136366212086549437801626015758789167455043851748560416003501637268653712148286072544482747238223
q = 6776895366785389188349778634427547683984792095011326393872759455291221057085426285502176493658280343252730331506803173791893339840460125807960788857396637337440004750209164671124188980183308151635629356496128717687

def find(e, p):
root = []
while len(root) < e:
a = random.randint(2, p - 1)
res = pow(a, (p - 1) // e, p)
if res not in root:
root.append(res)
return root
lp = []
e = 1009 * 7
c1 = c%p
m1 = AMM(c1,e,p)
root_list = find(e, p)
# print(m1)
# print(c1 == pow(m1, e, p))
for i in range(len(root_list)):
a = m1 * root_list[i] % p
lp.append(int(a))
assert c1 == pow(lp[1], e, p)
lq=[]
c2 = c%q
c20 = pow(c2,inverse(7,q-1),q)
m2 = AMM(c20,1009,q)
root_list = find(1009, q)
for i in range(len(root_list)):
a = m2 * root_list[i] % q
lq.append(int(a))
assert c2 == pow(lq[1], e, q)
#中国剩余定理脚本,m_list为余数,a_list为除数
def Get_Mi(m_list, m):
M_list = []
for mi in m_list:
M_list.append(m // mi)
return M_list

def Get_resMi(M_list, m_list):
resM_list = []
for i in range(len(M_list)):
resM_list.append(Get_ni(M_list[i], m_list[i])[0])
return resM_list

def Get_ni(a, b):
if b == 0:
x = 1
y = 0
q = a
return x, y, q
ret = Get_ni(b, a % b)
x = ret[0]
y = ret[1]
q = ret[2]
temp = x
x = y
y = temp - a // b * y
return x, y, q

def result(a_list, m_list):
for i in range(len(m_list)):
for j in range(i + 1, len(m_list)):
if 1 != math.gcd(m_list[i], m_list[j]):
print("不能直接利用中国剩余定理")
return
m = 1
for mi in m_list:
m *= mi
Mi_list = Get_Mi(m_list, m)
Mi_inverse = Get_resMi(Mi_list, m_list)
x = 0
for i in range(len(a_list)):
x += Mi_list[i] * Mi_inverse[i] * a_list[i]
x %= m
return x
def RSA_dec(n,e,c):
phi=n-1
gcd1 = gmpy2.gcd(e, phi)
t1 = e // gcd1
dt = gmpy2.invert(t1, phi)
m_gcd1 = gmpy2.powmod(c, dt, n)
m = gmpy2.iroot(m_gcd1, gcd1)[0]
return m
from tqdm import tqdm
clis=[p,q]
ylis=[0,0]
for i in tqdm(lp, desc="Processing lp"):
for j in tqdm(lq, desc="Processing lq", leave=False):
ylis[0]=i
ylis[1]=j
if b'HECTF' in long_to_bytes(result(ylis,clis)):
print(long_to_bytes(result(ylis,clis)))
break
# HECTF{go0d_jOb_At_AmM}

翻一翻

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 getPrime, isPrime, bytes_to_long
from secret import flag
import base64

def emirp(x):
y = 0
while x !=0:
y = y*10 + x%10
x = x//10
return y

while True:
p = getPrime(512)
q = emirp(p)
if isPrime(q):
break
n = p*q
e = 65537

flag = base64.b64encode(flag)

m = bytes_to_long(flag)

c = pow(m,e,n)
print(f"{n = }")
print(f"{c = }")

'''
n = 404647938065363927581436797059920217726808592032894907516792959730610309231807721432452916075249512425255272010683662156287639951458857927130814934886426437345595825614662468173297926187946521587383884561536234303887166938763945988155320294755695229129209227291017751192918550531251138235455644646249817136993
c = 365683379886722889532600303686680978443674067781851827634350197114193449886360409198931986483197030101273917834823409997256928872225094802167525677723275059148476025160768252077264285289388640035034637732158021710365512158554924957332812612377993122491979204310133332259340515767896224408367368108253503373778
'''


十进制逆序的板子题。

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
n = 404647938065363927581436797059920217726808592032894907516792959730610309231807721432452916075249512425255272010683662156287639951458857927130814934886426437345595825614662468173297926187946521587383884561536234303887166938763945988155320294755695229129209227291017751192918550531251138235455644646249817136993
def t(a, b, k):
# sqrt(n) has 155 digits, so we need to figure out 77 digits on each side
if k == 77:
if a*b == n:
print(a, b)
return
for i in range(10):
for j in range(10):
# we try to guess the last not-already-guessed digits of both primes
a1 = a + i*(10**k) + j*(10**(154-k))
b1 = b + j*(10**k) + i*(10**(154-k))
if a1*b1 > n:
# a1 and b1 are too large
continue
if (a1+(10**(154-k)))*(b1+(10**(154-k))) < n:
# a1 and b1 are too small
continue
if ((a1*b1)%(10**(k+1))) != (n%(10**(k+1))):
# The last digits of a1*b1 (which won't change later) doesn't match n
continue
# this a1 and b1 seem to be a possible match, try to guess remaining digits
t(a1, b1, k+1)

# the primes have odd number of digits (155), so we try all possible middle digits (it simplifies the code)
for i in range(10):
t(i*(10**77), i*(10**77), 0)
from Crypto.Util.number import long_to_bytes
p = 39316409865082827891559777929907275271727781922450971403181273772573121561800306699150395758615464222134092274991810028405823897933152302724628919678029201
n = 404647938065363927581436797059920217726808592032894907516792959730610309231807721432452916075249512425255272010683662156287639951458857927130814934886426437345595825614662468173297926187946521587383884561536234303887166938763945988155320294755695229129209227291017751192918550531251138235455644646249817136993
c = 365683379886722889532600303686680978443674067781851827634350197114193449886360409198931986483197030101273917834823409997256928872225094802167525677723275059148476025160768252077264285289388640035034637732158021710365512158554924957332812612377993122491979204310133332259340515767896224408367368108253503373778
q = n//p
phi = (p-1)*(q-1)
d = pow(65537, -1, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
#HECTF{I_rea1ly_l0ve_c2ypto!}

注意不要和二进制逆序的题搞混就行。

(想出难题其实可以调个奇怪的进制,这样就必须手写代码了)

(也许gpt可以出 不懂

不合格的魔药

看细节的题目,关键就看你能不能看的出来那个异或是有问题的,也靠一点做题的直觉?

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
from random import randrange
from Crypto.Util.number import *
from gmpy2 import *
from fastecdsa.curve import Curve
from Crypto.Cipher import AES
from hashlib import *
import secret

def A_function(key):
while True:
p = getPrime(512)
q = getPrime(512)
if p % 4 == 3 and q % 4 == 3:
n = p * q
break
a = randrange(p)
b = randrange(q)
while True:
x1 = randrange(p)
y1 = (x1**3 + a*x1) % p
x2 = randrange(n)
y2 = (x2**3 + a*x2 + b) % n
if pow(y1,(p-1)//2, p) == 1 and pow(y1,(q-1)//2, q) == 1:
yp = pow(y1,p//4+1,p)
yq = pow(y1,q//4+1,q)
_,s,t = gcdext(p,q)
y1 = (s*p*yq + t*q*yp) % p
if pow(y2,(p-1)//2, p) == 1 and pow(y2,(q-1)//2, q) == 1:
yp = pow(y2,p//4+1,p)
yq = pow(y2,q//4+1,q)
_,s,t = gcdext(p,q)
y2 = (s*p*yq + t*q*yp) % n
break
Ep = Curve(None, p, a, 0, None, x1, y1)
En = Curve(None, n, a, b, None, x2, y2)
print("p =", p)
print("q =", q)
print("a =", a)
print("b =", b)
hint1 = key * Ep.G
hint = [x1 ,hint1]
return En, hint



def A_encrypt(m, G, k):
blocks = [m[16*i:16*(i+1)] for i in range(len(m) // 16)]
ciph = []
aes = AES.new(k, AES.MODE_ECB)
for i in range(len(blocks)):
c = aes.encrypt(blocks[i])
if i%2==0:
G = G + G
ciph.append(G.x ^ bytes_to_long(c))
else:
ciph.append(G.y ^ bytes_to_long(c))
return ciph


flag = secret.flag
key = secret.key

m = flag[6:-1]
assert b'HECTF{'+ m+ b'}' == flag
A, hint = A_function(key)
k = md5(long_to_bytes(key)).hexdigest().encode()
c = A_encrypt(m, A.G,k)
print("hint =", hint)
print("c =", c)

'''
p = 9604080254440553624043823039323876524034439909584709693304859297324410855942111467832096190746534800378359779991381701244554754870303658957438266614583487
q = 7117529167860499983120234872664469946810713755399747931099511148595647881645694071900284496403308583631053530870961375928947111857317803005696543076720079
a = 4681007517868949260473646867708411042804596292653498068045093108939357065240201843535644313612886376810286247810943227474659270191834401055704514648846995
b = 5604862515726338933576748414825616582947323501967288114322080747741801017833194347273532400730033226601964489467416955741018175785792514035352083708135431
hint = [5544706922427110224110125906620053049906095568886481576326706308027915868515721429471522223193053363494813044921519216114372968191072598748704528735817403,
X: 0x2fa8e23f18ed4a9bd752a0c22b0750c17fbb66c76554e2089258fd979a5736b7766c974fb9788acf17fb065dc1daec6a8a6e98021de6c4ce3cde11dd54590e1d
Y: 0xa3ce4bb1e25563b577a45cd06153d2dab584a70130c7ae71e65fe5e11b60493ccb845fbe4989dbd4a60d6a1ff12baa268b8833ed30f7c7e21c32268a139b5b6b
(On curve <None>)]
c = [36780810764729391947601691590378765170863850291763672158886689602006275675399596108959250284869355070618680265311484525337488013177333417742808496794250706127014303883956401715343247310936978778751394980638177344654524711571648231122027699452582302505466999915200896495338587961829985149664712686944510559820, 20958199004445348755624931477686903609410629089817702686793041731031202915294487428236505796231417377524290926704880107242252471250791747709149963693453815320856114055076830778689575609444155241642860745570792018879816650383543271943138193405548674967958109800776284787612370057476837642989670234913968669332, 19758181515666300263334531148587391869707566215385658759724970483060039216682585723722462835458856503531814316860237786892749700501436669071048571605926728917066797641628644730857333648930286503355701843365288276242984029888215453858844295912023305616753086127934173496355853797241944921600781294012353332277, 45576628433681427718167093217006549620067042472164439269014690121698560736312716407875326404496263261341269644373184438703912129559084380247641072914940830606649124606611794031719696797961847217643536070335745057048220615012019629278484208808353027070994021979997462190775853832457224157083880895894000484461]
'''

前半部分把key求出来很简单,不说了,就是爆破

后半部分我们观察,他把加密后的block分别和2G.x 2G.y 4G.x 4G.y异或,然而后者都是很大的数字,而block只有128位,显然这是一道高位泄露的二元copper板子题。

给出板子。

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
from Crypto.Util.number import *
from hashlib 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 []
p = 9604080254440553624043823039323876524034439909584709693304859297324410855942111467832096190746534800378359779991381701244554754870303658957438266614583487
q = 7117529167860499983120234872664469946810713755399747931099511148595647881645694071900284496403308583631053530870961375928947111857317803005696543076720079
b = 5604862515726338933576748414825616582947323501967288114322080747741801017833194347273532400730033226601964489467416955741018175785792514035352083708135431
a = 4681007517868949260473646867708411042804596292653498068045093108939357065240201843535644313612886376810286247810943227474659270191834401055704514648846995
n = p*q
# PR.<x,y> = PolynomialRing(Zmod(n))
# f = (c[0]+x)^3 + a*(c[0]+x) + b - (c[1]+y)^2
# res = small_roots(f , bounds = (2^128,2^128) , m=7 , d=3)
# print(res)
res = [(68357321341453732583590940403026587328344892731944077254575006808038831458271202403541999170174232410086077636771351547302194106577358745970512941536233852688440456225502342627429665200872884114828150923495405975454488020137758914072446859850509335418728661081354578141557353569394002780358500805318763301377, 68357321341453732583590940403026587328344892731944077254575006808038831458271202403541999170174232410086077636771351547302194106577358745970512941536233852688440456225502342627429665200872884114828150923495405975454488020137758914072446859850509335418728661081354578141471813454790417539546837860552035976675)]
b1 = ((c[0]+res[0][0])%n^^c[0])
b2 = ((c[1]+res[0][1])%n^^c[1])
k = md5(long_to_bytes(51517)).hexdigest().encode()
aes = AES.new(k, AES.MODE_ECB)
print(aes.decrypt(long_to_bytes(b1)).decode())
print(aes.decrypt(long_to_bytes(b2)).decode())
# PR.<x,y> = PolynomialRing(Zmod(n))
# f = (c[2]+x)^3 + a*(c[2]+x) + b - (c[3]+y)^2
# res = small_roots(f , bounds = (2^128,2^128) , m=7 , d=3)
# print(res)
res = [(68357321341453732583590940403026587328344892731944077254575006808038831458271202403541999170174232410086077636771351547302194106577358745970512941536233852688440456225502342627429665200872884114828150923495405975454488020137758914072446859850509335418728661081354578141717508655153866310778791391698059548648, 68357321341453732583590940403026587328344892731944077254575006808038831458271202403541999170174232410086077636771351547302194106577358745970512941536233852688440456225502342627429665200872884114828150923495405975454488020137758914072446859850509335418728661081354578141672728573591121143225765447786474306203)]
b1 = ((c[2]+res[0][0])%n^^c[2])
b2 = ((c[3]+res[0][1])%n^^c[3])
k = md5(long_to_bytes(51517)).hexdigest().encode()
aes = AES.new(k, AES.MODE_ECB)
print(aes.decrypt(long_to_bytes(b1)).decode())
print(aes.decrypt(long_to_bytes(b2)).decode())
#HECTF{30C270291b3da6c12ai341s5aNqaTtb2cd1ae2gA4e41319DEA876D6B896E0599}

情书与破碎的证书

题意与实际完全不符,烂

小明喜欢上了小红,他使用rsa向小红发送了无数封含有中文字符的情书。终于小红忍不住了,找到了大嘿阔将小明的私钥证书打成碎片,移除了中间的内容并把上下段的私钥部分转化成16进制,以九个为一组用相同的方式打乱(转化时产生的0d0a换行符已被移除)。作为密码学大佬的你能恢复证书,找出小红忍无可忍的证据么?

实际上根本就不是相同的方式,前半部分是倒序,后半部分是正序,而且是9个字节而不是9个数字。

感觉很多人应该都卡这了,挺脑残的。

我们在这里分析一下他给的这个证书

1
2
0d30000102bc048230010df78648862a090630a604820400050101018202000102a20482238d771399dbbd0001416e7fbd7f589b44151fb0c984549c1c4d948984f85c35934453bfbb4dbb99420f63f9cb8f577a7b60d67bdf84166c500fa7d6c6e484b75d97e308151b31a49c5bd77dd1d2d6711e50d9f5f7bc37d4e2235cfd76a522713c766e240aa2eb5fb45a9015d1dbd6a58b5b28861233ae4eb75a97ced0b78e91024d195adde08f7430b7f160d1d60a4f9d0801db74ebc8c23f9397251faac5500d216acc623e8f6ab212b5e6e9495d5ef6cee995fea98f40b1db2d356dd3c4d2612c64a1295bb23936fad66dc5662cb4ba6a8929591f6b14ce30d67df5ec35edb1f0973f746bcc5fc1ca921ee9660e04c6f286677b92e12b61ba310501030219e5085e5254046204000182020100
中间被打碎了d48d90d80f02818051a5f7e7f4c050a50e18fde12fcee2646f2b43160b0c75ab4925e8269ae80e70cf12734f41fab18d0424ed7cceb7ddb27cbe0f554f7a6e1698d4ec5ba2b48d612e2337aeb75f8a57d8155a11d07b2c49d3d97c4ff0cfb89e6dd4f36cc37c010b5bc89356a39b576cc3edd03cdc4d791df5091a5571df1a6c15eedaa0773cf3cf0281800fc61f05d19c96eec3edcacca34e1d3e2cab439bebab6693a3ce2ca99f88ab9cdd183ceb8e801d8298f835359864ef191db3f53269976ba04b03606e540859decd05805c4aa79dc6db22380658eaf0bffba0f4e719bcf1b1e04169d8e0cb3af4d90b2e62d7c7ed3045d49b525ca715ca3b84f07b4ece27d04d1795299fa186cd02818025c20ab2529f1efd3d35347c573b282abfd95b264c92f6c4f9ec8b7c713206fbea1886880e29a36c47ef9bb753ce9567ea4d3e083c30f344022f95b7cd7114813bf6a28ecc67d5fe05953242684cd29c1c5dd8a74416890e5c943c70904ba70e349b15719a466f901fbf0cfc7840f8032e31afbccfb84f4a817ea51c8f90fd6a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d

让我们看看rsa private key的格式。

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
3082025d  	# Begin Sequence: len=0x025d

0201 # Version: (len=0x01)
00

028181 # n: (len=0x81)
00a0d154d5bf97c40f7797b44819d09c608fa4b5c38e70d83bc13267138c6eff4c1aacefe3ddb571e1b41d911c7ab6136cf90493189563450e1f4270cabbc4207c54c4da7b84a20311cfbbabe82b9fe60bdf48a08d57839d0cdf9464d84262bcc06bc308095a6987f60ad07d669a312b5a7e4133213788eecf25863248b91349ef

0203 # e: (len=0x03)
010001

028180 # d: (len=0x80)
0f8270c496903bf3e3ec4912450f15edc81cb1fcf4b154615aee11fbd428e64d402b5a8d66d5f770358f3e6df935b324e8d5349c83d7c992a5982249a31734acb1db19c4c8d829267514bc1ef7bbfbe242d4350f67a002a56d33e56d1a94adc71c68f020dc39ab7d0064c111b164e26ba0698dc94a03cdfd516ffd966e877949

0241 # p: (len=0x41)
00ca97e49c058237f96e99118ce383f91912cba1163de9236181ff754ef3ef1a260fac8d2d9aee866d51a8b6836983b05cf850e786289b6859925bc8695fc67c47

0241 # q: (len=0x41)
00cb3630aafffcb29607f0833dc7f05c143ee92fadfe975da4cf6719e71226bee72562e8631328a25d7351507a8d43c1295ab6ea242b60a28b109233a983f42119

0240 # d mod (p-1): (len=0x40)
1b4a32a541a8b4d988a85dd0d8a4e25d1a470bbfef3f0461121dd3337b706dd94aab37a9390180622169d48c071e921733ebd204245c2ac6460ccf0642bc7de9

0241 # d mod (q-1): (len=0x41)
008d9f44a7c823eaaa58fa2bdd20bcc8cf6b50c463f4acb51ca956e75c7ceff7d7cbdc74aca7ab880cacd39cccec2aae320e00b0896899be6e40ac43c8fe2763f1

0241 # (inverse of q) mod p: (len=0x41)
00c67ca6d988f53abea82159431a146512a8d942978d4a8f83f2d426f1095e3bf1b5b9b8b1ccbbad2a31c6401880447a45f5e0790269061ac13b5f68f1777d7f07

# End Sequence

这是一个常见的格式,这个一定是3082开头的,我们对照前9个字节,0d30000102bc048230,对他进行倒序,就是308204bc020100300d 非常巧,这个有020100,和我们正常的格式是对的上的(不过后面一大串我根本不知道是什么,但是也不影响)

所以正常的思路应该是,把整个私钥分成9字节1组,每组分别倒序再拼接。

1
2
3
4
5
6
308204bc020100300d06092a864886f70d0101010500048204a6308204a2020100
02820101
00bddb9913778d2315449b587fbd7f6e41944d1c9c5484c9b01fbf534493355cf88489cbf9630f4299bb4dbb84df7bd6607b7a578f84e4c6d6a70f506c16a4311b1508e3975db71e71d6d2d17dd75b9c23e2d437bcf7f5d9506e763c7122a576fd5c15905ab45feba20a241286285b8ba5d6dbd1b7d0ce975ab74eae338fe0dd5a194d02918e4f0ad6d160f1b730743fc2c8eb74db01089d210d50c5aa1f259793b512b26a8f3e62cc6a95e9cef65e5d49e9e66d352ddbb1408fa9fe5b29a1642c61d2c4d32c66c56dd6fa3639b2146b1f5929896abab4b1ed35ecf57dd630cecac15fcc6b743f97f086f2c6040e66e91e920531ba612be1927b6754525e08e519
0203010001
02820100
046204518081020fd8908dd4180ea550c0f4e7f7a5432b6f64e2ce2fe1fd26e82549ab750c0b16414f7312cf700ee89ab7ce7ced24048db1fa6e7a4f550fbe7cb2dd618db4a25becd49816d8578a5fb7ae37232ed9d3492c7bd0115a15f3d46d9eb8cff04f7c5693c85b0b017cc36cdc3cd0edc36c579ba3df71551a09f51d794df33c77a0daee156c1ad1051fc60f808102cf4ea3cccaedc3ee969c66abeb9b43ab2c3e1d9cab889fa92ccea39398821d808eeb3c18ddb31d19ef64983535f860034ba06b976932f55c8005cdde5908546e58063822dbc69da74abc19e7f4a0fbbff0ea3acbe0d86941e0b1f130edc7d7622e0bd9f43bca15a75c529bd445174dd027ce4e7bf084808102cd86a19f29953dfd1e9f52b20ac225d9bf2a283b577c34358becf9c4f6924c265b888618eafb0632717c53b79bef476ca3290e303c083e4dea6795ce1471cdb7952f0244f3fed567cc8ea2f63b811c9cd24c6842329505945c0e891644a7d85d159b340ea74b90703cfc0cbf1f906f469a71cfbcaf312e03f84078908f1ca57e814a4fb86afd

最后得到的是这样,其中这个0203010001也非常显眼,如果做过证书题的话应该会有搜这个的直觉。

所以我们可以得到n,但是我们往后看,完全就是一坨大的。

我在这里卡了很久,后面我感觉很有可能两部分一个是正序的一个是倒序的,在全局搜02,果不其然

1
2
3
4
5
6
7
d48d90d80f
028180
51a5f7e7f4c050a50e18fde12fcee2646f2b43160b0c75ab4925e8269ae80e70cf12734f41fab18d0424ed7cceb7ddb27cbe0f554f7a6e1698d4ec5ba2b48d612e2337aeb75f8a57d8155a11d07b2c49d3d97c4ff0cfb89e6dd4f36cc37c010b5bc89356a39b576cc3edd03cdc4d791df5091a5571df1a6c15eedaa0773cf3cf
028180
0fc61f05d19c96eec3edcacca34e1d3e2cab439bebab6693a3ce2ca99f88ab9cdd183ceb8e801d8298f835359864ef191db3f53269976ba04b03606e540859decd05805c4aa79dc6db22380658eaf0bffba0f4e719bcf1b1e04169d8e0cb3af4d90b2e62d7c7ed3045d49b525ca715ca3b84f07b4ece27d04d1795299fa186cd
028180
25c20ab2529f1efd3d35347c573b282abfd95b264c92f6c4f9ec8b7c713206fbea1886880e29a36c47ef9bb753ce9567ea4d3e083c30f344022f95b7cd7114813bf6a28ecc67d5fe05953242684cd29c1c5dd8a74416890e5c943c70904ba70e349b15719a466f901fbf0cfc7840f8032e31afbccfb84f4a817ea51c8f90fd6a

我们根据标准的私钥格式可以知道这里的三个数分别是dp,dq和qinvp。

所以就迎刃而解了,知道dp求p是入门就会的题

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
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Util.number import inverse, long_to_bytes

# 已知的值
n = 0x00bddb9913778d2315449b587fbd7f6e41944d1c9c5484c9b01fbf534493355cf88489cbf9630f4299bb4dbb84df7bd6607b7a578f84e4c6d6a70f506c16a4311b1508e3975db71e71d6d2d17dd75b9c23e2d437bcf7f5d9506e763c7122a576fd5c15905ab45feba20a241286285b8ba5d6dbd1b7d0ce975ab74eae338fe0dd5a194d02918e4f0ad6d160f1b730743fc2c8eb74db01089d210d50c5aa1f259793b512b26a8f3e62cc6a95e9cef65e5d49e9e66d352ddbb1408fa9fe5b29a1642c61d2c4d32c66c56dd6fa3639b2146b1f5929896abab4b1ed35ecf57dd630cecac15fcc6b743f97f086f2c6040e66e91e920531ba612be1927b6754525e08e519
e = 0x10001

# 计算 p 和 q
p = 168313094832701536524143692026188889238910657975201068946311023794045822939742190630742331098611520920441744509630055779303715469174526027959177454613815671084470633774174727533319238414598895408565062416121236098749371218415941909124603561136636662005500860470341786311483414446448235397821801077833382037591
q = n // p

# 检查 p * q 是否等于 n
assert p * q == n, "p * q does not equal n!"

# 计算 d, dp, dq 和 invpq
d = pow(e, -1, (p - 1) * (q - 1)) # 计算私钥指数
dp = d % (p - 1)
dq = d % (q - 1)
invpq = inverse(q, p)

# 检查 dp 和 dq 是否正确
assert dp == d % (p - 1), "dp is incorrect!"
assert dq == d % (q - 1), "dq is incorrect!"

# 检查 invpq 是否正确
assert invpq == inverse(q, p), "invpq is incorrect!"

# 验证 invpq 是否满足 (invpq * q) % p == 1
assert (invpq * q) % p == 1, "invpq * q is not congruent to 1 mod p!"

# 加密的消息 c
c = 0x6f4cb0df50eb133f104727316eb23e25463b6b46a1ff743507c7663094da88a091c77c1d686a91613fa2da697c23924798b40654ba420b9690c5ceb9a362cf48e72c39177c6a3ebecc4e0ba2b9673f070a23e535fff7a01b400381ede60a6f9bf86047b3dd2c663c329b9287749bdb3783303802129b93af083aa2045c500fe0c2a7a018c2403881115927ae56ff14338b9fb98d5a5f461916d962aea7c3379ec7f7d8d77b4cc8ff756895c1500d9f2cee3552f17216339b0d67f27e0dd07e9ec1861f14c962b977559561d709d57e58fd6e6aafd27892c6d43d16b3db267902b9ce8f9ff89a66ab822b5ea3a68c872a32c69961df15581b70c00e4c61804d0d

# 构造 RSA 密钥
key = RSA.construct((n, e, d, p, q))
cipher = PKCS1_OAEP.new(key)

# 解密消息
decrypted_message = cipher.decrypt(long_to_bytes(c))
print(decrypted_message)
#HECTF{t1an_dog_no_g3t_g00d_d1e}

2024-HECTF-wp-crypto
https://py-thok.github.io/2024/12/08/2024-HECTF-wp-crypto/
作者
PYthok-Ptk
发布于
2024年12月8日
许可协议