Crypto-AES

参考文章:http://f0und.icu/article/27.html

https://blog.csdn.net/qq_51999772/article/details/124301715

单独涉及AES_ECB模式

最常见的是RSA中套了一个简单的AES_ECB加密(AES加密flag),RSA加密的是AES_ECB模式的密钥key,解RSA得到密钥key进而求得flag

单独涉及AES_CBC模式

(陇原战疫2021网络安全大赛 Civet cat for Prince)

keywords: AES_CBCxor狸猫换太子

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
#题目.py
from Crypto.Cipher import AES
import os
from hashlib import sha256
import socketserver
import signal
import string
import random

table = string.ascii_letters + string.digits
BANNER = br'''
.d8888b. d8b 888 888
d88P Y88b Y8P 888 888
888 888 888 888
888 888 888 888 .d88b. 888888 .d8888b 8888b. 888888
888 888 888 888 d8P Y8b 888 d88P" "88b 888
888 888 888 Y88 88P 88888888 888 888 .d888888 888
Y88b d88P 888 Y8bd8P Y8b. Y88b. Y88b. 888 888 Y88b.
"Y8888P" 888 Y88P "Y8888 "Y888 "Y8888P "Y888888 "Y888



.d888 8888888b. d8b
d88P" 888 Y88b Y8P
888 888 888
888888 .d88b. 888d888 888 d88P 888d888 888 88888b. .d8888b .d88b.
888 d88""88b 888P" 8888888P" 888P" 888 888 "88b d88P" d8P Y8b
888 888 888 888 888 888 888 888 888 888 88888888
888 Y88..88P 888 888 888 888 888 888 Y88b. Y8b.
888 "Y88P" 888 888 888 888 888 888 "Y8888P "Y8888
'''

guard_menu = br'''
1.Tell the guard my name
2.Go away
'''

cat_menu = br'''1.getpermission
2.getmessage
3.say Goodbye
'''


def Pad(msg):
return msg + os.urandom((16 - len(msg) % 16) % 16)


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):
proof = (''.join([random.choice(table) for _ in range(12)])).encode()
sha = sha256(proof).hexdigest().encode()
self.send(b"[+] sha256(XXXX+" + proof[4:] + b") == " + sha)
XXXX = self.recv(prompt=b'[+] Give Me XXXX :')
if len(XXXX) != 4 or sha256(XXXX + proof[4:]).hexdigest().encode() != sha:
return False
return True

def register(self):
self.send(b'')
username = self.recv()
return username

def getpermission(self, name, iv, key):
aes = AES.new(key, AES.MODE_CBC, iv)
plain = Pad(name)+b"a_cat_permission"
return aes.encrypt(plain)

def getmessage(self, iv, key, permission):
aes = AES.new(key, AES.MODE_CBC, iv)
return aes.decrypt(permission)

def handle(self):
signal.alarm(50)
if not self.proof_of_work():
return
self.send(BANNER, newline=False)
self.key = os.urandom(16)
self.iv = os.urandom(16)
self.send(b"I'm the guard, responsible for protecting the prince's safety.")
self.send(b"You shall not pass, unless you have the permission of the prince.")
self.send(b"You have two choices now. Tell me who you are or leave now!")
self.send(guard_menu, newline=False)
option = self.recv()
if option == b'1':
try:
self.name = self.register()
self.send(b"Hello " + self.name)
self.send(b"Nice to meet you. But I can't let you pass. I can give you a cat. She will play with you")
self.send(b'Miao~ ' + self.iv)
for i in range(3):
self.send(b"I'm a magic cat. What can I help you")
self.send(cat_menu, newline=False)
op = self.recv()
if op == b'1':
self.send(b"Looks like you want permission. Here you are~")
permission = self.getpermission(self.name, self.iv, self.key)
self.send(b"Permission:" + permission)
elif op == b'2':
self.send(b"Looks like you want to know something. Give me your permission:")
permission = self.recv()
self.send(b"Miao~ ")
iv = self.recv()
plain = self.getmessage(iv, self.key, permission)
self.send(b"The message is " + plain)
elif op == b'3':
self.send(b"I'm leaving. Bye~")
break
self.send(b"Oh, you're here again. Let me check your permission.")
self.send(b"Give me your permission:")
cipher = self.recv()
self.send(b"What's the cat tell you?")
iv = self.recv()
plain = self.getmessage(iv, self.key, cipher)
prs, uid = plain[16:],plain[:16]
if prs != b'Princepermission' or uid != self.name:
self.send(b"You don't have the Prince Permission. Go away!")
return
else:
self.send(b"Unbelievable! How did you get it!")
self.send(b"The prince asked me to tell you this:")
f = open('flag.txt', 'rb')
flag = f.read()
f.close()
self.send(flag)
except:
self.request.close()
if option == b'2':
self.send(b"Stay away from here!")
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', 10005
print("HOST:POST " + HOST + ":" + str(PORT))
server = ForkedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()

