Back to Blog

~1026万美元:Term Finance 与 MAYAChain 攻击事件 | BlockSec

August 26, 2026
13 min read
Key Insights
  • 本周共收录 2 起重大安全事件,总损失约 1026 万美元。本周焦点事件 Term Finance(约 850 万美元)是发生在 Ethereum 上的一次治理权接管,而 MAYAChain(约 176 万美元)则是发生在 Cosmos-SDK 跨链网络上的一次会计与状态验证连环失败。

  • Term Finance 的每个金库治理机制依赖于相对支持率和参与率门槛,却没有设置绝对资本或参与广度的下限;由于几乎没有人将其金库份额包装成治理代币,参与度过低,导致任何反对票都无法击败提案。攻击者仅用约 0.5 ETH 就获得了某个金库的绝对多数投票权,而执行延迟机制只是推迟了结果的发生,且没有守护者可以介入干预。Term 的 6 个金库以这种方式被接管,总损失约 850 万美元。

  • 在 MAYAChain 上,一笔精心构造的存款破坏了链上内部会计系统,使得有效的提款被误判为失败,从而触发了一条恢复路径,导致一个低流动性资金池的记录原生代币余额被虚增,而没有真实资产支持;攻击者随后通过添加和撤出流动性,将这部分虚增的价值提取一空。

在过去一周(2026/08/17 - 2026/08/23)中,共有 2 起值得关注的重大安全事件,总损失约为 1026 万美元。

日期 事件 类型 估计损失
2026/08/18 MAYAChain 业务逻辑缺陷 ~176 万美元
2026/08/23 Term Finance 治理设计缺陷 ~850 万美元

选择理由

  • MAYAChain:入选原因是一次链式的账务和状态验证失败,使得单笔精心构造的存款破坏了出账对账流程,并使一个低流动性资金池的记录余额被虚增,而没有真实资产支撑,这展示了若干本身各自影响有限的低层级缺陷,是如何组合起来对一个跨链流动性网络造成资金流失的。
  • Term Finance:入选原因是几乎为零的治理参与率导致没有足够的投票群体来否决恶意提案,也没有守护者或撤销机制为执行延迟提供保障,使得只需极少资本的攻击者就能主导投票,通过恶意提案,并从六个协议金库中盗取约 850 万美元。

Best Security Auditor for Web3

Validate design, code, and business logic before launch

本周焦点:Term Finance

本周关注 Term Finance,是因为此次失败并非代码漏洞,而是一种治理设计缺陷——随着协议为每个金库都部署一个独立的链上 DAO,这类风险正在增加:当几乎没有人参与时,投票控制权就可以被低成本收购,金库自身的治理机制反而成为攻击面。

2026 年 8 月 23 日,以太坊上的固定利率借贷协议 Term Finance 在攻击者夺取其金库链上治理控制权后损失约 850 万美元。由于几乎没有存款人曾铸造过某个金库的治理代币,攻击者只需约 0.5 ETH 就能获得该金库绝对多数的投票权,通过协议的支持率阈值和最低参与率检查,并执行了一项恶意提案,撤销了该金库的策略并转移其资产;Term 的六个金库因此被以这种方式掏空 [1]

背景

Term Finance 是以太坊上的一个固定利率借贷协议。Term Vaults 是在其之上叠加的一个独立产品:每个金库都是基于 Yearn V3 代码构建的 ERC-4626 金库,其中一个元金库(meta vault)接受单一资产,并将其分配到一组策略金库中。Term 的框架会在每个金库部署时同时部署一个 Aragon OSx DAO 和一个 TokenVoting 合约,而该 DAO 对随其一起发布的金库拥有升级和角色权限。因此,一个金库从诞生之初就带有自己的治理面,与谁负责策展或是否有任何资金被分配进去无关。

治理决策由 TokenVoting 执行。每个金库都有自己的份额代币(share token)和相应的 Aragon GovernanceWrappedERC20 治理包装代币;在本次分析的 ETH Meta Vault 中,这两者分别是 tmvETHgtmvETH。任何份额代币持有者都可以调用 depositFor(),以 1:1 的比例获得治理代币,任何治理代币持有者都可以 delegate() 其所得投票权。当创建一项提案时,TokenVoting.createProposal() 会记录一个 snapshotBlock、一个 supportThreshold 以及基于该快照时代币供应量得出的 minVotingPower;随后,投票权会从该区块的包装代币中读取。

漏洞分析

本次事件的核心治理合约是 TokenVoting。提案执行仅由 _canExecute() 把关,对于普通(非提前)提案,该函数要求投票尚未被执行、提案已关闭,并且需要通过两项检查:isSupportThresholdReached()isMinParticipationReached()

这两项检查都是多数投票制的正确实现,但每一项衡量的都是相对比例,而不是绝对数量。isSupportThresholdReached() 仅要求赞成票数超过反对票数、且达到设定比例:

