题目合集

题目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
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from os import urandom
from flag import flag

def cbc_encrypt(msg: bytes):
msg = pad(msg, 16)
msg = [msg[i:i+16] for i in range(0, len(msg), 16)]
key = urandom(16)
out = []
for block in msg:
cipher = AES.new(key, AES.MODE_ECB)
next = cipher.encrypt(block)
out.append(next)
key = next
out = b"".join(out)
return key, out

def main():
key, ct = cbc_encrypt(flag*3)
# print(f"flag = {flag}")
# print(f"key = {key}")
print(f"ct = {ct}")

if __name__ == "__main__":
main()
"""
ct = b'\x179\xb8l\x97\xbew\xc2\xd5f~\x8e\xdc\xf2\x9b\xabR\xa9a\xd2\xf4\xde\xd6|\xd1\x9f\xe9q\x1d\xfcm\xfbj\xe9\x9e\xab\xf5fL\xb3\xb5_\xa5\x16\x8e\x7f\x9fV`\x8b\x16\xa1\xa6)\x08\x97\x91\xbd3\x1d\xeb\\\x86\xa2\xd6\x94>\xf3\xfdt\xd9\x14\xf3\xfc\xe2\x02\xd6\xc4\xcfq"\x1a\x14~2]4\x9f\xc9\x88\xf8\x12\xb6\xa2\xd7\xec\x0b\x7f\xd4d\xdc\xc6\xb4]\x10u\xc6f\x97m\xccA\x82\x02\xa5gh\x85\x85Wz\xd9.\xff\x9bx\x99J\x0e\x86\x16\x90\xad\x1e\x17\x86\x95\xb8S\x17\xea\x93v\xd0'
"""

思路
因为上一轮的加密结果是下一轮的密钥,且密文已知,那我们就可以进行倒推因为flag*3我们可以从循环中拼接出来
exp

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Cipher import AES
ct = b'\x179\xb8l\x97\xbew\xc2\xd5f~\x8e\xdc\xf2\x9b\xabR\xa9a\xd2\xf4\xde\xd6|\xd1\x9f\xe9q\x1d\xfcm\xfbj\xe9\x9e\xab\xf5fL\xb3\xb5_\xa5\x16\x8e\x7f\x9fV`\x8b\x16\xa1\xa6)\x08\x97\x91\xbd3\x1d\xeb\\\x86\xa2\xd6\x94>\xf3\xfdt\xd9\x14\xf3\xfc\xe2\x02\xd6\xc4\xcfq"\x1a\x14~2]4\x9f\xc9\x88\xf8\x12\xb6\xa2\xd7\xec\x0b\x7f\xd4d\xdc\xc6\xb4]\x10u\xc6f\x97m\xccA\x82\x02\xa5gh\x85\x85Wz\xd9.\xff\x9bx\x99J\x0e\x86\x16\x90\xad\x1e\x17\x86\x95\xb8S\x17\xea\x93v\xd0'

#每16个为一组
msg=[ct[i:i+16] for i in range(0,len(ct),16)]
for i in range(len(msg)-1,-1,-1):
enc=msg[i]
key=msg[i-1]
cipher=AES.new(key,AES.MODE_ECB)
dec=cipher.decrypt(enc)
print(dec)

NSSCTF{07104f28-45e4-11ed-bba0-28d0eab06969}

题目2

学习鉴赏糖醋小鸡块

题目

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
#!/usr/bin/python3
from random import sample, choice
from os import urandom
from secret import flag
from binascii import unhexlify
from hashlib import sha256
from string import hexdigits
from signal import alarm

logo = '''
_______ ________ ________ ___ ___
|\ ___ \ |\ __ \|\ ____\ |\ \ / /|
\ \ __/|\ \ \|\ \ \ \___|_ \ \ \/ / /
\ \ \_|/_\ \ __ \ \_____ \ \ \ / /
\ \ \_|\ \ \ \ \ \|____|\ \ \/ / /
\ \_______\ \__\ \__\____\_\ \ __/ / /
\|_______|\|__|\|__|\_________\\\___/ /
\|_________\|___|/

________ ________ ________
|\ ___ \|\ _____\\\ __ \
\ \ \_|\ \ \ \__/\ \ \|\ \
\ \ \ \\\ \ \ __\\\ \ __ \
\ \ \_\\\ \ \ \_| \ \ \ \ \
\ \_______\ \__\ \ \__\ \__\
\|_______|\|__| \|__|\|__|

'''


