深入了解价值 1300 万美元的 Abracadabra GMX V2 漏洞攻击

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

深入剖析 Abracadabra GMX V2 的 1300 万美元漏洞

由于有缺陷的抵押品计算,一起 1300 万美元的漏洞攻击了 Abracadabra 的 GmxV2 CauldronV4。以下是攻击者如何使用失败的订单绕过偿付能力检查的。

排干大锅:深入剖析 Abracadabra GMX V2 的 1300 万美元漏洞

深入剖析 Abracadabra GMX V2 的 1300 万美元漏洞

MIM Spell (Abracadabra.money) 简介

Abracadabra.money 是一个允许用户存入计息代币 (ibTKNs) 作为抵押品来借入 Magic Internet Money (MIM)(一种与美元Hook的稳定币)的平台。通过允许这些 ibTKNs(例如,yVault 代币)作为抵押品,用户可以解锁额外的资金,这些资金以前锁定在产生收益的位置中。

在底层,Abracadabra 由基于 BentoBox 技术的模块化智能合约组成。这种架构提供了基本功能,例如非托管金库(通过 DegenBox)、隔离的借贷市场 (Cauldrons) 和收益策略,这些策略可以从存储的抵押品中赚取利息。当用户将抵押品存入特定的 Cauldron 时,他们可以借入 MIM。如果他们的抵押品价值低于某个阈值,清算人可以偿还用户的债务,以换取没收抵押品。

架构概述

image

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

这些合约共同使 Abracadabra 能够创建多个具有自定义参数的隔离借贷市场,同时确保 MIM 稳定币始终有抵押品支持

GMX V2 CauldronV4 架构

image

为了支持 GMX V2(一种链上永久 DEX),Abracadabra 引入了一种专门的 GmxV2 CauldronV4。GMX V2 具有非原子性的存款和取款:用户下订单,然后由 keeper 或外部回调最终执行它。以下是让 Abracadabra 处理这种异步流程的关键调整:

  1. OrderAgent & RouterOrder
    • 当用户想要存入 GMX 仓位作为抵押品(或提取抵押品)时,Cauldron 会调用一个 OrderAgent
    • OrderAgent 部署一个专门与该用户绑定的 RouterOrder 代理。
    • RouterOrder 将存款/取款请求提交给 GMX。请求完成后,GMX 回调到 afterDepositExecution()afterWithdrawalCancellation()
  2. Router 中待处理的抵押品
    • 通常,Cauldron 仅使用链上抵押品来检查用户的偿付能力。
    • 使用 GMX V2,用户可能有一个“正在进行中”的存款请求,但尚未执行。
    • GmxV2 CauldronV4 扩展了 _isSolvent() 以计算用户在 RouterOrder 中“待处理”的抵押品。RouterOrder 合约中的函数 orderValueInCollateral() 返回用户实际“在途”的代币数量。
  3. Cook Actions
    • Abracadabra 的 cook() 函数可以捆绑诸如存入抵押品、借入 MIM、交换等操作。
    • GmxV2 CauldronV4 添加了专门的操作 ( ACTION_CREATE_ORDER, ACTION_WITHDRAW_FROM_ORDER, ACTION_CANCEL_ORDER) 来管理 GMX 存款/取款订单。
    • 这种设计允许高级的单笔交易流程(例如,“借款然后开设 GMX 存款订单”)。
  4. Liquidation & closeOrder(清算 & closeOrder)
    • 在正常的 Cauldrons 中,如果用户的抵押品太少,协议会没收链上代币。
    • GmxV2 CauldronV4 还必须考虑任何有效订单。如果用户无力偿债,它将 cancelOrder(),并强制从 RouterOrder 中提取 USDC 或 GMX 代币。
    • 函数 closeOrder() 将用户存储的 RouterOrder 地址设置为零,从而完成强制关闭的仓位。

流程示例:

  1. Deposit(存款):用户“创建订单”以在 GMX 中存入 USDC。存款最终产生 GM 代币,Router 将其作为抵押品发送回 Cauldron。
  2. Borrow(借款):如果考虑到链上抵押品和待处理的“orderValueInCollateral()”,用户仍然“有偿付能力”,则 Cauldron 允许他们借入 MIM。
  3. Withdrawal(取款):用户可以通过另一个 GMX 订单类似地提取抵押品,并且一旦 GMX 处理了它,代币将被退回或取消。
  4. 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 查看。

image

市场影响

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

image

漏洞利用