isMinParticipationReached() 要求投出的票数达到 minVotingPower,而该值本身是根据快照时的 minParticipation * totalSupply 计算得出的:

根本原因在于,这种治理设计没有为推动提案通过所需的资本量或参与广度设定任何绝对底线。这两项门槛检查都纯粹是相对于已投票数和代币供应量而言的,而由于几乎没有 tmvETH 持有者曾通过包装成 gtmvETH 的方式参与治理,该供应量以及随之而来的最低参与率门槛就几乎为零。因此,一个近乎空白的投票群体中的相对多数,就足以通过这两项检查,而由于投票者如此之少,也没有人能投出足以否决该提案的反对票。最短投票时长虽然延迟了执行,但由于没有守护者或撤销机制,这一延迟只是推迟了该提案的通过,而没有阻止它。

攻击分析

攻击者获取了某个金库治理代币的控制性份额,然后利用它通过并执行了一项将该金库资产掏空的提案;同样的手法被应用于 Term 的六个金库。以下分析基于交易 0xd354a1...d30141290x9f273f...44c2e8a0,追踪其中一个金库的情况。

  • 第一步:攻击者通过 Mayan Finance 转发器,将 0.5 ETH 兑换为 0.485 tmvETH,该转发器完成了兑换路由,并将金库份额转给了攻击者。

  • 第二步:攻击者将 0.485 tmvETH 以 1:1 比例包装为 0.485 gtmvETH,获得了后续步骤中所使用的投票权。

  • 第三步:攻击者调用 propose(),该函数读取了攻击者自身的投票权,并调用了 TokenVoting.createProposal()。合约记录了快照,读取到该区块的治理代币总供应量仅为 0.535 gtmvETH,因此攻击者的持仓约占整个投票群体的 90.66%。

  • 第四步:攻击者调用 vote(),将其全部投票权投给该提案,成为唯一的参与者。

  • 第五步:投票期结束后,攻击者调用了 executeProposal()。门槛检查 canExecute() 首先检查了 isSupportThresholdReached():由于攻击者是唯一的赞成票投票人,支持率远超 50% 的门槛。随后检查了 isMinParticipationReached():仅攻击者自身的投票权就已超过 minVotingPower。两项检查均通过。

  • 第六步:该恶意提案随后被执行,撤回了 tmvETH 金库分配给其策略的资金,将其重新转换为该金库持有的流动性 WETH,并让攻击者提取了这些资产。此攻击手法应用于 Term 的六个金库,共造成约 850 万美元的总损失。

结论

本次事件是由治理设计缺陷而非代码错误导致的:支持率和参与率检查都是纯相对性的,因此在一个几乎空白的投票群体中,低成本获得的多数票不会遇到任何反对票,而执行延迟背后也没有守护者或撤销机制的支撑。掌控资产托管权的治理机制应当强制设定绝对的法定人数或参与率下限,并为任何执行延迟配备守护者或撤销机制及被监控的提案信息流,因为时间锁除非有人能在此期间采取行动,否则只会推迟一个已经通过的提案,而无法阻止它。


本周其他事件

MAYAChain

2026 年 8 月 18 日,基于 Cosmos-SDK 的跨链流动性网络 MAYAChain 因单笔精心构造的存款破坏了链上内部账务而损失约 176 万美元。该存款通过使有效的提款看起来像是失败了,触发了一条恢复路径,从而虚增了一个低流动性资金池所记录的原生代币余额,而没有真实资产支撑;攻击者随后通过添加和撤出流动性来提取这部分虚增的价值 [2]。经确认的链上流出资金约为 136 万美元,主要为 20.83 BTC,加上攻击者残余的原生代币持仓,官方估计总损失约为 176 万美元。

背景

MAYAChain 是一个基于 Cosmos-SDK 的跨链流动性网络,其原生资产为 CACAO。外部链上的事件由验证者观测并作为已观测交易重放到 MAYAChain 上。当一笔入账操作需要向外发送资产时,节点会调度一个或多个 TxOutItem,随后将已观测的出账交易与这些调度记录进行对账。所有资金池资产统一由共享的 Asgard 金库托管;每个流动性池只是对这一共享托管资产的一种记账仓位,而非独立分离的余额,提款则根据某个资金池的记录余额从 Asgard 中支付。

交易账户(Trade accounts)是 MAYAChain 上针对交易资产(例如 ARB~ETHARB~LINK)的原生记账仓位。交易账户的提款是通过带有诸如 trade-:ARB~LINK 备注的原生 MsgDeposit 发起的。尽管用户只签署了单笔原生交易,但存款处理器会重建一个内部的已观测交易,并以该原生交易哈希作为键存储一个 ObservedTxVoter