_memu = '''
1. Encrypt
2. Get flag
3. Exit
'''


def proof():
plain = "".join([choice(hexdigits) for i in range(20)])
print(plain)
s = sha256(plain.encode()).hexdigest()
print(f"sha256({plain[:16]}xxxx) = {s}")
xxxx = input("plz enter the xxxx: ")
if xxxx != plain[16:]:
exit()


def rev(x):
for i in range(32):
x = rotl(x, 1)
return x


def rotl(x, n): return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)


def xorl(x, y): return list(map(lambda a, b: a ^ b, x, y))


def Int2List(x): return [x >> 24, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]


def List2Int(x): return x[0] << 24 | x[1] << 16 | x[2] << 8 | x[3]


class Enc:
def __init__(self, key):
self.K = key
self.S = sample([i for i in range(256)], 256)

def l(self, B: list) -> list:
B = List2Int(B)
B = B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24)
return Int2List(B)

def encrypt(self, plain):
T = xorl(self.K[:4], plain)
T = Int2List(rev(List2Int(T)))
T = self.l([self.S[i] for i in T])
T = xorl(self.K[4:], T)
return bytes(T).hex()


def E():
plain = input("plz enter your plaintext: ")
print(f"cipher = {C.encrypt(unhexlify(plain))}")


def Get_Flag():
print(f"cipher = {f}")
plain = input("plz enter your plaintext: ")
c = C.encrypt(unhexlify(plain))
if c == f:
print(flag)
exit(0)
else:
print("wrong!")
exit(0)


def memu():
choose = input("> ")
if choose == "1":
E()
elif choose == "2":
Get_Flag()
else:
print("Bye~")
exit(0)


if __name__ == "__main__":
proof()
print(logo)
key = urandom(8)
C = Enc(key)
f = C.encrypt(urandom(4))
print(f"sbox: {C.S}")
print(_memu)
alarm(10)
while True:
memu()

看到这题定义这么多函数,先看一下加密流程,再把定义的函数逐个拆开来看。
先通过proof:

1
2
3
4
5
6
7
8
def proof():
plain = "".join([choice(hexdigits) for i in range(20)])
print(plain)
s = sha256(plain.encode()).hexdigest()
print(f"sha256({plain[:16]}xxxx) = {s}")
xxxx = input("plz enter the xxxx: ")
if xxxx != plain[16:]:
exit()

把末四个十六进制数发送回去即可通过proof
开始加密

  • 生成一个8字节的随机密钥key
  • 用key去初始化一个Enc对象C
  • 生成4字节随机明文(plain),再用Enc对象C进行加密
  • 给出C中S盒
  • alarm(10)
    • 输入”1”可以自行构造一组明文进行加密,靶机端会返回加密后的密文
    • 输入”2”可以输入一组明文进行加密,靶机端会返回加密后的密文,并与flag进行比对,若相同则输出flag,否则输出”wrong!”

Enc:
定义S盒
有一个l函数

1
2
3
4
def l(self, B: list) -> list:
B = List2Int(B)
B = B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24)
return Int2List(B)

利用rotl函数和异或运算得到新的B,这个特定的位移组合(2, 10, 18, 24)是 SM4 算法设计者精心挑选的,目的是为了达到雪崩效应。

1
2
def rotl(x, n): return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)
def List2Int(x): return x[0] << 24 | x[1] << 16 | x[2] << 8 | x[3]

rotl函数:
本质循环左移n位多少就是把x二进制的高n位移到低位,低位32-n位移到高位
0xffffffff: 这是一个十六进制数,其二进制形式是32个1(11111111 11111111 11111111 11111111)。它与任何数进行按位与操作,都可以保留该数的低32位,并将高位全部清零
List2Int函数:
生成了一个32位比特流

