骆熙华 发表于 2025-6-16 09:21:15

SM4-CBC反转字节攻击(附代码)


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)
    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)

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


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: SM4-CBC反转字节攻击(附代码)