[安洵杯 2020]easyaes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python
from Crypto.Cipher import AES
import binascii
from Crypto.Util.number import bytes_to_long
from flag import flag
from key import key
iv = flag.strip(b'd0g3{').strip(b'}')#删去头尾指定的字符串
LENGTH = len(key)
assert LENGTH == 16#AES128,AES192,AES256(128 位、192 位或 256 位)128位对应的是16个字节,所以部分平台库上,会使用16个字符或者长度为16的字符串来做密码。
hint = os.urandom(4) * 8#os.urandom(n)返回一个有n个byte那么长的一个string;字符串*n->n个字符串
print(bytes_to_long(hint)^bytes_to_long(key))
msg = b'Welcome to this competition, I hope you can have fun today!!!!!!'
def encrypto(message):
aes = AES.new(key,AES.MODE_CBC,iv)
return aes.encrypt(message)
print(binascii.hexlify(encrypto(msg))[-32:])#返回二进制数据的16进制的表现形式,从后面截取32个字符
'''
56631233292325412205528754798133970783633216936302049893130220461139160682777
b'3c976c92aff4095a23e885b195077b66'
'''

明文都是先于混淆数据(第一组是于IV,之后都是与前一组的密文)进行异或,在执行分组加密的

加密:

1, 首先将明文分组(AES128以16字节为一组),位数不足的使用特殊字符填充.

2, 生成一个随机的初始化向量IV和一个密钥.

1
2
iv = flag.strip(b'd0g3{').strip(b'}')#删去头尾指定的字符串
from key import key

3, 将IV和第一组明文异或

4, 用密钥对3中的xor后产生的密文加密

5, 用4中产生的密文对第二组明文进行xor操作

6, 用密钥对5中产生的密文加密

7,重复4-6

1
aes = AES.new(key,AES.MODE_CBC,iv)#4-7

8, 将IV和加密后的密文拼接在一起,得到最终的密文.

解密:

img

每组解密时,先进行分组加密算法的解密,然后与前一组的密文进行异或才是最初的明文

对于第一组则是与IV进行异或

1
2
3
4
5
def decry(key, IV, ms):
aes = AES.new(key, AES.MODE_ECB)
return strxor(aes.decrypt(IV), ms) # strxor(str1,str2)对两个十六进制字符串(用一种十六进制书写的方式来表示一个数值,或一串的数值)进行异或
for ms in msgs:#
IV = decry(key, IV, ms)

1, 从密文中提取IV,然后将密文分组

2, 使用密钥对第一组的密文解密,然后和IV进行xor得到明文

3, 使用密钥对第二组密文解密,然后和2中的密文xor的到明文

4, 重复2-3,直到最后一组密文

根据这个加密流程我们可以得到

1
2
3
4
5
6
假设aes 加密函数为enc(msg,key),初始向量为iv,key = key, 则其加密流程大致为:
enc1 = enc(msg[0]^ iv ,key)
enc2 = enc(msg[1]^enc(msg[0]^ iv ,key),key)
enc3 = enc(msg[2]^enc(msg[1]^enc(msg[0]^ iv ,key),key),key)
enc4 = enc(msg[3]^enc(msg[2]^enc(msg[1]^enc(msg[0]^ iv ,key),key),key),key)
final_enc = enc1+enc2+enc3+enc4

