0%

CTF密码学之AES加密

前言

AES加密算法是密码学中的高级加密标准,属于对称、分组加密算法,在CTF现代密码学中作为一个比较常考的题目,本篇会对AES加密算法做较为详细的讲解,并对一些AES题型进行分析总结。

AES基础

AES算法,是DES算法的替代者,属于当今最为流行的对称加密算法之一。 要想具体了解学习AES算法,首先要清楚三个基本概念:密钥、填充、模式

密钥

密钥是AES算法实现加密和解密的根本。AES支持3种长度的密码 128位、192位、256位。

从安全性来看,AES256安全性最高。从性能来看,AES128性能最高。其本质原因是他们的加密处理轮数不同。

AES的加密标准中,每个分组的 长度固定为128bits(16Bytes),密钥长度为128bits,192bits,256bits,不同的密钥长度有不同的加密轮数,根据长度不同可以分为AES-128,AES-192,AES-256。

AES加密 密钥长度 分组长度 加密轮数
AES-128 128bits 128bits 10
AES-192 192bits 128bits 12
AES-256 256bits 128bits 14

填充

在AES加密中 有六种填充模式,分别为 NoPadding, PKCS#5, PKCS#7, ISO 10126, ANSI X9.23和ZerosPadding

  • Nopadding :不进行填充
  • ZerosPadding:缺少的字节全部使用 0x00填充
  • PKCS#5 & PKCS#7:缺少几个字节就填充几个字节,值为填充的字符数

例如,AAAAAAAA BBBBBBBB CCCCCCCC 刚好分成3个分组

  AAAAAAAA  BBBBBBBB CCCCC\____    缺少3个字节 所以使用 0x03进行填充      

(这里例子的A B C 均视为一个字节 以pkcs5为例 8个字节为一组)

如果明文块小于16个字节(128bit) 那么在明文块末尾补足响应数量的字符,且每个字符的值为填充字符数(缺少的字符数)

比如明文按照16个字节为一组的话:{1,2,3,4,5,a,b,c,d,e},缺少6个字节【128/8-10=6】,则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}

【不同在于 PKCS5 限定了块大小为 8bytes,而PKCS7没有限定,在AES加密当中严格来说是不能使用pkcs5的,因为AES的块大小是 16bytes,而 pkcs5 只能用于 8bytes,通常我们在AES加密中所说的 pkcs5 指的就是 pkcs7】

  • ISO 10126:最后一个字节是填充的字节数(包括最后一字节),其他全部填随机数

... | DD DD DD DD DD DD DD DD | DD DD DD DD 81 A6 23 04 | ...

  • ANSI X9.23:和ISO 10126相似,最后一个字节是填充的字节数,区别在 这种方法填充的其他字节都是0,而不是随机数

... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04 | ...

模式

AES分组加密的加密模式共五种可选加密模式。(如果在加密时使用了某种工作模式,则解密时也必须使用同种工作模式)

ECB ( Electronic Codebook Book , 电话本模式 ,默认) CBC ( Cipher Block Chaining , 密码分组链接模式 ) CTR ( Counter , 计算器模式 ) CFB ( Cipher FeedBack , 密码反馈模式 ) OFB ( Output FeedBack , 输出反馈模式 )

在CTF中比较常见的为ECB模式CBC模式,都属于分组密码算法(分组密码算法有四种模式:CBC/ECB/CFB/OCB),主要介绍下ECB模式和CBC模式。

ECB模式

ECB模式(电话本),依次出来一组明文分块,每次使用相同的密钥加密。

其加密模式如下所示,如果加密的明文块内容相同,那么得到的密文块也就会相同,相对来说不够安全,只适用于明文较短时。

CBC模式

CBC模式(密文分组链接),一次处理一组明文分块,每次使用相同的密钥加密。CBC模式主要引用了一个初始化向量(IV)来加强密码的随机性,保证相同明文通过相同密钥的加密结果不同。

AES加密流程

简要步骤如下图所示:

1.将明文按照128bit进行分组,拆分次若干个明文块;

2.按照选择的填充模式对最后一块明文块进行填充;

3.每个明文块利用AES加密器和密钥进行加密,得到对应的密文块;

4.拼接所有的密文块得到最终加密的密文。

AES具体加密流程

AES算法主要有四个操作处理,分别为 轮密钥加(AddRoundKey)、字节替换(SubBytes)、行位移(ShiftRows)、列混淆(MixColumns)。

