Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5Mobile wallpaper 6
2638 words
13 minutes
ISCTF 2025 WriteUp
2025-12-14

CRYPTO#

Power tower#

题目:

from Crypto.Util.number import *
import random
from numpy import number
m = b'ISCTF{****************}'
flag = bytes_to_long(m)
n = getPrime(256)
t = getPrime(63)
l = pow(2,pow(2,t),n)
c = flag ^ l
print(t)
print(n)
print(c)
'''
t = 6039738711082505929
n = 107502945843251244337535082460697583639357473016005252008262865481138355040617
c = 114092817888610184061306568177474033648737936326143099257250807529088213565247
'''
flag=cl=c22t(modn)22t22t mod φ(n)(modn)\begin{aligned} flag=c\oplus l=c\oplus 2^{2^t}\pmod{n}\\ 2^{2^t}\equiv2^{2^t~mod~\varphi(n)} \pmod{n} \end{aligned}

原题链接:[CryptoCTF 2019]Time Capsule

exp.py

from Crypto.Util.number import long_to_bytes
t = 6039738711082505929
n = 107502945843251244337535082460697583639357473016005252008262865481138355040617
c = 114092817888610184061306568177474033648737936326143099257250807529088213565247
# factordb n
# n = 127 * 841705194007 * 1005672644717572752052474808610481144121914956393489966622615553
p, q, r = 127, 841705194007, 1005672644717572752052474808610481144121914956393489966622615553
phi = (p - 1) * (q - 1) * (r - 1)
l = pow(2, pow(2, t, phi), n)
flag = c ^ l
print(long_to_bytes(flag).decode())
# ISCTF{Euler_1s_v3ry|useful!!!!!}

easy_RSA#

题目:

from Crypto.Util.number import *
p = getPrime(1024)
q = getPrime(1024)
N = p*q
e = 65537
msg = bytes_to_long(b"ISCTF{dummy_flag}")
ct1 = pow(msg, e, N)
ct2 = pow(msg, p+q, N)
print(f"{N = }")
print(f"{ct1 = }")
print(f"{ct2 = }")
"""
N = ...
ct1 = ...
ct2 = ...
"""

p + q在模n同余式的加密指数上等价于N + 1

mN+1mφ(N)+p+qmp+q(modN)m^{N+1}\equiv m^{\varphi(N)+p+q}\equiv m^{p+q}\pmod{N}

这里eN + 1互素,所以直接共模打就好了。

原题链接:[WHY2025 CTF]Somkracht 65537

exp.py

from Crypto.Util.number import long_to_bytes
from gmpy2 import gcdext
N = ...
e = 65537
ct1 = ...
ct2 = ...
g, s1, s2 = gcdext(e, N + 1)
m = pow(ct1, s1, N) * pow(ct2, s2, N) % N
print(long_to_bytes(m).decode())
# ISCTF{Congratulations_you_master_Mathematical_ability}

baby_math#

题目:

