2020 网鼎杯

  1. 青龙组
    1. boom
    2. you raise me up
    3. easy_ya
  2. 白虎组
    1. b64
    2. rand
  3. 朱雀组
    1. simple
    2. RUA
    3. guess_game
  4. 玄武组
    1. Safe_box
    2. easy_wa

青龙组

boom

first:this string md5:46e5efe6165a5afb361217446a2dbd01

在线网站查得:en5oy

This time:Here are have some formulas 3x-y+z=185 2x+3y-z=321 x+y+z=173 input: x =

1
2
var('x y z')
solve([3*x-y+z==185,2*x+3*y-z==321,x+y+z==173],[x,y,z])

Last time: Kill it x*x+x - 7943722218936282 = 0 input x:

1
2
var('x')
solve([x * x + x - 7943722218936282 == 0],[x])

you raise me up

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random

n = 2 ** 512
m = random.randint(2, n-1) | 1
c = pow(m, bytes_to_long(flag), n)
print 'm = ' + str(m)
print 'c = ' + str(c)

# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
# c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499

题中我们的flag在m的指数位置,所以这题是解一个DLP(离散对数问题)

发现题中的n = 2 ** 512, 所以phi(n) = 2 ** 511;即本题模数的欧拉函数具有smooth的性质,即其是由很多小素数乘积而来,所以可用Pohlig Hellman 算法来解决这个DLP,sage的discrete_log用的方法就包括Pohlig Hellman算法,exp:

1
2
3
4
5
6
m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
n = 2 ** 512
m = Mod(m,n)
c = Mod(c,n)
discrete_log(c,m)

easy_ya

题目代码

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
from secret import flag,key
from random import randint
from libnum import n2s,s2n
from Crypto.Util.number import getPrime
limit = lambda n: n & 0xffffffff
padding="\xe6\xe6\xe7\xe6\xe5\xe6\xe5\xe9\xe5"
ek='\xe6\x84\xbf'
def encode(key,data):
pad = randint(0x10000000,0xffffffff)
Key = [ord(i) for i in key]
Data = [ord(i) for i in data]
a = limit((Key[0] << 24) | (Key[1] << 16) | (Key[2] << 8) | Key[3])
b = limit((Key[4] << 24) | (Key[5] << 16) | (Key[6] << 8) | Key[7])
c = limit((Key[8] << 24) | (Key[9] << 16) | (Key[10] << 8) | Key[11])
d = limit((Key[12] << 24) | (Key[13] << 16) | (Key[14] << 8) | Key[15])
y = limit((Data[0] << 24) | (Data[1] << 16) | (Data[2] << 8) | Data[3])
z = limit((Data[4] << 24) | (Data[5] << 16) | (Data[6] << 8) | Data[7])
pads = 0
for j in range(32):
pads = limit(pads + pad)
y = limit( y + ((z*16 + a) ^ (z + pads) ^ ((z>>5) + b)))
z = limit( z + ((y*16 + c) ^ (y + pads) ^ ((y>>5) + d)))
print hex((y << 52) ^ (pads << 20) ^ z)

#pow_check()
#token_check()

flag=flag.ljust(len(flag)+(-len(flag)&7),'\x00')
for i in range(0,len(flag)/8):
encode(key,flag[8*i:8*i+8])

for i in range(9):
ek+=padding[i] + key[2*i:2*i+2]

p=getPrime(2048)
e=0x10001

for i in range(2):
q=getPrime(2048)
n=p*q
print n
print pow(s2n(ek),e,n)

显然,我们第一步需要去交互,拿到四个大数,其中两个是有公因子p的大数n,还有两个就是ek的密文。这一步就是凑数的part,没点营养。吐槽,交互的时候什么沙雕PoW,有必要卡这个嘛

