ctf
March 16, 2021

WriteUps of V&NCTF2021

记一次caibi在这场比赛中的记录…..cry拿了 两一血 一二血都没有进crypto rk前三…太菜了…最后还是压线rk 15太菜拉!要努力!

Web:

EZ_Game :

F12 审计代码,直接改health 还有个啥arrow 无敌!

Misc:

冰冰好像藏着秘密 :

下载下来说头文件有病…去查 没问题….试试看把后面ihdr后面的复制出来? 结果真可以…. 然后卡 了很久查了LSB无果….然后发现这个RAR叫做 FFT,百度 傅里叶变换..查了一会说是 音频?? 然后 再查查看png? 结果出来了 网上有脚本 . 就是装库真麻烦…..

Crypto:

White give:

先看题,一开始我是想把hint用在了指数上去操作…想了一个多点没想法… 然后发现这 娃意 phi上面这个p 是可以整出来的.好活! 那就出来了…

然后是url 进去 看题..emm short pad attack前几天刚整理了一下…还是不太懂这个时间多项式的意思, 借用tb师傅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
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
from gmpy2 import*
from Crypto.Util.number import*
c =
s =
n1=
e=0x10001
e1=pow(e,e,n1)
c1=e1*s
p=gcd(c1-1,n1)
q=n1//(p*p)
phi1=p*(p-1)*(q-1)
d=inverse(e,phi1)
e=pow(c,d,n1)
print(e)
n=
c=
e=
'''发现e很大 使用维纳不行,那就博纳试试
import time


debug = True
strict = False
helpful_only = True
dimension_min = 7
def helpful_vectors(BB, modulus):
nothelpful = 0
for ii in range(BB.dimensions()[0]):
if BB[ii,ii] >= modulus:
nothelpful += 1
print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")
def matrix_overview(BB, bound):
for ii in range(BB.dimensions()[0]):
a = ('%02d ' % ii)
for jj in range(BB.dimensions()[1]):
a += '0' if BB[ii,jj] == 0 else 'X'
if BB.dimensions()[0] < 60:
a += ' '
if BB[ii, ii] >= bound:
a += '~'
print(a)
# delete the unhelpful vectors
# we start at current = n-1
def remove_unhelpful(BB, monomials, bound, current):
if current == -1 or BB.dimensions()[0] <= dimension_min:
return BB
for ii in range(current, -1, -1):
if BB[ii, ii] >= bound:
affected_vectors = 0
affected_vector_index = 0
for jj in range(ii + 1, BB.dimensions()[0]):
if BB[jj, ii] != 0:
affected_vectors += 1
affected_vector_index = jj
# task 0
if affected_vectors == 0:
print("* removing unhelpful vector", ii)
BB = BB.delete_columns([ii])
BB = BB.delete_rows([ii])
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB
# task 1
elif affected_vectors == 1:
affected_deeper = True
for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
if BB[kk, affected_vector_index] != 0:
affected_deeper = False
if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
print("* removing unhelpful vectors", ii, "and", affected_vector_index)
BB = BB.delete_columns([affected_vector_index, ii])
BB = BB.delete_rows([affected_vector_index, ii])
monomials.pop(affected_vector_index)
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB
return BB
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
PR.<u, x, y> = PolynomialRing(ZZ)
Q = PR.quotient(x*y + 1 - u) # u = xy + 1
polZ = Q(pol).lift()
UU = XX*YY + 1
# x-shifts
gg = []
for kk in range(mm + 1):
for ii in range(mm - kk + 1):
xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
gg.append(xshift)
gg.sort()
monomials = []
for polynomial in gg:
for monomial in polynomial.monomials():
if monomial not in monomials:
monomials.append(monomial)
monomials.sort()
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
yshift = Q(yshift).lift()
gg.append(yshift)
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
monomials.append(u^kk * y^jj)
# 构造 lattice
nn = len(monomials)
BB = Matrix(ZZ, nn)
for ii in range(nn):
BB[ii, 0] = gg[ii](0, 0, 0)
for jj in range(1, ii + 1):
if monomials[jj] in gg[ii].monomials():
BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)
if helpful_only:
BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
nn = BB.dimensions()[0]
if nn == 0:
print("failure")
return 0,0
if debug:
helpful_vectors(BB, modulus^mm)
det = BB.det()
bound = modulus^(mm*nn)
if det >= bound:
print("We do not have det < bound. Solutions might not be found.")
print("Try with highers m and t.")
if debug:
diff = (log(det) - log(bound)) / log(2)
print("size det(L) - size e^(m*n) = ", floor(diff))
if strict:
return -1, -1
else:
print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")
if debug:
matrix_overview(BB, modulus^mm)
# LLL
if debug:
print("optimizing basis of the lattice via LLL, this can take a long time")
BB = BB.LLL()
if debug:
print("LLL is done!")
if debug:
print("looking for independent vectors in the lattice")
found_polynomials = False
for pol1_idx in range(nn - 1):
for pol2_idx in range(pol1_idx + 1, nn):
PR.<w,z> = PolynomialRing(ZZ)
pol1 = pol2 = 0
for jj in range(nn):
pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)
# resultant
PR.<q> = PolynomialRing(ZZ)
rr = pol1.resultant(pol2)
if rr.is_zero() or rr.monomials() == [1]:
continue
else:
print("found them, using vectors", pol1_idx, "and", pol2_idx)
found_polynomials = True
break
if found_polynomials:
break
if not found_polynomials:
print("no independant vectors could be found. This should very rarely happen...")
return 0, 0
rr = rr(q, q)
# solutions
soly = rr.roots()
if len(soly) == 0:
print("Your prediction (delta) is too small")
return 0, 0
soly = soly[0][0]
ss = pol1(q, soly)
solx = ss.roots()[0][0]
return solx, soly

def main():

N =
# the public exponent
e =
# the hypothesis on the private exponent (the theoretical maximum is 0.292)
delta = 0.25 # this means that d < N^delta
# Lattice (tweak those values)
# you should tweak this (after a first run), (e.g. increment it until a solution is found)
m = 4 # size of the lattice (bigger the better/slower)
# you need to be a lattice master to tweak these
t = int((1-2*delta) * m) # optimization from Herrmann and May
X = 2*floor(N^delta) # this _might_ be too much
Y = floor(N^(1/2)) # correct if p, q are ~ same size
# Don't touch anything below
# Problem put in equation
P.<x,y> = PolynomialRing(ZZ)
A = int((N+1)/2)
pol = 1 + x * (A + y)

if debug:
print("~~~ checking values ~~~")
print("[*] delta:", delta)
print("[*] delta < 0.292", delta < 0.292)
print("[*] size of e:", int(log(e)/log(2)))
print("[*] size of N:", int(log(N)/log(2)))
print("[*] m:", m, ", t:", t)
# boneh_durfee
if debug:
print("~~~ running ~~~ ")
start_time = time.time()
solx, soly = boneh_durfee(pol, e, m, t, X, Y)
# found a solution?
if solx > 0:
print("~~~ [!] found ~~~")
if False:
print("x:", solx)
print("y:", soly)
d = int(pol(solx, soly) / e)
print("private key found:", d)
else:
print("[!][!][!] no solution was found ...")
if debug:
print("=== %s seconds ===" % (time.time() - start_time))

if __name__ == "__main__":
main()
'''
d=
print(long_to_bytes(pow(c,d,n)))


'''
def short_pad_attack(c1, c2, e, n):
PRxy.<x,y> = PolynomialRing(Zmod(n))
PRx.<xn> = PolynomialRing(Zmod(n))
PRZZ.<xz,yz> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+y)^e - c2
q1 = g1.change_ring(PRZZ)
q2 = g2.change_ring(PRZZ)
h = q2.resultant(q1)
h = h.univariate_polynomial()
h = h.change_ring(PRx).subs(y=xn)
h = h.monic()
kbits = n.nbits()//(2*e*e)
diff = h.small_roots(X=2^kbits, beta=0.4)[0] # find root < 2^kbits with factor >= n^0.4
return diff

def related_message_attack(c1, c2, diff, e, n):
PRx.<x> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+diff)^e - c2
def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()
return -gcd(g1, g2)[0]


if __name__ == '__main__':
n =
e = 7
c1 =
c2 =
diff = short_pad_attack(c1, c2, e, n)
print("difference of two messages is %d" % diff)
m1 = related_message_attack(c1, c2, diff, e, n)
print("m1:", m1)
print("m2:", m1 + diff)
'''
m=
print(long_to_bytes(m))

Strange Function:

然后去获取数据.发现 1.0 的数据都很大 。看代码,里面是ord/这个数。是个random出来的数,那就可以 data 的+1去剪,然后就可以发现.很接近他自然的 ord 1.0五关就过了

2.0的话就是发现他变小了,一开始我想着改四舍五入的进制,甚至开了n个脚本一起跑….结果没过一个 xswl…然后想到 这些ascii 码值,用概率去分析他大概是多少.然后去加减…但是也不能保证百分百 所以 就让他如果断了就自动重连!

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 pwn import*
import string
from hashlib import*
from math import*
def pow():
io.recvuntil('XXXX')
str1=io.recvuntil(b"== ")[1:-5]
sha=io.recvuntil('\n')[:-1]
for i in a:
for j in a:
for k in a:
for m in a:
stringa=i+j+k+m+str(str1,encoding='utf-8')
has=sha256(stringa.encode()).hexdigest()
if bytes(has,encoding='utf8')==sha:
io.sendline(i+j+k+m)
return 1

def getdata():
io.recvuntil("?\n")
io.recvuntil("[")
data=[]
for i in range(7):
data.append(int(io.recvuntil(",")[:-1]))
data.append(int(io.recvuntil("]")[:-1]))
print(data)
return data

def getdata2(n):
io.recvuntil("win!\n[!]")
io.recvuntil("[")
data=[]
for i in range(n*8+7):
data.append(int(io.recvuntil(",")[:-1]))
data.append(int(io.recvuntil("]")[:-1]))
print(data)
return data

def task1():
charr=''
for i in range(len(data)):
io.sendline("1")
io.recvuntil("x: \n[-]")
io.sendline(str(data[i]+1).encode())
io.recvuntil("answer:")
num=(float(io.recvuntil("\n")[:-1]))
cha=0
for j in range(len(data)):
if j==i :
continue
cha += 85/(data[i]-data[j])
num=round(num-cha)
io.recvuntil("[-]")
charr+=(chr(num))
print(charr)
return charr

def task2():
io.sendline("2")
io.recvuntil("[-]")
io.sendline(charr)


if __name__=="__main__":
a=string.ascii_letters+string.digits
while 1:
try:
io=remote("127.0.0.1",28735)
pow()
data=getdata()
charr=task1()
task2()
for i in range(4):
data=getdata2(i+1)
charr=task1()
task2()
io.recvuntil("flag")
flag=b'flag'+io.recvuntil("}")
print(flag)
break
except:
continue
io.interactive()

Factor:

这道题没写出来…赛后复现…

审题, 特点:两组公钥 加密相同的信息。一开始我是想把他列出来 …但是我合到一起变成了只有一个方程. 问题是我并没有把shallow上次说让我们记忆的那条式子是干啥的并不清楚55555555 看了Wp之后发现还有一条 需要 上界 即

即取一个M 大于SVP1, 而由于N1 < N2 < 2 N1,d 是300位 N是1024位 因此 上界可以取M = 根号(N2)同时三维向量

为了使得他们差不多大小,因此取 M=根号N2 在中间则使得他的dM 值需要接近于2^1600^ 使得超过上界一点(其中 s1=notPhi1-N1 , s2=notPhi2-N2

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
from Crypto.Util.number import*
from gmpy2 import*
pubkeys = [(, ), (, )]
cs = (, )
(N1,e1) = pubkeys[0]
(N2,e2) = pubkeys[1]
c1 = cs[0]
c2 = cs[1]
M = #iroot(N2,2)[0]
'''
A=Matrix(ZZ,[[M,e1,e2],[0,-N1,0],[0,0,-N2]])
A.LLL()
'''
d= A.LLL()[0][0] //M
kkk1= A.LLL()[0][1]
kkk2= A.LLL()[0][2]
#d e1-k1 N1=1+k1 s1=kkk1
k1=(d*e1-kkk1)//N1
k2=(d*e2-kkk2)//N2
s1=(kkk1-1)//k1-1
s2=(kkk2-1)//k2-1
ss1=iroot(s1*s1-4*N1,2)[0]
p1=(ss1+s1)//2
q1=s1-p1
ss2=iroot(s2*s2-4*N2,2)[0]
p2=(ss2+s2)//2
q2=s2-p2
d1=inverse(e1,(p1-1)*(q1-1))
d2=inverse(e2,(p2-1)*(q2-1))
print(long_to_bytes(pow(c1,d1,N1)))
printlong_to_bytes(pow(c2,d2,N2)))

About this Post

This post is written by deebato, licensed under CC BY-NC 4.0.

#ctf