from Crypto.Util.number import bytes_to_long
print(len(flag))
R = RealField(1000)
a,b = bytes_to_long(flag[:len(flag)//2]),bytes_to_long(flag[len(flag)//2:])
x = R(...)
enc = a*cos(x)+b*sin(x)
# ...

直接基础格打就行,原题链接:[CyberSpaceCTF 2024]trendy windy trigonity

exp.py

# SageMath 10.7
from Crypto.Util.number import long_to_bytes
R = RealField(1000)
x = R(...)
c = ...
cx = int(cos(x) * 2 ** 1000)
sx = int(sin(x) * 2 ** 1000)
c = (c * 2 ** 1000)
M = matrix(ZZ, [[1, 0, cx],
[0, 1, sx],
[0, 0, -c]])
L = M.LLL()[0]
print((long_to_bytes(L[0])+long_to_bytes(L[1])).decode())
# ISCTF{164a3221-7306-4024-88c3-4ef557b86895}

baby_equation#

题目:

from Crypto.Util.number import *
from secret import a,b
flag = b'ISCTF{***********}'
c = bytes_to_long(flag)
4*b**6-2*a**3+3*a*c = ...
b**5+6*c**3+2*a*b*c = ...
3*a**3-3*a*c-3*b**6 = ...

原题链接:[陕西省省赛2023]ezmath

# SageMath 10.7
from Crypto.Util.number import long_to_bytes
from sage.matrix.matrix2 import Matrix
def resultant(f1, f2, var):
return Matrix.determinant(f1.sylvester_matrix(f2, var))
R.<a, b, c> = PolynomialRing(ZZ)
f1 = 4 * b ** 6 - 2 * a ** 3 + 3 * a * c - ...
f2 = b ** 5 + 6 * c ** 3 + 2 * a * b * c - ...
f3 = 3 * a ** 3 - 3 * a * c - 3 * b ** 6 + ...
h1 = resultant(f1, f2, a)
h2 = resultant(f1, f3, a)
h3 = resultant(h1, h2, b)
m = int(h3.univariate_polynomial().roots()[0][0])
print(long_to_bytes(m).decode())
# ISCTF{y0u_93t_7h3_3qu4710n_50lv3}

小蓝鲨的LFSR系统#

题目:

import secrets
import binascii
def simple_lfsr_encrypt(plaintext, init_state):
mask = [random.randint(0,1) for _ in range(128)]
state = init_state.copy()
for _ in range(256):
feedback = sum(state[i] & mask[i] for i in range(128)) % 2
state.append(feedback)
key = bytes(int(''.join(str(bit) for bit in mask[i*8:(i+1)*8]), 2)
for i in range(16))
keystream = (key * (len(plaintext)//16 + 1))[:len(plaintext)]
return bytes(p ^ k for p, k in zip(plaintext, keystream)), mask

Sage秒了。

exp.py

# SageMath 10.7
initState = [...]
outputState = [...]
ciphertext = bytes.fromhex('...')
F2 = GF(2)
allState = initState + outputState
n_eq = 256
n_var = 128
rows = []
for t in range(n_eq):
rows.append(allState[t:t + n_var])
M = Matrix(F2, rows)
b = vector(F2, outputState)
mask_vec = M.solve_right(b)
mask = [int(bit) for bit in mask_vec]
key = bytes(int(''.join(str(bit) for bit in mask[i * 8:(i + 1) * 8]), 2) for i in range(16))
keystream = (key * (len(ciphertext) // 16 + 1))[:len(ciphertext)]
print(bytes(c ^^ k for c, k in zip(ciphertext, keystream)).decode())
# ISCTF{lf5R_jUst_So_s0}

小蓝鲨的RSA密文#

题目:

import json, secrets
from Crypto.Util.number import getPrime, bytes_to_long
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
e = 3
N = getPrime(512) * getPrime(512)
a2_high = a2 >> LOW_BITS
aes_key = secrets.token_bytes(16)
m = bytes_to_long(aes_key)
f = a2 * (m * m) + a1 * m + a0
c = (pow(m, e) + f) % N
iv = secrets.token_bytes(16)
cipher = AES.new(aes_key, AES.MODE_CBC, iv=iv)
ct = cipher.encrypt(pad(FLAG, 16))

题目其实就是给了一个多项式的值,然后给出了三项系数以及一项系数的高位。关键就是怎么从多项式中恢复系数的低位以及原本的密钥值。多项式如下:

f=x3+a2x2+a1x+a0f=x^3+a_2x^2+a_1x+a_0

其中给出了c = f(m) % na2_high = a2 >> 16,下面令H = a2_highL = a2 - H << 16。我们有:

c=m3+(H+L)m2+a1m+a0G(m):=m3+Hm2+a1m+a0c=Lm2\begin{aligned} c=m^3+(H+L)m^2+a_1m+a_0\\ G(m):=m^3+Hm^2+a_1m+a_0-c=-Lm^2 \end{aligned}

这个时候可以推出,实际真的m满足:

G(m)<0G(m)=Lm2,0L216G(m)216m2,m2G(m)\begin{aligned} G(m)<0\\ -G(m)=Lm^2,0\le L\le 2^{16}\\ \Rightarrow -G(m)\le2^{16}m^2,m^2|-G(m) \end{aligned}

而求个导数可以发现,G(x)x > 0上是单调递增的,所以一定有正实根r,且m < r,可以用二分法在整数上找到一个数k,使得

G(k)<0,G(k+1)>0floor(r)=k,mkG(k)<0,G(k+1)>0\Rightarrow floor(r)=k,m\le k

然后从k开始往下枚举m,就可以找到唯一符合条件的(m, L),搜索区间也就是|m - r|的大小可以估算一下:

G(m)G(r)+G(r)(mr)G(r)=0,G(m)=Lm2Lr2mrLr2G(r)Θ(L)\begin{aligned} G(m)\approx G(r)+G'(r)(m-r)\\ G(r)=0,G(m)=-Lm^2\approx -Lr^2\\ m-r\approx -\frac{Lr^2}{G'(r)}\approx-\Theta(L) \end{aligned}

可以估算出|m - r|大概和L同级别,而L < 2 ** 16,所以区间可以取[k - 2 ** 16, k]

exp.py

# SageMath 10.7
from Crypto.Cipher import AES
from Crypto.Util.number import *
from Crypto.Util.Padding import unpad
N = ...
c = ...
a2_high = 9012778
LOW_BITS = 16
a1 = 621315
a0 = 452775142
iv = bytes.fromhex("...")
ct = bytes.fromhex("...")
H = a2_high << LOW_BITS
R.<x> = PolynomialRing(ZZ)
G = x ** 3 + H * x ** 2 + a1 * x + a0 - c
low = ZZ(0)
high = ZZ(1)
while G(high) <= 0:
high *= 2
while low + 1 < high:
mid = (low + high) // 2
if G(mid) > 0:
high = mid
else:
low = mid
k = low
bound = 1 << 16
m_found = None
a2_low = None
for d in range(bound + 1):
m = k - d
if m <= 0:
break
Gm = G(m)
if Gm >= 0:
continue
num = -Gm
if num >= (1 << 16) * m * m:
continue
if num % (m * m) != 0:
continue
L = num // (m * m)
if 0 <= L < (1 << 16):
m_found = m
a2_low = L
break
assert c == m_found ** 3 + ((a2_high << LOW_BITS) + a2_low) * m_found ** 2 + a1 * m_found + a0
key = int(m_found).to_bytes(16, "big")
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
pt = unpad(cipher.decrypt(ct), 16)
print(pt.decode())
# ISCTF{i7_533M5_Lik3_You_R34lLy_UNd3R574nd_Polinomials_4nD_RSA}

沉迷数学的小蓝鲨#

题目:

y² = x³ + 3x + 27 (mod p)

Q(0xa61ae2f42348f8b84e4b8271ee8ce3f19d7760330ef6a5f6ec992430dccdc167, 0x8a3ceb15b94ee7c6ce435147f31ca8028d1dd07a986711966980f7de20490080)

k= ?

最终flag请将解出k值的16进制转换为32位md5以ISCTF{}包裹提交

小蓝鲨最近沉迷于椭圆曲线,但是有一个椭圆曲线问题它始终做不出来,据说它广泛应用于区块链技术。如果你可以帮助小蓝鲨解决这个问题,它将会给予你丰厚的报酬。

HINT1:你验证过基点 G 真的在曲线上吗?(这只是个ez题,别想太复杂)

HINT2:G是这条曲线上的冒牌货,但代数运算不在乎,因为计算机可不是数学家

反正是凑着凑着对上脑电波了,无效曲线攻击,在La佬的博客翻到了。具体也不难理解,就是这个曲线给的a是对的,b是错误的,我们需要找一个b'满足基点GQ,然后具体是哪条曲线就只能爆了,在常用区块链曲线里找就行。

exp.py

# SageMath 10.7
from hashlib import md5
from math import isqrt
xQ = 0xa61ae2f42348f8b84e4b8271ee8ce3f19d7760330ef6a5f6ec992430dccdc167
yQ = 0x8a3ceb15b94ee7c6ce435147f31ca8028d1dd07a986711966980f7de20490080
a_attack = 3
candidates = [
{
"name": "SM2",
"p": 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF,
"Gx": 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7,
"Gy": 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
},
{
"name": "NIST P-256 (secp256r1)",
"p": 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff,
"Gx": 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
"Gy": 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5
},
{
"name": "secp256k1",
"p": 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
"Gx": 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
"Gy": 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
}
]
O = None
def inv_mod(x, p):
return pow(x, p - 2, p)
def point_add(P, Q, p, a, b):
if P is None:
return Q
if Q is None:
return P
x1, y1 = P
x2, y2 = Q
if x1 == x2 and (y1 + y2) % p == 0:
return O
if P == Q:
num = (3 * x1 * x1 + a) % p
den = (2 * y1) % p
else:
num = (y2 - y1) % p
den = (x2 - x1) % p
lam = (num * inv_mod(den, p)) % p
x3 = (lam * lam - x1 - x2) % p
y3 = (lam * (x1 - x3) - y1) % p
return (x3, y3)
def scalar_mul(k, P, p, a, b):
R = O
N = P
while k > 0:
if k & 1:
R = point_add(R, N, p, a, b)
N = point_add(N, N, p, a, b)
k >>= 1
return R
def is_on_curve(P, p, a, b):
if P is None:
return True
x, y = P
return (y * y - (x ** 3 + a * x + b)) % p == 0
def bsgs(G, Q, p, a, b, N):
m = isqrt(N) + 1
# baby steps: jG
table = {}
R = O
for j in range(m):
if R not in table:
table[R] = j
R = point_add(R, G, p, a, b)
# giant steps: Q - i * (mG)
mG = scalar_mul(m, G, p, a, b)
neg_mG = (mG[0], (-mG[1]) % p)
R = Q
for i in range(m + 1):
if R in table:
j = table[R]
k = i * m + j
if k < N:
return k
R = point_add(R, neg_mG, p, a, b)
return None
for item in candidates:
name = item['name']
p = item['p']
Gx = item['Gx']
Gy = item['Gy']
b_prime = (pow(Gy, 2, p) - pow(Gx, 3, p) - a_attack * Gx) % p
lhs_Q = pow(yQ, 2, p)
rhs_Q = (pow(xQ, 3, p) + a_attack * xQ + b_prime) % p
if lhs_Q == rhs_Q:
print(f"\n[+] 命中! 系统使用的是 {name} 的 p 和 G")
print(f"[+] 实际生效的 b' = {hex(b_prime)}")
G = (Gx, Gy)
Q = (xQ, yQ)
assert is_on_curve(G, p, a_attack, b_prime)
assert is_on_curve(Q, p, a_attack, b_prime)
N = 2_000_000
print(f"[*] 使用 BSGS 在 [0, {N}) 上求 k 使得 Q = kG ...")
k = bsgs(G, Q, p, a_attack, b_prime, N)
if k is None:
print("[-] 在设定范围内未找到 k,可适当增大 N 再试。")
break
print(f"[SUCCESS] k = {k} (dec) = {hex(k)}")
flag = md5(hex(k)[2:].encode()).hexdigest()
print(f"ISCTF{{{flag}}}")
break
else:
print(f"[-] {name} 不匹配。")
"""
[-] SM2 不匹配。
[-] NIST P-256 (secp256r1) 不匹配。
[+] 命中! 系统使用的是 secp256k1 的 p 和 G
[+] 实际生效的 b' = 0x92c4cc831269ccfaff1ed83e946adeeaf82c096e76958573f2287becbb17b19d
[*] 使用 BSGS 在 [0, 2000000) 上求 k 使得 Q = kG ...
[SUCCESS] k = 954761 (dec) = 0xe9189
ISCTF{43896099feea21a3d5804863075e1aaa}
"""

小蓝鲨的密码箱#

题目:

小蓝鲨有一个神秘的black-box,里面藏着它最珍贵的秘密。小蓝鲨告诉我们,只有知道密码箱的运算逻辑,你才能知道它的秘密。

黑盒,笑的,就测呗,先固定(a, b, c),修改m,发现密文一直在变,flag一直不变,说明flag的加密只依赖(a, b, c),和m无关,然后观察flag长度一直是 43,可以推测明文长度。然后开始瞎试,固定(a, b)c,会发现密文一直在c的范围内,很明显说明c应该是模数。

然后选(a, b, c) = (1, 1, 97),明文根据长度取 43 个 0,对应密文为 43 个 31(十六进制)。

(a, b, c) = (1, 1, 89)时也是 43 个 31(十六进制)。有点敏感了,好像是仿射加密hhhhh,那怎么拿flag呢,发现ab都不能取 0,那取 1 吧,然后c = 129,得到加密后的 flag 值减一转 ascii 就行了。最新靶机的测试结果:

加密结果

原文: 0000000000000000000000000000000000000000000

密文:

31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31

Flag:

4a 54 44 55 47 7c 63 39 31 63 35 65 39 31 2e 32 3a 33 3a 2e 35 32 63 38 2e 63 34 64 33 2e 31 3a 64 34 38 38 65 66 65 63 67 65 7e

使用的参数: a=1, b=1, c=129

c = "4a 54 44 55 47 7c 63 39 31 63 35 65 39 31 2e 32 3a 33 3a 2e 35 32 63 38 2e 63 34 64 33 2e 31 3a 64 34 38 38 65 66 65 63 67 65 7e"
c_list = c.split(" ")
for i in c_list:
print(chr(int(i, 16) - 1), end="")
# ISCTF{b80b4d80-1929-41b7-b3c2-09c377dedbfd}

小蓝鲨的费马谜题#

题目:

import random
import math
p = get_prime(1024)
q = get_prime(1024)
n = p * q
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n)
bases = get_primes_up_to(100)
hints = []
for i in range(len(bases)):
for j in range(i+1, len(bases)):
hint_value = (pow(bases[i], p-1, n) + pow(bases[j], p-1, n)) % n
hints.append((bases[i], bases[j], hint_value))

给了 50 组,爆一下找gcd(hint - 2, n)不为 1 的就是p了。

hintbip1+bjp1(modn)hintbip1+bjp1(modp)2(modn)\begin{aligned} hint\equiv b_i^{p-1}+b_j^{p-1}\pmod{n}\\ hint\equiv b_i^{p-1}+b_j^{p-1}\pmod{p}\equiv 2\pmod{n} \end{aligned}

exp.py

from Crypto.Util.number import long_to_bytes
from gmpy2 import gcd, invert
n = ...
e = ...
c = ...
hint_vals = [...]
for hint_val in hint_vals:
p = gcd(hint_val - 2, n)
if p != 1:
q = n // p
phi = (p - 1) * (q - 1)
d = invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m).decode())
break
# ISCTF{M0dIFi3D_f3RM47_7H30r3m_I5_fUn_8U7_h4rD3r!}
ISCTF 2025 WriteUp
https://q1uju.cc/posts/isctf-2025-writeup/writeup/
Author
Q1uJu
Published at
2025-12-14
License
CC BY-NC-SA 4.0

Some information may be outdated