CryptoCTF

farm

直接上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sage.all import *
import string, base64, math
ALPHABET = string.printable[:62] + '\\='
F = list(GF(64))
enc = "805c9GMYuD5RefTmabUNfS9N9YrkwbAbdZE0df91uCEytcoy9FDSbZ8Ay8jj"

for key in range(1,64):
msg = ""
pkey = F[key]
for j in enc:
msg += ALPHABET[F.index(F[ALPHABET.index(j)]/pkey)]
try:
print(base64.b64decode(msg))
expect:
pass

原题密钥的各种变化都是有限域上的多项式计算,范围比较小(一共64个多项式),直接爆就可以出了。

keybase

由于和巅峰极客撞上了这道题只能复现了。说得我好像能做出来似的

我们先来看看题目给的代码,我们可以得到被加密的flag数据,也可以自己发送数据并获得对应密文和密钥(都是不全的),并且加密我们发送的数据的密钥和iv也是加密flag的密钥和iv。很显然,我们就是要利用自己发出去的数据来得到key和iv。

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
from Crypto.Util import number
from Crypto.Cipher import AES
import os, sys, random
from flag import flag
def keygen():
iv, key = [os.urandom(16) for _ in '01']
return iv, key
def encrypt(msg, iv, key):
aes = AES.new(key, AES.MODE_CBC, iv)
return aes.encrypt(msg)
def decrypt(enc, iv, key):
aes = AES.new(key, AES.MODE_CBC, iv)
return aes.decrypt(enc)
def die(*args):
pr(*args)
quit()
def pr(*args):
s = " ".join(map(str, args))
sys.stdout.write(s + "\n")
sys.stdout.flush()
def sc():
return sys.stdin.readline().strip()
def main():
border = "+"
pr(border*72)
pr(border, " hi all, welcome to the simple KEYBASE cryptography task, try to ", border)
pr(border, " decrypt the encrypted message and get the flag as a nice prize! ", border)
pr(border*72)
iv, key = keygen()
flag_enc = encrypt(flag, iv, key).hex()
while True:
pr("| Options: \n|\t[G]et the encrypted flag \n|\t[T]est the encryption \n|\t[Q]uit")
ans = sc().lower()
if ans == 'g':
pr("| encrypt(flag) =", flag_enc)
elif ans == 't':
pr("| Please send your 32 bytes message to encrypt: ")
msg_inp = sc()
if len(msg_inp) == 32:
enc = encrypt(msg_inp, iv, key).hex()
r = random.randint(0, 4)
s = 4 - r
mask_key = key[:-2].hex() + '*' * 4
mask_enc = enc[:r] + '*' * 28 + enc[32-s:]
pr("| enc =", mask_enc)
pr("| key =", mask_key)
else:
die("| SEND 32 BYTES MESSAGE :X")
elif ans == 'q':
die("Quitting ...")
else:
die("Bye ...")
if __name__ == '__main__':
main()

然后我们连接上,拿到加密后的flag,再发送自己的数据,这里我们直接输入32个0(当然其他的也行,32个字节符合要求即可,主要是作为已知的明文)

回显数据如下

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
D:\python\python.exe D:/PyCode/进阶练习/CryptoCTF/keybase.py
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ hi all, welcome to the simple KEYBASE cryptography task, try to +
+ decrypt the encrypted message and get the flag as a nice prize! +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Options:
| [G]et the encrypted flag
| [T]est the encryption
| [Q]uit
G
| encrypt(flag) = 2350ee55955be41ebbceda04271b2d964a147f9ee518b836dad69f3a72923796
| Options:
| [G]et the encrypted flag
| [T]est the encryption
| [Q]uit
T
| Please send your 32 bytes message to encrypt:
00000000000000000000000000000000
| enc = 4bab****************************cd5e57af792fe0ff52458f9f8f5d2417
| key = eabbe1c4d727f51463e2726fd9a7****
| Options:
| [G]et the encrypted flag
| [T]est the encryption
| [Q]uit

根据回显的数据,我们可以知道key有只有4位被省略了,而返回的密文的后半段我们也是已知的。

根据ecb模式的特性,我们只要有完整的密文,密钥便可以爆破出来,虽然回显的密文不完整,但flag的密文是完整的。

flag的后一半与其对应密文的前一半异或后进入加密块得到的就是后半段密文,这里爆破密钥逆一下这个过程,flag肯定是可打印字符。

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.Cipher import AES
from string import printable

c = 0x2350ee55955be41ebbceda04271b2d964a147f9ee518b836dad69f3a72923796
key = 0xeabbe1c4d727f51463e2726fd9a70000
c = long_to_bytes(c)

for i in range(65536):
bkey = long_to_bytes(key+i)
aes = AES.new(bkey, AES.MODE_CBC, c[:16])
m2 = aes.decrypt(c[16:])
for j in m2:
if chr(j) in printable:
f = 1
else:
f = 0
break
if f == 1:
break
print(m2)
print(bkey)
#b'_7He_5eCrET_1V?}'
#b"\xea\xbb\xe1\xc4\xd7'\xf5\x14c\xe2ro\xd9\xa7\x0b\x9b"

现在我们有了密钥和后半段flag,可以开始求iv了。

我们自己发送的数据的后半段密文是已知的,明文已知,密钥也已知了,我们可以求出前半段密文,最后再求出iv