在进行加密前,明文和密钥都会分组为16字节(128bit)组成的块,按照自己的先后顺序,自上而下,从左往右依次排列。加密后的密文读取也是按照这个顺序。

(以下多处图片均引自https://bbs.kanxue.com/thread-253884.htm 感谢这位师傅的文章 讲解的很详细)

AES算法在处理轮数上也有点不同,在最后一轮中少了列混淆处理。

初始轮(1)

  • 轮密钥加(AddRoundKey)

普通轮(N-1)

  • 字节替换(SubBytes)
  • 行位移(ShiftRows)
  • 列混淆(MixColumns)
  • 轮密钥加(AddRoundKey)

最终轮(1)

  • 字节替换 (SubBytes)
  • 行位移 (ShiftRows)
  • 轮密钥加(AddRoundKey)

轮密钥加(AddRoundKey)

根据传入的轮数 将状态矩阵与对应的W[i]异或,该过程中有两个输入参数,分别为明文和子密钥k 都是128位(bit) [16字节]。

字节替换(SubBytes)

AES字节替换 简单说就是一个查表操作 。在AES中定义了一个S盒和 逆S盒

如上图 a[0,0]是 0x12 查s盒 [1,2]处 得到 0xC9;a[0,1]是 0xAB 查s盒 [A,B]处 也就是 [10,11] 得到 0x62。

如果是逆s盒 那方法也是相同,以 0xC9为例, 查逆s盒 [C,9] 即 [13,9],对应表中值为 0x12

行位移(ShiftRows)

行位移 将输入的数据作为一个4x4矩阵进行处理 可以理解为 左循环位移 操作

第一行保持不变,第二行左移1字节 ,第三行左移2字节,第四行左移3字节

逆向行位移

第一行保持不变,第二行右移1字节,第三行右移2字节,第四行右移3字节

列混淆(MixColumns)

列混合变换就是矩阵相乘,行位移后的状态矩阵和修补矩阵(fixed matrix) 做矩阵相乘,得出输出列。

逆向列混淆

在解密的逆向列混淆中 与正向列混淆的 左乘矩阵不同,互为逆矩阵。【即 数据矩阵同时乘这两个矩阵后不会发生变化】

AES-ECB模式

1.明文经过填充后,分为不同的组block,以组的方式对数据进行处理

2.密钥Key对明文块进行块加密,得到密文块

3.密文块拼接后得到加密后的密文

AES-CBC模式

加密过程

1.明文经过填充后,分为不同的组block,以组的方式对数据进行处理

2.初始化向量 IV 首先和第一组明文进行 XOR 得到中间值 Midvalue

3.采用密钥Key对中间值Midvalue进行块加密,删除第一组加密的密文

4.第一组加密的密文 作为第二组的初始化向量IV,参与第二组的明文异或操作

5.依次进行块加密,最终将得到的每一块密文拼接成密文

解密过程

  1. 将密文进行分组,第一组得到的是初始化向量,从第二组开始才是真正的密文
  2. 使用加密密钥Key对密文的第一组进行解密,得到中间值Midvalue
  3. 中间值Midvalue和IV进行XOR,得到该组的明文
  4. 前一块密文是最后一块密文的IV,通过异或中间值得到明文
  5. 块全部解密完成后拼接得到明文,密码算法校验明文填充格式是否正确
  6. 校验通过得到明文,校验失败则得到密文

CTF中的AES题目分析

1.BabyAES

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Cipher import AES
import os
iv = os.urandom(16)
key = os.urandom(16)
my_aes = AES.new(key, AES.MODE_CBC, iv)
flag = open('flag.txt', 'rb').read()
flag += (16 - len(flag) % 16) * b'\x00'
c = my_aes.encrypt(flag)
print(c)
print(iv)
print(key)

'''
b'C4:\x86Q$\xb0\xd1\x1b\xa9L\x00\xad\xa3\xff\x96 hJ\x1b~\x1c\xd1y\x87A\xfe0\xe2\xfb\xc7\xb7\x7f^\xc8\x9aP\xdaX\xc6\xdf\x17l=K\x95\xd07'
b'\xd1\xdf\x8f)\x08w\xde\xf9yX%\xca[\xcb\x18\x80'
b'\xa4\xa6M\xab{\xf6\x97\x94>hK\x9bBe]F'
'''

题目分析: 已知iv key 密文c 使用AES-CBC模式,全部已知可直接AES解密得到明文flag

1
2
3
4
5
6
7
8
9
from Crypto.Cipher import AES
c = b'C4:\x86Q$\xb0\xd1\x1b\xa9L\x00\xad\xa3\xff\x96 hJ\x1b~\x1c\xd1y\x87A\xfe0\xe2\xfb\xc7\xb7\x7f^\xc8\x9aP\xdaX\xc6\xdf\x17l=K\x95\xd07'
iv = b'\xd1\xdf\x8f)\x08w\xde\xf9yX%\xca[\xcb\x18\x80'
key = b'\xa4\xa6M\xab{\xf6\x97\x94>hK\x9bBe]F'

Cipher = AES.new(key,AES.MODE_CBC,iv)
decrypted = Cipher.decrypt(c)
print(decrypted)
# b'Dest0g3{d0e5fa76-e50f-76f6-9cf1-b6c2d576b6f4}\x00\x00\x00'

2.[2020年第三届安洵杯] easyaes

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
#!/usr/bin/python
from Crypto.Cipher import AES
import binascii
from Crypto.Util.number import bytes_to_long
from flag import flag
import random
import string
import os

def genkey(l):
return random.getrandbits(l)
# 求iv 去除掉收尾指定字符串
iv = flag.strip(b'flag{').strip(b'}')

key = ''.join([random.choice(string.ascii_letters+string.digits) for _ in xrange(16)])
LENGTH = len(key)
# 16字节 所以会使用16个字符或长度为16的字符串做密码
assert LENGTH == 16

# 32字节 且为4个字符(32bit)的重复 32bit*8
# 每个字节由 2个十六进制字符表示 所以有 32*2=64个十六机制字符
hint = os.urandom(4) * 8
print(bytes_to_long(hint)^bytes_to_long(key))

# 64 / 16 =4
msg = b'Welcome, ctfer. Dont try too hard, its no use. Have a good day!!'

def encrypto(message):
aes = AES.new(key,AES.MODE_CBC,iv)
return aes.encrypt(message)

# 对msg AES加密后 转十六进制后 输出后32位
print(binascii.hexlify(encrypto(msg))[-32:])

'''
99748265546679089946917295913637945222843938798184123305418691873367322323659
bc03f3ac4ff8064acbcfaf0b0bf2ba7b
'''

题目分析:

1.已知key长度为 16位,hint为32字节 且为4个字节的重复(32bit*8)

2.已知明文message 64位

AES-CBC解密流程: 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]

