CBC模式:Cipher Block Chaining mode,密文分组链接模式
CBC模式是先将明文分成若干个组块,然后每个明文分组与前一密文分组进行异或 XORXOR 运算,然后再进行加密。因此,每个密文分组都依赖于它前面的所有明文分组。
由于密文分组像链条一样相互连接在一起,因此称为密文分组链接模式。
由于CBC模式分组块与块之间有相互连接关系,解密时前一块的密文会参与后一块密文的解密,所以我们更改前一块密文的值时,也会对后一块解密后的明文造成影响,而当我们知晓完整的密文和后一块密文对应的明文时,后一块密文解密出的明文就是可控制的,即我们可以通过控制A的值,进而控制C的值。这个攻击方式就是“CBC模式的反转字节攻击”,下面详细介绍原理。(至于为什么这么叫,我猜测是因为原理依赖异或运算吧)
在上图中,密文分组1为A,密文分组2进行解密运算后为B。明文分组2为C。
由CBC解密原理或图片信息可得:C=A^B。
由于异或两次同一个数等于啥也没干可由上式推出:B=A^C。
现在开始操作密文的值,使A'=A^C,用A'替换原来A的位置。
这个时候C=A'^B=(A^C)^(A^C)=0 C就被操作成了全0的值。
聪明的你肯定想到了,x^0=x。
所以我们再操作一次密文,A''=A^C^x,用A''替代上图中A的位置。
这个时候C=(A^C^x)^(A^C)=x
通过以上方法,我们就做到了在仅已知一对明文密文的情况下,通过修改密文得到一组成立的明密文对,且可以根据我们的喜好自定义明文的一部分。
博主博主,这种方式看起来很巧妙,但感觉没什么用呀,有什么具体的使用示例吗?
下面展示一个具体示例,假设我们通过抓包、监听等方式得到了一组明密文:- 待加密内容: abcdefghijklm123456789111-staff
- sm4_cbc加密后的结果: a44668576540e3bbf1b95115481e373c5af8c5fdaf5f775b65da549c7ecb8483
复制代码 你可以观察到这里明文结尾为用户名,所以我们可以通过刚刚学到的字节反转攻击方式构造一段明密文,修改最后的用户名,达到伪装为任何用户的目的。
Talk is cheap,上代码,这里通过修改密文,我们伪装了用户名为draina的一段明密文- # -*-coding: utf-8-*-
- from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
- import binascii
- import base64
- class SM4_cbc:
- def __init__(self):
- self.crypt_sm4 = CryptSM4()
- def str_to_strBin(self, hex_str):
- hex_data = hex_str.encode('utf-8')
- str_bin = binascii.unhexlify(hex_data)
- return str_bin.decode('utf-8')
- def encrypt_cbc(self, cbc_key, iv, value):
- crypt_cbc = self.crypt_sm4
- crypt_cbc.set_key(binascii.a2b_hex(cbc_key), SM4_ENCRYPT)
- Enc_value = crypt_cbc.crypt_cbc(binascii.a2b_hex(iv), value.encode())
- return binascii.b2a_hex(Enc_value)
- def decrypt_cbc(self, cbc_key, iv, value):
- crypt_cbc = self.crypt_sm4
- crypt_cbc.set_key(binascii.a2b_hex(cbc_key), SM4_DECRYPT)
- Dec_value = crypt_cbc.crypt_cbc(binascii.a2b_hex(iv), value)
- return Dec_value
- def hex_to_base64(payload_hex2):
- bytes_out = bytes.fromhex(payload_hex2)
- str_out = base64.b64encode(bytes_out)
- # print("hex_to_base64:", str_out)
- return str_out
- def pkcs7_pad(data: bytes, block_size: int = 16) -> bytes:
- """对数据进行 PKCS7 填充"""
- padding_length = block_size - (len(data) % block_size)
- padding = bytes([padding_length] * padding_length)
- return data + padding
- def utf8_to_hex_with_padding(plain_text: str) -> str:
- """将 UTF-8 明文转换为十六进制字符串,并进行 PKCS7 填充"""
- utf8_bytes = plain_text.encode('utf-8')
- padded_bytes = pkcs7_pad(utf8_bytes)
- hex_string = padded_bytes.hex()
- return hex_string
- def xor_hex_strings(hex1, hex2):
- # 将16进制字符串转换为整数
- int1 = int(hex1, 16)
- int2 = int(hex2, 16)
- # 进行按位异或操作
- xor_result = int1 ^ int2
- # 将结果转换回16进制字符串,并去掉前缀 '0x'
- return hex(xor_result)[2:]
- if __name__ == '__main__':
- key = "b01b1e51ba8b9bfcd584ab5b73ab7670"
- str_data = "abcdefghijklm123456789111-staff"
- iv_str = "e5709dcac5e3016de93aaf7b364693c3"
- SM4 = SM4_cbc()
- print("待加密内容:", str_data)
- print("p=",utf8_to_hex_with_padding(str_data))
- cipher = SM4.encrypt_cbc(key, iv_str, str_data)
- cipher_hex = cipher.decode()
- print("sm4_cbc加密后的结果:", cipher_hex, type(cipher_hex))
- val = hex_to_base64(cipher_hex)
- decode_cbc = SM4.decrypt_cbc(key, iv_str, base64.b64decode(val))
- print("sm4_cbc解密结果是:", decode_cbc.decode(), "\n")
- print("下面是字节反转攻击,将staff改为draina")
- p22=utf8_to_hex_with_padding('abcdefghijklm12888888888-draina')[-32:]
- #新明文第二分组,将p2的尾部改为draina后转为HEX并填充
- temp=xor_hex_strings(cipher_hex[:32],utf8_to_hex_with_padding(str_data)[-32:])
- cipherx1= xor_hex_strings(p22,temp)
- #计算新密文的第一个分组,即原明文的第二个分组异或原密文的第一个分组 再异或需要得到的新明文的第二个分组
- cipherx12=cipherx1+cipher_hex[-32:]
- print('新的密文为',cipherx12)
- val2 = hex_to_base64(cipherx12)
- decode_cbc2 = SM4.decrypt_cbc(key, iv_str, base64.b64decode(val2))
- print("sm4_cbc解密结果是:",decode_cbc2, "\n")
复制代码
-END-
参考链接
http://f0und.icu/article/28.html
https://blog.csdn.net/weixin_42437696/article/details/133786366
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |