`[H-01] 跨链凭证重放可耗尽 Tempo 网络间的通道`

  • openai
  • 发布于 2026-02-19 23:41
  • 阅读 12

本文详细描述了Machine Payments Protocol (MPP) 的TempoStreamChannel智能合约中存在的跨链凭证重放攻击漏洞。由于EIP-712凭证验证时未将chainId包含在哈希计算中,攻击者可以在一个Tempo网络上获取有效凭证后,重放到其他Tempo网络上,从而耗尽多个网络上的通道资金。

[H-01] 跨链凭证重放允许耗尽 Tempo 网络中的通道

由安全研究员提交

总结

TempoStreamChannel.solMachine Payments Protocol (MPP) 流式支付通道的实现容易受到跨链重放攻击。为某个 Tempo 网络(例如 mainnet chainId)签名的支付凭证可以在另一个网络(例如 Moderato testnet chainId 42431)上重放,允许攻击者使用单个凭证耗尽多个网络上的通道。

漏洞详情

settle() 函数中,EIP-712 凭证验证未在 struct hash 计算中包含 chainIdVOUCHER_TYPEHASH 的定义如下:

bytes32 public constant VOUCHER_TYPEHASH = keccak256(
    "Voucher(bytes32 channelId,uint256 cumulativeAmount,uint256 nonce,uint256 expiry)"
);

struct hash 的计算没有包含链特定的数据:

bytes32 structHash = keccak256(abi.encode(
    VOUCHER_TYPEHASH,
    voucher.channelId,
    voucher.cumulativeAmount,
    voucher.nonce,
    voucher.expiry
));

这意味着,如果两个网络上存在相同的 channel ID,由授权签名者为 Tempo mainnet 上的通道签名的凭证,可以在 Tempo Moderato testnet (chainId 42431) 上重放。

影响

对 MPP 流式支付的严重影响:

  1. 跨网络双重支付:攻击者从某个 Tempo 网络上的 HTTP 402 Payment Required 工作流中获取到有效凭证后,可以在付款人拥有通道的另一个网络上重放该凭证。

  2. LLM 推理计费漏洞利用:在基于凭证的 LLM 代币计费微支付场景中,服务提供商可以:

    • Tempo mainnet 上接受支付凭证
    • Moderato testnet 上重放相同的凭证以耗尽付款人的测试资金
    • 反之亦然,具体取决于付款人在哪里维护余额
  3. AI 代理支付安全:使用 TIP-20 代币进行计量推理服务的 AI 代理 可能在多个 Tempo 网络上拥有用于测试和生产的通道。所有通道都将变得脆弱。

概念证明

// 攻击者从 Tempo 主网获取一个有效的凭证
Voucher memory voucher = Voucher({
    channelId: channelId,
    cumulativeAmount: 1000e6,  // 1000 TIP-20 代币
    nonce: 1,
    expiry: block.timestamp + 1 hours
});

// 凭证由授权签名者为主网签名
bytes memory mainnetSignature = signVoucher(voucher, signerPrivateKey);

// 攻击者在 Moderato 测试网(chainId 42431)上重放
// 由于 channelId 是确定性的,并且签名不包含 chainId,
// 相同的签名在测试网上也是有效的
channel.settle(voucher, mainnetSignature);  // 在测试网上成功!

代码位置

建议

在凭证 struct 中包含 chainId 并对照 block.chainid 进行验证:

bytes32 public constant VOUCHER_TYPEHASH = keccak256(
    "Voucher(bytes32 channelId,uint256 cumulativeAmount,uint256 nonce,uint256 expiry,uint256 chainId)"
);

function settle(Voucher calldata voucher, bytes calldata signature) external {
    // 验证 chainId 是否与当前链匹配
    if (voucher.chainId != block.chainid) revert InvalidChainId();

    bytes32 structHash = keccak256(abi.encode(
        VOUCHER_TYPEHASH,
        voucher.channelId,
        voucher.cumulativeAmount,
        voucher.nonce,
        voucher.expiry,
        voucher.chainId  // 在哈希中包含 chainId
    ));
    // ... 其他验证
}

参考

  • 原文链接: github.com/openai/fronti...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
openai
openai
江湖只有他的大名,没有他的介绍。