N1CTF Junior part

owo

做出了两道题,学到很多干货

sign in the ca7s

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
from Crypto.Util.number import bytes_to_long
from hashlib import md5
import os

FLAG = os.environ.get("FLAG", "flag{**redacted**}")

E = EllipticCurve(GF(0x1337_ca7_eae368ff5d702e6067aaaa77ca_ca7_1337), [0, 3])
G, n = E(1, 2), E.order()

def sign(priv, ctx, msg):
k = bytes_to_long(ctx + md5(str(priv).encode() + msg).digest())
z = bytes_to_long(md5(ctx + msg).digest())
r = int((k * G).x()) % n
s = (pow(k, -1, n) * (z + r * priv)) % n
return r, s

def verify(pub, ctx, msg, sig):
z = bytes_to_long(md5(ctx + msg).digest())
r, s = sig
if 0 < r < n and 0 < s < n:
return r == int((pow(s, -1, n) * (z * G + r * pub)).x()) % n

def chall(level, flag):
priv = randint(1, n - 1)
pub = priv * G
msg = os.urandom(64)

print(f"=== level {level} ===")
for _ in range(catalan_number(level)):
ctx = bytes.fromhex(input('context: '))
r, s = sign(priv, ctx, msg)
assert verify(pub, ctx, msg, (r, s))
if level <= 1: print('message:', msg.hex())
if level <= 2: print('sign:', r)
if level <= 3: print('ature:', s)

r, s = map(int, input('signature: ').split())
assert verify(pub, b'n1junior_2025', f'cat /flag{level}'.encode(), (r, s))
print(f'flag{level}:', flag)

if __name__ == "__main__":
chall(0, "💧")
chall(1, "🐱")
chall(2, FLAG)

分析题目代码不难发现大概流程是有三个level,逐步通过来获取最后的flag,这里的level0和1是一样的所以我们一起来分析。

对于level0和1,会输出

1
2
3
if level <= 1: print('message:', msg.hex())
if level <= 2: print('sign:', r)
if level <= 3: print('ature:', s)

三个数据。

我们已经知道了r = (k*G).x() mod n,是曲线上点的横坐标,而在而在椭圆曲线上,每个 x 对应最多 2 个点(kG).x()=r⟹kG=(r,y)或者(r,−y),又因为此时G很小为E(1, 2),

那么我们就可以直接E.lift_x(Integer(r))返回在椭圆曲线上的点然后.log(G)求椭圆曲线离散对数。那么ctx就无所谓了,直接空都行。

接下来分析level2,这里我们有两次交互机会,能拿到两对r,s。

服务端的签名函数是

1
2
3
4
5
k = bytes_to_long(ctx + md5(str(priv).encode() + msg).digest())
z = bytes_to_long(md5(ctx + msg).digest())
r = int((k * G).x()) % n
s = (k^{-1} * (z + r * priv)) % n

此时我们有等式为$$s≡k^{−1}(z+r⋅priv)(modn)$$,同乘k再移一下项即可得到

​ $r_1⋅priv≡s_1k_1−z_1(modn)$,同理也就有

​ $r_2⋅priv≡s_2k_2−z_2(modn)$

两式相减

​ $(r_1−r_2)⋅priv≡(s_1k_1−s_2k_2)−(z_1−z_2)(modn)$

移项得到 $priv≡(s_1k_1−s_2k_2−(z_1−z_2))⋅(r_1−r_2)^{−1}(modn)$

对于z1和z2,我们需要利用哈希碰撞(工具为fastcoll)来找到两个md5值相同的ctx,并对其进行填充。我们知道

1
z = bytes_to_long(md5(ctx + msg).digest())

并且md5的处理是从前往后读取消息并不断更新内部值的,我们先对找到的ctx进行填充。因此 md5(t1_pad) 的输出就是“处理完 t1 并完成填充后的最终内部状态”。也就是说,当进行 md5(t1_pad + msg) 时,t1_pad 被当作“普通数据块”处理后,MD5 的内部状态在 t1_pad 处理完时等于 md5(t1_pad) 的状态,随后 MD5 继续把 msg 当作额外数据处理。所以我们这样就能使得z1=z2,就能消去一个未知数,从而求解priv,然后正常签名流程即可。

(由于这俩题本质是一样的,所以解题脚本一起贴到下一道题处)

sign the ca7s

就加了一个level3,这里直接来分析。level3的交互次数提高到了5次,并且只给了s,也就是说我们能拿到5个s的值,我们先来推导一下对应的方程组。这里同样是构造5个md5值相同的ctx使得z相同。具体如何生成只需要用fastcoll先生成一对,用其中之一作前缀继续生成,这么递推下去即可。

这里同样是有五个方程

$$s_1·k_1≡(z+r_1⋅priv)(modn)$$

$$s_2·k_2≡(z+r_2⋅priv)(modn)$$

$$s_3·k_3≡(z+r_3⋅priv)(modn)$$

$$s_4·k_4≡(z+r_4⋅priv)(modn)$$

$$s_5·k_5≡(z+r_5⋅priv)(modn)$$

显然未知数个数是远大于方程个数的,这里我们就要利用bytes_to_long操作的特点

bytes_to_long(ctx + md5(str(priv).encode() + msg)) 等于 bytes_to_long(ctx) << (8*len(md5(str(priv).encode() + msg))) + bytes_to_long(md5(str(priv).encode() + msg))。也就是说我们可以将k写为如下形式

$k_1=ctx_1·256^{16}+md5(priv+msg)$

$k_2=ctx_2·256^{16}+md5(priv+msg)$

$k_3=ctx_3·256^{16}+md5(priv+msg)$

$k_4=ctx_4·256^{16}+md5(priv+msg)$

$k_5=ctx_5·256^{16}+md5(priv+msg)$

那么显然k2,k3,k4,k5都可以用k1表示,也就是减少了四个未知数。那么我们想想还能不能继续减少呢。显然是可以的

