深入了解价值 1300 万美元的 Abracadabra GMX V2 漏洞攻击
Abracadabra 平台的 GmxV2 CauldronV4 合约由于不完善的抵押品计算方式遭受了 1300 万美元的攻击。攻击者通过操纵失败的订单,绕过了偿付能力检查,从而提取了资金。文章详细分析了攻击的原理、流程,以及如何通过修改合约代码来防止此类攻击,并总结了此次攻击事件的影响。
深入剖析 Abracadabra GMX V2 的 1300 万美元漏洞
由于有缺陷的抵押品计算,一起 1300 万美元的漏洞攻击了 Abracadabra 的 GmxV2 CauldronV4。以下是攻击者如何使用失败的订单绕过偿付能力检查的。

深入剖析 Abracadabra GMX V2 的 1300 万美元漏洞
MIM Spell (Abracadabra.money) 简介
Abracadabra.money 是一个允许用户存入计息代币 (ibTKNs) 作为抵押品来借入 Magic Internet Money (MIM)(一种与美元Hook的稳定币)的平台。通过允许这些 ibTKNs(例如,yVault 代币)作为抵押品,用户可以解锁额外的资金,这些资金以前锁定在产生收益的位置中。
在底层,Abracadabra 由基于 BentoBox 技术的模块化智能合约组成。这种架构提供了基本功能,例如非托管金库(通过 DegenBox)、隔离的借贷市场 (Cauldrons) 和收益策略,这些策略可以从存储的抵押品中赚取利息。当用户将抵押品存入特定的 Cauldron 时,他们可以借入 MIM。如果他们的抵押品价值低于某个阈值,清算人可以偿还用户的债务,以换取没收抵押品。
架构概述

- DegenBox (BentoBox 变体)
- 核心金库,托管用户资金并将其转换为“份额”。
- 这些份额可以通过已部署的策略(例如,农业收益)赚取利息。
- Abracadabra 在 DegenBox 中存储用户抵押品和未借出的 MIM。
- Cauldrons(隔离市场)
- 每个 Cauldron 都是一个“mini‐Kashi”市场,专用于特定类型的抵押品。
- 处理借入 MIM、跟踪偿付能力和执行清算的逻辑。
- 用户与 Cauldron 交互以存入抵押品并请求 MIM 贷款。
- Oracles(预言机)
- 每个 Cauldron 都必须实时了解其抵押品的 USD 价值。
- 通常使用 Chainlink 或类似的外部预言机,或者可以使用更新的“Proxy Oracle”。
- Strategies(策略)
- 连接到 DegenBox 的可选模块,用于将闲置抵押品部署到收益农场中。
- 增加该代币中所有存款人的份额的整体价值。
- Periphery Contracts(外围合约)
- Swappers(交换器):原子“动作”,用于交换代币以实现杠杆或偿还债务。
- Wrappers(封装器):用于具有额外功能或奖励代币的特殊抵押品的适配器。
- Withdrawers(提取器):收集和分配协议费用。
- CauldronOwner:控制所有 Cauldron 参数的主合约所有者。
这些合约共同使 Abracadabra 能够创建多个具有自定义参数的隔离借贷市场,同时确保 MIM 稳定币始终有抵押品支持。
GMX V2 CauldronV4 架构