1
2
3
4
5
6
def encrypt(self, plain):
T = xorl(self.K[:4], plain)
T = Int2List(rev(List2Int(T)))
T = self.l([self.S[i] for i in T])
T = xorl(self.K[4:], T)
return bytes(T).hex()

然后是encrypt函数:

  • 将key的前4个字节与明文进行异或得到T
1
def Int2List(x): return [x >> 24, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]

这个就是把32位整数拆分成4个字节的列表,不过糖醋小鸡块师傅说这个没啥用。

跟着理解了一波题目,再来看看wp
给了两种解题思路

生日攻击

这个思路就是直接爆破那个proof,在$\frac{1}{2^{32}} $的概率下去测试

构造明文攻击

鸡块师傅这里用tqdm测试了一下10s可加密的30-40组明文。考虑到还要解密实际就只能构造10-20组明文(目的是有选择的去使用一些方法避免无端消耗)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
from tqdm import tqdm
import time

TARGET_URL = "服务器地址"
MAX_TIME = 10

print(f"正在测试服务器响应速度,限制时间 {MAX_TIME} 秒...")

# 使用 tqdm 包装循环,观察 it/s (每秒迭代次数)
# 这里假设我们尝试跑 100 次,看能在 10s 内跑完多少
try:
for i in tqdm(range(100), desc="压力测试", unit="req"):
# 构造明文并发送请求
payload = {'plaintext': 'A' * 16}
response = requests.post(TARGET_URL, data=payload, timeout=2)
# tqdm 会自动计算平均耗时
time.sleep(0.05)

except Exception as e:
print(f"测试中断: {e}")

# 运行结束后,查看 tqdm 输出的 "X.XX it/s"

->如何用有限的、少量的明文,去泄露Enc对象C的信息,从而解密密文。
由于encrypt函数充分利用key的8个字节,那么解密就变得容易。
好既然如此,就从key的前四个字节开始,由于我没有靶机可以测试,只能根据鸡块师傅的思路继续理解了。
第一步,发送四个字节的”\x00”给靶机端
通过encrypt函数:不难看出得到的xorl后的T就是key的前四个字节然后就是进行置换和调用l函数。
第二步,发送:

1
b"\x00" * 3 + b"\x01"

xorl后得到的T的前三个字节并没有影响,还是key的前三个字节,最后一个字节取反。
也就是这两次明文发送,l函数输入的参数只有最后一个发生了改变。
这里鸡块师傅直观的画了个图copy一下:

红色部分代表两次构造的明文在调l函数时的不同量,黄色部分代表得到密文,线条表示该线条与另一线条之间的五个部分异或得到下方的密文。

1
2
3
4
def l(self, B: list) -> list:
B = List2Int(B)
B = B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24)
return Int2List(B)

对应的刚好是l函数循环左移n位后的部分。

那么你仔细看这个图,你会发现,第二块密文和第三块密文,对于红色块异或的量是相同的!那么我们将两次服务器返回的密文异或,会发生什么呢?在下面的分析过程中,我们把key的前半部分记作key1,后半部分记作key2.
首先,我们把m经置换后的列表记作P(m),经l函数后的列表记作l(P(m)),也就是m经第三步变换后,记作(l(P(m)))。那么两次构造明文到第三步前分别是key1、key1’,则有:
$$
c1 \oplus c2 = (l(P(key_1)) \oplus key_2) \oplus (l(P(key_1’)) \oplus key_2)
$$
消掉key2
$$
c1 \oplus c2 = l(P(key_1)) \oplus l(P(key_1’))
$$

而根据刚才的画图分析,密文中间两块对红色的异或利用是相同的,因此我们可以单独分析上式的中间两块,得到:

中间两块异或=两次明文的不同红色量异或(这个我保留一点疑问,因为其实是key的第四个字节循环左移了10位)
两次不同的红色量分别是(key1)和(key1^1){本质取反}经置换得到的最后一字节,而我们拥有这个异或值以及S盒,因此可以反查到所有可能的解。一般来说会有1-4组。
这样就获得了key1的一个字节

1
2
3
b"\x00"*2 + b"\x01"*1 +b"\x00"*1
b"\x00"*1 + b"\x01"*1 +b"\x00"*2
b"\x01"*1 + b"\x00"*3