iv = msg[0] ^ decrypt(enc1,key) enc1 = msg[1] ^ decrypt(enc2,key) enc2 = msg[2] ^ decrypt(enc3,key) enc3 = msg[3] ^ decrypt(enc4,key)

其中 已知如下: 1.hint^key = tmp -> key = tmp^hint 2.enc4 3.msg[0] msg[1] msg[2] msg[3] 所以可以推出iv来 得到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
#!/usr/bin/python
from Crypto.Util.number import long_to_bytes
import binascii, sys
from Crypto.Util.strxor import strxor
from Crypto.Cipher import AES

# -----------get key---------

# tmp = hint ^ key -> key = tmp ^ hint
tmp = 99748265546679089946917295913637945222843938798184123305418691873367322323659
# hint为32字节 key为16字节 所以异或后 tmp高位就是 hint的高位
# [2:10] 前面有0x 所以从第二位开始取8位
# 32字节 64个十六进制字符
hint = int(str(hex(tmp))[2:10] * 8, 16)
key = long_to_bytes(tmp ^ hint)

# ----------get iv-----------
msg = b'Welcome, ctfer. Dont try too hard, its no use. Have a good day!!'
msgs = [msg[ii:(ii + 16)] for ii in range(0, len(msg), 16)]

print(msgs)
# [b'Welcome, ctfer. ', b'Dont try too har', b'd, its no use. H', b'ave a good day!!']

msgs.reverse()
# msg[3] msg[2] msg[1] msg[0]
# [b'ave a good day!!', b'd, its no use. H', b'Dont try too har', b'Welcome, ctfer. ']


IV = binascii.unhexlify('bc03f3ac4ff8064acbcfaf0b0bf2ba7b')


# iv = msg[0] ^ decrypt(enc1,key)
# enc1 = msg[1] ^ decrypt(enc2,key)
# enc2 = msg[2] ^ decrypt(enc3,key)
# enc3 = msg[3] ^ decrypt(enc4,key)

def decry(key, IV, ms):
aes = AES.new(key, AES.MODE_ECB)
return strxor(aes.decrypt(IV), ms)


