CISCN 往年题练习

2018

oldstreamgame

先总结一下关键计算关系:
$i=R&mask$
$out=i中1的个数\mod2$
$output = R[:-1]+out$
每8位$out$组成一个 key 的字符
然后我们可以发现一个关键点:
key 中每一位的生成都和前边 32 位相关,即跟生产时的 R 相关。
也就是说
$R_0 = f_{32}…f_1$
$R_1=f_{31}…f_1out_1$
$…$
$R_{31}=f_1out_1…out_{31}$
然后我们现在知道 800 位的 out,所以肯定知道 $out_{32}$,然后我们就可以尝试$f_1$的值是 0 还是 1,看$f_1$是哪个值得时候会生成$out_32$,以此类推就能得到 flag 所有 bit 值。

exp:

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
"""
@Author : Ramoor
@Date : 2025-12-26
"""

f = open("./key","rb")
key = f.read()

out = ''
flag = ''
mask = 0b10100100000010000000100010010100

for i in range(4):
out += bin(key[i])[2:].zfill(8)
print(len(out))
for i in range(32):
tmp = '0' + flag + out[:32-i-1]
tmp = bin(int(tmp,2) & mask)
print(out[32-i-1],tmp.count("1")%2)
if tmp.count("1")%2 == int(out[32-i-1]):
flag = '0' + flag
else:
flag = '1' + flag
print(f"NSSCTF{{{hex(int(flag,2))[2:]}}}")

f.close()

sm

$gen512num:$

  • $order$为 512 个 512 bits 的随机数组成的数组
  • $p_i$ 为 $512-order_i+10$ bits 的素数
  • $pre_i$ 为 $p_i$ 的前 $512-order_i$ 位拼接上 1
  • $ps_i$ 为 $pre_i$ 补齐到 512 位
    ps 共 512 组
    choose 是 512 bits 素数,bchoose 是其二进制形式
    若 $bchoose_i == 1$,r 就异或上 $ps_i$
    ef 是 AES 加密后并使用 base64 编码后的结果

思路分析:
现在的目的就是求 key,也就是求 choose。
现在就是要用 r 尝试异或 $ps_i$,更关键的是,我们知道每个$ps_i$都是以$10…$结尾的,而且0的个数还不一样,这样我们就能从 r 的结尾结构来求 flag 了。
exp:

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
"""
@Author : Ramoor
@Date : 2025-12-26
"""

from Crypto.Util.number import *
from Crypto.Cipher import AES
import hashlib
import base64

file = open("./ps","r")
ps = file.readlines()
#print(bin(int(p)))

file2 = open("./r","r")
r = file2.read()
#print(bin(int(r)))
r = bin(int(r))[2:]

dict = {}

for i in range(512):
p = bin(int(ps[i]))[2:]
for j in range(511,-1,-1):
#print(p)
if p[j] == '1':
dict[j] = i
break
#print(dict)

a = [0]*512
for i in range(511,-1,-1):
if r[i] == '1':
a[dict[i]] = 1
r = bin(int(r,2)^int(ps[dict[i]]))[2:].zfill(512)
choose_bin = "".join(map(str,a))

#print(choose_bin)
choose = int(choose_bin,2)

#print(bin(choose))

key=long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(),16))
aes_obj = AES.new(key, AES.MODE_ECB)

file3 = open("./ef","rb")
ef = file3.read()
ef = base64.b64decode(ef)
flag=aes_obj.decrypt(ef)
print(flag)

crackme

加密:

  • r 是 32 bits 随机整数
  • $ret_0=2^r\mod p$
  • $ret_1=h^r\mod p \times bVal$

解密:

  • $s = (ret_0^x)^{-1}\mod p$
  • $plain=ret_1*s \mod p$

我们这里肯定是求不出私钥 x 的,因为我们没有任何和私钥 x 相关的信息可以拿到。
但是如果对数据大小足够敏锐的话,可以发现 r 仅仅是 32 bit 的随机整数,我们知道,正常大小在 $2^{32}$ 以内,我们就可以尝试爆破了,而且这里我们还可以使用 BSGS 算法来进一步降低算法的复杂度。
我们利用这个 $ret_0=2^r\mod p$ 求出 r,之后就可以求出 $ret_1=h^r\mod p \times bVal$ 中的明文 bVal。

