VNCTF WriteUp


VNCTF WriteUp By huangx607087

0.Introduction

3月14日的VNCTF,自己一道题也不会,只能靠赛后复现了。这可以充分印证以下这个事实
$$
\mathrm{huangx607087 == feiwu}
$$

1.WhiteGive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *
from secret import url
from gmpy2 import *
m = bytes_to_long(url)
p = getPrime(512)
q = getPrime(512)
n = p * q
d = getPrime(256)
e = inverse(d,(p-1)*(q-1))
c = pow(m,e,n)
print(n)
print(c)
m = e
p = getPrime(512)
q = getPrime(512)
n = p * p * q
e = 0x10001
d = inverse(e,lcm(p,lcm(p-1,q-1)))
c = pow(m,e,n)
hint = pow(d,e,n)
print(n)
print(c)p
print(hint)

这道题给出了第一步加密的$n,c$的值,并对第一次的$e$进行了另一轮加密操作。其中$d$是$e=65537$模$\dfrac{p(p-1)(q-1)}{\gcd(p-1,q-1)}$的逆元。因此我们要求出第一轮$e$的值。而给出了$h\equiv d^e \pmod n$

一开始,自己想的是计算出$e^{e-1}$,由$c=m^e$,那么我们可以计算。

Q1

然后尝试枚举$\gcd(p-1,q-1)$的值,看看能不能解出一个小于$1024$比特的值,结果失败了。这道题就没有继续做下去了

然后看了西电大佬Deebato的Exp,自己才得知了这道题正确的解法:

我们可以推出这样的式子:由$ed\equiv 1 \pmod {\dfrac{p(p-1)(q-1)}{\gcd(p-1,q-1)}}$。既然我们已知了$d^e$,那我们也可以计算出$e$的值,那么我们就有了$(ed)^e\equiv 1 \pmod {\dfrac{p(p-1)(q-1)}{\gcd(p-1,q-1)}}$。因此,$(((ed)^e-1)\mod n)\equiv 0 \pmod {\dfrac{p(p-1)(q-1)}{\gcd(p-1,q-1)}}$。因此我们可以计算$(ed)^e$的值,然后与$n$取GCD即可。我们就得到了$p$值,接着就是正常的RSA解密了。

解出$p,q$后才发现$\gcd(p-1,q-1)=6$,不知道自己当时枚举的时候出现了什么问题

