nepsign

题面:

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
from gmssl import sm3
from random import SystemRandom
from ast import literal_eval
import os
flag = os.environ["FLAG"]
def SM3(data):
d = [i for i in data]
h = sm3.sm3_hash(d)
return h
def SM3_n(data, n=1, bits=256):
for _ in range(n):
data = bytes.fromhex(SM3(data))
return data.hex()[:bits // 4]


class Nepsign():
def __init__(self):
self.n = 256
self.hex_symbols = '0123456789abcdef'
self.keygen()

def keygen(self):
rng = SystemRandom()
self.sk = [rng.randbytes(32) for _ in range(48)]
self.pk = [SM3_n(self.sk[_], 255, self.n) for _ in range(48)]
return self.sk, self.pk

def sign(self, msg, sk=None):
sk = sk if sk else self.sk
m = SM3(msg)
m_bin = bin(int(m, 16))[2:].zfill(256)
a = [int(m_bin[8 * i: 8 * i + 8], 2) for i in range(self.n // 8)]
step = [0] * 48;
qq = [0] * 48
for i in range(32):
step[i] = a[i]
qq[i] = SM3_n(sk[i], step[i])
sum = [0] * 16
for i in range(16):
sum[i] = 0
for j in range(1, 65):
if m[j - 1] == self.hex_symbols[i]:
sum[i] += j
step[i + 32] = sum[i] % 255
qq[i + 32] = SM3_n(sk[i + 32], step[i + 32])
return [i for i in qq]

def verify(self, msg, qq, pk=None):
qq = [bytes.fromhex(i) for i in qq]
pk = pk if pk else self.pk
m = SM3(msg)
m_bin = bin(int(m, 16))[2:].zfill(256)
a = [int(m_bin[8 * i: 8 * i + 8], 2) for i in range(self.n // 8)]
step = [0] * 48;
pk_ = [0] * 48
for i in range(32):
step[i] = a[i]
pk_[i] = SM3_n(qq[i], 255 - step[i])
sum = [0] * 16
for i in range(16):
sum[i] = 0
for j in range(1, 65):
if m[j - 1] == self.hex_symbols[i]:
sum[i] += j
step[i + 32] = sum[i] % 255
pk_[i + 32] = SM3_n(qq[i + 32], 255 - step[i + 32])
return True if pk_ == pk else False


print('initializing...')
Sign = Nepsign()
while 1:
match int(input('> ')):
case 1:
msg = bytes.fromhex(input('msg: '))
if msg != b'happy for NepCTF 2025':
print(Sign.sign(msg))
else:
print("You can't do that")
case 2:
qq = literal_eval(input('give me a qq: '))
if Sign.verify(b'happy for NepCTF 2025', qq):
print(flag)

分析:

迭代哈希函数的代数性质

SM3_n(x,a+b)=SM3_n(SM3_n(x,a),b)SM3\_n(x,a+b)=SM3\_n(SM3\_n(x,a),b)

类似于一个加法群的结构:

  • H(x,k)=SM3_n(x,k)H(x, k) = SM3\_n(x, k)
  • 则有 H(x,a+b)=H(H(x,a),b)H(x, a + b) = H(H(x, a), b)

签名生成过程:

  • 前32个签名元素:

    • m 转换为 256 位二进制 m_bin
    • 分成 32 个 8 位组:a_i = m_bin[8i:8i+8] (0 ≤ i < 32)
    • 签名元素:qqi=SM3_n(ski,ai)qq_i = SM3\_n(sk_i, a_i)
  • 后16个签名元素:

    • 对于十六进制字符 c (0 ≤ i < 16):
      • 计算位置和:sumi={j  m[j]=c}sum_i = \sum \{j\ |\ m[j] = c\}
      • 取模:stepi=sumi mod 255step_i = sum_i \ mod\ 255
      • 签名元素:qq32+i=SM3_n(sk32+i,stepi)qq_{32+i} = SM3\_n(sk_{32+i}, step_i)

验证过程:

验证时使用逆操作:

pki=SM3_n(qqi,255stepi)pk_i = \text{SM3}\_n(qq_i, 255 - step_i)

这基于:

SM3_n(SM3_n(ski,stepi),255stepi)=SM3_n(ski,255)=pki\text{SM3}\_n(\text{SM3}\_n(sk_i, step_i), 255 - step_i) = \text{SM3}\_n(sk_i, 255) = pk_i

攻击原理:

根据迭代哈希的可组合性:

H(x,a+b)=H(H(x,a),b)H(x, a + b) = H(H(x, a), b)

如果已知:

  • 消息 M 的签名元素:qqM=H(sk,sM)qq_M = H(sk, s_M)
  • 目标消息 T 的步数:sTs_T

所以前32个步数值可以直接由位操作提取,后门16个步数值基于字符位置统计得到目标信息的step值

且满足 sMsTs_M \leq s_T,则可以计算目标签名:

qqT=H(qqM,Δ)=H(H(sk,sM),Δ)=H(sk,sM+Δ)=H(sk,sT)qq_T = H(qq_M, \Delta) = H(H(sk, s_M), \Delta) = H(sk, s_M + \Delta) = H(sk, s_T)

其中 Δ=sTsM\Delta = s_T - s_M

然后一直循环知道找到全部签名元素即可

题解:

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
from gmssl import sm3
from pwn import *
import os
import ast
from tqdm import tqdm
import time

SERVER_IP = 'nepctf32-efjp-shsj-n0cr-uqygeog3d763.nepctf.com'
SERVER_PORT = 443
TARGET_MSG = b'happy for NepCTF 2025'
MAX_ATTEMPTS = 3000
RETRY_DELAY = 1

def SM3(data):
if isinstance(data, str):
data = data.encode()
return sm3.sm3_hash([i for i in data])

def SM3_n(data, n=1, bits=256):
if isinstance(data, str):
data = bytes.fromhex(data)
for _ in range(n):
data = bytes.fromhex(SM3(data))
return data.hex()[:bits // 4]

def calc_step(msg):
if isinstance(msg, str):
msg = msg.encode()

m = SM3(msg)

m_int = int(m, 16)
step = []
for i in range(32):
step.append((m_int >> (248 - 8*i)) & 0xFF)

hex_symbols = '0123456789abcdef'
char_positions = {c: [] for c in hex_symbols}
for j in range(64):
char_positions[m[j]].append(j+1)

for c in hex_symbols:
s = sum(char_positions.get(c, []))
step.append(s % 255)

return step

def main():
target_steps = calc_step(TARGET_MSG)
print(f"目标消息的step值: {target_steps}")

target_qq = [None] * 48
collected = [False] * 48

print(f"连接到服务器 {SERVER_IP}:{SERVER_PORT}...")
conn = remote(SERVER_IP, SERVER_PORT,ssl=True)
conn.recvuntil(b'> ')

progress = tqdm(total=48, desc="收集签名元素", unit="element")

attempt_count = 0
while not all(collected) and attempt_count < MAX_ATTEMPTS:
attempt_count += 1

msg_rand = os.urandom(32).hex()

try:
conn.sendline(b'1')
conn.recvuntil(b'msg: ')
conn.sendline(msg_rand.encode())
response = conn.recvline().decode().strip()
conn.recvuntil(b'> ')
except EOFError:
print("\n连接中断,尝试重新连接...")
time.sleep(RETRY_DELAY)
conn = remote(SERVER_IP, SERVER_PORT)
conn.recvuntil(b'> ')
continue

try:
qq_list = ast.literal_eval(response)
except:
print(f"解析失败: {response}")
continue

current_steps = calc_step(bytes.fromhex(msg_rand))

new_collections = 0
for i in range(48):
if not collected[i] and current_steps[i] <= target_steps[i]:
delta = target_steps[i] - current_steps[i]
target_qq[i] = SM3_n(qq_list[i], delta, 256)
collected[i] = True
new_collections += 1

if new_collections > 0:
progress.update(new_collections)

progress.close()

if not all(collected):
print(f"未能在 {MAX_ATTEMPTS} 次尝试内收集所有签名元素")
print(f"缺失位置: {[i for i, c in enumerate(collected) if not c]}")
conn.close()
return

print("成功构造目标签名!")

try:
conn.sendline(b'2')
conn.recvuntil(b'give me a qq: ')
conn.sendline(str(target_qq).encode())
flag_response = conn.recvline().decode()

if b"NepCTF" in flag_response.lower():
print(f"获取flag: {flag_response}")
else:
print(f"验证失败: {flag_response}")
except Exception as e:
print(f"提交签名时出错: {str(e)}")

conn.close()

if __name__ == '__main__':
main()

image-20250726204950019

LatticeBros

题面:

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
#已知α的极小多项式为三次多项式f(x),即f(α)=0,且α≈54236.606188881754809671280151541781895183337725393
#上述极小多项式的常数项为a0

from secret import a0,alpha
import gmpy2
from Crypto.Util.number import long_to_bytes
import random
from math import sqrt,log2

d=981020902672546902438782010902608140583199504862558032616415
p = d - a0

k=sqrt(log2(p))+log2(log2(p))
B = 2**30
assert B < p/2**k

m = 30
assert m > 2*sqrt(log2(p))

samples = []
betas = []

f = open("samples.txt",'w')
for _ in range(m):
t = random.randint(1, p-1)
beta = random.randint(-B + 1, B - 1)
a = (t * alpha - beta) % p
samples.append((t, a))
betas.append(beta)

f.write(str(samples))

for i in range(0,30):
assert (betas[i]-samples[i][0]*alpha+samples[i][1])%p == 0

#flag = long_to_bytes(alpha)

分析:

经典HNP问题,但要先得到p,要得到p就要得到α\alpha的极小多项式的常数项a0a_0

极小多项式满足:

α3+a2α2+a1α+a0=0\alpha^3+a_2\alpha^2+a_1\alpha+a_0=0

构造格:

(α31α21α11)\begin{pmatrix}\alpha^3&1&&\\\alpha^2&&1&\\\alpha&&&1\\1&&&\end{pmatrix}

对第一列配平,然后规约即可得到a1,a2a_1,a_2,则a0a_0就得到了,至此得到了p

然后对于HNP问题,构造:

(t1t2t30a1a2a30ppp)\begin{pmatrix}t_1&t_2&\dots&t_{30}\\a_1&a_2&\dots&a_{30}\\p\\&p\\&&\ddots\\&&&p\end{pmatrix}

即可规约出βi\beta_i,由

α=(a+β)t1  mod  p\alpha = (a+\beta)t^{-1}\ \ mod\ \ 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
43
from Crypto.Util.number import *

B = 2**30
m = 30
d=981020902672546902438782010902608140583199504862558032616415

a = QQ(54236.606188881754809671280151541781895183337725393)
print(a)
print(a.denominator().bit_length())
M = Matrix(QQ,4)
for i in range(4):
M[i,(i+1)%4] = 1.0
if i != 3:
M[i,0] = a^(3-i)

M[:,0] *= getPrime(76)

a1 , a2 = M.LLL()[0][-1] , M.LLL()[0][-2]
a0 = -a^3 - a2*a^2 - a1*a

p = d - a0
print(int(p).bit_length())

from math import sqrt,log2
k=sqrt(log2(p))+log2(log2(p))

data = [(541847931463604073209188621415697353813245102261880389530448, 293760933113243563398917466885108625646262447370201484418246), (235213326900086489464935804156966465366154623411555613791270, 660823982268225103178763707015491421784294988488272636270997), (826464884761457937459245903152143755707241416981488127320435, 428521663319038461250005113612781686761766888058391496085911), (589542000504317435156560078533519448295689695687499354390208, 155284353896000150766154807679279597476176668344402166959399), (968823371588600973965757332601758200815345862153455338808286, 870008943690791009196027169525956126827736285614393106689402), (621636099728440147413990266662022925118216803638588918660041, 265635912066749696542909843111997941904342442664219734956888), (426696569424050102229606043215592727790577655338668728275370, 279313121876980354011480010042682666651614765507190502627689), (89450479064580125731654556963306718472532905610952012502649, 465933125964565419295325650759566635253450915499965633327941), (480355476500393865742379469913983270769356894135485925662119, 894041172171871806404285309781862268351135623868845025443422), (842436524669577199024236805258573090764419350786291073287889, 345478552143958037534551648319293899442551000874041707820740), (650054674429185550652935714084022116516082323269321462104664, 441999979283903658157822753439653947343822546158589507765994), (46289431385578693366971976442426853079852982529357847290686, 625618376463384339878849844467050454204685252824782609369180), (71444185449163133531919043374545893927347050624346741281881, 955925578289311966288639224625142299309823207245807788495453), (192579726169321656812883068526498248523814846320328766176253, 626481822474054336470183912297952839011392733501646931370367), (736527635648804640774976580747540045854351230084566721853611, 276626211757586963928788091386096607703513204646314683038338), (177922521867185878959621840269164617147915792720210315529733, 541058782621716573816245900423919799500476442285991532228641), (40610451174818168154306630612571678739921107216052349044576, 727642592899858828601137105077611015328512898368636299587376), (385012983728389322601149562441674995471397288632464238356283, 353921151307105661267278594470212933060655245893209524497156), (750447975601038834764379841158092390933760641866111445401426, 391626416964965737035878375834907580903143512300198923948189), (115058604943298010958881205548782439407592353731185670266593, 491630592857258949793489206081490523001249620510479961058022), (327389234395954477946639629629085910688793716425320663599360, 24975272330009592102362429346350824580378490147041708568130), (115595274689129534885608766476695918464309130165432995990883, 757961876891952019297626599379744405302595090402128271144165), (950804723308776351161744501221236453742418549093165078282534, 20307246759635231945223392614290397512873344480184942904518), (724537610412063699714461780160573528810830178440136810747811, 149681928388378582933943374524511804362928290938917573644613), (340891278018589324130004945217960336392205386747747011263373, 683307718413135477104477081812052183267507312278283317237187), (104379682905784169840335131193505192063050242530811180817410, 715010230598797717533306270232399781090458356371977748416491), (644160326926600986730919713173510327120201404569141824224075, 127877985489410167008195578625004740882394608402141169695352), (549253388716005399852261816416312267100135940382820676807345, 210560134643237517255193955173709174155305784935427470113433), (968265711632086435506163736279856957220961064226797549228006, 273174723915971720522674140326199419265943707917542063022561), (704367622558261900937184683100177434487519780290678439135652, 959106497548134540301589019840013331842784496835379005298630)]

Mat = Matrix(ZZ,32,30)



for i in range(30):
Mat[0,i] = data[i][0]
Mat[1,i] = data[i][1]
Mat[i+2,i] = int(p)

for betas in Mat.LLL():
for i in range(len(betas)):
flag = long_to_bytes((inverse(data[i][0],int(p))*(data[i][1]+betas[i]))%int(p))
if b'NepCTF' in flag:
print(flag)
break

ezRSA2

题面:

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
from Crypto.Util.number import getStrongPrime, getRandomNBitInteger, GCD, inverse, long_to_bytes, bytes_to_long, sieve_base
from flag import flag


def gen_parameters(gamma=0.33, beta=0.33):
p = getStrongPrime(1024)
q = getStrongPrime(1024)
N = p*q
phi = (p-1)*(q-1)
while True:
d = getRandomNBitInteger(int(2048*beta))
if GCD(d, phi) == 1:
break
e = inverse(d, phi)

hints = []
M = 1
for i in range(1, len(sieve_base)):
li = sieve_base[i]
hints.append(d%li)
M *= li
if M.bit_length() >= 1024*gamma:
break

return e, N, hints



def main():
e,N,hints = gen_parameters()
print(f'e={hex(e)}')
print(f'N={hex(N)}\n')
print(f'hints={hints}\n')

flag_prefix = b'NepCTF{'
assert flag.startswith(flag_prefix)
assert flag.endswith(b'}')

pt = bytes_to_long(flag[len(flag_prefix):-1])
ct = pow(pt, e, N)
print(f'ct={hex(ct)}')

main()


"""
e=0x73915608ed64c9cf1a2279684cab4f4a78fba229d45d4f860971a241481363470a19cb0dc0d00f816b5befdaca017cf71483e96ef17b36179012f5194a0e6bf481bb06c2644f74c6812efb65d05c00631f282d6aa55c0bc140a1830b95a1cf4b6024cb0db53f2c2189897c41f22e2eec773723f531ec4bfa537fae6de5fe480cf46fe17850f7eb47df08194d95db3d26ac923b26e110ee645239ab586bbc546ddc5906f280a106edbb727ccb05536b5a3f5c0ebcf865c95ce58be54f7f3547aa53baa218b0dfa98e42d925fa341e45f94a3b16b0c83802660c7f34de3336cb21f219073cf8e9f5e39d47f0a9a9ee7c255f09a6add9a2f7a47960f4a853183d29
N=0xba8956e81394f3f1265ca5d9c4ad1ab0078bb43c4b80a231ab2cc62246ae45f66a562252622aed2cbbfc08647ef2fec0f97a632bf2242845f4b3af0c427cec3d90f42e90278a5a0feeed0922a8cd2278074ac54e9cfc0e96ff68f8d8f266dd87dc1cc59c2895ec884de2022311767f6a9a7e0bd288c79620e28b83bb3c8d8ad1047c839d6ccf5544eaf434a5f00b951769ab3121298d04b63a162757beb3d49917cd0c9e02ee1ac29398c8130961d5a2f2833aba1e538edb7bb97071f40fae543d1622f0c9206c6d4d8abb2ac1b93ebfb603c2f3a909ede357ade4043550fe540d13a4e87db8d731fe130f15a43a1a00364f5da2d87f7b660c3a04e734218a11

hints=[1, 3, 0, 3, 9, 16, 10, 14, 5, 11, 21, 18, 30, 30, 38, 2, 20, 62, 66, 1, 22, 56, 41, 13, 78, 59, 51, 6, 57, 117, 73, 75, 96, 112, 50, 93, 158, 97, 146, 8, 65, 96, 186, 161, 90, 131, 46, 32, 140, 133, 50, 43, 151, 234]

ct=0x101b284ad196b5bbd3d3df00a7d3577caeb29c681bdd122582b705afc671febf45d4f3786640e55aadd6a31ecc49175f97b772720f1735f8555f768b137a4643cd6958f80a3dfca4d0270ad463d6dde93429940bd2abb5ad8408b0906fa8d776544a1c50cc0d95939bef4c3fb64d0b52dca81ff0f244fc265bfc0bc147435d05f8f1a146e963a1403b3c123b4d6e73d1fd897109995009be1673212607f0ea7ae33d23f3158448b05c28ea6636382eee9436c4a6c09023ead7182ecd55ac73a68d458d726e1abc208810468591e63f4b4c2c1f3ce27c4800b52f7421ccab432c03e88b3b255740d719e40e0226eabb7633d97ed210e32071e2ac36ed17ef442e
"""

分析:

hints给了d模若干小素数的结果,而且这些小素数我们都已知,所以可以crt得到d的低位,然后对于

e(dhM+dl)=1+k(n(p+q)+1)e(d_h*M+d_l)=1+k(n-(p+q)+1)

其中M是前面若干小素数的乘积,令p+q=s,即求一个k,s的二元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
68
69
70
71
72
73
74
75
from Crypto.Util.number import *
from sympy import nextprime
import itertools
from tqdm import *

e=0x73915608ed64c9cf1a2279684cab4f4a78fba229d45d4f860971a241481363470a19cb0dc0d00f816b5befdaca017cf71483e96ef17b36179012f5194a0e6bf481bb06c2644f74c6812efb65d05c00631f282d6aa55c0bc140a1830b95a1cf4b6024cb0db53f2c2189897c41f22e2eec773723f531ec4bfa537fae6de5fe480cf46fe17850f7eb47df08194d95db3d26ac923b26e110ee645239ab586bbc546ddc5906f280a106edbb727ccb05536b5a3f5c0ebcf865c95ce58be54f7f3547aa53baa218b0dfa98e42d925fa341e45f94a3b16b0c83802660c7f34de3336cb21f219073cf8e9f5e39d47f0a9a9ee7c255f09a6add9a2f7a47960f4a853183d29
n=0xba8956e81394f3f1265ca5d9c4ad1ab0078bb43c4b80a231ab2cc62246ae45f66a562252622aed2cbbfc08647ef2fec0f97a632bf2242845f4b3af0c427cec3d90f42e90278a5a0feeed0922a8cd2278074ac54e9cfc0e96ff68f8d8f266dd87dc1cc59c2895ec884de2022311767f6a9a7e0bd288c79620e28b83bb3c8d8ad1047c839d6ccf5544eaf434a5f00b951769ab3121298d04b63a162757beb3d49917cd0c9e02ee1ac29398c8130961d5a2f2833aba1e538edb7bb97071f40fae543d1622f0c9206c6d4d8abb2ac1b93ebfb603c2f3a909ede357ade4043550fe540d13a4e87db8d731fe130f15a43a1a00364f5da2d87f7b660c3a04e734218a11

hints=[1, 3, 0, 3, 9, 16, 10, 14, 5, 11, 21, 18, 30, 30, 38, 2, 20, 62, 66, 1, 22, 56, 41, 13, 78, 59, 51, 6, 57, 117, 73, 75, 96, 112, 50, 93, 158, 97, 146, 8, 65, 96, 186, 161, 90, 131, 46, 32, 140, 133, 50, 43, 151, 234]

ct=0x101b284ad196b5bbd3d3df00a7d3577caeb29c681bdd122582b705afc671febf45d4f3786640e55aadd6a31ecc49175f97b772720f1735f8555f768b137a4643cd6958f80a3dfca4d0270ad463d6dde93429940bd2abb5ad8408b0906fa8d776544a1c50cc0d95939bef4c3fb64d0b52dca81ff0f244fc265bfc0bc147435d05f8f1a146e963a1403b3c123b4d6e73d1fd897109995009be1673212607f0ea7ae33d23f3158448b05c28ea6636382eee9436c4a6c09023ead7182ecd55ac73a68d458d726e1abc208810468591e63f4b4c2c1f3ce27c4800b52f7421ccab432c03e88b3b255740d719e40e0226eabb7633d97ed210e32071e2ac36ed17ef442e

a = 3
primes = [a]
for i in range(53):
a = nextprime(a)
primes.append(a)

print(primes)

d0 = crt(hints,primes)

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.coefficients_monomials()
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 []


M = prod(primes)
PR.<k,s> = Zmod(e*M)[]
f = 1 + k*n - k*s +k - e*d0
k,s = small_roots(f,(2**680,2**1024))[0]


x, y = var('x y')
eq1 = x * y - n
eq2 = x + y - int(s)

p = solve((eq1, eq2), (x, y))[0][0].rhs()

d = inverse(e,p-1)
m = pow(ct,d,p)
print(b'NepCTF{'+long_to_bytes(m)+b'}')
# NepCTF{larg3r_M0du1u5_1nf0_g1ves_b3773r_b0und5}

Transition

题面:

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
from Crypto.Util.number import *
from Crypto.Cipher import AES
from hashlib import sha256
import socketserver
import signal
import os
import string
import random
from Crypto.PublicKey import RSA
from Crypto.Util.number import getPrime,getStrongPrime
from Crypto.Random import get_random_bytes
from sample import sample_clock,sample_serialdata

import numpy as np
from scipy.io import savemat,loadmat
from random import randint
FLAG=os.getenv("FLAG","FAKE{"+6*"0123456789").encode()

class Task(socketserver.BaseRequestHandler):
def _recvall(self):
BUFF_SIZE = 1024
data = b''
while True:
part = self.request.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
break
return data.strip()

def send(self, msg, newline=True):
try:
if newline:
msg += b'\n'
self.request.sendall(msg)
except:
pass

def recv(self, prompt=b'> '):
self.send(prompt, newline=False)
return self._recvall()

def handle(self):
'''
case of clock
generate_clock_signal_with_slack(
sampling_rate=500e6,
signal_frequency=4e6,
num_cycles=100,
output_file="clock_signal.mat"
)
'''

count=70
l=64
data=[random.randint(0,1) for i in range(l)]
print(data)
sample_serialdata(
sampling_rate=500e6,
signal_frequency=4e6,
data=data,
output_file="serial_data.mat"
)
samples = np.array(loadmat("serial_data.mat")["data"][0])

while count>=0:
self.send(b"function\n1.query\n2.get flag")
s=self.recv()
try:
s=int(s)
except:
exit()
if s==1:
self.send(b"ur sample position?")
s=self.recv()
try:
s=int(s)
except:
exit()
if 0<=s<len(samples):
self.send(str(samples[s]).encode())
else:
self.send(b"wrong position\n")

elif s==2:
for i in range(l):
self.send(b"ur guess data[%d]?"%i)
s=self.recv()
try:
s=int(s)
except:
exit()
if s!=data[i]:
self.send(b"wrong")
return
self.send(b"congratulations! your part flag is:")
self.send(FLAG[:count])
count=count-1










class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass


class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass


if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 9999
print("HOST:POST " + HOST+":" + str(PORT))
server = ForkedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()

分析:

拿到.mat文件里可以看到image-20250727190608500

仔细看看就可以得到大概的上界和下界的抖动范围,想到了数电知识,大概分为四种情况:

  • 稳定高电平
  • 稳定低电平
  • 上升沿
  • 下降沿

这四种情况都可以同时确定2bit,这样就可以得到完整flag了。

通过输出连续的采样点大概可以知道这四种情况的边界以及采样点的起始点的选择,间隔是500/4=125

题解:

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
from pwn import *
from tqdm import *

HOST = 'nepctf32-laom-rzt9-vzzd-hp7726mff271.nepctf.com'
PORT = 443

io = remote(HOST, PORT,ssl=True)
l = 64

bits = []
for count in range(l//2):
position = 127 + 250*count
io.recvuntil(b'function')
io.sendafter(b">", b"1\n")

io.recvuntil(b'ur sample position?')
io.sendafter(b">", (str(position) + "\n").encode())
value = eval(io.recvline().decode().strip())
print(value)

if value < 0.5: bits.extend([0,0])
elif 0.5 < value < 2: bits.extend([0,1])
elif 2 < value < 3: bits.extend([1,0])
elif 3 < value: bits.extend([1,1])

print(bits)
io.recvuntil(b'function')
io.sendafter(b">", b"2\n")
for i in trange(64):
io.recvuntil(b">")
if bits[i] == 0:
io.sendline(b"0\n")
else:
io.sendline(b"1\n")

io.recvuntil(b"congratulations! your part flag is:\n")
print(io.recvline())
# flag{lT'5-tR@Nsitl0n-l3akAgEf0e5d7f7}