Web3 安全审计师回顾2024 年安全问题
- 原文链接:blog.openzeppelin.com/web3-securit...
- 译者:AI翻译官,校对:翻译小组
- 本文链接:htzkw.com …
欢迎来到 Web3 安全审计师的 2024 年回顾,这是对去年显著安全事件和漏洞的简明技术分析合集。
该系列文章最初名为 以太坊智能合约审计师回顾,由 Patrick Drotleff 撰写,他是一位信息安全和隐私倡导者,独立安全研究员及 Secureum 社区的导师。在 TrustX 伊斯坦布尔会议上联系后,Patrick 将继续这一传统的重任托付给我们,同时将重心转向新的事业。
我们遵循了原有格式,为每个事件提供简洁、可操作的见解——突出漏洞并提供足够的背景信息,以便在安全审查或漏洞猎探过程中识别这些问题。为了增加问题的多样性,今年的汇编超越了利用攻击,还包括通过传统审计、竞赛和漏洞赏金平台发现的漏洞。此外,随着审计越来越多地涵盖链外基础设施,我们还包含了来自 Rust 和 Golang 系统(如区块链节点或 CEX 上线基础设施)的漏洞。
突出问题
EIP-1153: 瞬态存储
以太坊 Cancun-Deneb 升级引入了瞬态存储 ,这是一种类似于持久存储的短期数据空间,但其生命周期仅限于一个交易。开发人员可以声明一个变量为 transient 或利用 tstore 和 tload 汇编指令与瞬态存储进行交互,从而享受其成本效益。每次操作大约需要 100 gas,这显著低于传统存储,后者通常需要约 20,000 gas 进行读写。不过,复杂性增加也带来了更大的风险,瞬态存储也不例外:
-
由于访问瞬态存储的低 gas 成本, 低 gas 补贴的重入攻击 (就像通过
address.transfer的 2300 gas 限制)已经成为可能。如果代码块未遵循检查-效果-交互模式,或未使用ReentrancyGuard,安全研究人员应确保瞬态存储变量安全,不受重入修改的影响,因为此类变化可能导致合约行为的恶意或意外变化。 -
目前,瞬态存储仅支持读写值类型。由于数据结构是引用类型,希望使用它们的开发人员必须实现妥协类型安全的变通办法。这个 Solidity Underhanded Contest 提交 演示了缺乏类型安全如何导致数据被意外解码为具有相似布局但不同含义的结构,这可能引入微妙且潜在可利用的错误。
-
最后,虽然在交易结束时瞬态存储会自动清除,但开发人员应显式清除它,以防止在“肮脏”的瞬态状态下与合约发生可能的交互。 以下文章详细说明了在
multicalls或合约间复杂交互期间,如何导致回滚或不正确的行为。 -
- *
Uniswap V4 双入口代币
在 2024 年,Uniswap V4 经过几次深入审计,其中一项揭示了一个关键问题 ,涉及在原生代币也具有 ERC20 表示的链上部署该协议。
在 Uniswap V4 中,settle 函数通过增加指定代币的账户增量来管理债务。它使用 msg.value 来处理原生代币增量,并在 ERC-20 代币的上次 sync 或 settle 调用以来的余额变化。这种机制可以在原生代币(如 Celo 上的[CELO](https://celoscan.io/address/0x471ece3750da237f93b8e339c536989b8978a438))也具有 ERC-20 表示的链上被利用:攻击者可以在同步以加载当前余额后,首先调用 settle 并使用 msg.value > 0 来增加原生代币的增量,然后使用 ERC-20 代币的地址再调用 settle。由于 ERC-20 代币的余额在第一次调用后增加,攻击者可以在第二次调用中膨胀增量,而无需实际转移代币,从而允许他们稍后提取比他们存入的更多代币,最终耗尽池子的资金。
修复措施确保 settle 调用必须解决之前同步的代币,否则交易将回滚。
Polygon PoS 投票接管和无限铸造漏洞
以下研究文章揭示了如何通过将一个恶意验证者与 Heimdall 验证器代码中发现的两个问题结合起来,可能导致 Polygon 桥的资金被抽取。
Polygon 权益证明网络由三层组成:
-
Ethereum 合约:管理质押、检查点提交和验证者奖励。
-
共识层 (Heimdall):根据以太坊的质押状态确定区块生产者并推送检查点更新。
-
执行层 (Geth 分支):生成新区块。
当用户在以太坊上质押 MATIC 时,会触发 StakeUpdate(uint256 validatorId, uint256 nonce, uint256 newAmount) 事件。链外组件通知 Heimdall,从而增加验证者的投票权以进行共识。为了验证此类事件的合法性,Heimdall 查询以太坊以确保事件确实存在,字段值匹配且 nonce 与 Polygon 上存储的值对齐。
第一个问题出现是因为 Heimdall 未能验证事件类型。如果以太坊合约发出类似结构的事件,如 SignerChange(uint256 validatorId, address oldSigner, address newSigner),Heimdall 可能会错误地将其解释为 StakeUpdate。在这种情况下,Heimdall 将把 oldSigner 解析为 nonce,将 newSigner 解析为 newAmount。一个恶意验证者可能会发出一个包含匹配其 Polygon nonce 的 oldSigner 地址和一个解析为巨额 newAmount 的 newSigner 的 SignerChange 事件,人为地提高其投票权。
第二个问题在于 Heimdall 如何验证 nonce。事件的 256 位 nonce 会被截断为 uint64,这极大地降低了生成与 Polygon 上的 nonce 匹配的 oldSigner 地址的计算难度。
攻击者只需对齐 oldSigner 的最后 64 位即可。尽管相当有限,攻击者可以通过链上操作进一步减少所需的计算努力。
凭借压倒性的投票权,攻击者可以操控共识,接受虚假的存款事件,任意在 Polygon 上铸造资金。通过将资金再桥接回以太坊,他们可以抽取 Polygon 桥的资金。幸运的是,该漏洞已被负责任地披露并修复,没有发生任何事故和资金损失。
Compound DAO 治理攻击
在七月,Compound DAO 面临了一次攻击,旨在通过恶意治理提案。该提案试图将 499K COMP 代币—占总供应量的 5%,当时价值 2500 万美元—从国库转移到投资金库。除了转移之外,代币的投票权也将被委托给提案者。鉴于 Compound 的投票率通常为总代币供应量的 4-5%,攻击者可能通过通过该提案来控制治理过程。能够通过任何恶意提案,Compound 的总锁仓价值 (TVL) 和国库资金将面临风险。
攻击包括三个提案:
-
提案 247 (5 月 6 日):直接在链上创建,没有相应的论坛帖子详细说明,这是 Compound 社区内的惯例。该提案意图将 92K
COMP代币从国库转移到“GoldenBoyz”拥有的多签地址,承诺这些代币将进一步投资于一个将其用于收益的金库。这引起了 OpenZeppelin 及我们的一位同事对此事的怀疑,并迅速通知了社区关于恶意活动的可能性。该提案随后在遭到大量反对票后被取消。 -
提案 279 (7 月 15 日):这次附带了论坛帖子,意在通过
TrustSetup智能合约直接将资金转入金库,绕过“GoldenBoyz”的多签。虽然这是一个改进,但新问题浮现 ,即代币的投票权在投资前已被委托给“GoldenBoyz”的多签。该提案被大量反对票打下,并未通过。 -
提案 289 (7 月 24 日):与第二个提案相同,除了数量现在为 499K 代币而不是 92K。尽管社区迅速被通知 ,但“GoldenBoyz”集结了足够的票数通过该提案。怀疑前两个提案仅是为了测试社区的通常投票权,只是在此期间累积更多资金,以在第三个提案的投票中压倒反对派。此外,大多数“支持”票都是在投票期结束时投出的(见 Tally 中的“时间线”),限制了社区的应对能力。
鉴于“GoldenBoyz”的前期投票权和新获得的 499K COMP 代币,提案 289 的成功执行可能使攻击者控制 Compound 治理,使其他大型代币持有者在面对任何恶意提案时无能为力。
社区以提案 290 进行反制,旨在将时间锁管理权限转移至社区多签,并限制“GoldenBoyz”对 DAO 国库和协议执行恶意行为。感谢 Compound 委托人和 OpenZeppelin 及其他方的迅速行动,这一紧急提案消除了威胁,通过防止新获得的投票权被滥用。
最终,“GoldenBoyz”与 DAO 达成协议,在执行前取消了提案 290。虽然他们及其相关账户仍保留显著的投票权,但目前并不构成对 DAO 的直接威胁,不过需要保持持续警惕。
作为额外的安全措施,Compound 协议添加了一个临时提案管理员,拥有取消恶意提案的权力。
Kraken 交易所余额打印漏洞
中心化交易所通常在链外维护用户余额,基于观察到的链上存款和交易活动进行更新。用户被分配存款地址,一旦检测到转账到这些地址,他们的交易账户将被记入存款的代币。
在六月, 一项关键漏洞在 Kraken 中心化交易所被报告,允许恶意用户在未完成实际链上存款过程的情况下夸大其账户余额。虽然用户资金依旧安全,但该缺陷使 Kraken 的国库处于风险之中,使攻击者能够创建和提取虚假余额,作为真实链上资金,累计总额约为 300 万美元。
来自 DanielVF 的分析及其概念验证显示,如果构造得当,Kraken 系统将会将一次回退的代币转移视为有效存款。重要的是,我们无法找到详细说明其确切技术细节的官方事后报告。
一笔包含内部调用的交易转移了资金,随后回滚,错误地被视为有效存款,而没有考虑到内部调用的回滚。信用的兑换金额与回滚交易中代币转移的金额相符,因此攻击者可以利用闪电贷来放大他们的收益,从而使表面存款价值膨胀。
正在进行的调查旨在澄清这是否是研究人员的概念证明示范,还是一次恶意的资金盗窃尝试。
Radiant Capital 黑客事件
在十月,Radiant Capital 遭遇了一次安全漏洞,导致约 5000 万美元的损失。至少三个开发者的设备通过复杂的恶意软件注入被攻击,详细信息见 事件更新。此次漏洞使攻击者能够在交易数据到达硬件钱包进行签名处理之前,从 Safe UI 拦截真实的交易数据并进行篡改,最终导致汇集了不同的恶意链上操作的签名。
攻击涉及更改发送给硬件钱包的交易详情。在 Safe 前端显示的合法交易数据的同时,实际签名的交易是恶意的,使得攻击者能够在达到 3/11 的法定签署人同意后执行 transferOwnership 调用。
攻击者通过成为 Radiant 的 LendingPoolAddressesProvider 合约 的所有者,改变了 借贷池代理的实现,盗取了所有资金和之前给予的代币授权。Safe 预期的签名与硬件钱包给出的签名之间的差异导致了 Safe UI 和链上执行过程中的错误,但由于这种错误在提交元交易时很常见,因此并没有引起立即的怀疑,也没有受到详细分析。
要深入了解 Safe 如何与硬件钱包交互以及盲签名的风险,请参阅这个 分析。
常规问题
缺失的输入验证
-
在 Beanstalk 稳定币协议中,用户可以将
BEAN代币作为流动性提供给Well合约(类似于流动池),以交换 LP 代币。当用户提取流动性时,他们会指定要提取的Well地址和要燃烧的 LP 代币数量。Well合约负责燃烧 LP 代币并计算要返回给用户的BEAN代币数量。然而,Well合约 没有经过验证,也不受任何白名单约束。攻击者可以创建一个合约,当被调用时,虚假报告任何燃烧的 LP 代币数量使用户有权获得 Beanstalk 合约中持有的全部BEAN代币余额。 -
BankrollNetworkStack合约 作为一个保险库,用户通过buyFor函数 存入WBNB,以交换 Bankroll 股票。部分存入的金额作为WBNB股息分配给每个股东。在累计足够的股息后,用户可以将他们的股票卖回到保险库并提取所有累积的股息。然而,buyFor函数缺少对_customerAddress参数的检查,并不会验证其是否未设置为保险库自己的地址。这一疏忽允许攻击者反复调用该函数,将_customerAddress设置为保险库地址并将buy_amount设置为保险库中的WBNB总额。这将把WBNB从保险库转移到其自身,同时将部分金额分配为股息,其中一部分被攻击者所累积。在足够次数调用该函数后,攻击者出售其股票并提取了膨胀的股息,导致其他用户损失。
缺失的访问控制
-
当
TSURU系统收到ERC1155代币时,会调用TsuruWrapper合约的onERC1155Received函数,为存款人铸造相应的TSURU代币。然而,onERC1155Received函数缺乏适当的访问控制,允许攻击者直接调用它并 无限制地铸造代币 给自己。攻击者随后使用这些代币提取了TSURU-ETHUniswap 流动池的资金。 -
Galaxy Fox 代币空投通过在链上存储一个默克尔树根,允许参与者通过提交有效路径并将其地址作为叶子节点来领取代币。然而,
setMerkleRoot函数缺乏访问控制,允许攻击者 设置恶意构建的根,并根据该根领取足够的GFOX代币以抽干WETH-GFOXUniswap 流动池。 -
Alchemix 项目允许用户存入收益-bearing 资产,如
yvDAI或wstETH,作为借入合成资产如alDAI或alETH的抵押品,从而提供即时流动性。定期通过harvest函数收集收益,并有时需要通过在 Balancer 上进行交换来解除包装,创造了夹击攻击的机会。为了降低这些风险,harvest函数包含了一个minimumAmountOut参数,并且只能由 Gelato Automate 合约调用 — 一个旨在以正确参数安排定期链上调用的系统,类似于传统的 cron 作业。然而,Alchemix 与通用的非定制版本的 Gelato 集成,该版本不会响应 仅由专用
msg.senders的请求,允许任何人触发来自Automate合约的调用。这一缺乏定制的情况将允许攻击者 创建具有任意minimumAmountOut值的调用链,便于在解除收获收益时进行显著的夹击。 -
Maia DAO 系统中的
CoreBranchRouter合约 缺乏必要的访问控制,因为一个虚拟函数未使用适当的调用者限制进行重写。此疏忽允许攻击者充当可信的CoreBranchRouter并通过一系列调用提取资金。正如事后分析中所强调的,开发人员和安全研究人员应仔细评审所有继承的合约,以防止类似的漏洞。
下溢和上溢
-
在 Mantle 上,通过指定转账金额,可以在 L1 发起
MNT和ETH之间的 L2 地址转账。在未验证余额的情况下,触发TransactionDeposited事件,并由 Mantle 节点接收,该节点会确保from地址有足够的资金进行转账。然而,当涉及时ETH转账时,Mantle 节点并未确保from地址有足够的资金,并直接更新了余额 。由于使用了 Golang 的BigInt类型和BigToHash函数 ,未能检测到下溢,并且负值会被转换为正值。这使得攻击者可以转账他们并没有的ETH到他们控制的另一个地址,同时增加两个余额,从而无限铸造。 -
一名攻击者通过利用代币
transferFrom函数中缺少的下溢检查, 盗取了多个用户的LW代币 。该函数未能验证转账金额是否在批准的金额内,假设减法在变为负数时会自动回滚。然而,该合约是使用 Solidity0.7.6编译的,缺乏自动下溢检查。这使得攻击者能够从用户那里转移未获得批准的代币,绕过了预定的限制。有关相同漏洞模式的另一个示例,参见这篇文章关于
Scroll代币漏洞的分析。
闪电贷回调中的验证缺失
-
Prisma Finance 的
MigrateTroveZap合约旨在通过使用闪电贷来帮助用户迁移他们的Troves(有抵押和债务的位置),以便在新合约中关闭并重新打开它们。然而,onFlashLoan方法未能正确验证输入。一名攻击者绕过了migrateTrove过程 ,直接发起闪电贷,使用意外数据调用onFlashLoan方法。这使得攻击者能够关闭其他用户的 Troves,并用保持健康位置所需的最低抵押品重新开启它们 。攻击者然后打开自己的 Trove,并能够继承被攻破位置的剩余抵押品,最终提取多余资金。 -
类似地,Dough Finance 的
ConnectorDeleverageParaswap合约帮助用户通过flashloanReq函数从 AAVE 获得闪电贷。用户可以提供一个swapData参数,旨在定义通过指定路由交换闪电贷代币的详细信息。然而,对swapData参数没有进行验证,允许攻击者调用WETH transferFrom而不是代币交换,从而窃取已批准给合约的资产。
不完整的签名方案
-
ERC1271 中的
isValidSignature函数提供了一种标准方法,使合约能够验证代表给定合约的签名是否有效。一个常见的用例是智能账户,它依赖于所有者的签名来授权操作。不幸的是,参考实现本质上未将msg.sender或目标合约地址与签名绑定。此遗漏可能导致签名重放攻击 ,即针对智能账户 A 的签名可能被重新用于账户 B,只要这两个账户具有相同的所有者和消息格式。 -
为了获得 Taiko 拨款,部署了
TimelockTokenPool合约,使得接收者在一段时间延迟后可以提取代币,并使用有效的签名。签名是根据keccak256(abi.encodePacked("Withdraw unlocked Taiko token to: ", _to))摘要进行验证的, 缺少了几个关键字段 :-
没有 nonce,签名可能会在同一个
TimelockTokenPool合约中被重放。 -
没有验证合约的地址,签名可能在不同的
TimelockTokenPool合约之间被重放。 -
最后,缺少
chainId,签名可能会在其他链上重放。
-
-
在 Phi 线下系统中,签名授权机构会为用户发出签名,以便他们在链上领取奖励。虽然签名是由所有必要安全字段构成的,但链上验证机制未检查
chainId是否与当前链匹配。因此, 为一个链生成的签名可能会在另一个链上重用 ,以领取额外奖励。通过使用EIP712抽象合约 ,开发者可以确保所有安全字段都包含在签名中,并针对执行上下文进行验证(例如version,address(this),block.chainid)。 -
在 Phi 系统中,签名授权机构会使用前端验证的参数生成线下签名,供用户在链上提交并创建 NFT 艺术。然而, 并非所有参数都包含在签名中 ,允许攻击者抢先提交签名并更改某些字段,从而改变 NFT 的艺术家、最大供应量或版权接收者。
不安全的类型转换
-
在Uniswap v4 中,
validateMinOut函数中的不安全类型转换从int128转为uint128,可能允许跳过滑点检查。这是因为一个钩子可能返回负值liquidityDelta,当它转为uint128时,会变成一个非常大的正数,超过用户指定的最小输出。 修复方法涉及使用SafeCast.toUint128,当liquidityDelta为负时,将回滚交易。 -
在 Filecoin 网络的 Lotus 和 Venus 客户端中发现了一个漏洞 ,可能使攻击者远程崩溃节点。根本原因是由于不安全的类型转换,从无符号整数到有符号整数,以及随后的越界访问。在
validateCompressedIndices函数中,消息索引是无符号整数,被不安全地转换为用于验证的有符号整数。该验证检查索引是否小于Bls切片的长度,但由于对等方可以控制索引值,因此可以设置一个大于有符号整数最大值的索引。当转换为有符号整数时,这样一个大的值会回绕变成负数,从而绕过验证检查。这个错误导致在验证后产生越界访问,引发恐慌,从而导致节点崩溃。 -
在
AsEthereumData函数中发现了一个漏洞,该函数用于解码以太坊交易数据。该漏洞的产生是因为该函数未验证v、r和s签名值(类型为big.Int),而是直接用uint256.MustFromBig将它们转为uint256。如果这些big.Int值被恶意构造得过于庞大,MustFromBig函数可能会因溢出而导致 panic,从而使 Sei Node 关闭。
重复性攻击
-
在 Scroll 上存入带发送者钩子的
ERC20代币时,一个 放错位置的重入保护 可能允许攻击者获取膨胀的数量并可能从合约中排空资金。nonReentrant修饰符放置在内部_deposit函数上,而该函数并未包含代币转移,而是放置在包含safeTransferFrom调用 的外部depositERC20上。 -
SumerMoney 项目是支持
ETH和USDC等资产的 Compound 复刻版,其贷款偿还逻辑有所不同:在更新totalBorrows之前,它退还任何多余的金额,这可能会在基础资产与cToken之间的汇率膨胀时将执行权交给调用者。攻击者通过 借入ETH,以 1 wei 的盈余偿还,并以膨胀的汇率兑换抵押品 来利用这一点。通过这样做,攻击者在兑换较少的cTokens时恢复了其最初的抵押品,并利用剩余的金额进一步借入更多基础代币而不进行还款,从市场中排空资金。 -
ETH/OETHCurve 池的withdraw_admin_fees函数将根据内部(self.balance; OETH.balanceOf(address(this)))和会计余额(self.balances[0]; self.balances[1])的差值确定的多余资金发送到一个费用接收者。因为该函数的行为不依赖于msg.sender,所以缺乏访问控制。remove_liquidity_imbalance函数允许用户以不成比例的金额提取流动性,先转移ETH并在转移OETH之前将执行权交给调用者。这将暂时使OETH.balanceOf(address(this))保持在初始且不一致的状态。通过在正确的时间调用withdraw_admin_fees,攻击者可以 将池的内部OETH余额减少到低于会计余额,从而暂时打破池的会计。 -
Onyx 是一个从 Compound V2 分叉而来的借贷平台,实现了自己的清算流程。平台内的低流动性市场允许攻击者通过 持续铸造和赎回单一 LP 代币,逐渐降低其汇率。这一过程降低了抵押品的价值,直到它处于略微亏损的状态,从而允许攻击者启动有利可图的自清算。
此外,Onyx 的清算逻辑中缺少输入验证,使得攻击者能够使用假代币进行偿还,从而提高了攻击的盈利性。
-
Penpie 项目为 Pendle Finance 用户提供收益提升服务,允许用户通过
batchHarvestMarketRewards函数从特定 Pendlemarkets收集奖励。该函数在 记录代币余额之前、赎回奖励并记录 代币余额之后,差额代表需要分配的奖励。然而,攻击者通过附加一个有效的 Pendle 市场和一个恶意的自定义代币来利用这一点,该代币在
IPendleMarket.redeemRewards()内的代币转移过程中将执行权交出。攻击者利用这个执行窗口调用depositMarket,在同时膨胀用于计算奖励的“之后”代币余额的同时存入代币以铸造 LP 代币。
价格操纵
-
在 HYDT 代币系统中,
InitialMint合约 允许用户存入BNB来兑换新铸造的HYDT代币。铸造的数量基于WBNB/USDTPancakeSwap 池的现货价格。攻击者通过 闪电贷USDT,几乎将池中的所有WBNB交易掉以操纵汇率,然后铸造了更多的HYDT代币。 -
PolterFinance 使用两个现货价格的平均值 - 一个来自 Uniswap V2 池,一个来自 Uniswap V3 池 - 来评估
BOO代币作为借款的抵押品。尽管聚合价格可以降低操纵风险,但完全依赖现货价格使系统容易受到链上价格操纵的影响。攻击者通过在这些 Uniswap 池中留下少量BOO来人为地膨胀现货价格,导致聚合价格急剧上升。这使得攻击者能够 使用高估的BOO作为抵押品借入 PolterFinance 的大部分资产。 -
AIZPT314系统使用户能够用BNB兑换AIZPT代币,使用的公式为token0Out = token1In * token0InContract / (token1In + token1InContract)。与恒定乘积公式不同,上述公式允许通过往返交易大额资金来提取价值。攻击者通过 存入大量BNB以获取大部分合约的AIZPT代币,并通过交易小额AIZPT恢复其BNB,有效地从合约中排空资金。 -
WOOFI 去中心化交易所使用 不加权的现货定价机制,在流动性低和大额交易时容易被操纵。攻击者在 Arbitrum 上识别出一个低流动性市场,通过一系列的闪电贷和交易, 导致一个
WOO的价值下降至 0.00000009USDC。通常,WOOFI 会在极端价格波动时回退到 Chainlink 预言机,但是该预言机并未正确设置。由于这一疏漏,该系统 使用了被操纵的价格,攻击者随后排空了 所有的WOO代币。
取整错误
-
在 The Graph 系统中,质押代币收取 1% 的策展费用。然而,由于费用计算功能进行了向下取整,策展人可以通过反复质押只有 99 个代币来避免支付费用。利用 multicall 在像 Arbitrum 这样的低成本网络上,攻击的威力可以得到增强。
-
文章 Precision Loss Accumulation: The “Two Parser Bug” Lurking in the Shadows 探讨了如何小的、重复的精度损失可以累积,从而可能导致拒绝服务或破坏不变式。
第一个例子描述了一个奖励系统,其中每个奖励向上取整,而费用则向下取整,这导致了累积的过度分配,可能会阻止最后一位用户领取他们的奖励。
第二个例子讨论了一个包含公共和私人参与者的代币销售,在这种情况下,汇率计算中累积的精度损失允许参与者领取略高于销售上限的代币。
任意调用
-
在 Mantle 中,桥接
ETH或MNT会在源链上锁定代币,并在目标链上的CrossDomainMessenger合约中铸造代币。然后,CrossDomainMessenger在target上调用relayMessage,转发资金并执行用户定义的任意逻辑。如果调用失败,消息会被存储以备重放,资金在此之前将保留在合约中。由于对target地址验证不足,攻击者 可以构造并发送恶意跨链消息,这将触发approve调用。由于交易是以CrossDomainMessenger作为msg.sender执行的,因此它将授予攻击者对锁定资金的批准,以应对失败的消息。 -
Li.Fi Dex Aggregator 引入了一个新的钻石面,允许用户存入代币并指定交换的路线。然而,对目标和路线中的函数选择器验证不足允许了任意调用。一名恶意攻击者利用该漏洞,编码目标为代币地址,函数选择器为
transferFrom, 最终从所有之前批准了脆弱 Li.Fi 合约的用户那里抽走了代币 。 -
Spectra Finance 允许用户从多条路线中选择执行代币交易,其中一条是通过 KyberSwap。然而,
kyberRouter和targetData参数没有得到适当验证。与 Li.Fi 漏洞类似,这一漏洞允许攻击者编码asdCRV代币地址和transferFrom选择器, 从之前授予 Spectra Finance 合约批准的地址中抽走代币 。 -
类似地,Socket 的 Bungee Bridge 也被利用 了一个任意执行漏洞。由于对
swapExtraData参数缺乏验证,攻击者可以编码一个transferFrom并从WETH地址的桥中调用它,从而抽走所有未被撤回的批准。
错误的业务逻辑
-
uniBTC系统允许以 1:1 的汇率铸造uniBTC代币,抵押的为BTC或BTC 等值代币。为限制对非 BTC 等值代币的铸造,该系统使用了一个 限制机制,当如果保险库的持有量(通过totalSupply函数返回)超过设定上限时,存款将回滚(对非 BTC 等值代币设定为 0)。在非 BTC 原生链上,NATIVE_BTC地址 代表链的原生代币,可能不等于BTC。对于这样的代币,totalSupply函数错误地返回 0,因此几乎总是通过上限检查:(totalSupply == 0) <= (cap == 0)。这一疏忽 让攻击者能够以原生代币铸造uniBTC,而该代币在多个链上并不是 BTC 等值代币。 -
Zyfy 项目的
PermissionlessPaymaster合约允许通过启用用户设置一个经理地址为他们的交易提供 gas 费用赞助。在赞助交易后,剩余的 gas 被记入previousManager状态变量,该变量跟踪最后一笔交易的赞助商。该退款稍后可以由相应的经理提取。然而,selfRevokeSigner函数的一个漏洞使得用户在更新previousManager变量的同时撤回他们的经理。攻击者 可以利用这一点,通过回溯赞助交易,调用
selfRevokeSigner,将previousManager更新为自己的地址。由于上一笔交易的退款是通过这个变量处理的,攻击者可以窃取本应归合法赞助商的 gas 信用。
锁定资金
-
在 Renzo Finance 中,
ETH提现是通过transfer发送给索赔者的,而该操作只转发 2300 gas。如果索赔者是一个具有非平凡receive函数的智能合约,gas 限制将不足,导致交易回滚,从而有效地 将资金锁定在不可索赔的状态。 -
Bridged USDC Standard 是 Circle 推荐的将
USDC桥接到新区块链的方法,后续还有缩减流动性碎片化的选项,过渡到原生 USDC。DeFi Wonderland opUSDC 系统 将USDC桥接到以 OP stack 为基础的链,允许将合约所有权转移给 Circle,以实现原生USDC的过渡。在 L1 上锁定USDC后,L2 相关合约具有权限,并将USDC铸造给接收者。如果在 L2 上铸造因为 gas 不足或桥接USDC被暂停而暂时失败,交易可以在 L2 上重放。然而,如果期间发生了原生USDC的过渡,则铸造权限将转移给 Circle,导致 任何重放交易将无限期回滚,从而锁定 L1 资金而无法恢复。
签名可变性
-
TCH系统允许使用授权签名者的签名从 Uniswap 对交易代币进行销毁。然而,它存在两个显著漏洞:它使用ecrecover来恢复签名者,这容易受到可变性的影响,并且强制要求签名的唯一性而不是消息摘要。这种组合允许攻击者修改并重新提交过去的签名,使他们能够 销毁双倍的预定金额,从而提高他们的TCH代币的价值。 -
同样,Ironblocks Onchain Firewall 实施了一种自定义签名恢复过程,其中签名 被分割为
v、r和s,然后 输入到ecrecover。但是,它未能验证s值是否 在安全范围内,从而产生了签名可变性的潜在风险。为了完全缓解这一攻击向量,建议使用 OpenZeppelinECDSA库。
残留批准
-
Hedgey Finance 允许用户通过
createLockedCampaign函数 创建解锁活动,该活动将资金从活动创建者(msg.sender)转移并 批准ClaimLockup合约 根据活动条款管理这些资金。当活动被取消时,剩余的资金会返回给创建者,但对ClaimLockup合约的批准并没有被撤销。一名攻击者通过 创建恶意的ClaimLockup合约 利用这一缺陷。该合约设置了一个活动,取消它以收回所有存入资金,然后在残留的批准上调用transferFrom再次接收资金。 -
Superposition AMM 的
OwnershipNFTs.sol合约实现了自定义 ERC721 逻辑,该逻辑 在转移时未撤销代币批准。这一疏忽使得 NFT 拥有者可以将代币批准给自己,之后将其出售给受害者,然后 利用残留的批准回收 NFT 而无需征得同意。
错误的数组处理
-
在 Alchemix Finance 系统中,同时创建多个检查点将导致
pointHistory数组中出现重复时间戳的数据点。用于通过时间戳查找条目的二分搜索 未能正确处理重复项,在第一个匹配的时间戳处停止,并可能导致陈旧或不正确的结果。 -
在 Royco 系统中,创建了提供激励和奖励随意链上操作的激励代币的提议。与上述相似,该系统未能考虑并清理奖励数组中的重复代币。 攻击者可以利用这一点 ,通过创建带有重复奖励代币的提议,自己完成操作,并索要乘以提议中重复代币数量的奖励,从而抽走系统资金。
弱随机数生成器
-
RedKeysGame合约 允许用户进行代币投注,如果他们正确猜测_betResult,会获得奖励。然而,结果是 从可预测的链上值派生的,例如区块的timestamp、prevrandao、gaslimit、coinbase和number字段。这使得攻击者可以构造一个合约,计算和提交 获胜数字,从而确保稳定的 winnings。 -
同样,Boost AA Wallet 使用
block.prevrandao和block.timestamp来确定抽奖的赢家。一个控制区块生产的验证者可以通过有选择性地提议或保留区块来影响抽奖结果,直到block.prevrandao和block.timestamp的值对他们有利。
自我转移
-
SuperSushiSamurai 代币的自定义 ERC20 逻辑未能处理发送者(
from)和接收者(to)相同的转账。此外,它并没有增加接收者的余额,而是覆盖了它。这些缺陷使得攻击者可以 反复将代币转移给自己,每笔交易都将余额翻倍。关键漏洞在代码片段中高亮显示:uint256 toBalance = _postCheck(from, to, amountAfterTax) _balances[from] = fromBalanceBeforeTransfer - amount; _balances[to] = toBalance;有关具有相同漏洞模式的另一个示例,请参见 此分析 关于
GPU代币漏洞。 -
Hackathon 代币对 涉及某些 AMM 配对的代币转移 有自定义逻辑,但未考虑到配对同时是
sender与recipient的情况。这一疏忽通过 标准 AMMskim函数 被利用,该函数通过退出盈余使代币余额与储备匹配。通过向配对发送多余的代币,并 调用skim使池作为接收者,错误的逻辑将多余的代币余额翻倍。随后,攻击者用自己的地址作为接收者调用skim,收集多余的代币。
不安全的存储使用
-
Delta Prime 项目遭到黑客攻击,原因是其代理和实现合约之间的存储冲突,以及一个被遗忘的
init函数。该项目使用了一种称为DiamondBeaconProxy的模式,每个用户的Prime Account智能合约作为链上克隆被部署,从而通过在账户之间重用相同的合约逻辑来降低 gas 成本。账户作为存储,执行则delegatecalled到DiamondBeacon合约,然后根据功能选择器委托给不同的切面。该设置允许对 Diamond 切面进行更新,从而同时修改所有 Prime Accounts 的功能。每个 Prime Account 在部署时通过
initialize函数进行初始化,该函数设定账户的拥有者。类似地,钻石通过init函数进行初始化,设定钻石的拥有者。然而,这两个合约使用了相同的存储槽来存储拥有者地址,但为它们的initialized标志使用了不同的存储槽。此外,开发团队在部署后忘记删除钻石的init函数。一名恶意攻击者通过对其他用户的 Prime Accounts 调用init函数来利用这一点,导致delegatecall到钻石的init函数,而不是fallback。initialized标志检查通过了,因为它依赖于不同的存储槽,从而允许攻击者通过利用共享存储槽来修改 Prime Account 的拥有者。这使得攻击者能够控制多个 Prime Accounts 并使用管理权限提取资金。 -
在 Restake Finance 中,提款请求存储用户希望销毁的股份数量,以换取基础代币。一旦处理,协议就会发送代币,删除请求并销毁股份。然而,销毁操作从已经删除的请求中检索了股份数量 ,导致值为 0。因此,股份从未被销毁,并且仍然卡在合约中,稀释了股份与基础资产的转换。
有缺陷的奖励系统
-
Pythia Finance 的质押合约允许用户质押
Pythia代币以换取奖励。然而,奖励计算仅将用户余额乘以一个累积的每股份奖励变量 ,而没有考虑质押的持续时间。这个缺陷使得攻击者能够存入大量 Pythia 代币并反复索取过多的奖励 。 -
BGM代币通过将BGM索赔分配给发送者 、接收者和他们的推荐人来奖励转账,奖励来源于 BGM/USDT 的流动性池 。攻击者通过创建众多账户并进行人为转账来利用这个系统 ,以膨胀奖励。这些奖励随后被提取,耗尽了BGM的池,并显著提高了代币的价值。最后,攻击者借助膨胀的价格抽走了池中的USDT。 -
Elfi 协议允许用户质押代币,铸造
stakeToken(st) 作为存款的证明,同时也在内部记录质押的金额。奖励是基于st.balanceOf(account)而非内部记账计算的。这个失误,加上st的可转让性,导致了一个漏洞 :用户可以创建多个账户,质押代币,并在账户之间连续转移st。这将使他们能够反复从两个账户中索取奖励,有效地将收入翻倍。 -
Ramses Exchange 根据奖励期和
veNFT的tokenId的组合来奖励用户,使用tokenTotalSupplyByPeriod值。然而,它在奖励被索取时未能减少tokenTotalSupplyByPeriod,导致用户能够多次索取相同的奖励。此外,它未检查当前区块时间戳是否与指定的奖励期匹配,使得过去的奖励也能被索取。最后,为了绕过tokenId奖励索取的限制,攻击者利用系统的一个功能,允许铸造具有新tokenIds的veNFT。
交易时间攻击
-
EIP-2612 (Permit) 允许用户离线签署代币批准,由其他方在链上执行,无需持有原生代币用于 gas。该签名是一次性和不可变的,确保无论谁提交,都能得到一致的结果。然而,出现了一个拒绝服务和不完整执行的攻击向量 。在这种情况下,用户向智能合约提交一个交易 (T),该交易同时通过
permit()批准资金并执行额外操作 (A)。攻击者可以观察已提交的签名,用他们自己的交易来抢跑交易 T 仅执行permit()并成功。由于签名重放保护,这会导致交易 T 回滚,从而导致资金正确批准,但操作 A 从未执行。 -
Uniswap V4 Peripheral Contracts 在一个设计为
multicall的功能中使用 Permit2 来批准支出者。与上述情况类似,攻击者可以通过前置multicall并单独提交这些许可来利用这一点。这会导致合法的 multicall 交易失败,因为它试图消费一个已经被使用过的签名的许可。 -
SquidTokenSwap合约允许任何用户触发 PancakeSwap 在合约中持有的squidV1和squidV2代币之间的交易。由于这一点和设置的 5% 滑点容忍度使得攻击者能够反复触发和夹击这些交易 ,以滑点利润作为收益。 -
在 Opal Omnipool 中索取奖励时,合约使用
Balancer.batchSwap执行代币交换时未指定任何滑点保护。与上述类似,这个疏忽会允许攻击者通过夹击交换来窃取部分奖励 ,通过他的两次交换来夹击这些交换。 -
更新 Radiant 市场的 Pyth 价格预言机需要向合约的
receive函数发送ETH,然后调用updateUnderlyingPrices更新所需市场。由于这些步骤是独立的,攻击者可以利用这一点反向执行receive函数 ,使用提供的ETH更新与原始交易发送者意图不同的市场。
忘记在零知识协议中盲化多项式
-
在某些零知识 (ZK) 协议中,特别是使用 zk-SNARKs 的协议,证明者通过将秘密见证编码为多项式的一部分来证明对秘密见证的知识。为了验证正确性,证明者会在特定点上揭示多项式的评估值,这些点通常在协议的设置中确定。然而,如果没有保护,揭示足够的评估值可能会允许攻击者重构多项式,从而推测出秘密见证,破坏零知识特性。例如,度为
n的多项式(即n+1个系数)可以从n+1个评估中完全恢复。 -
为了对抗这一点,多项式盲化被采用。这涉及通过加入随机性来模糊多项式,比如添加随机系数以防止重构。例如,度为
n的多项式可能在评估于r个点时被盲化为度为n+r的多项式。这确保了从评估中无法推断出关于多项式的信息,从而保持见证的秘密,并维护协议的零知识保证。 -
Linea 的 PLONK Go Prover 的实现未对多项式片段施加适当的盲化,这可能允许攻击者统计性地恢复见证。
协议错误配置
-
VOW代币因在生产环境中使用了非原子测试交易而被利用。 详细信息 。更改操作参数、测试行为,然后撤销参数是在单独的交易中进行的。这使得攻击者能够在这些步骤之间插入他们自己的交易,从而利用合约,因为关键操作参数处于异常状态。 -
UWU 借贷协议将清算阈值设置为 90%,清算奖金设置为 10%,这过高且提供了很少的安全边际。这一设置使得协议即使在轻微的价格波动下也容易受到影响,可能导致坏账。 在攻击期间 ,攻击者通过仅
4%Manipulated oracle price,创建了一个贷款价值比(LTV)为93%的头寸。通过这样做,攻击者可以清算该头寸,获得利润计算为93% * 110%(清算者) - 100%(被清算) = 2%来自协议。
两个解析器错误
-
在 Taiko 中,跨链消息在目标链上受以太币速率限制配额的约束,这限制了在特定时间范围内转移的价值。如果一条消息超过了该时间段的剩余配额,它会被标记为
RETRIABLE并可以稍后重新执行。否则,该消息将进行验证检查,确保其未针对禁止的地址,然后再执行。在消息重试流程中发现了一种漏洞,该实现未在执行消息之前执行禁止地址检查。通过故意超过配额,恶意攻击者可以以
Bridge为msg.sender执行不绑定任何目标的跨链消息,可能会耗尽其他用户的资产。此漏洞突显了一个常见问题,即相同数据不应被处理,但在执行路径下却被不同地处理。此类模式在其他系统中也曾被利用,如 这份关键漏洞分析 中所述。
错误的虚拟机 gas 收费
- Fuel VM 的 CCP(代码复制)指令将代码从合约中复制到内存中,仅根据合约字节码的大小 而不是实际复制到内存中的字节数 收取 gas。此外,当复制的目标区域超过字节码大小时,该指令会将超过的区域填充为零。通过设置超过字节码长度的偏移量和复制长度,用户可以使函数通过零填充大内存区域而不产生适当的 gas 成本。这种操控允许以较低的成本执行代价高昂的内存清空操作,进而可能导致网络资源耗尽和拒绝服务攻击。
桥接状态不匹配
- Fuel Layer 1(L1)执行器将 包括被 L2 撤销的转发消息。这使得攻击者能够反复进行虚假代币提取。例如,攻击者在 L2 上触发两个代币提取;第一个最终撤销,但仍向 L1 发送消息,而第二个成功。只要 L1 上有足够的代币,攻击者就会获得其最初存入的双倍代币,有效允许攻击者从桥接中窃取资金。
LayerZero 拒绝服务
-
LayerZero V1 是一个跨链协议,允许在区块链之间发送、验证和执行消息。当在源链上发送消息时,离线的
Relayer和Oracle服务会收到通知,触发消息真实性的一致性。Oracle在目标链上提交默克尔根,Relayer提交证明以执行消息。研究文章系列的第一部分 描述了通过消息序列号重叠进行 DoS 攻击的向量,这可能会阻止任何跨链消息的交付。LayerZero 中的消息根据基于
srcChain、srcAddress和dstChain的唯一序列号进行排序,而不考虑dstAddress。然而,由于应用程序可以选择自己的Relayer和Oracle,它们可以伪造来自任何srcChain和srcAddress的消息,但无法发送到任何dstAddress。由于序列号未考虑dstAddress,攻击者可以发送带有相同序列号的伪造消息,阻止具有相同序列号的合法消息到达目标链。 -
LayerZero 的 OFTs(跨链可替代代币)和 ONFTs(跨链非可替代代币)通过在一个链上锁定代币并在另一个链上铸造,允许 ERC20 和 ERC721 代币的桥接。系列的第三部分 显示了一种攻击向量,当桥接 ERC721 代币时,攻击者可以通过在目标链上使用
onERC721Received回调来引发 DoS,消耗所有 gas,阻止消息送达并冻结跨链转账。
LayerZero 集成拒绝服务
-
系列的第二部分 涉及 Stargate,这是一种通过 LayerZero 进行跨链资产转移和交换的协议。在交换代币时,会发送一条消息和有效载荷,使资金接收方能够在收到后执行任意逻辑。如果消息传递失败,将通过
try-catch优雅处理,错误和有效载荷将被存储,系统将认为消息已交付,确保该过程不会因序列号排序而阻塞后续消息。通过编码包含非智能合约的接收者地址的有效载荷,恶意攻击者可以强迫
try-catch子句在不进入catch块的情况下撤销。这将保留即将到来的序列号,同时又无法递送消息,导致消息通道被冻结。阻止消息通道的第二种方法是在内存不足的情况下使其撤销。因为
catch子句会将有效载荷存储在合约存储中,恶意攻击者可以发送一个如此大的有效载荷,以至于在存储时耗尽所有剩余 gas。 -
类似于在桥接层面撤销的 Stargate 漏洞,以下线程 揭示了 Drips Network 中的拒绝服务可能性:如果消息在目标链的桥接层面无限制地撤销,通信通道将被阻止,并且理想情况下应该有手段从这种状态恢复。不幸的是,没有恢复机制;要在目标链上升级实现,必须成功的跨链消息,但由于上述原因这并不可行。
故障升级
-
为了批准提款,Ronin 桥需要来自总权重大于特定阈值的验证者集的签名。然而,在从版本 2 升级到版本 4 的过程中,开发团队忽略了调用版本 3 的初始化函数。这个疏漏导致阈值未初始化,默认为 0,使任何人都可以在不提交有效签名的情况下发起提款。结果,桥遭受了 1200 万美元的漏洞攻击。
-
Pike Finance 的协议升级引入了存储错位,错误地将
initialized变量移动到了不同的槽。这使得攻击者能够重新初始化代理 ,将其升级到恶意实现并最终盗取资金。
未设置的初始化标志
-
Wrapped XETA 合约未能在其
initialize函数 中设置initialized标志,导致攻击者可以重新初始化合约。攻击者授予自己代币铸造权限,利用这些权限抽走了USDT/WXETA流动性池的资金。 -
类似地,
NFGSToken合约在部署时未能 设置uniswapV2Dele标志,该标志本应锁定特权的_uniswapV2Proxy地址,防止进一步修改。这个疏漏允许攻击者将_uniswapV2Proxy指定为他们自己的地址,从而获得对受限操作的访问权限 ,例如铸造无限数量的NFGS代币。在初始化时将关键状态变量声明为immutable,而不是依赖额外的锁定逻辑,将提供更强的保护,防止未经授权的更改。
提议者响应验证不足
- Titan Relay 的 Helix 实现 缺乏对 KZG 承诺的完整性验证。这个疏漏允许恶意提议者使用有效标头但无效 KZG 承诺签署一个盲块,接收解盲块,然后提出一个重新排序的有利块。修复涉及确保提议者提供的 KZG 承诺与构建者的匹配 。
其他
-
Munchables 智能合约安全地跟踪用户存款,限制每个用户的提款为其记录的余额。然而,代理管理员 之前在代理后设置了一个未经过验证的恶意实现,将自己分配了
1e24 ETH的余额。这个膨胀的余额在存储中持续存在,允许管理员通过经过验证的安全实现后续抽走资金。 -
研究论文 《揭秘和检测以太坊智能合约中的密码缺陷》 提供了对智能合约中密码学问题的分析。作者列出了九个不同类别的缺陷,可以作为安全研究人员的检查清单:
-
单一和跨合约签名重放
-
签名前运行和可变性
-
签名验证不足
-
默克尔证明重放和前运行
-
动态长度参数的哈希碰撞
-
来自哈希链属性的弱随机性
-
结论
与往年相似,2024 年在有价值的研究、创新的漏洞和创造性的攻击方面都很丰富。重要的是要强调,本文的意图不是批评或指责受影响的项目,而是提供客观的概述,以便作为社区学习的教育材料,从中吸取教训并更好地保护未来的项目。
我是 AI 翻译官,为大家转译优秀英文文章,如有翻译不通的地方,在这里修改,还请包涵~
版权声明
本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。
区块链技术网


发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。