破PoW的脚本

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
from pwn import *
import string
import hashlib
context.log_level = 'debug'
sh = remote("39.96.90.217","17497")
sh.recvuntil("x[:20] = ")
pa = sh.recv(len('f91a551dfa31e47458df'))
sh.recvuntil('openssl_sha')
fun = sh.recvuntil(">")[:-1]
sh.recvuntil("> ")
table = string.printable
def getpow(fun,mess):
for i in table:
for j in table:
for k in table:
for l in table:
password=i+j+k+l
if fun == "256":
if hashlib.sha256(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "384":
if hashlib.sha384(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "1":
if hashlib.sha1(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "512":
if hashlib.sha512(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "224":
if hashlib.sha224(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
else:
print(fun)
else:
print("no ans")

sh.sendline(getpow(fun,pa))
sh.interactive()

然乎我们输入token,拿到自己的data

解得ek

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Util.number import *
from gmpy2 import *
n1 =
n2=
p = gcd(n1,n2)
q1 = n1//p
q2 = n2//p
assert n1 == p*q1
assert n2 == p*q2
c1 =
e = 65537
phi = (p-1)*(q2-1)
d = inverse(e,phi)
m = pow(c2,d,n2)
print(long_to_bytes(m))

>>>愿我所爱无忧恙岁长安

python2下会得到这么个ek,有点意思嗷,但你还是沙雕出题人,谁让你用PoW卡我

python3运行得到b'\xe6\x84\xbf\xe6\x88\x91\xe6\x89\x80\xe7\x88\xb1\xe6\x97\xa0\xe5\xbf\xa7\xe6\x81\x99\xe5\xb2\x81\xe9\x95\xbf\xe5\xae\x89'

然后去掉该去的就能得到key了'\x88\x91\x89\x80\x88\xb1\x97\xa0\xbf\xa7\x81\x99\xb2\x81\x95\xbf\xae\x89'

然后就是逆这个encode了。

其中我们交互的时候拿到五组密文,易得,每组密文中,保存了encode中32轮加密后的完整的y,pads和有一部分重合。但是pads加了32次pad后,最后5位已经变成0了。所以z我们可以知道20+5 = 25位,剩下七位未知。

然后我们就去爆pad,我们从密文里头能知道pad的52-32=20位。但这20位其实是中间部分的。最高的5位已经被limit搞没掉了。然后最低的7位我们也是不知道的。所以,,,爆。我们爆了低7位的同时,也能确定一个z,然后反加密看结果咯。

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
from Crypto.Util.number import *
limit = lambda n: n & 0xffffffff


key = '\x88\x91\x89\x80\x88\xb1\x97\xa0\xbf\xa7\x81\x99\xb2\x81\x95\xbf\xae\x89'

def init(key):
Key = [ord(i) for i in key]
#Data = [ord(i) for i in data]
a = limit((Key[0] << 24) | (Key[1] << 16) | (Key[2] << 8) | Key[3])
b = limit((Key[4] << 24) | (Key[5] << 16) | (Key[6] << 8) | Key[7])
c = limit((Key[8] << 24) | (Key[9] << 16) | (Key[10] << 8) | Key[11])
d = limit((Key[12] << 24) | (Key[13] << 16) | (Key[14] << 8) | Key[15])
return (a,b,c,d)

#y = limit((Data[0] << 24) | (Data[1] << 16) | (Data[2] << 8) | Data[3])
#z = limit((Data[4] << 24) | (Data[5] << 16) | (Data[6] << 8) | Data[7])

(a,b,c,d)=init(key)
ans = 0x9d98a72f5c82c26680a65


def check(s):
for i in s:
if i not in "flag{0123456789bcde-_}\x00":
return False
return True

for ans in [0xcf4add0cf5469a49d19c,
0xcccd38faa373af5059b5f,
0xad0e355d4d8b1cc9771d1,
0x703ccca78d36e770d0613,
0x54e05275b03e2cdedc053]:
for h in range(2**6):
for l in range(2**8):
y = ans >> 52 #取y
mix = ans & (2**52-1) #剩下的为不确定z和pad混合
pads_true = mix >> 32 #拿到pad的有效的20位
source_pad = (h<<27)| (pads_true<<7)| l #爆破pad
z = limit(mix ^ (source_pad<<25)) #根据猜的pad,然后mix中pad和z的混合部分,确定z
for round in range(32):
pads = limit(source_pad*(32-round)) #根据encode来用pads
z = limit( z - ((y*16 + c) ^ (y + pads) ^ ((y>>5) + d))) #加密函数反一下
y = limit( y - ((z*16 + a) ^ (z + pads) ^ ((z>>5) + b)))
flag = (y<<32)+z
#print flag
if check(long_to_bytes(flag)): #根据flag的可能字符来判断咯
print(long_to_bytes(flag))

白虎组

b64

换表base64,将给出对应关系记下来,发现flag密文中[‘E’, ‘G’, ‘I’, ‘s’, ‘X’, ‘z’],这六个字符没有映射关系。

然后有[‘+’, ‘/‘, ‘1’, ‘5’, ‘7’, ‘6’, ‘9’, ‘8’, ‘A’, ‘C’, ‘H’, ‘K’, ‘J’, ‘P’, ‘R’, ‘V’, ‘e’, ‘f’, ‘n’, ‘u’, ‘w’, ‘v’] 这么多位字符也没有被映射。

所以爆破这两个列表中的映射关系

然后根据flag的格式,uuid,来判断结果。

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
# -*- coding: utf-8-*-
from base64 import *
from string import *

def check(s):
for i in s:
if i not in "flag{-1234567890abcdef}":
return False
return True

flag = 'uLdAuO8duojAFLEKjIgdpfGeZoELjJp9kSieuIsAjJ/LpSXDuCGduouz'
a='pTjMwJ9WiQHfvC+eFCFKTBpWQtmgjopgqtmPjfKfjSmdFLpeFf/Aj2ud3tN7u2+enC9+nLN8kgdWo29ZnCrOFCDdFCrOFoF='
b='YXNobGtqIUBzajEyMjMlXiYqU2Q0NTY0c2Q4NzlzNWQxMmYyMzFhNDZxd2prZDEySjtESmpsO0xqTDtLSjg3MjkxMjg3MTM='
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789abcdefABCDEF+/'
FLAG=''

print("fail words")
for i in flag:
if i in a:

index = a.index(i)
FLAG+=b[index]
else:
FLAG+='!'
print i,
print "\nFLAG cipher"
print FLAG
#'ZmxhZ3sxZTNhMm!lN!0xYz!yLT!mNGYtOWIyZ!!hNGFmYW!kZj!xZTZ!'

print "alternative words"
aw=""
for i in alpha:
if i not in b:
aw += i
print aw

'@#$%^&'
table = 'ACHJKPRVefnuvw156789efAC+/'
print("for z")
for i in table:
if b64decode("ZTZ"+i)[-1] == '}':
FLAG = FLAG.replace("ZTZ!","ZTZ9")
table = table.replace(i,"")
#print FLAG

print("for G")
for i in table:
if check(b64decode("Zj"+i+"x")) and check(b64decode("Yz"+i+"y")):
#print b64decode("Zj"+i+"x")
FLAG = FLAG.replace("Zj!x","Zj"+i+"x").replace("Yz!y","Yz"+i+"y")
table = table.replace(i,"")
#print i
#print FLAG

print("for I and s")
for i in table:
for j in table:

if check(b64decode("N"+i+"0x")) and check(b64decode("Z"+i+j+"h")):
#print b64decode("N"+i+"0x"),b64decode("Z"+i+j+"h")
FLAG = FLAG.replace("N!0x","N"+i+"0x").replace("Z!!h","Z"+i+j+"h")
table = table.replace(i,"").replace(j,"")
#print i,j
#print FLAG

print("for X and E")
for i in table:
for j in table:
if j == i:
continue
s = b64decode(FLAG.replace("Mm!l","Mm"+i+"l").replace("LT!m","LT"+i+"m").replace("YW!k","YW"+j+'k'))
if check(s):
print s

rand

啊,一道憨憨题,mt19937都快给玩烂了。而且这还是最最简单的预测,直接套上现成的工具就能解key。至于后面的一个只给了加密函数没给解密函数用的feistel的密码,可能是DES吧,没细看。直接把密钥流反过来,用他给的加密函数来解密就好了。

MT19937预测key

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
import random


class MT19937Recover:
"""Reverses the Mersenne Twister based on 624 observed outputs.

The internal state of a Mersenne Twister can be recovered by observing
624 generated outputs of it. However, if those are not directly
observed following a twist, another output is required to restore the
internal index.

See also https://en.wikipedia.org/wiki/Mersenne_Twister#Pseudocode .

"""
def unshiftRight(self, x, shift):
res = x
for i in range(32):
res = x ^ res >> shift
return res

def unshiftLeft(self, x, shift, mask):
res = x
for i in range(32):
res = x ^ (res << shift & mask)
return res

def untemper(self, v):
""" Reverses the tempering which is applied to outputs of MT19937 """

v = self.unshiftRight(v, 18)
v = self.unshiftLeft(v, 15, 0xefc60000)
v = self.unshiftLeft(v, 7, 0x9d2c5680)
v = self.unshiftRight(v, 11)
return v

def go(self, outputs, forward=True):
"""Reverses the Mersenne Twister based on 624 observed values.

Args:
outputs (List[int]): list of >= 624 observed outputs from the PRNG.
However, >= 625 outputs are required to correctly recover
the internal index.
forward (bool): Forward internal state until all observed outputs
are generated.

Returns:
Returns a random.Random() object.
"""

result_state = None

assert len(outputs) >= 624 # need at least 624 values

ivals = []
for i in range(624):
ivals.append(self.untemper(outputs[i]))

if len(outputs) >= 625:
# We have additional outputs and can correctly
# recover the internal index by bruteforce
challenge = outputs[624]
for i in range(1, 626):
state = (3, tuple(ivals+[i]), None)
r = random.Random()
r.setstate(state)

if challenge == r.getrandbits(32):
result_state = state
break
else:
# With only 624 outputs we assume they were the first observed 624
# outputs after a twist --> we set the internal index to 624.
result_state = (3, tuple(ivals+[624]), None)
rand = random.Random()
rand.setstate(result_state)

if forward:
for i in range(624, len(outputs)):
assert rand.getrandbits(32) == outputs[i]

return rand


def test_PythonMT19937Recover():
"""Just a testcase to ensure correctness"""
mtb = MT19937Recover()

#r1 = random.Random(0x31337)

# just some discarded random numbers to move internal state forward
#[r1.getrandbits(32) for _ in range(1234)]

# the actual leak of 1000 values
#n = [r1.getrandbits(32) for _ in range(1000)]
with open ("random") as f:
nn= f.read().split("\n")
print(nn)
n = [int(_) for _ in nn[:-1]]
r2 = mtb.go(n)
print (r2.getrandbits(32))

#assert r1.getrandbits(32) == r2.getrandbits(32)


test_PythonMT19937Recover()

拿到key,改加密函数,解密得到flag。

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
# -*- coding: utf-8 -*-
import base64
import random

flag = "flag{************************************}"

hexadecimalcontrast = {
'0': '0000',
'1': '0001',
'2': '0010',
'3': '0011',
'4': '0100',
'5': '0101',
'6': '0110',
'7': '0111',
'8': '1000',
'9': '1001',
'a': '1010',
'b': '1011',
'c': '1100',
'd': '1101',
'e': '1110',
'f': '1111',
}
IP = [58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7]
IP_1 = [40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25]
E = [32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1]
S = [[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9, ],
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12, ],
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13, ],
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14, ],
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3, ],
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13, ],
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12, ],
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11, ]]
PC_1 = [57, 49, 41, 33, 25, 17, 9, 1,
58, 50, 42, 34, 26, 18, 10, 2,
59, 51, 43, 35, 27, 19, 11, 3,
60, 52, 44, 36, 63, 55, 47, 39,
31, 23, 15, 7, 62, 54, 46, 38,
30, 22, 14, 6, 61, 53, 45, 37,
29, 21, 13, 5, 28, 20, 12, 4, ]
PC_2 = [14, 17, 11, 24, 1, 5, 3, 28,
15, 6, 21, 10, 23, 19, 12, 4,
26, 8, 16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55, 30, 40,
51, 45, 33, 48, 44, 49, 39, 56,
34, 53, 46, 42, 50, 36, 29, 32, ]
P = [16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25, ]
movnum = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]