漏洞分析

根本原因并非某个孤立的单一漏洞;而是一系列账务和状态验证缺陷组合叠加所致。存在缺陷的逻辑位于 MAYANode 的存款和出账处理器中。

首先,在 handler_deposit.go 中,一笔原生交易中的每条消息都会以该交易哈希为键构建一个新的 ObservedTxVoter,并通过 SetObservedTxInVoter() 保存:

txIn := ObservedTx{Tx: tx}
txInVoter := NewObservedTxVoter(txIn.Tx.ID, []ObservedTx{txIn})
txInVoter.Height = ctx.BlockHeight()
txInVoter.FinalisedHeight = ctx.BlockHeight()
txInVoter.Tx = txIn
h.mgr.Keeper().SetObservedTxInVoter(ctx, txInVoter)

由于一个批次中的每条消息共享相同的 tx.ID,后面的消息会覆盖前面消息写入的 voter 状态,从而丢弃它们的出账调度元数据。

其次,在 handler_common_outbound.go 中,出账对账从 voter.OutboundHeight(当该值为零时则使用 voter.FinalisedHeight)开始,按签名周期向前扫描:

outHeight := voter.OutboundHeight
if outHeight == 0 {
    outHeight = voter.FinalisedHeight
}
for height := outHeight; height <= ctx.BlockHeight(); height += signingTransPeriod {
    txOut, err = h.mgr.Keeper().GetTxOut(ctx, height)
    ...
}

当 voter 以这种方式被覆盖后,扫描会从最终确认的存款高度开始,永远不会检查到存有已调度出账的那个区块,因此处理器会将这些出账视为缺失,并触发惩罚性恢复路径。

第三,在 helpers.go 中,恢复路径会使用原始观测数量,将“缺失”资产按 CACAO 补贴进行估值,而没有针对该资金池实际资产深度设置任何上限:

f.stolenAsset = f.stolenAsset.Add(coin.Amount)
runeValue := pool.AssetValueInRune(coin.Amount)
f.subsidiseRune = f.subsidiseRune.Add(runeValue)

在一个仅持有约 0.11 LINK 资产侧深度的资金池中,这种无上限的转换可能将一个观测到的 LINK 数量映射成一个极其庞大的 CACAO 价值。同一处逻辑随后会在尝试转账之前,先将虚增的资金池余额提交至状态:

pool.BalanceCacao = pool.BalanceCacao.Add(f.subsidiseRune)
pool.BalanceAsset = common.SafeSub(pool.BalanceAsset, f.stolenAsset)
if err = mgr.Keeper().SetPool(ctx, pool); err != nil {
    ...
}

runeToAsgard := common.NewCoin(common.BaseAsset(), f.subsidiseRune)
if err = mgr.Keeper().SendFromModuleToModule(ctx, ReserveName, AsgardName, common.NewCoins(runeToAsgard)); err != nil {
    ctx.Logger().Error("fail to send subsidy from bond to asgard", "error", err)
    return err
}

由于该资金池的写入操作在转账之前就已提交,如果该转账无法完成资金支持,资金池的 BalanceCacao 也永远不会被回滚。最后,在 handler_observed_txout.go 中,调用方吞掉了返回的错误,将 voter 标记为已完成并继续执行,因此一种不一致的资金池状态便得以持续存在:

_, err = handler(ctx, m)
if err != nil {
    ctx.Logger().Error("handler failed:", "error", err)
    slashObservedOutbound("failed_outbound")
    voter.SetDone()
    h.mgr.Keeper().SetObservedTxOutVoter(ctx, voter)
    continue
}

攻击分析

攻击者将这些缺陷组合在单笔原生交易中,随后从虚增的资金池中提取价值。以下分析基于 MAYAChain 交易 516BA14D...E9B7

  • 第一步:攻击者提交了包含 23 条消息的原生交易 516BA14D...E9B7:20 笔 trade-:ARB~ETH 提款、2 笔 trade-:ARB~LINK 提款,以及最后一笔数量为一个基本单位的 DONATE:ARB.LINK

  • 第二步:交易提款消息创建了有效的调度出账,但最后的 DONATE 消息覆盖了同一原生交易哈希的共享入账 voter,抹去了前面提款消息的出账调度信息。

  • 第三步:当后来观测到 ARB.LINK 的出账时,对账扫描使用了被覆盖的 voter 的高度字段,从未到达存有已调度出账的区块,因此将其视为缺失。

  • 第四步:惩罚性恢复路径针对稀薄的 ARB.LINK 资金池,为缺失的 LINK 进行估值,将资金池的 CACAO 侧虚增约 4945 万 CACAO。原本应为该补贴提供资金的从储备金到 Asgard 的转账失败了,因为储备金仅持有约 16.8 万 CACAO,远低于虚增的补贴数额;但被篡改的资金池余额依然保留下来,且失败的处理逻辑被标记为已完成。

  • 第五步:在稍后的一个区块高度,攻击者向这个被扭曲的资金池添加了 100 CACAO 和少量 LINK 作为流动性。由于该资金池的一侧比例被严重推高失衡,添加流动性的计算公式使攻击者获得了相对于现有约 7.31 亿 LP 单位而言约 1 万亿 的 LP 单位。

  • 第六步:攻击者立即撤出了几乎全部持仓,获得了约 4887 万 CACAO98.82 LINK,这些资金是从共享 Asgard 金库中按照虚增的资金池余额支付出来的,随后攻击者将提取到的 CACAO 通过 MAYAChain 资金池兑换成了 BTC 和其他资产。抛售期间,CACAO 的价格从约 0.115 美元 跌至接近 0.013 美元 的低点。