该漏洞的核心在于 GmxV2CauldronV4 及其关联的 GmxV2CauldronRouterOrder 合约中的两个关键设计缺陷

  1. [ sendValueInCollateral](<https://github.com/Abracadabra-money/abracadabra-money-contracts/blob/dff69a19a219bbff90ab7b752c9f9c0ab5e8fe6f/src/periphery/GmxV2CauldronOrderAgent.sol#L241>)() 函数在清算期间从路由中删除真实的代币,但不更新内部状态变量,例如 inputAmountminOutminOutLong。这种遗漏意味着 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    }

完整的漏洞利用流程

image

设置阶段:使用幻影抵押品的失败 GMX 存款

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

image

初始借款以提供资金

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

image

漏洞利用交易

精确的 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。

image

最终偿付能力检查(已绕过)

cook() 批处理结束时,Cauldron 调用 _isSolvent() 以确保用户在所有操作后仍具有偿付能力。然而:因此,协议仍然根据过时的内部状态将用户视为有偿付能力,并且 交易不会恢复。攻击者带着 借入的 MIM 和在清算期间没收的 实际抵押品 离开,同时仍然显得完全抵押。_isSolvent()cook() 批处理结束时运行并错误地通过,因为它使用了未更改的 orderValueInCollateral()

  • _isSolvent() 依赖于 RouterOrder 中的 orderValueInCollateral()
  • 此函数继续根据 inputAmountminOutminOutLong 报告原始的、虚高的抵押品价值
  • 即使在成功清算并从订单中提取资金后,这些字段也永远不会更新。

因此,协议仍然根据过时的内部状态将用户视为有偿付能力,并且 交易不会恢复

攻击者带着 借入的 MIM 和在清算期间没收的 实际抵押品 离开,同时仍然显得完全抵押。

_isSolvent()cook() 批处理结束时运行并错误地通过,因为它使用了未更改的 orderValueInCollateral()

image

重复和耗尽 攻击者可以在不同的 Cauldrons 和钱包中重复这种模式,每次都耗尽真实的资产,同时根据无效的内部会计传递偿付能力检查。

最大的漏洞利用交易(≈932 ETH)

通过在一个大的 cook() 调用中重复漏洞利用步骤,攻击者从这单次操作中获得了 ≈932 ETH。

如何防止攻击

下面是一个 diff,说明如何修补 sendValueInCollateral() 以减少用户的“账面”抵押品字段。

注意:此代码段仅用于说明。在真实的生产场景中,正确修复漏洞需要深入了解 RouterOrder 的内部会计逻辑,包括 inputAmountminOutminOutLong 如何在所有流程(例如,存款、清算和取消)中使用。全面的修复还需要更新 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

其他最佳实践

  1. 中间偿付能力检查:如果协议的 cook() 函数“批量处理”多个操作,则它可以在每个主要步骤之后调用 _isSolvent(),而不仅仅是在最后。
  2. 严格的审计:引入非原子性订单系统(带有“纸质”抵押品)应该触发更深入的会计检查。
  3. 限制自我清算:除非经过彻底验证,否则一些协议不允许在单笔交易中进行部分或自我清算。

后果

  • 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

image

注意:这不是 Abracadabra 的架构第一次被利用。早在 2024 年 2 月,其 CauldronV4 债务会计机制中的一个早期漏洞就被用来提取超过 640 万美元

我们的 Three Sigma 团队发布了对该事件的详细分析,涵盖了份额通货膨胀如何允许操纵内部借贷逻辑。

阅读更多Three Sigma 的 Abracadabra Money 漏洞利用分析 - 2024 年 2 月

地址

受影响的合约

🧑‍💻攻击者钱包

常见问题 (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 中删除了真实的代币,但未能减少内部价值,如 inputAmountminOut。这导致 orderValueInCollateral() 返回了虚高的数字。

8. Abracadabra 如何应对攻击?

Abracadabra 停止了受影响市场上的借款,与 Chainalysis 和其他合作伙伴协调以追踪资金,并提供了 20% 的赏金(约 258 万美元),用于安全归还被盗资产。

9. DeFi 协议可以从这次漏洞利用中学到什么?

DeFi 协议应严格测试涉及异步资产流的极端情况,确保内部会计与实际代币移动相匹配,并避免在偿付能力检查期间仅依赖乐观的“纸质抵押品”。

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

0 条评论

请先 登录 后评论
Three Sigma
Three Sigma
Three Sigma is a blockchain engineering and auditing firm focused on improving Web3 by working closely with projects in the space.