获得其他三个字节。
而有了key1后,我们就可以发送key1作为明文,那么经第一步异或后得到的T就是四个全零字节,那么也就自然的可以得到第三步置换以及加密后的值,再与服务器返回的密文异或就能得到key2.
简直醍醐灌顶,思路非常清晰。

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
from random import sample, choice
from os import urandom
from binascii import *
from hashlib import sha256
from string import hexdigits
from Crypto.Util.number import *
from z3 import *

def rev(x):
for i in range(32):
x = rotl(x, 1)
return x

def l(B):
B = List2Int(B)
B = B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24)
return Int2List(B)

def rotl(x, n): return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)


def xorl(x, y): return list(map(lambda a, b: a ^ b, x, y))


def Int2List(x): return [x >> 24, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]


def List2Int(x): return x[0] << 24 | x[1] << 16 | x[2] << 8 | x[3]

def decrypt(K,cipher):
T = List2Int(xorl(K[4:],cipher))
s = Solver()
B = BitVec('B',32)
s.add(B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24) == T)
if s.check() == sat: #检测是否有解
result = str(s.model())
T = Int2List(int(result[5:-1]))
for i in range(len(T)):
T[i] = inv_S[T[i]]
T = List2Int(xorl(K[:4],T))
return long_to_bytes(T).hex()


class Enc:
def __init__(self, key):
self.K = key
self.S = sample([i for i in range(256)], 256)

def l(self, B: list) -> list:
B = List2Int(B)
B = B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24)
return Int2List(B)

def encrypt(self, plain):
T = xorl(self.K[:4], plain)
T = Int2List(rev(List2Int(T)))
temp = [self.S[i] for i in T]
T = self.l(temp)
T = xorl(self.K[4:], T)
return bytes(T)

def E(m):
plain = hexlify(m)
return C.encrypt(unhexlify(plain))

fin = 0
for kk in range(10):
count = 0
while(1):
try:
key = urandom(8)
C = Enc(key)
x = []
x.append(E(b"\x00"*4))
x.append(E(b"\x00"*3 + b"\x01"*1 +b"\x00"*0))
x.append(E(b"\x00"*2 + b"\x01"*1 +b"\x00"*1))
x.append(E(b"\x00"*1 + b"\x01"*1 +b"\x00"*2))
x.append(E(b"\x00"*0 + b"\x01"*1 +b"\x00"*3))
c = urandom(4)

S = C.S
inv_S = [0 for i in range(256)]
for i in range(256):
inv_S[S[i]] = i

#获取key
key_prefix = []

#1.获取key[:4]
for i in range(1,5):
if(i % 2 == 0):
temp = (xorl(x[0],x[i]))[i-2]
elif(i % 2 == 1):
temp = (xorl(x[0],x[i]))[i]
for j in range(256):
t1 = (S[j]>>6) + ((S[j]&0b111111)<<2)
t2 = (S[j^1]>>6) + ((S[j^1]&0b111111)<<2)
if(t1^t2 == temp):
key_prefix.append(inv_S[S[j]])
break
key_prefix = key_prefix[::-1]

key_suffix = Int2List(bytes_to_long(E(long_to_bytes(List2Int(key_prefix)))))
T = [0,0,0,0]
temp = l([S[i] for i in T])
key_suffix = xorl(temp, key_suffix)
key_final = long_to_bytes(List2Int(key_prefix)) + long_to_bytes(List2Int(key_suffix))

count += 1
if(decrypt(key_final,E(c)) == c.hex()):
break
except:
pass

print(count)
fin += count

print(fin//10)

代码抄的可以看看。

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
from Crypto.Util.number import *
from pwn import *
from binascii import *
from z3 import *

def rotl(x, n): return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)

def xorl(x, y): return list(map(lambda a, b: a ^ b, x, y))

def List2Int(x): return x[0] << 24 | x[1] << 16 | x[2] << 8 | x[3]

def Int2List(x): return [x >> 24, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]

def l(B):
B = List2Int(B)
B = B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24)
return Int2List(B)