就要用到我们在level0和1里用到的$r=(k·G)_x$,那么我们同时也可以定义(k·G)的纵坐标。用点加法代进去就可以把r2,r3,r4,r5用r1表示,有消去了四个未知数,但同时我们又引入了纵坐标,所以此时一共有5个未知数,和加上曲线方程的6个方程,是可以求解的。

我们直接用grobner基即可。

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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
from pwn import *
from sage.all import *
from gmpy2 import invert
from Crypto.Util.number import *
from hashlib import md5


def Sign(priv, ctx, msg):
k = bytes_to_long(ctx + md5(str(priv).encode() + msg).digest())
z = bytes_to_long(md5(ctx + msg).digest())
r = int((k * G).x()) % n
s = (pow(k, -1, n) * (z + r * priv)) % n
return r, s

def SmartAttack(P,Q,p):
E = P.curve()
Eqp = EllipticCurve(Qp(p, 2), [ ZZ(t) + randint(0,p)*p for t in E.a_invariants() ])

P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True)
for P_Qp in P_Qps:
if GF(p)(P_Qp.xy()[1]) == P.xy()[1]:
break

Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True)
for Q_Qp in Q_Qps:
if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]:
break

p_times_P = p*P_Qp
p_times_Q = p*Q_Qp

x_P,y_P = p_times_P.xy()
x_Q,y_Q = p_times_Q.xy()

phi_P = -(x_P/y_P)
phi_Q = -(x_Q/y_Q)
k = phi_Q/phi_P
return ZZ(k)

E = EllipticCurve(GF(0x1337_ca7_eae368ff5d702e6067aaaa77ca_ca7_1337), [0, 3])
G, n = E(1, 2), E.order()
ch = 0
while True:
try:
ch += 1
#io = remote("60.205.163.215", int(12559))
io=process(['sage', 'servercat.sage'])
level = 1
# =============Level 0=============
time.sleep(1)
io.recv()
io.sendline(b"")
msg = bytes.fromhex(io.recvline().split()[-1].decode())
r = eval(io.recvline().split()[-1].decode())
s = eval(io.recvline().split()[-1].decode())

# s = (pow(k, -1, n) * (z + r * priv)) % n
z = bytes_to_long(md5(msg).digest())
"""
k1 = (E.lift_x(Integer(r))).log(G)
k2 = (-E.lift_x(Integer(r))).log(G)
priv1 = ((s*k1-z)*invert(r, n))%n
priv2 = ((s*k2-z)*invert(r, n))%n
if bytes_to_long(md5(str(priv1).encode() + msg).digest()) != k1:
priv = priv2
else:
priv = priv1
"""
k = (E.lift_x(Integer(r))).log(G)
priv = ((s*k-z)*invert(r, n))%n
r, s = Sign(priv, b'n1junior_2025', f'cat /flag{level}'.encode())
io.recv()
io.sendline(b" ".join([str(r).encode(), str(s).encode()]))
print(io.recvline().decode())
level += 1
# =============Level 2=============
def md5_pad(msg: bytes) -> bytes:
bit_len = len(msg) * 8
msg += b'\x80'
while (len(msg) * 8) % 512 != 448:
msg += b'\x00'
msg += bit_len.to_bytes(8, 'little')
return msg

t1 = b'test\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\xf7\x8f\xebX\xbfx\xaf\xa5\x16\x11vy\x84"\x1e\xb2d7g\xde\x88\xc8\xa2 \xaay\xaa\x10\xac\xae&9\xf3\xf8\xe8\xc2k\xff\x90\xeb%\xbca\xf8\xa7\xa8\xbd\xd4X\x1f\x04\xad\xc2\x8dW8]sg_J\x10\xca=\xdd\xa6zr\xe0`\x0c\xc3;\x83\xa5P._,c%\xf3l+O\x17~v\xfd\xd3\xdfO!K\xc1nx\xc0\n\x84\xac\xa27\xad\x92\xef\xf8\r\xb3\x01G\x8a\xab\xd3Y\x8b)F_\x83\x03E\xfe\x93\xfe\xfa#'
t2 = b'test\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\xf7\x8f\xebX\xbfx\xaf\xa5\x16\x11vy\x84"\x1e\xb2d7\xe7\xde\x88\xc8\xa2 \xaay\xaa\x10\xac\xae&9\xf3\xf8\xe8\xc2k\xff\x90\xeb%\xbca\xf8\'\xa9\xbd\xd4X\x1f\x04\xad\xc2\x8dW8]s\xe7_J\x10\xca=\xdd\xa6zr\xe0`\x0c\xc3;\x83\xa5P._,c%\xf3\xec+O\x17~v\xfd\xd3\xdfO!K\xc1nx\xc0\n\x84\xac\xa27\xad\x92\xef\xf8\r3\x01G\x8a\xab\xd3Y\x8b)F_\x83\x03E~\x93\xfe\xfa#'
t1_pad = md5_pad(t1) # 256 B
t2_pad = md5_pad(t2) # 256 B
time.sleep(1)
io.recv()
io.sendline(t1_pad.hex().encode())
r1 = eval(io.recvline().split()[-1].decode())
s1 = eval(io.recvline().split()[-1].decode())

io.sendline(t2_pad.hex().encode())
r2 = eval(io.recvline().split()[-1].decode())
s2 = eval(io.recvline().split()[-1].decode())