而由于题目中所给出的hint长度与key长度不一样,导致我们可以推出hint,从而恢复出key

1
2
3
4
5
tmp = 56631233292325412205528754798133970783633216936302049893130220461139160682777
hint = int(str(hex(tmp))[2:10] * 8, 16)
# 由于hint为32位,key为16位所以他们异或后tmp的高位就是hint的高位
# str() 函数将对象转化为适于人阅读的形式也就是说从hex()到str(hex())表现内容没有改变,2个16进制为一个字节,int(a,16)转16进制
key = long_to_bytes(tmp ^ hint)#所以tmp与hint再次异或就是key

而此时我们已经有的信息包括

1
2
3
key
enc4
msg[0],msg[1],msg[2],msg[3]

我们要通过这些已知量求出初始向量

解密图:image-20210811141323359

1
2
3
4
5
6
7
8
9
10
11
12
假设aes 解密函数为decrypt(enc,key),初始向量为iv,key = key, 则其解密流程大致为:
msg[0] = decrypt(enc1,key)^iv
msg[1] = decrypt(enc2,key)^enc1
msg[2] = decrypt(enc3,key)^enc2
msg[3] = decrypt(enc4,key)^enc3
msg = msg[0]+msg[1]+msg[2]+msg[3]

而我们已经有enc4,key,msg[0],msg[1],msg[2],msg[3],因此:
enc3 = decrypt(enc4,key)^msg[3]
enc2 = decrypt(enc3,key)^msg[2]
enc1 = decrypt(enc2,key)^msg[1]
iv = decrypt(enc1,key)^msg[0]

所以就有

1
2
3
4
5
def decry(key, IV, ms):
aes = AES.new(key, AES.MODE_ECB)
return strxor(aes.decrypt(IV), ms) # strxor(str1,str2)对两个十六进制字符串(用一种十六进制书写的方式来表示一个数值,或一串的数值)进行异或
for ms in msgs:
IV = decry(key, IV, ms)
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
from Crypto.Util.number import long_to_bytes
from Crypto.Util.number import bytes_to_long
import binascii, sys
from Crypto.Util.strxor import strxor
from Crypto.Cipher import AES
# ----------get key---------
tmp = 56631233292325412205528754798133970783633216936302049893130220461139160682777
hint = int(str(hex(tmp))[2:10] * 8, 16)
# 由于hint为32位,key为16位所以他们异或后tmp的高位就是hint的高位
# str() 函数将对象转化为适于人阅读的形式也就是说从hex()到str(hex())表现内容没有改变,2个16进制为一个字节,int(a,16)转16进制
key = long_to_bytes(tmp ^ hint)#所以tmp与hint再次异或就是key
# ----------get iv-----------
msg = b'Welcome to this competition, I hope you can have fun today!!!!!!'
msgs = [msg[ii:(ii + 16)]for ii in range(0, len(msg), 16)]#将长字符串msg分割成16字节(128位)的块,并将这些块存储在列表msgs中。
# 将明文分组(常见的以16字节为一组)
msgs.reverse()
# 倒转msgs
IV = binascii.unhexlify('3c976c92aff4095a23e885b195077b66') # ???
# decry(key,IV,ms)???
def decry(key, IV, ms):
aes = AES.new(key, AES.MODE_ECB)
return strxor(aes.decrypt(IV), ms) # strxor(str1,str2)对两个十六进制字符串(用一种十六进制书写的方式来表示一个数值,或一串的数值)进行异或
for ms in msgs:
IV = decry(key, IV, ms)
print(b'NSSCTF{' + IV + b'}')

[SWPUCTF 2022 新生赛]AES

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
#timu.py
import base64
from Crypto.Cipher import AES
from flag import getflag
iv = '1229002635654321'
key = 'nssctfneedcrypto'
data = getflag()

def pad(data):
pad_data = data
for i in range(0, 16 - len(data)):
pad_data = pad_data + ' '
return pad_data

def AES_en(key, data):
if len(data) < 16:
data = pad(data)
AES_obj = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8"))
AES_en_str = AES_obj.encrypt(data.encode("utf-8"))
AES_en_str = base64.b64encode(AES_en_str)
AES_en_str = AES_en_str.decode("utf-8")
return AES_en_str