1
2
3
4
5
6
7
8
9
10
11
12
#取密文后16个字节,这里的密文是自己发送的数据对应的密文
c_z = 0xcd5e57af792fe0ff52458f9f8f5d2417
c_z = long_to_bytes(c_z)
aes = AES.new(bkey, AES.MODE_CBC, b'0'*16)
c_a = aes.decrypt(c_z)#前半段密文
aes2 = AES.new(bkey, AES.MODE_CBC, b'0'*16)
iv = aes2.decrypt(c_a)#向量iv
aes3 = AES.new(bkey, AES.MODE_CBC, iv)
#最后求得flag
flag = aes3.decrypt(c)
print(flag)
#b'CCTF{h0W_R3cOVER_7He_5eCrET_1V?}'

最后,解出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
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from string import printable

c = 0x2350ee55955be41ebbceda04271b2d964a147f9ee518b836dad69f3a72923796
key = 0xeabbe1c4d727f51463e2726fd9a70000
c = long_to_bytes(c)

for i in range(65536):
bkey = long_to_bytes(key+i)
aes = AES.new(bkey, AES.MODE_CBC, c[:16])
m2 = aes.decrypt(c[16:])
for j in m2:
if chr(j) in printable:
f = 1
else:
f = 0
break
if f == 1:
break

#取密文后16个字节
c_z = 0xcd5e57af792fe0ff52458f9f8f5d2417
c_z = long_to_bytes(c_z)
aes = AES.new(bkey, AES.MODE_CBC, b'0'*16)
c_a = aes.decrypt(c_z)
aes2 = AES.new(bkey, AES.MODE_CBC, b'0'*16)
iv = aes2.decrypt(c_a)
aes3 = AES.new(bkey, AES.MODE_CBC, iv)
flag = aes3.decrypt(c)
print(flag)

这题主要是得搞清逻辑,各个环节都理顺了就还好做了(但这不影响我菜

hyper_normal

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
import random
from FLAG import flag
p = 8443
def transpose(x):
result = [[x[j][i] for j in range(len(x))] for i in range(len(x[0]))]
return result
def vsum(u, v):
assert len(u) == len(v)
l, w = len(u), []
for i in range(l):
w += [(u[i] + v[i]) % p]
return w
def sprod(a, u):
w = []
for i in range(len(u)):
w += [a*u[i] % p]
return w
def encrypt(msg):
l = len(msg)
hyper = [ord(m)*(i+1) for (m, i) in zip(list(msg), range(l))]
V, W = [], []
for i in range(l):
v = [0]*i + [hyper[i]] + [0]*(l - i - 1)
V.append(v)
random.shuffle(V)
for _ in range(l):
R, v = [random.randint(0, 126) for _ in range(l)], [0]*l
for j in range(l):
v = vsum(v, sprod(R[j], V[j]))
W.append(v)
random.shuffle(transpose(W))
return W
enc = encrypt(flag)
print('W=',enc)

我们来看看加密流程,先将明文转化为对角矩阵V且V的每一个元素都乘以了自己在明文中对应的位置(从1开始算),然后打乱行的顺序,再乘以矩阵R(可以这样看),由于对角矩阵的关系,即便行的顺序被打乱,得到的矩阵的每一行对应的明文字符顺序不会变,而后转置并在此打乱行,经过这个变换后,我们虽然无法准确地知道明文字符在哪个位置,但大概的位置我们可以知道,最终得到的W的每一列都一次含有一个明文字符,我们可以对明文字符进行爆破,查看是否有对应的结果在这一列中,虽然顺序被打乱,但正确的明文对应的结果的每一个元素都能在W对应的列里找到。

解题代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from string import printable
m=""
w=#太长了这里就不放了
result = [[W[j][i] for j in range(len(W))] for i in range(len(W[0]))]
for j in range(55):
for i in printable:
f = 0
may_group = []
for R in range(127):
test_i = (ord(i)*(j+1)*R)%8443
may_group.append(test_i)
for w in result[j]:
if w not in may_group:
f = 1
if f == 0:
m += i
print(m)
#CCTF{H0w_f1Nd_th3_4lL_3I9EnV4Lu35_iN_FiN173_Fi3lD5!???}

Rima

题目代码

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 long_to_bytes,bytes_to_long
f = open("g.enc","rb")
a = 257
b = 263
c = 67
cipher = bytes_to_long(f.read())
arr_m = []
while cipher:
arr_m.append(cipher % 5)
cipher //= 5
arr_m = arr_m[::-1]
for i in range(len(arr_m) - c - 1, -1, -1):
arr_m[i] -= arr_m[i+c]
arr_m = arr_m[67:]
arr_m = arr_m[:256]
for i in range(len(arr_m) - 2, -1, -1):
arr_m[i] -= arr_m[i + 1]
m = ''
for i in arr_m:
m += str(i)
flag = long_to_bytes(int(m, 2))
print(flag)

当我看到题目的时候我就知道我不会了,因为我根本想不到什么办法去求a,b,c后面看了一些大佬的博客,发现原来可以爆破flag的长度去求出a,b,c(不断加长flag知道写出来的字节数和g.enc文件一样),然后恍然大悟。有了这三个值之后就好办了,其他过程都是可逆的。

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 long_to_bytes,bytes_to_long
f = open("g.enc","rb")
a = 257
b = 263
c = 67
cipher = bytes_to_long(f.read())
arr_m = []
while cipher:
arr_m.append(cipher % 5)
cipher //= 5
arr_m = arr_m[::-1]
for i in range(len(arr_m) - c - 1, -1, -1):
arr_m[i] -= arr_m[i+c]
arr_m = arr_m[67:]
arr_m = arr_m[:256]
for i in range(len(arr_m) - 2, -1, -1):
arr_m[i] -= arr_m[i + 1]
m = ''
for i in arr_m:
m += str(i)
flag = long_to_bytes(int(m, 2))
print(flag)
#CCTF{_how_finD_7h1s_1z_s3cr3T?!}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!