for ms in msgs:
IV = decry(key, IV, ms)
print(b'flag{' + IV + b'}')

3.aes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Cipher import AES
import binascii
import hashlib
from secret import flag

assert flag[:5] == "flag{" and flag[-1:] == "}"

key = b"J1fx2g1jDak1c***"
l = len(key)

message = b"I have had my invitation to this world's festival, and thus my life has been blessed" + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]

iv = flag[5:-1]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = "utf-8")
aes = AES.new(key, AES.MODE_CBC, iv)
print(binascii.hexlify(aes.encrypt(message)))
#******************************************************************************************************************************************************6ece036e495d363b647d7f2749c4c2f3dd78f8637b

#题目及WP来源:https://blog.csdn.net/luochen2436/article/details/125884846
#记录下解题思路学习下

题目分析:

1.key后三位未知 可爆破

2.明文message 分3部分 第一部分明文已知,中间部分 10个十六进制字符 后部分为 2个0x02 共192个十六进制字符

message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = "utf-8")

即 bytes((16-94)%16 * chr((16-(94%16))))

= bytes(2 * chr (2) ) -> 0x02 0x02

3.iv = flag[5:-1] 即flag{iv} 求出iv即flag

4.已知明文后21字节 根据分组 每组16字节 所以已知最后一块明文 及 倒数第二块后5字节明文

在CBC中 与ECB相比 多的部分为IV 异或一步

密文 ^ key (AES_ECB) ^ IV → 明文

最后一块的明文已知,key 可通过爆破后三位

最后一块的密文为 b"ssed" + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10] + bytes((l - len(message) % l) * chr(l - len(message) % l) 如果key已知 则密文也就是已知的

我们还知道倒数第二块中的后5个字节 可以作为最后一次加密的IV 的一部分进行校验,如果结果中包含 已知的5个字节,则说明key正确。

IV = 明文 ^ ( 密文 ^ key )

求得key后 可进而推出 IV 即 flag

Plaintext[0] = Ciphertext[0] ^ key ^ IV

Plaintext[1] = Ciphertext[1] ^ key ^ Ciphertext[0]

Plaintext[2] = Ciphertext[2] ^ key ^ Ciphertext[1]

知道最后一个明文 密文 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
from Crypto.Cipher import AES
from tqdm import tqdm
import binascii
import hashlib


# 1. 生成key字典
import string

dic = string.printable[:62]
# print(dic)
# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
with open('key_table.txt','wb') as file:
for i in dic:
for j in dic:
for k in dic:
key = b"J1fx2g1jDak1c"+i.encode()+j.encode()+k.encode()
file.write(key+b'\n')

file.close()

# 2.爆破key
def xor(m: bytes, c: bytes):
return bytes([i ^ j for i, j in zip(m, c)])

enc = binascii.unhexlify('5d363b647d7f2749c4c2f3dd78f8637b')
five_part = binascii.unhexlify(b"6ece036e49")
f = open("key_table.txt","rb+")
pbar = tqdm(range(238328))
for i in f:
key = i[:16]
aes = AES.new(key, AES.MODE_ECB)
dec = aes.decrypt(enc)
# 最后一块 16个字节 4+10+2
m = b"ssed" +binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]+b'\x02\x02'
xor_result = xor(m,dec)
pbar.update(1)
if five_part in xor_result:
print("[+] key:",key)
break
f.close()

# 3.求IV
l = len(key)
# 192位
message = b"I have had my invitation to this world's festival, and thus my life has been blessed" + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding="utf-8")
for i in range(0,len(message),16):
aes_ecb = AES.new(key,AES.MODE_ECB)
dec_c = aes_ecb.decrypt(enc)
enc = xor(message[len(message)-i-16:len(message)-i],dec_c)
print(b'flag{'+enc+b'}')

4.moectf ez_cbc

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
from Crypto.Util.number import *
import random
from secret import flag

IV = bytes_to_long(b'cbc!')
K = random.randrange(1,1<<30)

assert flag[:7] == b'moectf{'
assert flag[-1:] == b'}'