攻击者提取的 4887 万 CACAO 按被利用前的价格计算名义价值超过 500 万美元,但并非全部都变现为外部收益:其中很大一部分又被兑换回资金池,使 CACAO 价格崩溃,而与攻击者无关的套利者也在这次崩盘中顺势提取了部分价值。经直接确认的链上提取金额约为 136 万美元,主要是 20.83 BTC;再加上攻击者残余的 CACAO 和交易账户余额,得出官方估计的约 176 万美元这一数字。

结论

本次事件是由一系列账务和状态验证缺陷叠加所致,而非单一漏洞:单独来看没有一步是灾难性的,但它们共同作用,让一笔精心构造的存款把一个稀薄资金池的记录余额变成了无支撑的虚增价值。核心要求是原子性和有边界的信任:一次账务变更以及为其提供资金支持的转账必须同成功或同失败,一次失败的恢复流程绝不应被标记为已完成,而针对低流动性资金池的估值必须设有上限。共享的入账状态也绝不应被同一交易中的后续消息静默覆盖。

Get Started with Phalcon Explorer

Dive into Transactions to Act Wisely

Try now for free

Get Started with Phalcon Security

Detect every threat, alert what matters, and block attacks.

Try now for free

参考资料

关于 BlockSec

BlockSec 是一家全栈式区块链安全和加密合规服务提供商。我们打造的产品和服务能够帮助客户在协议和平台的整个生命周期中执行代码审计(包括智能合约、区块链和钱包审计)、实时拦截攻击、分析事件、追踪非法资金,并履行反洗钱/反恐怖融资(AML/CFT)义务。

BlockSec 已在多个顶级学术会议上发表区块链安全相关论文,曾报告过多起 DeFi 应用的零日攻击,成功阻止多次黑客攻击并挽回超过 2000 万美元资金,并为数十亿美元的加密资产提供了安全保障。

Sign up for the latest updates
Harmony跨分片ONE铸币漏洞 + 约4700万美元私钥丢失 | BlockSec
Security Insights

Harmony跨分片ONE铸币漏洞 + 约4700万美元私钥丢失 | BlockSec

2026年8月10日至16日期间,共5起重大安全事件,量化损失约4700万美元。重点分析Harmony第一层链的实现缺陷:跨分片收据重放导致原生ONE被非法铸造——目标分片依据未验证的MerkleProof.ShardID和BlockNum字段而非签名的源区块头来标记收据已花费,致使已入账收据可被重放而无对应源分片扣款。约3.01万亿ONE被伪造,但其名义价值远超代币市值,未变现也未确认为实际损失,故不计入总额。约4700万美元损失来自私钥泄露(未知巨鲸钱包约2500万美元、Kite约1400万美元、Coinsbuy约790万美元)及Fox业务逻辑漏洞(约11.7万美元)。

~$160万损失:Moke代币与LpdFi遭黑客攻击 | BlockSec周报
Security Insights

~$160万损失:Moke代币与LpdFi遭黑客攻击 | BlockSec周报

2026年8月3-9日,BNB Chain发生2起重大安全事件,总损失约160万美元,均源于价格操纵。LpdFi事件(约69.7万美元):攻击者利用同一PancakeSwap流动性池储备同时用于订单估值和利息赎回的漏洞,虚增本金并操纵池子以索取超额利息。Moke Token事件(约90.6万美元):攻击者结合可操纵的现货价格与重复的LP分红计算,多次领取虚高的MOKE及BNB分红。

COLDCARD事件:当钱包的"随机"助记词并不随机
Security Insights

COLDCARD事件:当钱包的"随机"助记词并不随机

COLDCARD固件存在静默构建集成漏洞,导致比特币种子生成回退至软件随机数生成器,弱随机性使钱包种子可被离线恢复。由于漏洞存于种子本身,固件更新无法修复;截至2026年8月7日,已核实损失达1,405 BTC(约9100万美元),私下估计高达2,055 BTC。