You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python3中替换SHA256初始值实现HMAC*长度扩展攻击

嘿,这个问题我刚好做过类似的大学实验!完全不用重写SHA256,Python里有两种靠谱的方法,我给你一步步讲清楚:

核心思路先理清楚

你的场景是典型的长度扩展攻击:已知h = SHA256(k || m),要构造h' = SHA256(k || m || pad(k||m) || a),其中pad是SHA256的标准填充规则。关键是让SHA256算法跳过前面k||m||pad(k||m)的计算,直接从已知的哈希值h作为初始状态,继续哈希后续的a。

方法一:利用CPython hashlib的内部状态(简单直接)

CPython的hashlib模块里的SHA256对象有几个私有属性可以直接修改,用来替换初始哈希状态和已处理的字节数,完全不用重写算法。

实现代码

import hashlib
import struct

def sha256_continue(prev_hash: bytes, data: bytes, total_processed_bytes: int) -> bytes:
    """
    从已知的SHA256哈希结果继续哈希后续数据
    :param prev_hash: 之前的SHA256哈希值(32字节bytes)
    :param data: 要追加的新数据
    :param total_processed_bytes: 之前已经处理的总字节数(即k||m的长度 + pad(k||m)的长度,必须是64的整数倍)
    :return: 新的SHA256哈希值
    """
    # 初始化一个SHA256对象
    sha = hashlib.sha256()
    
    # 将已知哈希值拆分为SHA256的8个32位大端初始状态值
    sha._h = struct.unpack('>8I', prev_hash)
    
    # 设置已处理的块数和剩余字节数(因为total_processed_bytes是64的整数倍,剩余字节数为0)
    block_size = 64
    sha._count = (total_processed_bytes // block_size, total_processed_bytes % block_size)
    
    # 追加新数据并计算哈希
    sha.update(data)
    return sha.digest()

为什么能行?

SHA256的计算是分块进行的,每个块处理后会更新内部的8个32位状态值。我们直接把这些状态值替换成已知的h,再告诉算法已经处理了多少字节,它就会自动从正确的位置继续计算后续数据的哈希。

方法二:用cryptography的底层接口(更规范)

如果不想依赖CPython的私有属性(毕竟私有属性理论上可能变动),可以用cryptography库的hazmat模块,它提供了更底层的哈希操作接口。

实现代码

首先需要安装库:pip install cryptography

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
import struct

def sha256_continue_crypto(prev_hash: bytes, data: bytes, total_processed_bytes: int) -> bytes:
    """
    用cryptography库实现从已知哈希继续计算
    """
    backend = default_backend()
    # 创建SHA256哈希上下文
    ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    
    # 复制并修改内部状态
    state = ctx._ctx.copy()
    # 替换初始状态为已知哈希值
    state.h = struct.unpack('>8I', prev_hash)
    # 设置已处理的块数
    state.nblocks = total_processed_bytes // 64
    
    # 应用修改后的状态并更新数据
    ctx._ctx = state
    ctx.update(data)
    return ctx.finalize()

完整攻击步骤示例

光有上面的函数还不够,你还需要生成正确的填充pad(k||m),并计算总处理字节数:

1. 实现SHA256填充函数

def sha256_pad(message_byte_length: int) -> bytes:
    """
    生成SHA256标准填充
    :param message_byte_length: 原始消息(k||m)的字节长度
    :return: 填充后的字节数据
    """
    pad = b'\x80'  # 先添加一个0x80字节
    # 补0直到总长度模64等于56(留出8字节存长度)
    pad += b'\x00' * ((56 - (message_byte_length + 1) % 64) % 64)
    # 添加原始消息的总比特数(大端64位整数)
    pad += struct.pack('>Q', message_byte_length * 8)
    return pad

2. 执行攻击

假设你已知h(32字节)、m(原始消息),要追加的消息是a:

# 已知参数
h = b'your_known_hmac_star_value_here'  # 32字节
m = b'original_message_m_here'
a = b'your_arbitrary_message_a_here'

# 枚举密钥k的可能长度(因为k未知,常见长度比如16、32字节,或者0到64字节)
for k_length in range(0, 65):
    # 计算k||m的总长度
    km_length = k_length + len(m)
    # 生成填充
    pad = sha256_pad(km_length)
    # 计算已经处理的总字节数(k||m + pad),这个数一定是64的整数倍
    total_processed = km_length + len(pad)
    # 计算新的哈希h'
    h_prime = sha256_continue(h, a, total_processed)
    # 构造新消息m' = m + pad + a
    m_prime = m + pad + a
    print(f"猜测k长度为{k_length}时:")
    print(f"新消息m' = {m_prime}")
    print(f"新HMAC* h' = {h_prime.hex()}")

这样就能得到对应每个猜测k长度的有效h'和m'了,实际测试时只要找到符合验证的结果即可。

内容的提问来源于stack exchange,提问作者Atilla1899

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:05:43