exp:

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
"""
@Author : Ramoor
@Date : 2025-12-27
"""

from gmpy2 import isqrt
from Crypto.Util.number import *
ret0 = "a9hgrei38ez78hl2kkd6nvookaodyidgti7d9mbvctx3jjniezhlxs1b1xz9m0dzcexwiyhi4nhvazhhj8dwb91e7lbbxa4ieco"
ret1 = "2q17m8ajs7509yl9iy39g4znf08bw3b33vibipaa1xt5b8lcmgmk6i5w4830yd3fdqfbqaf82386z5odwssyo3t93y91xqd5jb0zbgvkb00fcmo53sa8eblgw6vahl80ykxeylpr4bpv32p7flvhdtwl4cxqzc"

p = 11360738295177002998495384057893129964980131806509572927886675899422214174408333932150813939357279703161556767193621832795605708456628733877084015367497711
h = 7854998893567208831270627233155763658947405610938106998083991389307363085837028364154809577816577515021560985491707606165788274218742692875308216243966916

def bsgs(g, y, p, N):
m = int(isqrt(N) + 1)

# baby steps: g^j
table = {}
e = 1
for j in range(m):
if e not in table:
table[e] = j
e = (e * g) % p

# giant steps: y * g^{-im}
factor = pow(g, -m, p)
gamma = y
for i in range(m + 1):
if gamma in table:
x = i * m + table[gamma]
if x < N:
return x
gamma = (gamma * factor) % p
return None

def int2base36(n: int) -> str:
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
if n == 0:
return "0"
out = []
while n > 0:
n, r = divmod(n, 36)
out.append(chars[r])
return "".join(reversed(out))



N = 2**31
ret0 = int(ret0,36)
ret1 = int(ret1,36)
r = bsgs(2, ret0, p, N)
print(r)

m = ret1 * inverse(pow(h,r,p),p)%p
#print(m)
flag = int2base36(m)
print(f"NSSCTF{{{flag}}}")

2021

RSA

flag 被分成了三份,每份都分别进行了 RSA 加密,所以显然是一个对 RSA 多种题型的一个缝合,看着都比较简单。
第一段 e = 3,明显是低加密指数攻击,首先尝试直接开方或者小范围爆破。
第二段对同一段密文在相同的模数下进行了加密,因此是共模攻击。
第三段明显是 p 的高位泄露攻击。
接下来我们逐个进行求解即可,最后完成拼接。

exp:

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
"""
@Author : Ramoor
@Date : 2025-12-27
@Tool : sagemath
"""

from Crypto.Util.number import *
import gmpy2
import hashlib

c1 = 19105765285510667553313898813498220212421177527647187802549913914263968945493144633390670605116251064550364704789358830072133349108808799075021540479815182657667763617178044110939458834654922540704196330451979349353031578518479199454480458137984734402248011464467312753683234543319955893
(e1 , N1) = (3, 123814470394550598363280518848914546938137731026777975885846733672494493975703069760053867471836249473290828799962586855892685902902050630018312939010564945676699712246249820341712155938398068732866646422826619477180434858148938235662092482058999079105450136181685141895955574548671667320167741641072330259009)
c2 = 54995751387258798791895413216172284653407054079765769704170763023830130981480272943338445245689293729308200574217959018462512790523622252479258419498858307898118907076773470253533344877959508766285730509067829684427375759345623701605997067135659404296663877453758701010726561824951602615501078818914410959610
c3 = 91290935267458356541959327381220067466104890455391103989639822855753797805354139741959957951983943146108552762756444475545250343766798220348240377590112854890482375744876016191773471853704014735936608436210153669829454288199838827646402742554134017280213707222338496271289894681312606239512924842845268366950
(e2 , N2) = (17, 111381961169589927896512557754289420474877632607334685306667977794938824018345795836303161492076539375959731633270626091498843936401996648820451019811592594528673182109109991384472979198906744569181673282663323892346854520052840694924830064546269187849702880332522636682366270177489467478933966884097824069977)
(e3 , N2) = (65537, 111381961169589927896512557754289420474877632607334685306667977794938824018345795836303161492076539375959731633270626091498843936401996648820451019811592594528673182109109991384472979198906744569181673282663323892346854520052840694924830064546269187849702880332522636682366270177489467478933966884097824069977)
c4 =59213696442373765895948702611659756779813897653022080905635545636905434038306468935283962686059037461940227618715695875589055593696352594630107082714757036815875497138523738695066811985036315624927897081153190329636864005133757096991035607918106529151451834369442313673849563635248465014289409374291381429646
(e3 , N3) = (65537, 113432930155033263769270712825121761080813952100666693606866355917116416984149165507231925180593860836255402950358327422447359200689537217528547623691586008952619063846801829802637448874451228957635707553980210685985215887107300416969549087293746310593988908287181025770739538992559714587375763131132963783147)
p_high = 7117286695925472918001071846973900342640107770214858928188419765628151478620236042882657992902


