文章总结: 本文分析Truebit协议因Solidity0.6.10整数溢出漏洞导致2600万美元损失的事件。攻击者利用定价计算溢出缺陷零成本购入TRU并高价赎回,耗尽储备。根源在于未防护溢出的旧版本合约及不对称定价机制。建议升级至Solidity0.8+或引入SafeMath库以防范此类算术漏洞。 综合评分: 100 文章分类: 漏洞分析,安全大事件,区块链安全,代码审计,应急响应
Truebit安全事件深度分析
原创
BlockSec BlockSec
BlockSec
2026年1月15日 18:01 浙江
2026 年 1 月 8 日,Truebit Protocol 在以太坊上的合约遭到攻击,造成约 2600 万美元的资产损失 [1]。本次事件的根本原因是该协议的 TRU 代币购买定价逻辑中存在整数溢出漏洞。由于合约使用Solidity v0.6.10编译(该版本默认不启用溢出检查),在购买成本的价格计算过程中,一个中间计算值发生溢出并回绕为一个极小的数值。攻击者可以用极低甚至 0 ETH 的成本购买大量 TRU,并立即将这些 TRU 以远高于成本的价格卖回合约换取 ETH,从而逐步耗尽协议储备金。
0x0 背景
Truebit 在以太坊上通过链下计算(off-chain computation)与交互式验证(interactive verification)提供计算服务 [2]。在该协议中,TRU 代币作为核心的经济工具,用于协调系统内的激励机制,包括质押以及与任务相关的支付。
协议对外暴露了两个用于 TRU 买入与赎回的公共函数:
- buyTRU() :执行 TRU 的购买操作。所需支付的 ETH 数量由一个私有的定价函数计算,该定价函数同样被 getPurchasePrice() 调用,因此 getPurchasePrice() 所返回的结果反映了 buyTRU() 在实际执行购买时所采用的链上定价逻辑。
- sellTRU() :执行 TRU 的出售(赎回)操作。用户可通过 getRetirePrice() 查询赎回 TRU 时可获得的 ETH 数量。
该协议在定价机制上的一个关键设计特点是价格非对称性:
- 买入侧采用凸型联合曲线(convex bonding curve),即随着代币供应量的增加,边际价格不断上升。
- 卖出侧采用线性赎回规则(linear redemption rule),即赎回价格与合约储备金成比例。
由于实现合约的源代码未公开,以下分析基于对合约字节码的反编译结果。
TRU购买逻辑
buyTRU()(以及 getPurchasePrice())会将定价计算委托给内部函数 _getPurchasePrice(),用于计算购买指定数量 TRU 所需的 ETH。
function buyTRU(uint256 amount)public payable { require(msg.data.length - 4 >= 32); v0 = _getPurchasePrice(amount); // get the purchase price require(msg.value == v0, Error('ETH payment does not match TRU order')); v1 = 0x18ef(100 - _setParameters, msg.value); v2 = _SafeDiv(100, v1); v3 = _SafeAdd(v2, _reserve); _reserve = v3; require(bool(stor_97_0_19.code.size)); v4 = stor_97_0_19.mint(msg.sender, amount).gas(msg.gas); require(bool(v4), 0, RETURNDATASIZE()); // checks call status, propagates error data on error return msg.value;}
function getPurchasePrice(uint256 amount)public nonPayable { require(msg.data.length - 4 >= 32); v0 = _getPurchasePrice(amount); // get the purchase price return v0;}
function _getPurchasePrice(uint256 amount) private { require(bool(stor_97_0_19.code.size)); v0, /* uint256 */ v1 = stor_97_0_19.totalSupply().gas(msg.gas); require(bool(v0), 0, RETURNDATASIZE()); // checks call status, propagates error data on error require(RETURNDATASIZE() >= 32); v2 = 0x18ef(v1, v1) v3 = 0x18ef(_setParameters, v2); v4 = 0x18ef(v1, v1); v5 = 0x18ef(100, v4); v6 = _SafeSub(v3, v5);// denominator = 100 * totalSupply**2 - _setParameters * totalSupply**2 v7 = 0x18ef(amount, _reserve); v8 = 0x18ef(v1, v7); v9 = 0x18ef(200, v8);// numerator_2 = 200 * totalSupply * amount * _reserve v10 = 0x18ef(amount, _reserve); v11 = 0x18ef(amount, v10); v12 = 0x18ef(100, v11);// numerator_1 = 100 * amount**2 * _reserve v13 = _SafeDiv(v6, v12 + v9); // purchasePrice = (numerator_1 + numerator_2) / denominator return v13;}
从反编译逻辑可推导出,购买价格可表示为如下联合曲线公式:
其中:
- amount:待购买的TRU数量
- reserve (_reserve):合约的以太坊储备金
- totalSupply:TRU的总供应量
- \theta (_setParameters):系数,固定为75
此曲线旨在使大额购买越来越昂贵(成本呈凸性增长),从而抑制投机和大额购买操纵价格的行为。
TRU出售(赎回)逻辑
sellTRU()函数(以及getRetirePrice()函数)利用内部函数_getRetirePrice()来计算赎回TRU时支付的ETH。
function sellTRU(uint256 amount)public nonPayable { require(msg.data.length - 4 >= 32); require(bool(stor_97_0_19.code.size)); v0, /* uint256 */ v1 = stor_97_0_19.allowance(msg.sender, address(this)).gas(msg.gas); require(bool(v0), 0, RETURNDATASIZE()); // checks call status, propagates error data on error require(RETURNDATASIZE() >= 32); require(v1 >= amount, Error('Insufficient TRU allowance')); v2 = _getRetirePrice(amount); // get the retire price v3 = _SafeSub(v2, _reserve); _reserve = v3; require(bool(stor_97_0_19.code.size)); v4, /* uint256 */ v5 = stor_97_0_19.transferFrom(msg.sender, address(this), amount).gas(msg.gas); require(bool(v4), 0, RETURNDATASIZE()); // checks call status, propagates error data on error require(RETURNDATASIZE() >= 32); require(bool(stor_97_0_19.code.size)); v6 = stor_97_0_19.burn(amount).gas(msg.gas); require(bool(v6), 0, RETURNDATASIZE()); // checks call status, propagates error data on error v7 = msg.sender.call().value(v2).gas(!v2 * 2300); require(bool(v7), 0, RETURNDATASIZE()); // checks call status, propagates error data on error return v2;}
function getRetirePrice(uint256 amount)public nonPayable { require(msg.data.length - 4 >= 32); v0 = _getRetirePrice(amount); // get the retire price return v0;}
function _getRetirePrice(uint256 amount) private { require(bool(stor_97_0_19.code.size)); v0, /* uint256 */ v1 = stor_97_0_19.totalSupply().gas(msg.gas); require(bool(v0), 0, RETURNDATASIZE()); // checks call status, propagates error data on error require(RETURNDATASIZE() >= 32); v1 = v2.length; v3 = v2.data; v4 = 0x18ef(_reserve, amount);// numerator = _reserve * amount if (v1 > 0) { assert(v1); return v4 / v1;// retirePrice = numerator / totalSupply } else { // ...}
赎回规则是线性的:
赎回价格与赎回数量占总供应量的比例(即 amount / totalSupply)成正比。
这种刻意的不对称性造成了巨大的价差:购买是凸性的(大额买入的成本昂贵),而出售是线性的(仅按比例赎回储备金)。正常情况下,这种价差使得买入后立即卖出的行为无利可图。
0x1 漏洞分析
尽管协议的设计意图是通过高成本抑制大额买入,但_getPurchasePrice()函数在算术运算中存在整数溢出漏洞。由于合约使用Solidity 0.6.10编译,uint256运算在没有明确保护(例如通过SafeMath)的情况下会静默溢出并发生回绕,其行为等价于在2^{256}下取模(mod 2^{256})。
function _getPurchasePrice(uint256 amount) private { require(bool(stor_97_0_19.code.size)); v0, /* uint256 */ v1 = stor_97_0_19.totalSupply().gas(msg.gas); require(bool(v0), 0, RETURNDATASIZE()); // checks call status, propagates error data on error require(RETURNDATASIZE() >= 32); v2 = 0x18ef(v1, v1) v3 = 0x18ef(_setParameters, v2); v4 = 0x18ef(v1, v1); v5 = 0x18ef(100, v4); v6 = _SafeSub(v3, v5);// denominator = 100 * totalSupply**2 - _setParameters * totalSupply**2 v7 = 0x18ef(amount, _reserve); v8 = 0x18ef(v1, v7); v9 = 0x18ef(200, v8);// numerator_2 = 200 * totalSupply * amount * _reserve v10 = 0x18ef(amount, _reserve); v11 = 0x18ef(amount, v10); v12 = 0x18ef(100, v11);// numerator_1 = 100 * amount**2 * _reserve v13 = _SafeDiv(v6, v12 + v9); // purchasePrice = (numerator_1 + numerator_2) / denominator return v13;}
在_getPurchasePrice()中,足够大的amount会在两个大分子项相加时(v12 + v9,反编译代码片段第17行)触发溢出。发生溢出时,分子回绕为一个很小的值,导致最终除法返回人为压低的购买价格,甚至可能为零。
关键点在于,溢出仅影响买入定价。 卖方函数保持线性并按预期运行,因此攻击者可以:
- 以低价(或零成本)购买大量TRU;
- 然后通过sellTRU()以高得多的有效汇率赎回为ETH。
0x2 攻击分析
攻击者在单笔交易中循环执行下列操作,完成多轮套利:
getPurchasePrice() -> buyTRU() -> sellTRU()
- 第一轮:零成本购买,然后卖出获利
攻击者通过精心构造的购买数量(240,442,509.453,545,333,947,284,131),成功触发_getPurchasePrice()中的溢出,使得计算出的购买价格降至0 ETH,从而零成本获得约2.4亿TRU。
以下我们用一段简单的 Python 代码说明:当除法运算中的分子超过 2^{256} 时,数值会发生回绕,导致计算得到的购买价格变成一个极小的分数;在将该结果转换为整数时会被截断为 0:
>>> _reserve = 0x1ceec1aef842e54d9ee>>> totalSupply = 161753242367424992669183203>>> amount = 240442509453545333947284131>>> numerator = int(100 * amount * _reserve * (amount + 2 * totalSupply))>>> numerator > 2**256True>>> denominator = (100 - 75) * totalSupply**2>>> purchasePrice = (numerator - 2**256) / denominator>>> purchasePrice0.00025775798757211426>>> int(purchasePrice)0
随后攻击者立即调用sellTRU(),从协议储备金中赎回TRU获得5,105 ETH。
- 后续轮次:低成本购买,然后卖出获利
攻击者多次重复此循环。后续的购买并非总是严格零成本,但溢出继续使购买价格远低于相应的卖出回报。
在整个过程中,攻击者提取了大量ETH。我们的调查表明,在第一轮之后可能仍存在零成本购买的机会,但攻击者仍选择进行一些非零成本轮次,原因尚不明确。 总体而言,攻击者从Truebit的储备金中提取了8,535 ETH。
0x3 总结
本次事件的根本原因是 Truebit 买入侧定价逻辑中未受保护的整数溢出漏洞。尽管协议采用了非对称的买卖定价模型来抵御投机行为,但 使用 Solidity 0.8 之前的编译器版本且未进行系统性的溢出防护,最终使这一设计失效,并导致储备金被完全抽干。
安全建议
对于仍在生产环境中使用 Solidity < 0.8 的合约,开发者应:
- 对所有相关算术操作使用 SafeMath 或等效的溢出检查
- 或 优先迁移至 Solidity 0.8+,以利用其默认启用的溢出保护机制
参考资料
[1]https://x.com/Truebitprotocol/status/2009328032813850839
[2]https://docs.truebit.io/v1docs
[3]https://app.blocksec.com/explorer/tx/eth/0xcd4755645595094a8ab984d0db7e3b4aabde72a5c87c4f176a030629c47fb014
加密支付合规培训研修计划
在 BlockSec 加密支付合规培训研修计划 · 第一期 圆满结束后,我们开启了第二期培训的报名:BlockSec 加密支付合规培训研修计划 · 第二期正式开启报名!
面向支付机构、交易平台与合规负责人,提供系统的合规与反洗钱培训。课程由周亚金教授与多位合规专家联合授课,内容涵盖全球监管体系、链上风险识别、合规体系搭建与实战演练。
📅 时间:2026 年 3 月 14–15 日 📍 地点:杭州 🎯 名额:限量30席
扫描下方二维码了解详情并报名。
关于BlockSec
BlockSec 是全球领先的区块链安全和合规公司,于 2021 年由多位业内知名专家联合创立。BlockSec 致力于提升 Web3 世界的安全性和易用性,提供一站式安全服务,包括智能合约/链/钱包安全审计服务、协议安全和数字货币合规(AML/CFT)平台 Phalcon Security / Phalcon Compliance / Phalcon Network、资金追踪调查平台 MetaSleuth 和区块链交易分析工具 Phalcon Explorer 等。
目前,BlockSec 已服务全球逾 500 家客户,既涵盖 Web3 知名公司 Coinbase、Cobo、Uniswap、Compound、MetaMask、Bybit、Mantle、Puffer、FBTC、Manta、Merlin、PancakeSwap 等,也包括了权威监管机构及咨询机构,如联合国、SFC、PwC、FTI Consulting 等。
官网:https://blocksec.com/
Twitter:https://twitter.com/BlockSecTeam
推荐阅读👇
BlockSec
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:BlockSec BlockSec BlockSec《Truebit安全事件深度分析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论