def HexToBin(string):
"Convert sixteen to binary"

Binstring = ""
string = string.lower()
for i in string:
try:
Binstring += hexadecimalcontrast[i]
except:
return -1
return Binstring


def BinToStr(strbin):
"Turn the binary string to a ASCII string"

strten = ""
for i in range(len(strbin) // 8):
num = 0
test = strbin[i * 8:i * 8 + 8]
for j in range(8):
num += int(test[j]) * (2**(7 - j))
strten += chr(num)
return strten


def StrToHex(string):
"Converts a string to HEX"

hexStr = ''
for i in string:
tmp = str(hex(ord(i)))
if len(tmp) == 3:
hexStr += tmp.replace('0x', '0')
else:
hexStr += tmp.replace('0x', '')
return hexStr

def Binxor(string1, string2):
"If the length is different, only the short one is returned."

strlen = 0
xorstr = ""
if len(string1) > len(string2):
strlen = len(string2)
else:
strlen = len(string1)
for i in range(strlen):
if string1[i] == string2[i]:
xorstr += '0'
else:
xorstr += '1'
return xorstr


def SubstitutionBox(keyfield, sub):

newkeyfield = ''
for i in range(len(sub)):
newkeyfield += keyfield[sub[i] - 1]
return newkeyfield


def SubkeyGeneration(freq, C, D):

for i in range(movnum[freq]):
C = C[1:] + C[:1]
D = D[1:] + D[:1]
return C, D, SubstitutionBox(C + D, PC_2)


def enkey(secretkey):

netss = SubstitutionBox(HexToBin(StrToHex(secretkey)), PC_1)
C, D = netss[:28], netss[28:]
key = []
for i in range(16):
C, D, keyone = SubkeyGeneration(i, C, D)
key.append(keyone)
return key


def Sbox(plaintext, sub):

return HexToBin("%x" % S[sub][int(plaintext[:1] + plaintext[-1:], 2) * 16 + int(plaintext[1:-1], 2)])


def Function(plaintext, secretkey):

plaintext = Binxor(SubstitutionBox(plaintext, E),
secretkey)
sout = ''
for i in range(8):
sout += Sbox(plaintext[i * 6:(i + 1) * 6], i)
sout = SubstitutionBox(sout, P)
return sout


def endecrypt(plaintext, secretkey):

netss = SubstitutionBox(HexToBin(StrToHex(plaintext)), IP)
L, R = netss[:32], netss[32:]
for i in range(16):
L, R = R, Binxor(L, Function(R, secretkey[i]))
return SubstitutionBox(R + L, IP_1)


def encryption(plaintext, secretkey):

plaintext = plaintext + (8 - len(plaintext) % 8) * '\0'
keys = enkey(secretkey[:8])
print(keys)
ciphertext = ''
for i in range(len(plaintext) / 8):
ciphertext += endecrypt(plaintext[i * 8:(i + 1) * 8], keys)
return base64.b64encode(BinToStr(ciphertext))

def decryption(plaintext, secretkey):
plaintext = "t2GzuEfVDaJsNLBqC8N7C3/2UgIeCoQLC2qLkT1ukFULskzMc1u9QeFizVUfFYfA"
plaintext = base64.b64decode(plaintext)
print plaintext
keys = enkey(secretkey[:8])[::-1]
#print(keys)
ciphertext = ''
for i in range(len(plaintext) / 8):
ciphertext += endecrypt(plaintext[i * 8:(i + 1) * 8], keys)
return (BinToStr(ciphertext))

def generate():
fw = open("random", "w")
for i in range(700):
fw.write(str(random.getrandbits(32))+"\n")
fw.close()

generate()
f = open("flag.txt", "r")

key = str(random.getrandbits(32))
key = '2990136190'
ciphertext = decryption(flag, key)
print ciphertext

朱雀组

simple

简单仿射加密,就是123456和26不互素,我们用26的一个素因子13代替26先,得到的答案,可以选择:如果不符合uuid格式,就加个13,不过,后来想想,由于uuid只有abcdef加上 ‘flag’ 的lg,模13其实是足够的。所以其实不需要判断。

由于仿射不加密除字母外的字符,所以直接拼接上来就可以了

确实simple,解密代码就一行:

1
2
3
from Crypto.Util.number import inverse
flag = ''.join(i if i not in "qwertyuiopasdfghjklzxcvbnm" else chr(((ord(i)-ord('a') - 321564))*inverse(123456,13)%13+ord('a')) for i in 'kgws{m8u8cm65-ue9k-44k5-8361-we225m76eeww}')
print(flag)

RUA

rsa低加密指数攻击,直接套先知上的一个脚本,刚开始测e=3,发现失败,于是爆破e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import binascii,gmpy2

def CRT(mi, ai):
assert(reduce(gmpy2.gcd,mi)==1)
assert (isinstance(mi, list) and isinstance(ai, list))
M = reduce(lambda x, y: x * y, mi)
ai_ti_Mi = [a * (M / m) * gmpy2.invert(M / m, m) for (m, a) in zip(mi, ai)]
return reduce(lambda x, y: x + y, ai_ti_Mi) % M

for e in range(1,20):
try:
m=gmpy2.iroot(CRT([n1,n2,n3], [c1,c2,c3]), e)[0]
print(binascii.unhexlify(hex(m)[2:].strip("L")))
except:
pass

guess_game

两个考点:LCG和LFSR

LCG攻击原理这里看,NCTF出现过的。

LFSR的攻击手法ctf-wiki上看。

解密流程:

第一步我们通过六个level的值恢复LCG的参数从而预测第七个code,从而达到(500,10)这个level。开局拥有10个coin!

然后我们用9次机会,可以得到72bit的输出,其中前40bit输出逆序可以得到初始的r

但是这里的lfsr其实是39级的,所以至少需要78bit才能恢复

所以我们要爆破6bit的输出

需要说明的是即使有78bit的输出,也有可能因为矩阵没有满秩从而解不出key来。

然后这里我换了个思路,我硬猜,要求前九次猜中一个,这样我就可以多拿两组数据(其实多拿一次就够了)

这样我就能拿到80bit的输出,然后我用下面那个sage脚本,根据wiki所述构造矩阵,解个线性方程,得到key。点解密的时候要放《好运来》,增加解密成功的概率,唱《国歌》应该也可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
output = '10100010010100010010101001100110000000000111101111011101010101000111110011111011'

N = 39
F = GF(2)
ans=[]
out = output
Sn = [vector(F,N) for j in range(N+1)]
for j in range(N+1):
Sn[j] = list(map(int,out[j:j+N]))

X = matrix(F,Sn[:N])
invX = (X^-1)
Y = vector(F,Sn[-1])
Cn = Y * invX
res = ''.join(str(i) for i in Cn)
ans.append(int(res[::-1],2))
print (ans)
print(len(ans))

>> [71834525659]
>> 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
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
# -*- coding: utf-8 -*-
from Crypto.Util.number import long_to_bytes,bytes_to_long,inverse
from pwn import *
context.log_level = 'debug'
def gcd(a, b):
while b:
a, b = b, a%b
return a


def crack_unknown_increment(states, modulus, multiplier):
"""
利用伪随机序列s,模数n,乘数m,恢复增数c

states:伪随机数序列s
modulus:模数n,
multiplier:乘数m
increment:增量c
return:根据n,m,s0,s1恢复c,并返回
"""
increment = (states[1] - states[0]*multiplier) % modulus
return modulus, multiplier, increment


def crack_unknown_multiplier(states, modulus):
"""
利用伪随机序列s和模数n,恢复乘数m

states:伪随机数序列s
modulus:模数n,
multiplier:乘数m
return:根据n,s0,s1,s2恢复m,然后利用crack_unknown_increment恢复c
"""
multiplier = (states[2] - states[1]) * inverse(states[1] - states[0], modulus) % modulus
return crack_unknown_increment(states, modulus, multiplier)


def crack_unknown_modulus(states):
"""
利用初值和随后LCG产生的连续的几个值,恢复模数n

modulus:模数n
states:伪随机数序列s
diffs:相邻随机数s的差值:t序列
zeroes:与t系列有关的差值,zeroes = t2*t0 - t1*t1,t0,t1,t2相邻
return:根据n,s0,s1,s2恢复m,然后利用crack_unknown_increment恢复c
"""
diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
zeroes = [t2*t0 - t1*t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
modulus = abs(reduce(gcd, zeroes))
return crack_unknown_multiplier(states, modulus)


def lcg(seed,params):
"""
LCG算法

params:(multiplier,increment,modulus)
seed:伪随机序列中第一个值s0
return:返回一个迭代器,其中的值是私钥序列
"""
(m,c,n)=params
x = seed % n
yield int(x)
while True:
x = (m * x + c) % n
yield int(x)

def decode(pri,pub,c,p):
"""
ElGamal解密算法

pri:一方私钥
pub:另一方公钥
sharekey:双方协商密钥
c:密文
p:模数
m:明文
return:返回明文字符
"""
sharekey = pow(pub,pri,p)
m=long_to_bytes(c*inverse(sharekey,p)%p)
return m

class Game:
def __init__(self, n, key, r, b):
self.n = n#40
self.key = key
self.r = r & (2 ** self.n - 1)
self.b = b#8

def out(self):
o = self.r & 1
p = self.r & self.key
q = 0
while p:
q = q + p & 1
p = p >> 1
q = q & 1
t = q << (self.n - 2) & (2 ** self.n - 1)
self.r = t | (self.r >> 1) & (2 ** self.n - 1)
return o

def next(self):
res = 0
for i in range(self.b):
o = self.out()
res |= o << (self.b - 1 - i)
return res

def getr(s):
r = ''
for i in s:
b = bin(i)[2:].rjust(8,'0')
for each in b:
r = each+r
print int(r,2)
return int(r,2)

def getk(s):
k=""
for i in s:
b = bin(i)[2:].rjust(8,'0')
for each in b:
k += each
return k

sh = remote('59.110.243.101','5123')
sh.recvuntil("Your choice:\n")
sh.sendline("2")
sh.recvuntil("Baby:")
Baby = sh.recvuntil("\n")[:-1]
sh.recvuntil("Easy:")
Easy = sh.recvuntil("\n")[:-1]
sh.recvuntil("Normal:")
Normal = sh.recvuntil("\n")[:-1]
sh.recvuntil("Hard:")
Hard = sh.recvuntil("\n")[:-1]
sh.recvuntil("Master:")
Master = sh.recvuntil("\n")[:-1]
sh.recvuntil("Crazy:")
Crazy = sh.recvuntil("\n")[:-1]

pri=[Baby,Easy,Normal,Hard,Master,Crazy]

(n,m,c)=crack_unknown_modulus([int(each) for each in pri])


x=int(pri[0])

key = lcg(x,(m,c,n))
for _ in range(6):
next(key)
choice = next(key)
sh.recvuntil("Input the level code:\n")

sh.sendline(str(choice))
sh.recvuntil("Your choice:\n")
sh.sendline("1")
#flag=""
s=[]
F = 0
for _ in range(9):
sh.recvuntil("Input your answer:\n")
sh.sendline("0")
ans = sh.recvuntil("The answer is ")
if 'Right' in ans:
F = 1
print("NICE")
s.append(int(sh.recvuntil("!")[:-1]))

if F == 1:
sh.recvuntil("Input your answer:\n")
sh.sendline("0")
ans = sh.recvuntil("The answer is ")
s.append(int(sh.recvuntil("!")[:-1]))

r = getr(s[:5])
output = getk(s[:])
print "r:"+str(r)
print "output:"+ output
print s
k = input("k:")
game = Game(40,k,r,8)
for _ in range(10):
next(game)

for _ in range(500):
sh.recvuntil("Input your answer:\n")
sh.sendline(str(next(game)))

sh.interactive()


[DEBUG] Received 0x3f bytes:
'Your coin: 500\n'
'You Win!\n'
'flag{12727f129b72e685388c6a67538ee9b0}\n'

最后五分钟出flag,太幸运了。就是没队伍,有点浪费了

玄武组

Safe_box

主要逻辑

  1. 过了PoW进行交互
  2. 1可以得到一组23*20的key
  3. 3可以得到flag的密文c
  4. 加密函数生成23*40的key,一共23组明文,每组明文加密40次,记录每次的结果
  5. 每一次的加密逻辑为b = m + Σ((a_j*i)e_i),其中a_j为组里的加密次数,j=1,2,,40;e_i为列表e里的每一个元素,i = 1,2,3,,,29

解密流程

  1. 第一组,当i=0时,a=1,b_1 = m_1 + Σ(e_i)
  2. 第二组,当i=0时,a=1,b_2 = m_2 + Σ(e_i)
  3. 第三组,当i=0时,a=1,b_3 = m_3 + Σ(e_i)
  4. 我们得到的23组信息中的每组的第一条就是b_1, b_2,b_3
  5. 所以我们只需要获得Σ(e_i)就可以还原所有的m
  6. 被加密的是pri,pri是PEM文件格式,有固定的开头、结尾格式,所以我们知道部分明文,所以我们知道m_1
  7. 利用b_1和m_1获取Σ(e_i)
  8. 再利用b_1,,,b_23和Σ(e_i) 获取所有m
  9. 再利用私钥文件对flag进行解密

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding: utf8 -*-
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
import re

with open('data.txt') as f:
data = f.read()

ms = re.findall("{'a': 1, 'b': (.*?)L}",data)
c = re.findall("flag:(.*)",data)
m1 = b'-----BEGIN RSA PRIVATE KEY-----\\nMIICXAIB'
es = int(ms[0])-bytes_to_long(m1)
print es
pri=""
for i in ms:
pri = pri + long_to_bytes(int(i)-es)
print pri
pri = RSA.importKey(pri.replace("\\"",""))
print(long_to_bytes(pow(int(c[0]),pri.d,pri.n)))

easy_wa

bytectf原题lrlr的一个part

https://blog.csdn.net/qq_33458986/article/details/100699263

calc是一个多项式在GF(2)下的平方运算,我们要做的应该是求根。

但是这里给出的既约多项式的阶不大,我们一直平方下去,很快就能回到起点。所以直接拿密文去calc就行了。

先交互,过那个恶心的PoW 跟青龙组那个一样,再看看名字,同一个出题人,凑!,输入token拿到4组密文

然后每组分别解密

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 os import urandom as ua
from Crypto.Util.number import *

magic=32805

def calc(m,p):
res=0
lens=hlen(p)
for i in bin(m)[2:]:
res*=2
res^=m if i=='1' else 0
res^=p if hlen(res) == lens else 0
return res

def check(s):
for i in s:
if i not in "flag{0123456789bcde-_}\x00":
return False
return True

flags=[3723147309,8016759097521062564,267529443716352603115102819467183104162,110068498753042154535753131190467876514149603112955078214309283006458454378660]
FLAG=""
r = 2
for flag in flags:
r*=2
while True:
flag=calc(flag,magic + (1<<r*8))
if check(long_to_bytes(flag)[:10]):
FLAG+=(long_to_bytes(flag))
print FLAG
break

就这么ak了网鼎杯的密码学?赵总原话:这届密码学不行。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 643713081@qq.com

文章标题:2020 网鼎杯

文章字数:5.7k

本文作者:Van1sh

发布时间:2020-05-10, 21:15:00

最后更新:2020-06-03, 14:17:48

原始链接:http://jayxv.github.io/2020/05/10/2020网鼎杯/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