def decrypt(K,cipher,inv_S):
T = List2Int(xorl(K[4:],cipher))
s = Solver()
B = BitVec('B',32)
s.add(B ^ rotl(B, 2) ^ rotl(B, 10) ^ rotl(B, 18) ^ rotl(B, 24) == T)
if s.check() == sat: #检测是否有解
result = str(s.model())
T = Int2List(int(result[5:-1]))
for i in range(len(T)):
T[i] = inv_S[T[i]]
T = List2Int(xorl(K[:4],T))
return long_to_bytes(T).hex()

def getflag():
while(1):
try:
r = remote("node4.anna.nssctf.cn",28579)
temp = r.recvline().strip().decode()[-4:]
r.sendline(temp.encode())
r.recvuntil(b"sbox: ")
S = eval(r.recvline())
inv_S = [0 for i in range(256)]
for i in range(256):
inv_S[S[i]] = i

x = []
#0
temp = r.recvuntil(b"> ")
r.sendline(b"1")
temp = r.recvuntil(b" plaintext: ")
r.sendline(hexlify(b"\x00"*4))
temp = r.recvuntil(b"cipher =")
x.append(unhexlify(r.recvline().strip()))

#1
temp = r.recvuntil(b"> ")
r.sendline(b"1")
temp = r.recvuntil(b" plaintext: ")
r.sendline(hexlify(b"\x00"*3 + b"\x01"*1 +b"\x00"*0))
temp = r.recvuntil(b"cipher =")
x.append(unhexlify(r.recvline().strip()))

#2
temp = r.recvuntil(b"> ")
r.sendline(b"1")
temp = r.recvuntil(b" plaintext: ")
r.sendline(hexlify(b"\x00"*2 + b"\x01"*1 +b"\x00"*1))
temp = r.recvuntil(b"cipher =")
x.append(unhexlify(r.recvline().strip()))

#3
temp = r.recvuntil(b"> ")
r.sendline(b"1")
temp = r.recvuntil(b" plaintext: ")
r.sendline(hexlify(b"\x00"*1 + b"\x01"*1 +b"\x00"*2))
temp = r.recvuntil(b"cipher =")
x.append(unhexlify(r.recvline().strip()))

#4
temp = r.recvuntil(b"> ")
r.sendline(b"1")
temp = r.recvuntil(b" plaintext: ")
r.sendline(hexlify(b"\x00"*0 + b"\x01"*1 +b"\x00"*3))
temp = r.recvuntil(b"cipher =")
x.append(unhexlify(r.recvline().strip()))


#获取key
key_prefix = []

#1.获取key[:4]
for i in range(1,5):
if(i % 2 == 0):
temp = (xorl(x[0],x[i]))[i-2]
elif(i % 2 == 1):
temp = (xorl(x[0],x[i]))[i]
for j in range(255):
t1 = (S[j]>>6) + ((S[j]&0b111111)<<2)
t2 = (S[j^1]>>6) + ((S[j^1]&0b111111)<<2)
if(t1^t2 == temp):
key_prefix.append(inv_S[S[j]])
break
key_prefix = key_prefix[::-1]

#2.获取key[4:]
temp = r.recvuntil(b"> ")
r.sendline(b"1")
temp = r.recvuntil(b" plaintext: ")
r.sendline(hexlify(long_to_bytes(List2Int(key_prefix))))
temp = r.recvuntil(b"cipher =")
key_suffix = Int2List(bytes_to_long(unhexlify(r.recvline().strip())))
T = [0,0,0,0]
temp = l([S[i] for i in T])
key_suffix = xorl(temp, key_suffix)
key_final = long_to_bytes(List2Int(key_prefix)) + long_to_bytes(List2Int(key_suffix))


#获取flag
temp = r.recvuntil(b"> ")
r.sendline(b"2")
temp = r.recvuntil(b"cipher =")
cipher = unhexlify(r.recvline().strip())
t = decrypt(key_final,cipher,inv_S)
r.sendline(t)
temp = r.recvline()
print(temp)
if(b"wrong" not in temp):
return

r.close()
except:
pass

getflag()

#NSSCTF{4f9d3982-be4b-4c4a-8ca0-db1a69b28b03}

等一下这个flag原来是NSSCTF上的题目那我去实操一波。