为了支持 GMX V2(一种链上永久 DEX),Abracadabra 引入了一种专门的 GmxV2 CauldronV4。GMX V2 具有非原子性的存款和取款:用户下订单,然后由 keeper 或外部回调最终执行它。以下是让 Abracadabra 处理这种异步流程的关键调整:
- OrderAgent & RouterOrder
- 当用户想要存入 GMX 仓位作为抵押品(或提取抵押品)时,Cauldron 会调用一个 OrderAgent。
- 此 OrderAgent 部署一个专门与该用户绑定的 RouterOrder 代理。
- RouterOrder 将存款/取款请求提交给 GMX。请求完成后,GMX 回调到
afterDepositExecution()或afterWithdrawalCancellation()。
- Router 中待处理的抵押品
- 通常,Cauldron 仅使用链上抵押品来检查用户的偿付能力。
- 使用 GMX V2,用户可能有一个“正在进行中”的存款请求,但尚未执行。
- GmxV2 CauldronV4 扩展了
_isSolvent()以计算用户在 RouterOrder 中“待处理”的抵押品。RouterOrder 合约中的函数orderValueInCollateral()返回用户实际“在途”的代币数量。
- Cook Actions
- Abracadabra 的
cook()函数可以捆绑诸如存入抵押品、借入 MIM、交换等操作。 - GmxV2 CauldronV4 添加了专门的操作 (
ACTION_CREATE_ORDER,ACTION_WITHDRAW_FROM_ORDER,ACTION_CANCEL_ORDER) 来管理 GMX 存款/取款订单。 - 这种设计允许高级的单笔交易流程(例如,“借款然后开设 GMX 存款订单”)。
- Abracadabra 的
- Liquidation & closeOrder(清算 & closeOrder)
- 在正常的 Cauldrons 中,如果用户的抵押品太少,协议会没收链上代币。
- GmxV2 CauldronV4 还必须考虑任何有效订单。如果用户无力偿债,它将
cancelOrder(),并强制从 RouterOrder 中提取 USDC 或 GMX 代币。 - 函数
closeOrder()将用户存储的 RouterOrder 地址设置为零,从而完成强制关闭的仓位。
流程示例:
- Deposit(存款):用户“创建订单”以在 GMX 中存入 USDC。存款最终产生 GM 代币,Router 将其作为抵押品发送回 Cauldron。
- Borrow(借款):如果考虑到链上抵押品和待处理的“orderValueInCollateral()”,用户仍然“有偿付能力”,则 Cauldron 允许他们借入 MIM。
- Withdrawal(取款):用户可以通过另一个 GMX 订单类似地提取抵押品,并且一旦 GMX 处理了它,代币将被退回或取消。
- Liquidation(清算):如果用户的有效抵押品低于阈值,则 Cauldron 会调用
liquidate(),可能会取消任何未完成的订单(返回剩余的 USDC)或从刚刚到达的存款中没收 GM 代币。
在实践中,这种方法弥合了 GMX V2 的异步设计与 Abracadabra 的标准 Cauldron 架构之间的差距。但是,正如漏洞攻击中所看到的,如果系统在部分清算或取款后没有仔细更新“待处理的抵押品”,攻击者可能会在表面上显得有偿付能力,同时在现实中耗尽协议。
审计覆盖范围
gmCauldronV2 合约已于 2023 年 11 月 14 日 由 Guardian Audits 在一份报告中进行了审查(审计 PDF)。审计发现大量问题,包括 4 个严重/高严重性发现和 10 个中等严重性发现——这表明代码库需要进一步完善。
重要的是,这是合约部署之前进行的唯一审计。虽然 Guardian 专业地完成了他们的范围,但在架构发生重大变化后,没有进行后续审计
发现了如此多的漏洞,不仅建议进行第二次审计,而且绝对有必要。
随后的漏洞攻击并非由审计疏忽引起,而是由于协议团队未能寻求集成更改后的更深入验证。
攻击分析
背景:GmxV2 CauldronV4 的非原子存款
Abracadabra 的 GmxV2 CauldronV4 旨在处理 GMX V2 的非原子存款模型,用户可以在其中创建稍后执行的存款订单。当存款成功时,GM 代币会记入用户的抵押品。但是,当存款失败时——例如,由于无法访问的 minOut——GMX 合约会将原始代币(例如,USDC)返回给 RouterOrder。在这种情况下,Cauldron 仍然认为这些返回的代币是有效的抵押品。
攻击者通过故意强制存款失败来利用这一点,从而在 RouterOrder 合约中创建“幻影抵押品”。orderValueInCollateral() 函数继续报告失败存款的全部价值,即使在从 RouterOrder 中提取真实的资金后,Cauldron 也没有将该订单标记为已关闭。
准备和攻击时间线
以下序列概述了攻击者在执行漏洞利用之前的设置。
准备阶段
- 钱包 1 从 Tornado Cash 收到 1 ETH。
- 钱包 6 通过 Tornado Cash 收到 10 ETH(10 × 1 ETH)。
- 钱包 6 通过 Stargate 将 ETH 桥接到 Arbitrum。
- 钱包 1 通过 GMX 获得 3.31700399 gmETH/ETH 代币。
- 钱包 1 设置批准并与 gmETH/ETH Cauldron 交互。
- 钱包 1 将 0.5 gmETH/ETH 代币分发给钱包 2、3、4 和 5。
- 钱包 1 还向上述每个钱包发送 0.1 ETH。
- 钱包 2–5 批准主合约并存入 gmETH/ETH。
攻击阶段
- 钱包 1 部署漏洞利用合约:
0xf29120acd274a0c60a181a37b1ae9119fe0f1c9c - 钱包 6 为漏洞利用合约提供 9.93 ETH 的资金。
- 攻击者跨多个钱包和 Cauldrons 发起一系列
cook()交易。 - 每个
cook()都包含故意失败的 GMX 存款,然后是清算和重新借款序列。
到活动结束时,攻击者通过大约 100 分钟内的 56 笔交易,从五个 GM Cauldrons 中耗尽了 ~1340 万美元。
完整的活动日志可通过死后分析中的官方 Google Sheet 查看。