不过我们进入第二步,我们看到了$\ln d = 0.25 \ln n$,考虑Wienerattack,不过好像失败了(。还好,我在RSA Notes 4中还有一个脚本,这个能够跑出来。

解出来后,是一个网站。。。里面竟然还有一题(

1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Util.number import *
from secret import flag,padding
from gmpy2 import *
m = bytes_to_long(flag)
e = 7
p = getPrime(512)
q = getPrime(512)
n = p * q
c1 = pow(m,e,n)
c2 = pow(m+padding,e,n)
print(n)
print(c1)
print(c2)

很显然,乍一看以为是Related Message Attack。结果发现我们不知道Padding。查了一下才知道是Short-Padding Attack。上一下脚本,以后再研究(gugugu)

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
#Sagemath
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)

最后得出flagVNCTF{H4ppyNeWy34r!2021_V&N_figHt1ng!}

2.FACTOR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from Crypto.Util.number import *
from gmpy2 import *
from secret import flag
def get_public_key(d):
while 1:
p, q = getPrime(512), getPrime(512)
N, phi, not_phi = p * q, (p - 1) * (q - 1), (p + 1) * (q + 1)
try:
e = inverse(d, not_phi)
assert gcd(e, phi) == 1
break
except:
pass
return (N, e)
def encrypt(pubkey, m):
N, e = pubkey
c = pow(m, e, N)
return c
m = bytes_to_long(flag)
d = getPrime(300)
pubkeys = [get_public_key(d), get_public_key(d)]
cs = encrypt(pubkeys[0], m), encrypt(pubkeys[1], m)
print(pubkeys)
print(cs)

题目的代码很简单,简单理解一下,就会发现实际上是通过一个很小的解密指数$d$所生成的两组公钥。一开始自己看的不认真,以为是Extending Wiener Attack,结果怎么做也做不出来(x

最后才知道,这原来是一种新的攻击方法。如果解密指数$d$比较低,我们可以通过构造格的方法来解决。

相关的论文是这样解释的:如果使用一个小$d$为私钥来构造$r$组公钥,而构造出的模数的数量级接近,那么我们可以根据$ed\equiv 1 \pmod n$构造如下的式子:
$$
e_id = 1 + k_i \phi(n_i)
$$
其中我们假设$n_1<n_2<n_3<…<n_r<2n_1$。论文中说:如果$\dfrac{\ln d}{\ln n}=\delta$,则若$\delta<0.5-\dfrac{1}{2(r+1)}-\dfrac{\ln \delta}{\ln n_r}$,那么所有的模数$n$都可以在多项式的时间复杂度内被分解。

下面我们简单地说一下解决方法:假设$M=(\sqrt n_5)^-$,$s=n-\phi(n)$。(注:本博客用上标$+$表示向上取整或无限从右趋近,用下标$-$表示向下取整或者无限从左趋近)。那么我们可以构造这样的式子:

1

然后我们可以构造以下的矩阵(以$r=5$为例)

2

根据上面的式子和上面的矩阵(假设矩阵记为$B$),我们就可以构造$\vec v=(d,k_1,k_2,k_3,k_4,k_5),\vec w=(d\sqrt{n_5},1-k_1s_1,1-k_2s_2,1-k_3s_3,1-k_4s_4,1-k_5s_5)$。这样我们就满足$\vec vB=\vec w$的构造了。不过跟过去不同的是:这次解出来的最短向量为$\vec v$而不是前几次的$\vec w$。根据这个递推式也可以发现

由此可见,在数据足够多的情况下,如果$\ln d<0.5\ln n$,那么我们就有可能解出来$d$的值。所以根据我们在RSA中的经验,在发放公钥的时候,一定要保证$\ln d>\ln n$。不然很容易被攻破。

总结一下,Wienerattack是$n$相同,$d$改变,$\ln d<0.5\ln n$。而共私钥指数攻击是$d$相同,$n$改变,$\ln d<0.5\ln n$。

所以我们可以上一下脚本,这里$r=2$,因此格是$3$维的。

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
#Sagemath
from Crypto.Util.number import *
n1=5362254810180378444......
e1=3762917428094791884......
n2=1154833387078533235......
e2=5794296112064899907......
c1=5320050759114401782......
c2=5186139432313258226......
assert n2>n1
M=int(sqrt(n2))
B=[[0,0,0],[0,0,0],[0,0,0]]
B[0][0],B[0][1],B[0][2]=M,e1,e2
B[1][1],B[2][2]=n1,n2
B=Matrix(ZZ,B)
B=B.LLL()
if(B[0][0]<0):
B=-B
d,t1,t2=B[0][0]//M,B[0][1],B[0][2]
k1,k2=(d*e1-t1)//n1,(d*e2-t2)//n2
k1.nbits(),k2.nbits()
s1,s2=(t1-1)//k1-1,(t2-1)//k2-1
var('x')
F,G=x^2-s1*x+n1,x^2-s2*x+n2
p1,q1=F.roots()[0][0],F.roots()[1][0]
p2,q2=G.roots()[0][0],G.roots()[1][0]
phi1,phi2=(p1-1)*(q1-1),(p2-1)*(q2-1)
d1,d2=inverse(e1,ZZ(phi1)),inverse(e2,ZZ(phi2))
print(long_to_bytes(pow(c2,d2,n2)),long_to_bytes(pow(c1,d1,n1)))
#b'vnctf{7d47956b-bc55-4897-a550-cda0b221ce67} vnctf{7d47956b-bc55-4897-a550-cda0b221ce67}'

3.Strange Function 1

按照惯例,首先看一下题目:

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
import socketserver
import os
import signal
import string
import random
from gmpy2 import *
from secret import flag
from hashlib import sha256
from Crypto.Util.number import *
MENU = br'''[+] 1.function
[+] 2.only_function_numerator
[+] 3.only_function_denominator
[+] 4.exit
'''
class Task(socketserver.BaseRequestHandler):
def _recvall(self):
BUFF_SIZE = 2048
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 proof_of_work(self):
random.seed(os.urandom(8))
proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
_hexdigest = sha256(proof.encode()).hexdigest()
self.send(f"[+] sha256(XXXX+{proof[4:]}) == {_hexdigest}".encode())
x = self.recv(prompt=b'[+] Plz tell me XXXX: ')
if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
return False
return True
def function_only_numerator(self, x):
'''
********
* hide *
********
'''
return
def function_only_denominator(self, x):
'''
********
* hide *
********
'''
return
def my_round(self, value, x):
return round(value * (10**x)) / ((10**x) * 1.0)
def function(self, x):
res = 0
for i in range(self.lenth):
numerator = ord(flag[i])
denominator = x - self.data[i]
try:
tmp = numerator / denominator
except Exception as e:
self.send(b'[+] Error!')
return
res += tmp
assert self.my_round(res,10) == self.my_round(self.function_only_numerator(x) / self.function_only_denominator(x),10)
return res
def handle(self):
signal.alarm(500)
if not self.proof_of_work():
return
self.data = []
for i in range(len(flag)):
self.data.append(getRandomInteger(16))
self.data.sort()
self.lenth = len(flag)
assert self.lenth == len(self.data)
self.send(b'[+] Welcome!')
self.send(b'[+] Can you find the flag through the calculating?')
while True:
self.send(MENU, newline=False)
choice = self.recv()
if(choice == b'1'):
self.send(b"[+] Plz give me your x: ")
now = int(self.recv().strip().decode())
now = self.function(now)
self.send(("[+] let me show you the answer: "+str(now)).encode())
elif(choice == b'2'):
self.send(b"[+] Plz give me your x: ")
now = int(self.recv().strip().decode())
now = self.function_only_numerator(now)
self.send(("[+] let me show you the answer: "+str(now)).encode())
elif(choice == b'3'):
self.send(b"[+] Plz give me your x: ")
now = int(self.recv().strip().decode())
now = self.function_only_denominator(now)
self.send(("[+] let me show you the answer: "+str(now)).encode())
else:
break
self.request.close()
class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 10001
server = ForkedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()

通过代码审计,我们发现,题目会给出一个秘密的字符串$s$,并给出一组数据$a$,其中构造一个函数($t$为程序进行的轮数,$5$轮后给出flag)
$$
f(x)=\sum_{i=1}^{16t} \dfrac{s_i}{x-a_i}
$$
此处$a$中的数字的规模为$2^{32}$,所以我们可以直接对服务器发送$a_i+1$的值,这样我们就可以获得每一个$s_i$值的非常精确的值了。

下面直接给出解密脚本。实际上对于远程题来说:确定接收的内容还是比较困难的,有的时候即使使用recvuntil也比较复杂。所以截取稳定的两段内容的时候个人一般习惯于数下标(

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
from Crypto.Util.number import *
from pwn import *
from hashlib import sha256
def getyanzhengma(s16,s64):
assert len(s16)==16 and len(s64)==64
table="0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"
for i1 in table:
for i2 in table:
for i3 in table:
for i4 in table:
s=i1+i2+i3+i4+s16
if(sha256(s.encode()).hexdigest()==s64):
return i1+i2+i3+i4
def str_2_list(s):
li,inli,num,table=[],0,0,"0123456789"
for i in s:
if i in table:
inli,num=1,num*10+int(i)
else:
if inli==1:
li.append(num)
inli,num=0,0
return li,len(li)
def str_2_float(s):
flo,uit,dot=0,0.1,0
for i in s:
if i=='.':
dot=1
continue
if dot==0:
flo=flo*10+int(i)
else:
flo+=int(i)*uit
uit=uit/10
return flo
#--------MAIN BELOW--------#
sh=remote("node3.buuoj.cn",26452)
s1=sh.recvuntil(':')
s2=getyanzhengma(s1[16:32],s1[37:101])
sh.send(s2)
print('[#] sent yanzhengma')
for T in range(5):
print('[#] '+str(T+1)+'th Round Begin')
if not T:
for i in range(3):
s1=sh.recvline(keepends=False)
else:
s1=sh.recvline(keepends=False)
tokn,lentokn=str_2_list(s1)
subans=""
for i in range(lentokn):
s1=sh.recvuntil('xit')
sh.send('1')
s1=sh.recvuntil('x:')
sh.send(str(tokn[i]+1))
s1=sh.recvline(keepends=False)
s1=sh.recvline(keepends=False)
subans+=chr(int(str_2_float(s1[36:48])+0.5))
print('[#] '+str(T+1)+' th Round Answer: '+subans)
sh.recvuntil('xit')
sh.send('2')
sh.send(subans)
print('[#] Sent '+str(T+1)+'th Round Answer')
for i in range(3):
s1=sh.recvline(keepends=False)
flag=sh.recvline(keepends=False)
flag=sh.recvline(keepends=False)
print(flag)

4.Strange Function 2

我们再来看看2.0版本发生了什么变化(

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
from Crypto.Util.number import *
from hashlib import sha256
from secret import flag
import socketserver
import signal
import string
import random
import os
MENU = br'''[+] 1.function
[+] 2.check_answer
[+] 3.exit
'''
class Task(socketserver.BaseRequestHandler):
def _recvall(self):
BUFF_SIZE = 2048
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 proof_of_work(self):
random.seed(os.urandom(8))
proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
_hexdigest = sha256(proof.encode()).hexdigest()
self.send(f"[+] sha256(XXXX+{proof[4:]}) == {_hexdigest}".encode())
x = self.recv(prompt=b'[+] Plz tell me XXXX: ')
if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
return False
return True
def function(self, x):
res = 0
for i in range(self.lenth):
numerator = ord(self.token[i])
denominator = x - self.data[i]
try:
tmp = numerator / denominator
except Exception as e:
self.send(b'[+] Error!')
return
res += tmp
return res
def handle(self):
signal.alarm(1000)
if not self.proof_of_work():
return
self.send(b'[+] Welcome!')
self.send(b'[+] Can you find the flag through the calculating?')
self.score = 0
self.token = ''.join(random.sample(string.ascii_letters + string.digits, 8))
self.lenth = len(self.token)
self.data = []
for i in range(self.lenth):
self.data.append(getRandomInteger(17))
self.send(str(self.data).encode())
while True:
self.send(MENU, newline=False)
choice = self.recv()
if(choice == b'1'):
self.send(b"[+] Plz give me your x: ")
now = int(self.recv().strip().decode())
now = self.function(now)
self.send(("[+] let me show you the answer: "+str(now)).encode())
elif(choice == b'2'):
guess = self.recv().strip().decode()
if(guess == self.token):
self.score += 1
self.send(b"[+] You win!")
self.send(("[!] Now your score: " + str(self.score)).encode())

self.token = ''.join([random.choice(string.digits + string.ascii_letters) for i in range((self.score+1)*8)])
self.lenth = len(self.token)
self.data = []
for i in range(self.lenth):
self.data.append(getRandomInteger(17))
self.send(str(self.data).encode())

if(self.score >= 5):
self.send(flag.encode())
else:
self.send(b'[+] What do you want to say???')
self.send(b'[!] Go away!')
break
else:
break
self.request.close()
class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 10002
server = ForkedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()

很显然,我们发现了一个很重要的不同:

在函数
$$
f(x)=\sum_{i=1}^{8t} \dfrac{s_i}{x-a_i}
$$
里面,$a_i$的规模从$2^{32}$降到了$2^{17}$,这就导致了每当我们输入$x=a_i+1$后,给出来的数据偏离整数较大,如果我我们这个时候继续用第三题中的代码,会导致部分数字出现形如$109.47886234,53.53195675$等这种离整数较远的数据,甚至出现了$61.06428516$(因为9的ASCII码值是$57$,A的ASCII码值是$65$,说明这种情况下噪声已经远远超过了$1$)。直接跑的话必然会出错。

然而,虽然这里会出现一个较大的误差,不过是多个误差叠加而成,这里我们获得的数据实际上反应了$s_i$的大概值,因此我们考虑降噪的时候,

这里我们就需要对接收到的数据进行一个处理:

观察这个函数(我突然想到了XDU的RE大佬Innerspace含树,不知道为什么,快逃
$$
f(x)=\sum_{i=1}^{8t} \dfrac{s_i}{x-a_i}
$$
我们改变一下,假设我们想要提取$s_j$(我们已知$x=a_j+1$,因此左边的分母值是$1$,不过由于浮点误差等原因,$x-a_i$不会严格等于$1$,但非常接近$1$)。
$$
\dfrac{s_j}{x-a_j}=f(x)-\sum_{i\in [1,8t],i\not =j}\dfrac{s_i}{x-a_i}
$$
因此我们可以先获取我们通过每次发送$a_j+1$的值,先存储我们接收到的浮点数,然后通过降噪处理,获得我们真正需要的$s_j$值。

1
2
3
4
5
6
7
8
9
10
def ReduceError(tokn,lflo):
lflo2=[]
for i in range(len(tokn)):
cnt=lflo[i]
for j in range(len(tokn)):
if i==j:
continue
cnt-=lflo[j]/(-tokn[j]+tokn[i]+1)
lflo2.append(cnt)
return lflo2

然后我们通过ReduceError处理一下,就获得了我们想要的数据。然后直接一遍过。

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
from Crypto.Util.number import *
from pwn import *
from hashlib import sha256
def getyanzhengma(s16,s64):
assert len(s16)==16 and len(s64)==64
table="0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"
for i1 in table:
for i2 in table:
for i3 in table:
for i4 in table:
s=i1+i2+i3+i4+s16
if(sha256(s.encode()).hexdigest()==s64):
return i1+i2+i3+i4
def str_2_list(s):
li,inli,num,table=[],0,0,"0123456789"
for i in s:
if i in table:
inli,num=1,num*10+int(i)
else:
if inli==1:
li.append(num)
inli,num=0,0
return li,len(li)
def str_2_float(s):
flo,uit,dot=0,0.1,0
for i in s:
if i=='.':
dot=1
continue
if dot==0:
flo=flo*10+int(i)
else:
flo+=int(i)*uit
uit=uit/10
return flo
def ReduceError(tokn,lflo):
lflo2=[]
for i in range(len(tokn)):
cnt=lflo[i]
for j in range(len(tokn)):
if i==j:
continue
cnt-=lflo[j]/(-tokn[j]+tokn[i]+1)
lflo2.append(cnt)
return lflo2
#--------MAIN BELOW--------#
sh=remote("node3.buuoj.cn",25102)
s1=sh.recvuntil(':')
s2=getyanzhengma(s1[16:32],s1[37:101])
sh.send(s2)
print('[#] sent yanzhengma')
for T in range(5):
lflo=[]
print('[#] '+str(T+1)+'th Round Begin')
if not T:
for i in range(3):
s1=sh.recvline(keepends=False)
else:
s1=sh.recvline(keepends=False)
tokn,lentokn=str_2_list(s1)
subans=""
for i in range(lentokn):
s1=sh.recvuntil('xit')
sh.send('1')
s1=sh.recvuntil('x:')
sh.send(str(tokn[i]+1))
s1=sh.recvline(keepends=False)
s1=sh.recvline(keepends=False)
lflo.append(str_2_float(s1[36:48]))
print(lflo)
lflo=ReduceError(tokn,lflo)
print(lflo)
for i in lflo:
subans+=str(chr(int(i+0.5)))

print('[#] '+str(T+1)+' th Round Answer: '+subans)
sh.recvuntil('xit')
sh.send('2')
sh.send(subans)
print('[#] Sent '+str(T+1)+'th Round Answer')
for i in range(3):
s1=sh.recvline(keepends=False)
flag=sh.recvline(keepends=False)
flag=sh.recvline(keepends=False)
print(flag)

以下的运行截图,反映了降噪后数据的合理性,我们可以发现,降噪后的数据还是非常靠近整数值的。

F7

这里DeeBaTo大佬也提供了一种方法,用的是中间ASCII码交叉的方式,从中间的ASCII值$85$逼近,相当于一种丢番图的思想。

网页链接Deebato-VNCTFWP-2021-3-28

(本来打算把代码放进来的,不过复制出了问题,就不放了吧)

5.总结

VNCTF的5道题目,个人认为总体质量还是比较高的。不过自己水平还是太菜了,导致一个月后才进行复现。

最近首当其冲的是CET6的准备,争取一次过,过了CET6,英语直接跑路。

然后还要复习高数大物电路C++

huangx607087,去复习,gkd,不然你期末就挂科了

我仿佛已经看到了自己期末高数和大物挂了的场景


文章作者: huangx607087
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 huangx607087 !
  目录