#低加密指数攻击
m1 = gmpy2.iroot(c1,3)[0]
ans1 = long_to_bytes(m1)
print(ans1)


#共模攻击(m相同,n相同,e不同)
_ , k1 , k2 = gmpy2.gcdext(e2,e3)
m2 = pow(c2,k1,N2)*pow(c3,k2,N2) % N2
ans2 = long_to_bytes(m2)
print(ans2)

#p高位泄露
kbits=200

R.<x> = PolynomialRing(Zmod(N3))
p_high <<= kbits
p = p_high + x
roots = p.small_roots(X=2^kbits,beta=0.4)

p = p_high + int(roots[0])

q = N3 // p
phi = (p-1)*(q-1)
d = inverse(e3,phi)
m3 = pow(c4,d,N3)
ans3 = long_to_bytes(m3)
print(ans3)

text = ans1 + ans2 + ans3
flag = hashlib.md5(text).hexdigest()
print(f"NSSCTF{{{flag}}}")

small

$x,y$的都是$(2^{70},2^{71})$的数,看着不是很大,都是小根,要求解方程
$c=(1+axy^2+bx^2y)\mod p$
尝试 coppersmith 求小根

exp:

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
"""
@Author : Ramoor
@Date : 2025-12-27
@Tool : sagemath
"""

from Crypto.Util.number import *
import hashlib
import itertools

def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
print(d)
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 = 8813834626918693034209829623386418111935369643440896703895290043343199520112218432639643684400534953548489779045914955504743423765099014797611981422650409
a = 2817275225516767613658440250260394873529274896419346861054126128919212362519165468003171950475070788320195398302803745633617864408366174315471102773073469
b = 1763620527779958060718182646420541623477856799630691559360944374374235694750950917040727594731391703184965719358552775151767735359739899063298735788999711
c = 2298790980294663527827702586525963981886518365072523836572440106026473419042192180086308154346777239817235315513418426401278994450805667292449334757693881

kbits = 70

R.<x,y> = PolynomialRing(Zmod(p))
f = 1 + a*x*y^2 + b*x^2*y - c
res = small_roots(f,(2^kbits,2^kbits),m=1,d=4)
(x1,y1) = res[0]
x1 = Integer(x1)
y1 = Integer(y1)

m = str(x1) + str(y1)
h = hashlib.sha256()
h.update(m.encode())
flag = "NSSCTF{" + h.hexdigest() + "}"
print(flag)

classic

考察古典密码

1
AXAXDFDXXAFADFGFXAADFGDXGFDXFFDFAXDXFFFFXGXGAAXAGAFDDXFFDXFFDXDXDXDXGFDFAXFXAADXAAGAXGDGXAXAFAXXFFXADFFGAADXDXAXDFDFDXXAXXDXDAAAAAFAXAAAFGGAFGFGXADXXADFGADXDFDFGAGFDGAXFGAXDGDADXFFFFDAGFADXGDX

直接上工具

得到

1
mmyobfysbhkosoxymoxxiipbcdoxoxoooosymrpopcinbbflxbykpoomyyobloeppfbpkckkbobycoyycsnmkmneoxxeshio

然后尝试栅栏密码解密找到开头和 ciscn 结构一致的明文

尝试 casar 密码爆破

成功找到 ciscn 开头的明文