data = AES_en(key, data)
print(data)
#data=862EoKZMO3sqpNlzyvIW5G/8MFeAI/zgGXcgi5eNOL8=




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#decry.py
import base64
from Crypto.Cipher import AES

iv = '1229002635654321'
key = 'nssctfneedcrypto'
data = '862EoKZMO3sqpNlzyvIW5G/8MFeAI/zgGXcgi5eNOL8='

def AES_de(key, data):
# 解密过程逆着加密过程写
# 将密文字符串重新编码成二进制形式
data = data.encode("utf-8")
#base64解码
data = base64.b64decode(data)
#创建解密对象
aes = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8"))
#完成解密
flag= aes.decrypt(data)
return flag

data = AES_de(key, data)
print(data)

[SWPUCTF 2022 新生赛]Caesar?Ceaasr!

栅栏密码加密后出现类似flag格式…

image-20230311102606230

然后就是移位

[HNCTF 2022 WEEK3]AES

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'
ciper = [ct[i:i+16]for i in range(0,len(ct),16)]
def cbc_decry(ct):
ciper = [ct[i:i + 16] for i in range(0, len(ct), 16)]
for i in range(1,len(ciper)):
aes_d = AES.new(ciper[i-1],AES.MODE_ECB)
flag = aes_d.decrypt(ciper[i])
print(flag.decode(), end='')#变量 flag 中的字节序列解码为字符串并输出,不换行。
cbc_decry(ct)

[CISCN 2021初赛]oddaes(未解决)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#chall.py
from aes import AES
from flag import key,flag
import os,hashlib,random
assert (flag[:5] == 'CISCN')
assert (flag[6:-1]==hashlib.md5(key).hexdigest())
plain = os.urandom(16)
print (AES(key).encrypt_block(plain))
cipher,k = AES(key).encrypt_block_(plain,random.randint(0,255))
print (cipher)
piece1 = [k[0],k[1],k[4],k[7],k[10],k[11],k[13],k[14]]
print (hashlib.md5(bytes(piece1)).hexdigest())
piece2 = [k[2],k[3],k[5],k[6],k[8],k[9],k[12],k[15]]
print (hashlib.md5(bytes(piece2)).hexdigest())
1
2
3
4
5
6
#out

b'\xb7\x84\xd7\xdd\xa7hW\x1f8V,\xf1\x92\x18\xabu'
b'\xa3\xf7\xeae!\xa0\xc7\xa4\x80e39\xed\xc2a\xb4'
973f5ae78bc933a8fc7f7ab98d53d16f
628aab012199cdab83cc1aa72204ea98

参考文章:https://www.tr0jan.top/archives/107/

chall调用了aes,常规CBC加密模式,只有密文和进行了部分混淆的密文,给了key的序列验证md5值,aes是正常的AES,GitHub上工具爆破得到可能的key。

[GWCTF 2019]aes(未解决)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import hashlib
import random

string = "abcdefghijklmnopqrstuvwxyz1234567890QWERTYUIPOLKJMNHGBVFDCXSAZ"
for i in range(11111111111):
prefix = ''.join(random.sample(string, 5))
suffix = "0fae"
target = "69e37"

found = False
j = 0

while not found:
test_str = prefix + str(j)
hash_str = hashlib.md5((test_str + suffix).encode('utf-8')).hexdigest()
if hash_str[0:5] == target:
print("Found the string! It is:", test_str)
found = True
j += 1

[安洵杯 2019]This_is_not_a_standard_AES256(未解决)

nc 获取信息