# s = (pow(k, -1, n) * (z + r * priv)) % n
k1 = (E.lift_x(Integer(r1))).log(G)
k2 = (E.lift_x(Integer(r2))).log(G)
priv = ((s1*k1-s2*k2)*invert(r1-r2, n))%n
r, s = Sign(priv, b'n1junior_2025', f'cat /flag{level}'.encode())
io.recv()
io.sendline(b" ".join([str(r).encode(), str(s).encode()]))
flag = io.recvline().decode()
print(flag)
level += 1
# =============Level 3=============
ctx1=b'\xac}\xd44G\xb3\x83<\xa13\x9fS\xa6\xb6\x89c5\x9e\xd4\x802\t\xca\xdd\xfe\xbdzQ\xe9\xab\xbf\xb1\xfa\xf4\xcc\xe6:kG!\xeb\xd9H\xdcB\xf0*R\x8bz\x9da\xea\x02\xdfd6\x83dO\xa8\xab\xa1\x83\xa3 \x7f\x94\xf2\x98R\xaf\x95\x8b\xfc\x95\x8f\x1d\xb1\x19s\xf4\xcdZ\x13\xc0\x92O\x84-\xf0\xcf_`\xa5\xc1\xb5A\xb5\xea\xf4E\xa3\xf7\x97\x91\xe7\xb8s\xe6\xe0H\xa5\xfa&7\x0e8X\x0c\xcc\x82:\t\x92+S]\xb8V1f\xb2U\xc3\xb7\xd1Y\xbf\xc4\xd8Y\xf6uw\x10\x91u\x03\xf6\x8d\xfa.>Ae(\x8a\x0c\xa45\x1a\xc1\xbf|J\xb6\x17G\x10b\x1c\xd7\xc5\x94`\x98\xaa\xc7\t\x10\x15gT\x9e\x1e\x1a\xd4\x14\x8d\x05\xf8{\x7f\x81(\xc2{X\xa7#\xdb\x1d\r5\xdb\xb3\xed\xbaN\xaa\xb0\x11\xa6.\xee\xc9Z\xfe\xc8\xec\x07\xe2\xed\xf6\x8b\x9e\x9a\x06\x0b\xdfQ\xa8\x8cH\xb7\xa7PAfa\xc1WD\xb8\xce1.9\xd8P\xa0s\x00\xe5\xfdV\xb4t\xe4n\xc5\xd8\xf9[\xb6n{\x00\x96\xdd\xde\xbcQ\xb8[kMk\xc28\xaa;\x02\x8c\xc2"\xb8/\xa9\xc5\xe5\xee\x85MyG\xe2\xbd\x1a\xce\x0b\xd3dbs\x04\x1aY]\x84\x85\xe9\xb1\xa9\x01\x96\xfa}\xa0\xcf\xb5]\xe1`\xba\x98\x885\x17o$\xd5\x16M^8-\xca\xc9!\x86\x0e&\xd1\x1a~7\xec\x06\xdec6e\xa6X\xf4\xca\xe3\x8f\xaaj@w\xbehyj3\x13\xfa\x87\x869\xe6\xa1\xeb\xa2\x00\xdei:&\x05\xbc=\x989\xe4\x86\x1d\r\x869\x10\xe3gM\x19\xc1\xce\xdbE\xa1\x9fj\x9a\x98\x05\xb6\x7f9\xf9\x1d\x13\x03^\r\x0e\xe6Zj}\xfe\x0b\xeeRY\xcb\x97(\\\xe31\t\x18\xf2\xdf\x16\xccWA\x01\xf9.\x15\r\xfdK\x852\xc7\xc67$\x18[\\\x1e5\xb5\xdf\x19[$?\x03\xd2\x05&\x16\xea\xd1\x1a\xf6\xcb\xecG\xfe\xef\xcee\xc6\x95\xf2P\x08\x10x&Y\x0f\x8bd\xf1\xe84K\x886\xe27G\xfe\xd6\xd6\xb4\x9f/\\Y\xaa"\xc9\x1a\x85\xdc\xbc\x990\xd0{\x8f$\x06.h\xa5\xff\xdd\xdaIzB*\xbf\x00\x16y!\x8a>\x18\xa8\x1b\xc6\xc7\x07\\\x8a\x99\xba\xef\x03?\xf4\x1f~lK\xd5\x8c\xcdO\xdb\x1b\xaeu=\x9f\xef\x82\xee5\xf4\x9e\x00\xf7f\xc8#\xec\x9dw\x1f\x99\xb55\xfbE\x11\xa2\xce\x1dFo\xf8=\xe1U\xd1;\x14\xc5P\x1aY\xae\x0b+\xde\xdd[\x19\x83w\x91\xa4\xa1/\xebjT\x84\x8b\x8eP\xb2\xf8a\xea\x9c2\xdaPv\xbd\x89\xc3\xa9*\xf3\x07\x07\xd3\xdd|\x08z_!\xee\x11\xf0J\x9e\xfc\xf9\x1b\xc8\x9c&\xc2\xc5\x15*\x84b.B\xad\x9b=\r\xb9\xc9\xea\xa1\xf1U\xc7\xe7\xde\x1a\xde\x954;\xf1\xd2\x16yi\xdb\x8b%\xebK7dL\xe3\x92\xb7\t2\x190\x1a?\xf1\xf73\xf9=\xb5b$\x8d\xc60/9\x95\xef\xe7\x82\xdd\xf0H\xef\xa4\xd0\x16u}m\x1f?\xe9}\x15\x99&~OUr\x99\x0c.\xbe\xf9\\\xc1xm\xbd30\x16Z\xf9`*\x18\xbcI:[\x8a\t\xde\xdco\xa8\xe3\x81\xae\xe7.F\x94\xcbG\x179\x03#\xc6a\x0e:\x0eC%5,\x8e9\xb2\xed\xfa\xe0\xba\r\x08UG\xe8`Y&\n[n\xfcKQ`\xf7\xe6\xcc\xfb7-\x90\xd1\xca\xac:f\xb5\xd1\xd8\xfa\x8a\xca\x05\x9a\x13\xb9\x9f4C\xc7\xe0\xd5B\xaeA\xa5\x17F\x1e\x9e\xcbZ\x86;\xec\xc7\xfd\xe7\xf0Z\x1e\x98\x06\x8f\xe4\x83\x9a\x86A\xef\xb5\x04vlB_8\x1cP\xac]Y\xad\xd8\x19\x93Ygk\xb8\xa9G\xca[\xca"\xb4{\xc9K\xabf\xf9G\xb5\xc2\xc3\xe0\xc8\xc2s\x8b4\x8c-\x82C\x91S\xb66\x8c"\xc2\xe7\xc5\\. \xceg>dY4\x1e\x87)\x17+?\xb4\xd0h\xa5\x9bLnr\xf0<\x1dA\xb8\xe4\xf1\xf2?\xd3%\xa6\x8b\x1e\xbf\x82\x80M\xea\xbc.\xa8\xbc\xc5\xb1\x1b\xe6\x0e^\xcb\x1an9\xec\x86\xe2\xe5\xe8\x84\x9eW\x02\x92\xfb\x83\x8a\'<\x8f\xb2x\xe6\xea\xe11fn\xec!\xd48^%<\xc4Y\xfbn\xda'
ctx2=b'\xac}\xd44G\xb3\x83<\xa13\x9fS\xa6\xb6\x89c5\x9e\xd4\x002\t\xca\xdd\xfe\xbdzQ\xe9\xab\xbf\xb1\xfa\xf4\xcc\xe6:kG!\xeb\xd9H\xdcBp+R\x8bz\x9da\xea\x02\xdfd6\x83d\xcf\xa8\xab\xa1\x83\xa3 \x7f\x94\xf2\x98R\xaf\x95\x8b\xfc\x95\x8f\x1d\xb1\x19s\xf4\xcd\xda\x13\xc0\x92O\x84-\xf0\xcf_`\xa5\xc1\xb5A\xb5\xea\xf4E\xa3\xf7\x97\x91\xe7\xb8sf\xe0H\xa5\xfa&7\x0e8X\x0c\xcc\x82:\x89\x92+S]\xb8V1f\xb2U\xc3\xb7\xd1Y\xbf\xc4\xd8Y\xf6uw\x10\x91u\x03\xf6\x8d\xfa.>Ae(\x8a\x0c\xa45\x1a\xc1\xbf|J\xb6\x17G\x10b\x1c\xd7\xc5\x94`\x98\xaa\xc7\t\x10\x15gT\x9e\x1e\x1a\xd4\x14\x8d\x05\xf8{\x7f\x81(\xc2{X\xa7#\xdb\x1d\r5\xdb\xb3\xed\xbaN\xaa\xb0\x11\xa6.\xee\xc9Z\xfe\xc8\xec\x07\xe2\xed\xf6\x8b\x9e\x9a\x06\x0b\xdfQ\xa8\x8cH\xb7\xa7PAfa\xc1WD\xb8\xce1.9\xd8P\xa0s\x00\xe5\xfdV\xb4t\xe4n\xc5\xd8\xf9[\xb6n{\x00\x96\xdd\xde\xbcQ\xb8\xdbkMk\xc28\xaa;\x02\x8c\xc2"\xb8/\xa9\xc5\xe5\xee\x85MyG\xe2\xbd\x1a\xce\x8b\xd2dbs\x04\x1aY]\x84\x85\xe9\xb1\xa9\x81\x96\xfa}\xa0\xcf\xb5]\xe1`\xba\x98\x885\x17o$\xd5\x16M^8-\xcaI!\x86\x0e&\xd1\x1a~7\xec\x06\xdec6e\xa6X\xf4\xca\xe3\x8f\xaaj@w\xbe\xe8yj3\x13\xfa\x87\x869\xe6\xa1\xeb\xa2\x00^i:&\x05\xbc=\x989\xe4\x86\x1d\r\x869\x10\xe3gM\x19\xc1\xce\xdbE\xa1\x9fj\x9a\x98\x05\xb6\x7f9\xf9\x1d\x13\x03^\r\x0e\xe6Zj}\xfe\x0b\xeeRY\xcb\x97(\\\xe31\t\x18\xf2\xdf\x16\xccWA\x01\xf9.\x15\r\xfdK\x852\xc7\xc67$\x18[\\\x1e5\xb5\xdf\x19[$?\x03\xd2\x05&\x16\xea\xd1\x1a\xf6\xcb\xecG\xfe\xef\xcee\xc6\x95\xf2P\x08\x10x&Y\x0f\x8bd\xf1\xe84K\x886\xe27G\xfe\xd6\xd6\xb4\x9f/\\Y\xaa"\xc9\x1a\x85\xdc\xbc\x990\xd0{\x8f$\x06.h\xa5\xff\xdd\xda\xc9zB*\xbf\x00\x16y!\x8a>\x18\xa8\x1b\xc6\xc7\x07\\\x8a\x99\xba\xef\x03?\xf4\x1f\xfelK\xd5\x8c\xcdO\xdb\x1b\xaeu=\x9f\xef\x02\xee5\xf4\x9e\x00\xf7f\xc8#\xec\x9dw\x1f\x99\xb55\xfbE\x11\xa2\xce\x1dF\xef\xf8=\xe1U\xd1;\x14\xc5P\x1aY\xae\x0b+\xde\xdd[\x19\x83w\x91\xa4\xa1/\xeb\xeaS\x84\x8b\x8eP\xb2\xf8a\xea\x9c2\xdaP\xf6\xbd\x89\xc3\xa9*\xf3\x07\x07\xd3\xdd|\x08z_!\xee\x11\xf0J\x9e\xfc\xf9\x1bH\x9c&\xc2\xc5\x15*\x84b.B\xad\x9b=\r\xb9\xc9\xea\xa1\xf1U\xc7\xe7\xde\x1a\xde\x155;\xf1\xd2\x16yi\xdb\x8b%\xebK7\xe4L\xe3\x92\xb7\t2\x190\x1a?\xf1\xf73\xf9=\xb5b$\x8d\xc60/9\x15\xef\xe7\x82\xdd\xf0H\xef\xa4\xd0\x16u}m\x1f?\xe9}\x15\x99&~OUr\x99\x8c-\xbe\xf9\\\xc1xm\xbd30\x16Z\xf9\xe0*\x18\xbcI:[\x8a\t\xde\xdco\xa8\xe3\x81\xae\xe7.F\x94\xcbG\x179\x03#\xc6a\x0e:\x0eC%5,\x8e9\xb2\xed\xfa\xe0\xba\r\x08UG\xe8`Y&\n[n\xfcKQ`\xf7\xe6\xcc\xfb7-\x90\xd1\xca\xac:f\xb5\xd1\xd8\xfa\x8a\xca\x05\x9a\x13\xb9\x9f4C\xc7\xe0\xd5B\xaeA\xa5\x17F\x1e\x9e\xcbZ\x86;\xec\xc7\xfd\xe7\xf0Z\x1e\x98\x06\x8f\xe4\x83\x9a\x86A\xef\xb5\x04vlB_8\x1cP\xac]Y\xad\xd8\x19\x93Ygk\xb8\xa9G\xca[\xca"\xb4{\xc9K\xabf\xf9G\xb5\xc2\xc3\xe0\xc8Bs\x8b4\x8c-\x82C\x91S\xb66\x8c"\xc2\xe7\xc5\\. \xceg>dY4\x9e\x86)\x17+?\xb4\xd0h\xa5\x9bLnrp<\x1dA\xb8\xe4\xf1\xf2?\xd3%\xa6\x8b\x1e\xbf\x82\x80M\xea\xbc.\xa8\xbc\xc51\x1b\xe6\x0e^\xcb\x1an9\xec\x86\xe2\xe5\xe8\x84\x9eW\x02\x92\xfb\x83\x8a\'<\x8f\xb2\xf8\xe6\xea\xe11fn\xec!\xd48^%<DY\xfbn\xda'
ctx3=b'\xac}\xd44G\xb3\x83<\xa13\x9fS\xa6\xb6\x89c5\x9e\xd4\x002\t\xca\xdd\xfe\xbdzQ\xe9\xab\xbf\xb1\xfa\xf4\xcc\xe6:kG!\xeb\xd9H\xdcBp+R\x8bz\x9da\xea\x02\xdfd6\x83d\xcf\xa8\xab\xa1\x83\xa3 \x7f\x94\xf2\x98R\xaf\x95\x8b\xfc\x95\x8f\x1d\xb1\x19s\xf4\xcd\xda\x13\xc0\x92O\x84-\xf0\xcf_`\xa5\xc1\xb5A\xb5\xea\xf4E\xa3\xf7\x97\x91\xe7\xb8sf\xe0H\xa5\xfa&7\x0e8X\x0c\xcc\x82:\x89\x92+S]\xb8V1f\xb2U\xc3\xb7\xd1Y\xbf\xc4\xd8Y\xf6uw\x10\x91u\x03\xf6\x8d\xfa.>Ae(\x8a\x0c\xa45\x1a\xc1\xbf|J\xb6\x17G\x10b\x1c\xd7\xc5\x94`\x98\xaa\xc7\t\x10\x15gT\x9e\x1e\x1a\xd4\x14\x8d\x05\xf8{\x7f\x81(\xc2{X\xa7#\xdb\x1d\r5\xdb\xb3\xed\xbaN\xaa\xb0\x11\xa6.\xee\xc9Z\xfe\xc8\xec\x07\xe2\xed\xf6\x8b\x9e\x9a\x06\x0b\xdfQ\xa8\x8cH\xb7\xa7PAfa\xc1WD\xb8\xce1.9\xd8P\xa0s\x00\xe5\xfdV\xb4t\xe4n\xc5\xd8\xf9[\xb6n{\x00\x96\xdd\xde\xbcQ\xb8[kMk\xc28\xaa;\x02\x8c\xc2"\xb8/\xa9\xc5\xe5\xee\x85MyG\xe2\xbd\x1a\xce\x0b\xd3dbs\x04\x1aY]\x84\x85\xe9\xb1\xa9\x01\x96\xfa}\xa0\xcf\xb5]\xe1`\xba\x98\x885\x17o$\xd5\x16M^8-\xca\xc9!\x86\x0e&\xd1\x1a~7\xec\x06\xdec6e\xa6X\xf4\xca\xe3\x8f\xaaj@w\xbehyj3\x13\xfa\x87\x869\xe6\xa1\xeb\xa2\x00\xdei:&\x05\xbc=\x989\xe4\x86\x1d\r\x869\x10\xe3gM\x19\xc1\xce\xdbE\xa1\x9fj\x9a\x98\x05\xb6\x7f9\xf9\x1d\x13\x03^\r\x0e\xe6Zj}\xfe\x0b\xeeRY\xcb\x97(\\\xe31\t\x18\xf2\xdf\x16\xccWA\x01\xf9.\x15\r\xfdK\x852\xc7\xc67$\x18[\\\x1e5\xb5\xdf\x19[$?\x03\xd2\x05&\x16\xea\xd1\x1a\xf6\xcb\xecG\xfe\xef\xcee\xc6\x95\xf2P\x08\x10x&Y\x0f\x8bd\xf1\xe84K\x886\xe27G\xfe\xd6\xd6\xb4\x9f/\\Y\xaa"\xc9\x1a\x85\xdc\xbc\x990\xd0{\x8f$\x06.h\xa5\xff\xdd\xda\xc9zB*\xbf\x00\x16y!\x8a>\x18\xa8\x1b\xc6\xc7\x07\\\x8a\x99\xba\xef\x03?\xf4\x1f\xfelK\xd5\x8c\xcdO\xdb\x1b\xaeu=\x9f\xef\x02\xee5\xf4\x9e\x00\xf7f\xc8#\xec\x9dw\x1f\x99\xb55\xfbE\x11\xa2\xce\x1dF\xef\xf8=\xe1U\xd1;\x14\xc5P\x1aY\xae\x0b+\xde\xdd[\x19\x83w\x91\xa4\xa1/\xeb\xeaS\x84\x8b\x8eP\xb2\xf8a\xea\x9c2\xdaP\xf6\xbd\x89\xc3\xa9*\xf3\x07\x07\xd3\xdd|\x08z_!\xee\x11\xf0J\x9e\xfc\xf9\x1b\xc8\x9c&\xc2\xc5\x15*\x84b.B\xad\x9b=\r\xb9\xc9\xea\xa1\xf1U\xc7\xe7\xde\x1a\xde\x954;\xf1\xd2\x16yi\xdb\x8b%\xebK7dL\xe3\x92\xb7\t2\x190\x1a?\xf1\xf73\xf9=\xb5b$\x8d\xc60/9\x95\xef\xe7\x82\xdd\xf0H\xef\xa4\xd0\x16u}m\x1f?\xe9}\x15\x99&~OUr\x99\x0c.\xbe\xf9\\\xc1xm\xbd30\x16Z\xf9`*\x18\xbcI:[\x8a\t\xde\xdco\xa8\xe3\x81\xae\xe7.F\x94\xcbG\x179\x83#\xc6a\x0e:\x0eC%5,\x8e9\xb2\xed\xfa\xe0\xba\r\x08UG\xe8`Y&\x8aZn\xfcKQ`\xf7\xe6\xcc\xfb7-\x90Q\xca\xac:f\xb5\xd1\xd8\xfa\x8a\xca\x05\x9a\x13\xb9\x9f4C\xc7\xe0\xd5B\xaeA%\x17F\x1e\x9e\xcbZ\x86;\xec\xc7\xfd\xe7\xf0Z\x1e\x98\x06\x8f\xe4\x83\x9a\x86A\xef\xb5\x84vlB_8\x1cP\xac]Y\xad\xd8\x19\x13Ygk\xb8\xa9G\xca[\xca"\xb4{\xc9K\xabf\xf9G\xb5\xc2\xc3\xe0\xc8Bs\x8b4\x8c-\x82C\x91S\xb66\x8c"\xc2\xe7\xc5\\. \xceg>dY4\x9e\x86)\x17+?\xb4\xd0h\xa5\x9bLnrp<\x1dA\xb8\xe4\xf1\xf2?\xd3%\xa6\x8b\x1e\xbf\x82\x80M\xea\xbc.\xa8\xbc\xc51\x1b\xe6\x0e^\xcb\x1an9\xec\x86\xe2\xe5\xe8\x84\x9eW\x02\x92\xfb\x83\x8a\'<\x8f\xb2\xf8\xe6\xea\xe11fn\xec!\xd48^%<DY\xfbn\xda'
ctx4=b'\xac}\xd44G\xb3\x83<\xa13\x9fS\xa6\xb6\x89c5\x9e\xd4\x802\t\xca\xdd\xfe\xbdzQ\xe9\xab\xbf\xb1\xfa\xf4\xcc\xe6:kG!\xeb\xd9H\xdcB\xf0*R\x8bz\x9da\xea\x02\xdfd6\x83dO\xa8\xab\xa1\x83\xa3 \x7f\x94\xf2\x98R\xaf\x95\x8b\xfc\x95\x8f\x1d\xb1\x19s\xf4\xcdZ\x13\xc0\x92O\x84-\xf0\xcf_`\xa5\xc1\xb5A\xb5\xea\xf4E\xa3\xf7\x97\x91\xe7\xb8s\xe6\xe0H\xa5\xfa&7\x0e8X\x0c\xcc\x82:\t\x92+S]\xb8V1f\xb2U\xc3\xb7\xd1Y\xbf\xc4\xd8Y\xf6uw\x10\x91\xf5\x03\xf6\x8d\xfa.>Ae(\x8a\x0c\xa45\x1a\xc1\xbf|J\xb6\x17G\x10b\x1c\xd7E\x94`\x98\xaa\xc7\t\x10\x15gT\x9e\x1e\x1aT\x14\x8d\x05\xf8{\x7f\x81(\xc2{X\xa7#\xdb\x1d\r5\xdb\xb3\xed\xbaN\xaa0\x11\xa6.\xee\xc9Z\xfe\xc8\xec\x07\xe2\xed\xf6\x8b\x9e\x9a\x06\x0b\xdfQ\xa8\x8cH\xb7\xa7\xd0Afa\xc1WD\xb8\xce1.9\xd8P s\x00\xe5\xfdV\xb4t\xe4n\xc5\xd8\xf9[\xb6n{\x00\x96\xdd\xde\xbcQ\xb8[kMk\xc28\xaa;\x02\x8c\xc2"\xb8/\xa9\xc5\xe5\xee\x85MyG\xe2\xbd\x1a\xce\x0b\xd3dbs\x04\x1aY]\x84\x85\xe9\xb1\xa9\x01\x96\xfa}\xa0\xcf\xb5]\xe1`\xba\x98\x885\x17o$\xd5\x16M^8-\xca\xc9!\x86\x0e&\xd1\x1a~7\xec\x06\xdec6e\xa6X\xf4\xca\xe3\x8f\xaaj@w\xbehyj3\x13\xfa\x87\x869\xe6\xa1\xeb\xa2\x00\xdei:&\x05\xbc=\x989\xe4\x86\x1d\r\x869\x10\xe3gM\x19\xc1\xce\xdbE\xa1\x9fj\x9a\x98\x05\xb6\x7f9\xf9\x1d\x13\x03^\r\x0e\xe6Zj}\xfe\x0b\xeeRY\xcb\x97(\\\xe31\t\x18\xf2\xdf\x16\xccWA\x01\xf9.\x15\r\xfdK\x852\xc7\xc67$\x18[\\\x1e5\xb5\xdf\x19[$?\x03\xd2\x05&\x16\xea\xd1\x1a\xf6\xcb\xecG\xfe\xef\xcee\xc6\x95\xf2P\x08\x10x&Y\x0f\x8bd\xf1\xe84K\x886\xe27G\xfe\xd6\xd6\xb4\x9f/\\Y\xaa"\xc9\x1a\x85\xdc\xbc\x990\xd0{\x8f$\x06.h\xa5\xff\xdd\xda\xc9zB*\xbf\x00\x16y!\x8a>\x18\xa8\x1b\xc6\xc7\x07\\\x8a\x99\xba\xef\x03?\xf4\x1f\xfelK\xd5\x8c\xcdO\xdb\x1b\xaeu=\x9f\xef\x02\xee5\xf4\x9e\x00\xf7f\xc8#\xec\x9dw\x1f\x99\xb55\xfbE\x11\xa2\xce\x1dF\xef\xf8=\xe1U\xd1;\x14\xc5P\x1aY\xae\x0b+\xde\xdd[\x19\x83w\x91\xa4\xa1/\xeb\xeaS\x84\x8b\x8eP\xb2\xf8a\xea\x9c2\xdaP\xf6\xbd\x89\xc3\xa9*\xf3\x07\x07\xd3\xdd|\x08z_!\xee\x11\xf0J\x9e\xfc\xf9\x1bH\x9c&\xc2\xc5\x15*\x84b.B\xad\x9b=\r\xb9\xc9\xea\xa1\xf1U\xc7\xe7\xde\x1a\xde\x155;\xf1\xd2\x16yi\xdb\x8b%\xebK7\xe4L\xe3\x92\xb7\t2\x190\x1a?\xf1\xf73\xf9=\xb5b$\x8d\xc60/9\x15\xef\xe7\x82\xdd\xf0H\xef\xa4\xd0\x16u}m\x1f?\xe9}\x15\x99&~OUr\x99\x8c-\xbe\xf9\\\xc1xm\xbd30\x16Z\xf9\xe0*\x18\xbcI:[\x8a\t\xde\xdco\xa8\xe3\x81\xae\xe7.F\x94\xcbG\x179\x03#\xc6a\x0e:\x0eC%5,\x8e9\xb2\xed\xfa\xe0\xba\r\x08UG\xe8`Y&\n[n\xfcKQ`\xf7\xe6\xcc\xfb7-\x90\xd1\xca\xac:f\xb5\xd1\xd8\xfa\x8a\xca\x05\x9a\x13\xb9\x9f4C\xc7\xe0\xd5B\xaeA\xa5\x17F\x1e\x9e\xcbZ\x86;\xec\xc7\xfd\xe7\xf0Z\x1e\x98\x06\x8f\xe4\x83\x9a\x86A\xef\xb5\x04vlB_8\x1cP\xac]Y\xad\xd8\x19\x93Ygk\xb8\xa9G\xca[\xca"\xb4{\xc9K\xabf\xf9G\xb5\xc2\xc3\xe0\xc8\xc2s\x8b4\x8c-\x82C\x91S\xb66\x8c"\xc2\xe7\xc5\\. \xceg>dY4\x1e\x87)\x17+?\xb4\xd0h\xa5\x9bLnr\xf0<\x1dA\xb8\xe4\xf1\xf2?\xd3%\xa6\x8b\x1e\xbf\x82\x80M\xea\xbc.\xa8\xbc\xc5\xb1\x1b\xe6\x0e^\xcb\x1an9\xec\x86\xe2\xe5\xe8\x84\x9eW\x02\x92\xfb\x83\x8a\'<\x8f\xb2x\xe6\xea\xe11fn\xec!\xd48^%<\xc4Y\xfbn\xda'
ctx5=b'\xac}\xd44G\xb3\x83<\xa13\x9fS\xa6\xb6\x89c5\x9e\xd4\x002\t\xca\xdd\xfe\xbdzQ\xe9\xab\xbf\xb1\xfa\xf4\xcc\xe6:kG!\xeb\xd9H\xdcBp+R\x8bz\x9da\xea\x02\xdfd6\x83d\xcf\xa8\xab\xa1\x83\xa3 \x7f\x94\xf2\x98R\xaf\x95\x8b\xfc\x95\x8f\x1d\xb1\x19s\xf4\xcd\xda\x13\xc0\x92O\x84-\xf0\xcf_`\xa5\xc1\xb5A\xb5\xea\xf4E\xa3\xf7\x97\x91\xe7\xb8sf\xe0H\xa5\xfa&7\x0e8X\x0c\xcc\x82:\x89\x92+S]\xb8V1f\xb2U\xc3\xb7\xd1Y\xbf\xc4\xd8Y\xf6uw\x10\x91\xf5\x03\xf6\x8d\xfa.>Ae(\x8a\x0c\xa45\x1a\xc1\xbf|J\xb6\x17G\x10b\x1c\xd7E\x94`\x98\xaa\xc7\t\x10\x15gT\x9e\x1e\x1aT\x14\x8d\x05\xf8{\x7f\x81(\xc2{X\xa7#\xdb\x1d\r5\xdb\xb3\xed\xbaN\xaa0\x11\xa6.\xee\xc9Z\xfe\xc8\xec\x07\xe2\xed\xf6\x8b\x9e\x9a\x06\x0b\xdfQ\xa8\x8cH\xb7\xa7\xd0Afa\xc1WD\xb8\xce1.9\xd8P s\x00\xe5\xfdV\xb4t\xe4n\xc5\xd8\xf9[\xb6n{\x00\x96\xdd\xde\xbcQ\xb8[kMk\xc28\xaa;\x02\x8c\xc2"\xb8/\xa9\xc5\xe5\xee\x85MyG\xe2\xbd\x1a\xce\x0b\xd3dbs\x04\x1aY]\x84\x85\xe9\xb1\xa9\x01\x96\xfa}\xa0\xcf\xb5]\xe1`\xba\x98\x885\x17o$\xd5\x16M^8-\xca\xc9!\x86\x0e&\xd1\x1a~7\xec\x06\xdec6e\xa6X\xf4\xca\xe3\x8f\xaaj@w\xbehyj3\x13\xfa\x87\x869\xe6\xa1\xeb\xa2\x00\xdei:&\x05\xbc=\x989\xe4\x86\x1d\r\x869\x10\xe3gM\x19\xc1\xce\xdbE\xa1\x9fj\x9a\x98\x05\xb6\x7f9\xf9\x1d\x13\x03^\r\x0e\xe6Zj}\xfe\x0b\xeeRY\xcb\x97(\\\xe31\t\x18\xf2\xdf\x16\xccWA\x01\xf9.\x15\r\xfdK\x852\xc7\xc67$\x18[\\\x1e5\xb5\xdf\x19[$?\x03\xd2\x05&\x16\xea\xd1\x1a\xf6\xcb\xecG\xfe\xef\xcee\xc6\x95\xf2P\x08\x10x&Y\x0f\x8bd\xf1\xe84K\x886\xe27G\xfe\xd6\xd6\xb4\x9f/\\Y\xaa"\xc9\x1a\x85\xdc\xbc\x990\xd0{\x8f$\x06.h\xa5\xff\xdd\xda\xc9zB*\xbf\x00\x16y!\x8a>\x18\xa8\x1b\xc6\xc7\x07\\\x8a\x99\xba\xef\x03?\xf4\x1f\xfelK\xd5\x8c\xcdO\xdb\x1b\xaeu=\x9f\xef\x02\xee5\xf4\x9e\x00\xf7f\xc8#\xec\x9dw\x1f\x99\xb55\xfbE\x11\xa2\xce\x1dF\xef\xf8=\xe1U\xd1;\x14\xc5P\x1aY\xae\x0b+\xde\xdd[\x19\x83w\x91\xa4\xa1/\xeb\xeaS\x84\x8b\x8eP\xb2\xf8a\xea\x9c2\xdaP\xf6\xbd\x89\xc3\xa9*\xf3\x07\x07\xd3\xdd|\x08z_!\xee\x11\xf0J\x9e\xfc\xf9\x1b\xc8\x9c&\xc2\xc5\x15*\x84b.B\xad\x9b=\r\xb9\xc9\xea\xa1\xf1U\xc7\xe7\xde\x1a\xde\x954;\xf1\xd2\x16yi\xdb\x8b%\xebK7dL\xe3\x92\xb7\t2\x190\x1a?\xf1\xf73\xf9=\xb5b$\x8d\xc60/9\x95\xef\xe7\x82\xdd\xf0H\xef\xa4\xd0\x16u}m\x1f?\xe9}\x15\x99&~OUr\x99\x0c.\xbe\xf9\\\xc1xm\xbd30\x16Z\xf9`*\x18\xbcI:[\x8a\t\xde\xdco\xa8\xe3\x81\xae\xe7.F\x94\xcbG\x179\x03#\xc6a\x0e:\x0eC%5,\x8e9\xb2\xed\xfa\xe0\xba\r\x08UG\xe8`Y&\n[n\xfcKQ`\xf7\xe6\xcc\xfb7-\x90\xd1\xca\xac:f\xb5\xd1\xd8\xfa\x8a\xca\x05\x9a\x13\xb9\x9f4C\xc7\xe0\xd5B\xaeA\xa5\x17F\x1e\x9e\xcbZ\x86;\xec\xc7\xfd\xe7\xf0Z\x1e\x98\x06\x8f\xe4\x83\x9a\x86A\xef\xb5\x04vlB_8\x1cP\xac]Y\xad\xd8\x19\x93Ygk\xb8\xa9G\xca[\xca"\xb4{\xc9K\xabf\xf9G\xb5\xc2\xc3\xe0\xc8Bs\x8b4\x8c-\x82C\x91S\xb66\x8c"\xc2\xe7\xc5\\. \xceg>dY4\x9e\x86)\x17+?\xb4\xd0h\xa5\x9bLnrp<\x1dA\xb8\xe4\xf1\xf2?\xd3%\xa6\x8b\x1e\xbf\x82\x80M\xea\xbc.\xa8\xbc\xc51\x1b\xe6\x0e^\xcb\x1an9\xec\x86\xe2\xe5\xe8\x84\x9eW\x02\x92\xfb\x83\x8a\'<\x8f\xb2\xf8\xe6\xea\xe11fn\xec!\xd48^%<DY\xfbn\xda'