block_length = 4
flag = flag + ((block_length - len(flag) % block_length) % block_length) * b'\x00'
plain_block = [flag[block_length * i: block_length * (i + 1)] for i in range(len(flag) // block_length)]

c = []
c0 = (IV ^ bytes_to_long(plain_block[0])) ^ K
c.append(c0)

for i in range(len(plain_block)-1):
c.append(c[i] ^ bytes_to_long(plain_block[i+1]) ^ K)

print(c)

'''
[748044282, 2053864743, 734492413, 675117672, 1691099828, 1729574447, 1691102180, 657669994, 1741780405, 842228028, 1909206003, 1797919307]
'''
# flag 为 moectf{flag}

题目分析:

  1. IV已知,flag部分已知 moectg{}
  2. block_length =4 且 已知flag前四位为 moec

所以可以通过此去恢复K

密文块 = 明文块 ^ iv ^ K

明文块 = 密文块 ^ iv ^ K

K = 密文块 ^ 明文块 ^ iv

K = IV ^ bytes_to_long(plain_block[0]) ^ c[0]

3.求得K后 进而求得m, m = K ^ c[i] ^ c[i+1]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Util.number import *

c = [748044282, 2053864743, 734492413, 675117672, 1691099828, 1729574447, 1691102180, 657669994, 1741780405, 842228028, 1909206003, 1797919307]
IV = bytes_to_long(b'cbc!')
block_length = 4

flag = b'moectf{'
plain_block = [flag[block_length * i: block_length * (i + 1)] for i in range(len(flag) // block_length)]

K = IV ^ bytes_to_long(plain_block[0]) ^ c[0]
print(K)

m = b''
for i in range(len(c)-1):
m += long_to_bytes(c[i]^c[i+1]^K)
print(m)

print(b'moec'+m)
# 580598200
# b'tf{es72b!a5-njad!@-#!@$sad-6bysgwy-1adsw8}\x00\x00'
# b'moectf{es72b!a5-njad!@-#!@$sad-6bysgwy-1adsw8}\x00\x00'

5.moectf ez_chain

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

with open("key.txt", "r") as fs:
key = int(fs.read().strip())
with open("flag.txt", "rb") as fs:
flag = fs.read().strip()
assert len(flag) == 72

m = bytes_to_long(flag)

base = bytes_to_long(b"koito")
iv = 3735927943

def blockize(long):
out = []
while long > 0:
out.append(long % base)
long //= base
return list(reversed(out))

blocks = blockize(m)

def encrypt_block_cbc(blocks, iv, key):
encrypted = [iv]
for i in range(len(blocks)):
encrypted.append(blocks[i] ^ encrypted[i] ^ key)
return encrypted[1:]

print(encrypt_block_cbc(blocks, iv, key))
# [8490961288, 122685644196, 349851982069, 319462619019, 74697733110, 43107579733, 465430019828, 178715374673, 425695308534, 164022852989, 435966065649, 222907886694, 420391941825, 173833246025, 329708930734]

题目分析:

1.flag 72位,m为flag 需求项

2.需要写2个解密函数 decrypt_block_cbc 和 deblockize

K = 密文块 ^ 明文块 ^ 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
from Crypto.Util.number import *

base = bytes_to_long(b"koito")
iv = 3735927943

blocks = [8490961288, 122685644196, 349851982069, 319462619019, 74697733110, 43107579733, 465430019828, 178715374673, 425695308534, 164022852989, 435966065649, 222907886694, 420391941825, 173833246025, 329708930734]

def blockize(long):
out = []
while long > 0:
# 取余数 存入 out
out.append(long % base)
long //= base
return list(reversed(out))

def deblockize(blocks):
out = 0
for block in blocks:
out = out * base + block
return out

def encrypt_block_cbc(blocks, iv, key):
encrypted = [iv]
for i in range(len(blocks)):
encrypted.append(blocks[i] ^ encrypted[i] ^ key)
return encrypted[1:]
# blocks[0] ^ iv ^ key = flag[0]

def decrypt_block_cbc(blocks, iv, key):
# iv 初始化向量
encrypted = [iv, *blocks]
decrypted = []
for i in range(len(blocks)):
decrypted.append(encrypted[i] ^ encrypted[i + 1] ^ key)
return decrypted

# flag = moectf{xxx}
# 72个字符 其中 moectf 6 {} 2 72-2-6=64个
# flag[0] = blocks[0] ^ key ^ iv
# key = iv ^ blocks[0] ^ flag[0]
print("flag:",blockize(bytes_to_long(b"moectf{" + b"0"*64 + b"}"))[0] )
# print(blockize(bytes_to_long(b"moectf{" + b"0"*64 + b"}")))
key = blockize(bytes_to_long(b"moectf{" + b"0"*64 + b"}"))[0] ^ iv ^ blocks[0]
m = long_to_bytes(deblockize(decrypt_block_cbc(blocks, iv, key)))

print("key:",key)
print(m)

6.Afctf MyOwnCBC

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
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

from Crypto.Cipher import AES
from Crypto.Random import random
from Crypto.Util.number import long_to_bytes

def MyOwnCBC(key, plain):
# key 32位 但key未知
if len(key)!=32:
return "error!"
cipher_txt = b""
cipher_arr = []
# 自定义加密 ECB
cipher = AES.new(key, AES.MODE_ECB, "")
# 等价于 cipher = AES.new(key, AES.MODE_ECB)
plain = [plain[i:i+32] for i in range(0, len(plain), 32)]
# 32位(16字节)为一组 然后进行ECB加密
print plain
# 加密后 存入密文数组
cipher_arr.append(cipher.encrypt(plain[0]))
cipher_txt += cipher_arr[0]

# cipher_txt = [cipher_arr[i]]
# 上一个密文 作为 下一个密文的密钥key进行加密
# key0 为第一组的密文 即 key0 = cipher[0:32]
# 然后以 前一个 cipher_arr[i-1]作为key 进行ecb加密
# cipher_arr[i-1] ^ cipher_arr[i]
for i in range(1, len(plain)):
cipher = AES.new(cipher_arr[i-1], AES.MODE_ECB, "")
cipher_arr.append(cipher.encrypt(plain[i]))
cipher_txt += cipher_arr[i]
return cipher_txt

# 生成256位的随机整数
key = random.getrandbits(256)
key = long_to_bytes(key)

s = ""
with open("flag.txt","r") as f:
s = f.read()
f.close()

# 密文写入到flag_cipher 是已知项
# 长度为 6A0h = 1696
# 1696 / 32 = 53组
# 由于上一个密文作为key 参与下一次密文加密 所以长度都相同
with open("flag_cipher","wb") as f:
f.write(MyOwnCBC(key, s))
f.close()

plain = [plain[i:i+32] for i in range(0, len(plain), 32)] # 32位一组

第一次密文 作为密钥key0参与ECB加密 key = cipher[: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
from Crypto.Cipher import AES

with open("flag_cipher", "rb") as fp:
# print(len(fp.read()))
cipher = fp.read()
fp.close()

key0 = cipher[:32]
print(key0)

def MyOwnCBC(key0,cipher):
cipher = [cipher[i:i + 32] for i in range(0, len(cipher), 32)]
m = b''
tmpkey = key0
# 上一次的密文作为key参与下一次ECB
for i in range(1,len(cipher)):
aes = AES.new(tmpkey,AES.MODE_ECB)
m += aes.decrypt(cipher[i])
tmpkey = cipher[i]
return m

print(MyOwnCBC(key0,cipher))
#b'\xe5\xdf\x94sJ\xc2\xcd\x04\xeb\xb7\xcf\x05(\xbe\x98\\\xe9\xc3^\x1f!\xfb\xea6\xdac\x1f\xfe\x901\xbb\x13'
#b"mode of operation is an algorithm that uses a block cipher to provide an information service such as confidentiality or authenticity. A block cipher by itself is only suitable for the secure cryptographic transformation (encryption or decryption) of one fixed-length group of bits called a block. A mode of operation describes how to repeatedly apply a cipher's single-block operation to securely transform amounts of data larger than a block.\n\nMost modes require a unique binary sequence, often called an initialization vector (IV), for each encryption operation. The IV has to be non-repeating and, for some modes, random as well. The initialization vector is used to ensure distinct ciphertexts are produced even when the same plaintext is encrypted multiple times independently with the same key. Block ciphers have one or more block size(s), but during transformation the block size is always fixed. Block cipher modes operate on whole blocks and require that the last part of the data be padded to a full block if it is smaller than the current block size. There are, however, modes that do not require padding because they effectively use a block cipher as a stream cipher.\n\nHistorically, encryption modes have been studied extensively in regard to their error propagation properties under various scenarios of data modification. Later development regarded integrity protection as an entirely separate cryptographic goal. Some modern modes of operation combine confidentiality and authenticity in an efficient way, and are known as authenticated encryption modes.\n\nAh you found it~ afctf{Don't_be_fooled_by_yourself}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"

References

https://bbs.kanxue.com/thread-253884.htm

https://www.cxyxiaowu.com/3239.html

欢迎关注我的其它发布渠道

------------- 💖 🌞 本 文 结 束 😚 感 谢 您 的 阅 读 🌞 💖 -------------