1
2
3
4
5
6
7
8
9
10
This is you Sbox(tuple)
126, 143, 160, 15, 180, 170, 84, 152, 109, 20, 40, 136, 49, 179, 125, 165, 53, 251, 215, 87, 167, 41, 241, 178, 253, 239, 233, 193, 60, 110, 14, 246, 89, 228, 31, 42, 72, 213, 22, 102, 111, 52, 28, 254, 5, 219, 98, 94, 36, 161, 223, 191, 62, 57, 200, 133, 130, 149, 93, 248, 3, 13, 164, 76, 17, 101, 236, 51, 182, 81, 127, 185, 197, 100, 166, 183, 10, 18, 243, 196, 201, 119, 85, 131, 80, 46, 7, 224, 32, 158, 146, 71, 19, 25, 112, 249, 138, 139, 55, 175, 226, 1, 78, 106, 181, 211, 118, 218, 9, 64, 173, 207, 105, 63, 108, 235, 77, 134, 45, 44, 88, 171, 240, 65, 232, 33, 82, 227, 217, 129, 70, 8, 177, 128, 252, 135, 61, 190, 12, 150, 58, 216, 59, 121, 206, 188, 187, 104, 27, 242, 34, 212, 30, 176, 203, 244, 230, 141, 247, 114, 117, 0, 237, 39, 234, 21, 148, 2, 238, 250, 74, 4, 145, 29, 189, 192, 103, 229, 123, 163, 144, 153, 255, 154, 202, 86, 225, 195, 67, 37, 245, 43, 142, 221, 147, 209, 174, 97, 92, 231, 90, 107, 122, 56, 157, 35, 6, 156, 199, 120, 68, 116, 162, 79, 69, 198, 95, 38, 140, 169, 205, 204, 115, 210, 113, 23, 24, 132, 91, 73, 184, 172, 54, 159, 214, 47, 186, 208, 96, 66, 151, 83, 168, 99, 222, 26, 75, 124, 48, 137, 50, 155, 194, 16, 220, 11
----------------
This is you key(str)
letrvcgspwxjknzfylteavqbimszrwhk
----------------
This is you cipher(list)
93, 78, 25, 81, 18, 232, 250, 12, 94, 128, 81, 128, 88, 156, 180, 110, 151, 87, 197, 247, 8, 228, 51, 71, 102, 38, 146, 218, 40, 70, 6, 16
----------------
Enjoy it!

[EIS 2019]aes

1
b'(\xf1\xa9\x0f!\x9b\x98`\xc6-\x1b\xb6?C3M\xa8M-\x9b\xf9y<E\x93\x9a\x18\x86\xc4\x01\xf0p\xa4\xe7(:\x1aaP\xb9\xd3\xf1_b\x88'

qsnctf-crypto-aes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Cryptodome.Cipher import AES
import os
import gmpy2
from flag import FLAG
from Cryptodome.Util.number import *

def main():
key=os.urandom(2)*16
iv=os.urandom(16)
print(bytes_to_long(key)^bytes_to_long(iv))
aes=AES.new(key,AES.MODE_CBC,iv)
enc_flag = aes.encrypt(FLAG)
print(enc_flag)
if __name__=="__main__":
main()

bitsbytes都是计算机存储数据的单位,但是它们的表示方式和含义不同。

bit(比特)是二进制位的简称,表示信息的最小单位,通常用 01 来表示。byte(字节)是计算机中数据的基本单位,通常由8个二进制位(即8个bit)组成,可以表示 256 种不同的状态。

在Python中,bytes是一种不可变的序列类型,它表示二进制数据,由一个或多个byte组成。而bits则可以使用 intbool 类型来表示,通常使用位运算操作来处理二进制数据。需要注意的是,在Python中,一个字节可以用一个整数来表示,如 0x1A 表示一个字节,而一个整数默认使用4个字节(即32个bit)来表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#python3
from Crypto.Cipher import AES
import os
from gmpy2 import*
from Crypto.Util.number import*

xor = 91144196586662942563895769614300232343026691029427747065707381728622849079757
enc_flag = b'\x8c-\xcd\xde\xa7\xe9\x7f.b\x8aKs\xf1\xba\xc75\xc4d\x13\x07\xac\xa4&\xd6\x91\xfe\xf3\x14\x10|\xf8p'
out = long_to_bytes(xor)
key = out[:16]*2
# print(key)
iv = bytes_to_long(key[16:])^bytes_to_long(out[16:])
# print(iv)
iv = long_to_bytes(iv)
# print(iv)
aes = AES.new(key,AES.MODE_CBC,iv)
flag = aes.decrypt(enc_flag)
print(flag)