1
ciscnbracethreeconefourdeoneafourcefffsevensevenoneyeroninedcfouryeroasixyerosixafiveadyerobrace

然后把字符串中的数字英文换成数字,brace 是大括号的意思,但是其中yero是 0。

1
ciscn{3c14de1a4cefff77109dc40a606a5ad0}

move

p,q 都是 3k+2 形式的素数,且 p < q
$gen:$

  • n = pq
  • bound = $\sqrt{2pq}//12$
  • x 为 $(1,\sqrt{\sqrt{2pq}//12})$ 中的随机整数,y 为 $(1,\sqrt{2pq}//12)$ 中的随机整数
  • $zbound = ((p-q)n^{0.25}y)//(3(p+q))$
  • $z = zbound - ((p+1)(q+1)y+zbound)\mod x$
  • $e=\frac{(p+1)(q+1)y+z}{x}$,可以推出$\frac{e}{n}=\frac{y}{x}$

然后 add 和 mul 一看就像是 ECC,我们要敏感,比如看见 $p\equiv 2 \mod 3$,就要知道,对应的曲线通常是 $y^2=x^3+1$,曲线的阶是 $(p+1)(q+1)$。
我们从 output 中可以看到,e 是非常大的,我们还能看到敏感信息 $n^{0.25}$,很容易联想到 Wiener 攻击。
至于 h1 和 h2,貌似出题人想要缝上 coppersmith,但实际上根本用不到。

exp:

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
from Crypto.Util.number import inverse, long_to_bytes
from math import isqrt

n = 80263253261445006152401958351371889864136455346002795891511487600252909606767728751977033280031100015044527491214958035106007038983560835618126173948587479951247946411421106848023637323702085026892674032294882180449860010755423988302942811352582243198025232225481839705626921264432951916313817802968185697281
e = 67595664083683668964629173652731210158790440033379175857028564313854014366016864587830963691802591775486321717360190604997584315420339351524880699113147436604350832401671422613906522464334532396034178284918058690365507263856479304019153987101884697932619200538492228093521576834081916538860988787322736613809
h1 = 3518005
h2 = 641975
c = (6785035174838834841914183175930647480879288136014127270387869708755060512201304812721289604897359441373759673837533885681257952731178067761309151636485456082277426056629351492198510336245951408977207910307892423796711701271285060489337800033465030600312615976587155922834617686938658973507383512257481837605,38233052047321946362283579951524857528047793820071079629483638995357740390030253046483152584725740787856777849310333417930989050087087487329435299064039690255526263003473139694460808679743076963542716855777569123353687450350073011620347635639646034793626760244748027610309830233139635078417444771674354527028)

K = round(n ** 0.25)

def cont_frac(a: int, b: int, limit: int = 5000):
qs = []
while b and len(qs) < limit:
q = a // b
qs.append(q)
a, b = b, a - q * b
return qs

def convergents(qs):
n0, d0 = 0, 1
n1, d1 = 1, 0
for q in qs:
n2 = q * n1 + n0
d2 = q * d1 + d0
yield n2, d2
n0, d0, n1, d1 = n1, d1, n2, d2

def ceil_2sqrt_n(n: int) -> int:
s = isqrt(n)
S = 2 * s
while S * S < 4 * n:
S += 1
return S

def try_solve_with_xy(y: int, x: int):
C0 = e * x - (n + 1) * y

lo = ceil_2sqrt_n(n)
if y == 0:
return None
hi = C0 // y
if hi <= lo:
return None

hi = min(hi, 2 * isqrt(2 * n) + 5)

def f(S: int) -> int:
z = C0 - S * y
if z < 0:
return -10**100
D = S * S - 4 * n
if D < 0:
return 10**100
Delta = isqrt(D)
zbound = (Delta * K * y) // (3 * S)
return z - zbound

flo = f(lo)
fhi = f(hi)
if not (flo > 0 and fhi < 0):
return None

L, R = lo, hi
while L + 1 < R:
mid = (L + R) // 2
if f(mid) > 0:
L = mid
else:
R = mid

S = R
fv = f(S)
if not (-x < fv <= 0):
return None

D = S * S - 4 * n
Delta = isqrt(D)
if Delta * Delta != D:
return None

p = (S + Delta) // 2
q = (S - Delta) // 2
if p * q != n:
return None
return p, q, x, y, S

def factor_n():
LIM = 1 << 256

qs = cont_frac(e, n, limit=2000)
for num, den in convergents(qs):
y, x = num, den
if x == 0 or y == 0:
continue
if x >= LIM or y >= LIM:
continue
res = try_solve_with_xy(y, x)
if res is not None:
return res
return None

def ec_add(P, Q, modn):
if P == (0, 0):
return Q
if Q == (0, 0):
return P
if P[0] == Q[0] and (P[1] != Q[1] or P[1] == 0):
return (0, 0)
if P[0] == Q[0]:
lam = (3 * P[0] * P[0]) * inverse(2 * P[1], modn) % modn
else:
lam = (Q[1] - P[1]) * inverse(Q[0] - P[0], modn) % modn
x3 = (lam * lam - P[0] - Q[0]) % modn
y3 = (lam * (P[0] - x3) - P[1]) % modn
return (int(x3), int(y3))

def ec_mul(k, P, modn):
R = (0, 0)
T = P
while k > 0:
if k & 1:
R = ec_add(R, T, modn)
T = ec_add(T, T, modn)
k >>= 1
return R


solved = factor_n()
p, q, x, y, S = solved

pbin = bin(p)[2:]
pbin = pbin.zfill(512)

phi = (p + 1) * (q + 1)
d = inverse(e, phi)

pt = ec_mul(d, c, n)
flag = long_to_bytes(pt[0]) + long_to_bytes(pt[1])
print(flag)

2024

rasnd

flag 被拆成两段加密
$crypto1:$

  • $hint1 = x_1p+y_1q-0x114$
  • $hint2=x_2p+y_2q-0x514$
  • 其中$x_1,x_2\in (0,2^{11})$,$y_1\in (0,2^{114})$,$y_2\in(0,2^{514})$
    这时候我们又需要敏感了,大小在 $2^{30}$ 以内的数,我们其实都可以考虑爆破的,现在 $x_1,x_2$ 甚至只有 $2^{11}$ 以内,即使相乘也只有 $2^{22}$,可以直接爆破 $x_1,x_2$。

$crypto2:$

  • $hint \equiv (514p-114q)^{n-p-q} \equiv (514p-114q)^{phi-1} \equiv (514p-114q)^{-1}\mod n$
    很明显,我们能求出 $(514p-114q)$ 整体的值,解方程组即可

exp:

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
"""
@Author : Ramoor
@Date : 2025-12-27
"""

from Crypto.Util.number import *
from sympy import symbols,Eq,solve
import tqdm

n1 =20755363432644361831280473143486004654887761825482545816825886178590427481001590105103928820416234549350190189102400138997504692954894918136891987916464496663447784445449129164506385979083605704015871564868799762148271386659918337411612938229728403711452979143340387296953244284961250373596930021693360310334113183783125461541760550414518223530003665196095161061319103889360639923949143548188651930521039226712057804269362549982607178032676245289077045192208178473968319193935490458947363928767019197003414979669590920710147036286780638209295714191173059822984330987619349607669745847193251082146525504975715889878713
c1 =2674431575908899480226879812713662592729324872333704337956222927395190195495462477535254431584048073502965634639130723398920239402677270575878904375703106682752125060026069920595360191304529421441307860335422399222133023408373973823751938994842195110751306610439825876211040356032340006280253170145339782130014594853423849471546516603350049741846670400700339828073621277468023170884101288093862034471889365984024504146307544578343310116566800501189548999340876682579597523917871452785313311508925409843687628184968569628209905811756655043296128125707503475648277213193810280041336257022981965720356227251962285987502
hint1 =1313543101033270957911526653517192346412598649953603700678030839401226252776178935014617945176222533320447160093635576249282584294470532647236823925060104162240503953722898439735898361354383570140205606787258941622337468767211018282152442298853786875399194419991477708907797687679018385841041567636398787820700811607634392809362256743503793919
hint2 =3992355021009213192525823140014863259096397490721972077793230136735613313867847587368925889568210804220646470090235896614285925417642418891248539801592299185316771698800225889022133173428871769212102898248361153130738588642122190371051771132729385796011865238459728810034839608161309069542769920072917621020872916462022381105748951412202180316405658553876852099469551193343596904503228783273289232933540914471099809077130245355594914739199870325966550637683666051
n2 =14766783583546666286964612750495952969891707929382794227531788353468561066118855866412183544062052260179764660558919795114999513736676779602042192564291936025030161991770512649527252143388595432848356129352457800676032379903012181282275780206400093245663237010961342454312588033818868977509156469566581247723348990537086211596051807783208391891287963459623812714263788255562130552663323981339496393352108688343238503103542694501440205158262614957082365957556001399630253758725034530732368289951742398190104866929866185896643998008436957045075172486238682805768521484923633679060384277438578584396194157630910641572667
c2 =8479362605414322218638686808323088075692474256325413399017953664122307643387502656487093767562095821647644070821407689700677742181141808367915660979660645220083490092750419199744203806197119038341303118814498524028703356103531056020201350016828426158322744476820341082161334024541820660685059565186930014528540578853569431895857677011808704428290740309879378093164327693511972629572576225410190396514214949608980613636947100201824291187390746808479271846226541045530247240625087700189670050846258383007918717653195508213436276569966563047373547324115366738616693197266256030751085965492907212386596132951203658323877
hint =11091946030232592316851114093577688428636316484189542623750079738557976735788710715239137511773827588019987895698890043318389075920560679264323291035553182447945087366654333832265312810031021538425007444472798189429813628335230232672236946244905518663341992762773423572208146901510050488641720223979039803731597664942765369222623870254895891261912132146945170282436686913392354993932072461228969970088615616711454123302775650320813921946779210105894309319162696029151278367612062415688113350178040627948832065733896287879990583158835086260610273629798661954527530638631633732948828255905164477997496479603298932673440
e = 65537

hint1 += 0x114
hint2 += 0x514
# crypto 1
for i in tqdm.trange(1,2**11):
for j in range(1,2**11):
diff = hint1*j-hint2*i
if(GCD(diff, n1)>1):
q = GCD(diff, n1)
p = n1//q
break
phi = (p-1)*(q-1)
d = inverse(e, phi)
m1 = pow(c1, d, n1)
flag1 = long_to_bytes(m1)
print(flag1)


# crypto 2
tmp = inverse(hint, n2) % n2
#print(tmp)
x,y = symbols('x y')
eq1 = Eq(x*y,n2)
eq2 = Eq(514*x - 114*y, tmp)
sol = solve((eq1, eq2), x, y)
for s in sol:
if s[0] > 0 and s[1] > 0:
p = int(s[0])
q = int(s[1])
break

phi = (p-1)*(q-1)
d = inverse(e, phi)
m2 = pow(c2, d, n2)
flag2 = long_to_bytes(m2)
print(flag2)

print(flag1+flag2)

fffffhash

分析一下思路
目的是输入一串十六进制字符串,让它的十进制转换为字节,经过giaogiao函数运算之后得到的值等于题目给出的预期值 giao
我们重点看一下函数giaogiao

  • 基值 $base_num$,参数 $x$,模数 $2^{128}$
  • 遍历每个字节,每次 $base_num=(base_num\times x)\mod 2^{128}$。然后跟字节异或 $base_num = base_num \oplus i$
    每次的 $i$ 都是一个小值,可以把异或 $i$ 当作加减一个小值 $a<255$,拿前四轮举例一下:
    $G \equiv (((base\cdot x+a_1)x+a_2)x+a_3)x+a_4 \equiv base\cdot x^4+a_1x^3+a_2x^2+a_3x+a_4 \mod M$
    造格:
    $$
    (a_1,a_2,a_3,a_4,-1,k)

\begin{pmatrix}
1 & & & & & & x^3 \
& 1 & & & & & x^2\
& & 1 & & & & x\
& & & 1 & & & 1\
& & & & 1 & & G-base\cdot x^4 \
& & & & & 1 & p &
\end{pmatrix}
=(a_1,a_2,a_3,a_4,-1,k,0)
$$
具体代码待补充,,,


CISCN 往年题练习
http://ramoor.github.io/2025/12/25/CISCN 往年题练习/
作者
Ramoor
发布于
2025年12月25日
许可协议