市场影响
- GMX 价格:下跌 $55.20 → $46.92(下跌 15.0%)
- MIM Spell 价格:下跌 $1.20 → $1.08(下跌 10.0%)
- 范围:只有“gmCauldron”集成受到影响;其他 Abracadabra cauldrons 未受影响

漏洞利用
该漏洞的核心在于 GmxV2CauldronV4 及其关联的 GmxV2CauldronRouterOrder 合约中的两个关键设计缺陷:
- [
sendValueInCollateral](<https://github.com/Abracadabra-money/abracadabra-money-contracts/blob/dff69a19a219bbff90ab7b752c9f9c0ab5e8fe6f/src/periphery/GmxV2CauldronOrderAgent.sol#L241>)()函数在清算期间从路由中删除真实的代币,但不更新内部状态变量,例如inputAmount、minOut或minOutLong。这种遗漏意味着 Cauldron 认为仍然存在相同数量的“潜在抵押品”,即使在其中一些抵押品被提取之后。
1function sendValueInCollateral(address recipient, uint256 shareMarketToken) public onlyCauldron {
2 (uint256 shortExchangeRate, uint256 marketExchangeRate) = getExchangeRates();
3
4 uint256 amountShortToken = (degenBox.toAmount(IERC20(market), shareMarketToken, true) * oracleDecimalScale) /
5 (shortExchangeRate * marketExchangeRate);
6
7 shortToken.safeTransfer(address(degenBox), amountShortToken);
8 degenBox.deposit(IERC20(shortToken), address(degenBox), recipient, amountShortToken, 0);
9 }
orderValueInCollateral() 函数继续使用那些未更改的内部字段来计算用户的待处理抵押品。由于这些值在发生部分清算或存款失败时从未减少,因此用户看起来持有的抵押品比实际持有的要多——从而实现了欺诈性借贷。
1 function orderValueInCollateral() public view returns (uint256 result) {
2 (uint256 shortExchangeRate, uint256 marketExchangeRate) = getExchangeRates();
3
4 if (depositType) {
5 uint256 marketTokenFromValue = (inputAmount * shortExchangeRate * marketExchangeRate) / oracleDecimalScale;
6 result = minOut < marketTokenFromValue ? minOut : marketTokenFromValue;
7 } else {
8 uint256 marketTokenFromValue = ((minOut + minOutLong) * shortExchangeRate * marketExchangeRate) / oracleDecimalScale;
9 result = inputAmount < marketTokenFromValue ? inputAmount : marketTokenFromValue;
10 }
11 }
完整的漏洞利用流程

设置阶段:使用幻影抵押品的失败 GMX 存款
攻击者在通过 GMX 存入抵押品时,以不切实际的 minOut 值发起 cook() 调用。这会导致 GMX 拒绝存款并将输入代币(例如,USDC)返回给 RouterOrder 合约。尽管收回了代币,但由于未更改的会计字段,Cauldron 仍然认为此失败的存款已成功。

初始借款以提供资金
攻击者使用 cook() 来借入少量 MIM,用于资助将在漏洞利用中稍后回收的抵押品。

漏洞利用交易
精确的 cook() 步骤 第二个 cook() 调用包含多个操作:
- 操作 5 (借款):借入 MIM,将贷款价值比率推高到清算阈值以上。
- 操作 30 (调用):调用攻击者的合约函数 (
get_before_liquidate_amount) 来计算在清算期间应提取多少抵押品以实现最大提取。 - 操作 31 (清算):自我清算该仓位。清算逻辑首先尝试用 BentoBox 抵押品支付债务,然后通过
sendValueInCollateral()从攻击者的 RouterOrder 中提取额外的代币。这些代币是真正的 USDC,已从失败的 GMX 存款中退回。 - 操作 30 (调用):调用攻击合约中的另一个函数 (
get_after_liquidate_amount) 来计算现在可以根据未更改的幻影抵押品借入多少 MIM。 - 操作 5 (借款):攻击者再次借款,这次由不再存在但仍由
orderValueInCollateral()报告的抵押品支持。- 操作 30 (调用):调用以交换并从协议中提取所有借入的 MIM。

最终偿付能力检查(已绕过)
在 cook() 批处理结束时,Cauldron 调用 _isSolvent() 以确保用户在所有操作后仍具有偿付能力。然而:因此,协议仍然根据过时的内部状态将用户视为有偿付能力,并且 交易不会恢复。攻击者带着 借入的 MIM 和在清算期间没收的 实际抵押品 离开,同时仍然显得完全抵押。_isSolvent() 在 cook() 批处理结束时运行并错误地通过,因为它使用了未更改的 orderValueInCollateral()。
_isSolvent()依赖于 RouterOrder 中的orderValueInCollateral(),- 此函数继续根据
inputAmount、minOut或minOutLong报告原始的、虚高的抵押品价值, - 即使在成功清算并从订单中提取资金后,这些字段也永远不会更新。
因此,协议仍然根据过时的内部状态将用户视为有偿付能力,并且 交易不会恢复。
攻击者带着 借入的 MIM 和在清算期间没收的 实际抵押品 离开,同时仍然显得完全抵押。
_isSolvent() 在 cook() 批处理结束时运行并错误地通过,因为它使用了未更改的 orderValueInCollateral()。

重复和耗尽 攻击者可以在不同的 Cauldrons 和钱包中重复这种模式,每次都耗尽真实的资产,同时根据无效的内部会计传递偿付能力检查。
最大的漏洞利用交易(≈932 ETH)
通过在一个大的 cook() 调用中重复漏洞利用步骤,攻击者从这单次操作中获得了 ≈932 ETH。
如何防止攻击
下面是一个 diff,说明如何修补 sendValueInCollateral() 以减少用户的“账面”抵押品字段。
注意:此代码段仅用于说明。在真实的生产场景中,正确修复漏洞需要深入了解 RouterOrder 的内部会计逻辑,包括
inputAmount、minOut和minOutLong如何在所有流程(例如,存款、清算和取消)中使用。全面的修复还需要更新orderValueInCollateral()并正确跟踪已关闭或已使用的订单。
1 function sendValueInCollateral(address recipient, uint256 shareMarketToken) external onlyCauldron {
2 (uint256 shortExchangeRate, uint256 marketExchangeRate) = getExchangeRates();
3
4 uint256 amountShortToken =
5 (degenBox.toAmount(IERC20(market), shareMarketToken, true) * oracleDecimalScale)
6 / (shortExchangeRate * marketExchangeRate);
7
8+ // Example approach: Decrement the "inputAmount" or "minOut"
9+ // so that orderValueInCollateral() won't keep overstating the user's collateral
10+ if (depositType) {
11+ inputAmount = (inputAmount >= someEquivalentShort) ? (inputAmount - someEquivalentShort) : 0;
12+ if (minOut > someEquivalentShort) minOut -= someEquivalentShort;
13+ } else {
14+ // Similar logic adjusting (minOut + minOutLong) or inputAmount
15+ }
16
17 shortToken.safeTransfer(address(degenBox), amountShortToken);
18 degenBox.deposit(IERC20(shortToken), address(degenBox), recipient, amountShortToken, 0);
19 }
20
其他最佳实践:
- 中间偿付能力检查:如果协议的
cook()函数“批量处理”多个操作,则它可以在每个主要步骤之后调用_isSolvent(),而不仅仅是在最后。 - 严格的审计:引入非原子性订单系统(带有“纸质”抵押品)应该触发更深入的会计检查。
- 限制自我清算:除非经过彻底验证,否则一些协议不允许在单笔交易中进行部分或自我清算。
后果
- 1340 万美元的抵押不足的 MIM:通过 56 笔漏洞利用交易,在 五个 GM Cauldrons 中铸造了大约 1340 万 MIM。
- 持续时间:攻击持续了约 1 小时 40 分钟,从 UTC 时间 07:57:52 AM 开始,到 UTC 时间 09:37:36 AM 结束。
- 市场影响:
- GMX:价格从 55.20 美元 → 46.92 美元(-15.0%)
- MIM Spell:价格从 1.20 美元 → 1.08 美元(-10.0%)
- 运营中断:为了控制进一步的损失,在 UTC 时间 09:46:22 AM 停止了所有 GM Cauldrons 的借款。
协议的响应
在发现漏洞利用之后,Abracadabra 实施了紧急对策并发起了恢复工作:
- 禁用借款:在漏洞利用发生后,立即暂停所有 gmCauldrons,以防止进一步的损害。
- OrderAgent 失效:
orderAgent地址设置为0x000…000,以防止进一步创建 GMX 存款。 - 营救被困资金:已成功追回了大约 260,000 美元的资产,这些资产在漏洞利用后仍位于 RouterOrder 合约中。
- 使用 Hexagate 进行实时监控:
- Hexagate 已集成以监控 Cauldrons,但 DegenBox(持有被利用资产的金库)未被监控。
- 事后看来,包括 DegenBox 可以更早地检测到并自动响应。
- 多方协调:安全研究人员、Guardian Audits、Chainalysis 和 Seal 911 社区协助进行资金追踪和法证分析。
- 沟通和赏金:
- 提供 20% 的赏金:宣布了一项 20% 的赏金(约 258 万美元),用于安全归还被盗资金。
- DAO 宣布它正在等待攻击者的沟通,无论是通过链上消息还是通过 reward@abracadabra.money。

注意:这不是 Abracadabra 的架构第一次被利用。早在 2024 年 2 月,其 CauldronV4 债务会计机制中的一个早期漏洞就被用来提取超过 640 万美元。
我们的 Three Sigma 团队发布了对该事件的详细分析,涵盖了份额通货膨胀如何允许操纵内部借贷逻辑。
地址
受影响的合约
- GmxV2CauldronV4 (gmETH/ETH):
0x625Fe79547828b1B54467E5Ed822a9A8a074bD61 - 其他被利用的 Cauldrons:
??攻击者钱包
- 钱包 1 (主要来源):
0xe9A4034E89608Df1731835A3Fd997fd3a82F2f39(通过以太坊上的 Tornado Cash 资助,与 GMX + Cauldrons 交互) - 钱包 6 (资金和洗钱):
0xaf9e33aa03caaa613c3ba4221f7ea3ee2ac38649(通过 Tornado Cash 资助,通过 Stargate 桥接 ETH,在主网上洗钱) - 关联的攻击者钱包 (Arbitrum):
常见问题 (FAQ)
1. 什么是 Abracadabra GMX 漏洞?
Abracadabra GMX 漏洞是指对该协议的 GmxV2 CauldronV4 的一起 1300 万美元的攻击,其中攻击者操纵内部会计逻辑,使其在通过自我清算提取实际抵押品的同时显得有偿付能力。
2. Abracadabra 漏洞损失了多少?
通过一系列精心构建的 cook() 交易,从该协议中提取了大约 1300 万美元的加密资产(≈6,262 ETH)。
3. Abracadabra 黑客攻击是否涉及预言机操纵?
否。该漏洞利用不涉及预言机操纵。偿付能力计算中使用的价格是准确的;问题在于 RouterOrder 合约中过时的抵押品会计。
4. 什么是 GmxV2 CauldronV4?
GmxV2 CauldronV4 是 Abracadabra 上的一个专门的借贷市场合约,它支持 GMX V2 的异步存款和取款系统,允许用户针对 GMX LP 头寸借入 MIM。
5. 攻击者如何绕过偿付能力检查?
攻击者利用了 orderValueInCollateral() 在部分清算后从未更新的事实。他们借入了 MIM,进行了自我清算,提取了实际资金,并且仍然通过了 cook() 调用结束时的 _isSolvent()。
6. Abracadabra 合约中的 cook() 函数是什么?
cook() 是一个批处理函数,允许用户在一笔交易中执行多个操作(例如,存款、借款、取款、清算)。仅在最后检查偿付能力,这在该漏洞利用中发挥了关键作用。
7. 是什么导致了虚高的抵押品价值?
sendValueInCollateral() 函数从 RouterOrder 中删除了真实的代币,但未能减少内部价值,如 inputAmount 和 minOut。这导致 orderValueInCollateral() 返回了虚高的数字。
8. Abracadabra 如何应对攻击?
Abracadabra 停止了受影响市场上的借款,与 Chainalysis 和其他合作伙伴协调以追踪资金,并提供了 20% 的赏金(约 258 万美元),用于安全归还被盗资产。
9. DeFi 协议可以从这次漏洞利用中学到什么?
DeFi 协议应严格测试涉及异步资产流的极端情况,确保内部会计与实际代币移动相匹配,并避免在偿付能力检查期间仅依赖乐观的“纸质抵押品”。
- 原文链接: threesigma.xyz/blog/expl...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~