ctx_list = [ctx1, ctx2, ctx3, ctx4, ctx5] # bytes
msg = f'cat /flag{level}'.encode()
CTX_num = [bytes_to_long(md5_pad(i)) for i in ctx_list]

PR = PolynomialRing(GF(n), "ke, z, xe, ye, d")
ke, z, xe, ye, d = PR.gens()
eqs = [ye**2 - (xe**3 + 3)]

for i in range(5):
io.recvuntil(b'context: ')
print(f"[DEBUG] Sending ctx {i+1}")
io.sendline(md5_pad(ctx_list[i]).hex().encode())

s_line = io.recvline()
s_i = int(s_line.strip().split()[-1])
print(f"[DEBUG] Received s_line: {s_i}")



if i == 0:
eqs.append((s_i*ke - (z + xe*d)))
else:
ki = ke + (CTX_num[i] - CTX_num[0]) * 256**16
delta = (CTX_num[i] - CTX_num[0]) * 256**16 * G
delta_x, delta_y = delta.xy()
slope = (int(delta_y) - ye)/(int(delta_x) - xe)
xi = slope**2 - xe - int(delta_x)
eqs.append((s_i*ki - (z + xi*d)).numerator())
'''
I = PR.ideal(eqs)
priv = int(I.groebner_basis()[-1].univariate_polynomial().roots()[0][0])
r, s = Sign(priv, b'n1junior_2025', f'cat /flag{level}'.encode())
io.recv()
io.sendline(b" ".join([str(r).encode(), str(s).encode()]))
flag = io.recvline().decode()
print(flag)
'''

I = PR.ideal(eqs)
d_val = int(I.groebner_basis()[-1].univariate_polynomial().roots()[0][0])

r, s = Sign(d_val, b'n1junior_2025', msg)
io.recvuntil(b'signature: ')
io.sendline(f"{r} {s}".encode())
flag = io.recvline().decode()
print(flag)

if "Traceback" not in flag:
print(ch)
break


except:
pass

还有两道等我学会了来补()