1月18日,我们的监控系统检测到针对AnySwap项目(也称为Multichain)的攻击。该漏洞源于anySwapOutUnderlyingWithPermit()函数存在缺陷,其验证机制可以被绕过以提取已授权的代币。
尽管项目已经采取了不同的措施(例如,向用户发送交易,如图1所示)来通知受影响的用户,但仍有部分用户未能及时采取行动(即撤销授权)来保护其资金。因此,攻击者可以持续发动攻击以获取受害者的资金。

为了保护潜在的受害者,经过深思熟虑,我们的团队决定对以太坊上的AnySwap易受攻击的合约(0x6b7a87899490EcE95443e979cA9485CBE7E71522)执行紧急救援。具体来说,我们可以将易受攻击账户的资金转移到我们的白帽钱包,这是一个位于以太坊上的多重签名钱包(0xd186540FbCc460f6a3A9e705DC6d2406cBcc1C47)。
为了使我们的白帽救援透明化,我们将我们的意图记录在一个PDF文件中,并与社区分享文件哈希。这可以区分我们的紧急救援和攻击,同时不泄露细节(因为漏洞仍然可以被利用)。我们从2022年1月21日开始,到2022年3月11日结束了紧急救援,并发布了相应的公开消息,如下所示:


紧急救援并非易事。事实上,要成功进行救援,存在着技术和非技术方面的挑战。随着紧急救援的结束,我们可以全面回顾整个过程,并分享我们学到的经验教训。我们相信这次经历可以为DeFi生态系统的安全提供一些启示。
主要收获(TL;DR)
- 不同参与者之间存在竞争,包括白帽和攻击者。Flashbots的支付费用随着时间的推移迅速增加。
- Flashbots并非总是奏效。相反,一些攻击者通过采用一些复杂的策略,转向使用普通内存池来成功发动攻击。
- 一些攻击者通过归还部分被盗资金而被“洗白”,其余部分作为赏金。这种现象虽然不是第一次出现,但在社区中颇具争议,因为这种激励机制可能对真正的白帽不公平。
- 为了赢得社区的信任,白帽最好提前公开其行动,而不泄露任何详细的敏感信息。
- 社区可以共同努力,更有效、更高效地进行救援。例如,建立协调机制以减少/避免白帽之间的竞争。
接下来,我们将首先介绍此期间救援的整体情况。然后,我们将阐述我们的救援方式以及需要解决的挑战。之后,我们将讨论从救援中吸取的一些教训。最后,我们将提供一些可能对保障生态系统安全有意义的想法/建议。
0x1 救援与攻击
0x1.1 总体结果
本报告中观察和调查的攻击和救援活动持续了数月,从区块14028474(2022年1月18日)到区块14421215(2022年3月20日)。
执行救援和攻击的账户总结在下表中。为简洁起见,仅给出地址的前四位以代表一个EOA。 一个账户要么是救援账户,要么是攻击账户。值得注意的是,账户的类型是根据Etherscan.io的标签或我们观察到的转移目的地地址来确定的。
总共有9个救援账户,共计救援了483.027693 ETH(支付费用295.970554 ETH,占总金额的61.27%),以及21个攻击账户,共计获利1433.092224 ETH(支付费用148.903707 ETH,占总金额的10.39%)。值得注意的是,损失是粗略估计,因为存在一些复杂的交互。例如,一个攻击账户在与AnySwap协商后可能转变为救援账户,这一点我们将在后面讨论。表格的最后一列显示了支付给矿工的费用,这些费用用于赢得Flashbots的竞争。
| 编号 | 账户 | 类型 | 受害者数量 | 损失金额(ETH) | 费用金额(ETH) |
|---|---|---|---|---|---|
| 1 | 0x14ca** | 救援账户 | 50 | 432.958062 | 287.849654 |
| 2 | 0x9a65** | 救援账户 | 23 | 22.569429 | 0.000000 |
| 3 | 0x9117** | 救援账户 | 14 | 18.897622 | 7.213585 |
| 4 | 0x17d2** | 救援账户 | 3 | 3.552833 | 0.000000 |
| 5 | 0x6360** | 救援账户 | 21 | 3.540061 | 0.907168 |
| 6 | 0x0edd** | 救援账户 | 7 | 1.498706 | 0.000000 |
| 7 | 0x281e** | 救援账户 | 1 | 0.006000 | 0.000000 |
| 8 | 0xd83b** | 救援账户 | 1 | 0.004000 | 0.000000 |
| 9 | 0x8af3** | 救援账户 | 6 | 0.000980 | 0.000147 |
| 10 | 0x4986** | 攻击账户 | 332 | 456.004547 | 0.000000 |
| 11 | 0xfa27** | 攻击账户 | 42 | 433.438935 | 46.636389 |
| 12 | 0x48e9** | 攻击账户 | 66 | 312.014657 | 0.000000 |
| 13 | 0x5738** | 攻击账户 | 67 | 83.589240 | 62.587238 |
| 14 | 0x34b2** | 攻击账户 | 7 | 63.599821 | 20.642705 |
| 15 | 0xd374** | 攻击账户 | 86 | 45.452703 | 12.824763 |
| 16 | 0x1fe7** | 攻击账户 | 9 | 12.817241 | 0.000000 |
| 17 | 0x98f5** | 攻击账户 | 20 | 8.381273 | 0.000000 |
| 18 | 0x455d** | 攻击账户 | 11 | 5.047377 | 0.544263 |
| 19 | 0x1b45** | 攻击账户 | 6 | 4.942442 | 3.074813 |
| 20 | 0x3ec7** | 攻击账户 | 6 | 3.705686 | 0.741137 |
| 21 | 0xbca4** | 攻击账户 | 1 | 2.784250 | 1.392125 |
| 22 | 0xb0ab** | 攻击账户 | 18 | 0.834068 | 0.296000 |
| 23 | 0x0a5b** | 攻击账户 | 1 | 0.286750 | 0.143375 |
| 24 | 0x2d3a** | 攻击账户 | 2 | 0.080090 | 0.000000 |
| 25 | 0x835d** | 攻击账户 | 5 | 0.063945 | 0.000000 |
| 26 | 0x1dbd** | 攻击账户 | 1 | 0.027431 | 0.012893 |
| 27 | 0x813d** | 攻击账户 | 1 | 0.019528 | 0.008007 |
| 28 | 0x85dd** | 攻击账户 | 6 | 0.002240 | 0.000000 |
| 29 | 0x2394** | 攻击账户 | 1 | 0.000000 | 0.000000 |
| 30 | 0x6360** | 攻击账户 | 2 | 0.000000 | 0.000000 |
0x1.2 竞标Flashbots的费用趋势
如前所述,白帽需要与攻击者竞争以发送交易。因此,支付给矿工的费用百分比(在Flashbots交易中)可能反映了竞争的程度。为了量化这一点,我们调查了每个区块的费用百分比(分别包括攻击交易和救援交易)。
图4显示了我们迄今为止观察到的趋势(从区块14028474到区块14369199)。 最初的几次攻击交易均不涉及任何费用,这意味着在此期间的竞争很少(甚至没有)。这是合理的,因为这些早期攻击可能不为外界所知。
事实上,第一次涉及费用的攻击(10%)发生在区块14029765。 从那时起,随着更多参与者的加入,费用百分比迅速增加。 例如,在区块14072385,百分比达到80%,并在区块14129449很快达到91%。
简而言之,这种趋势表明,通过增加支付给矿工的费用来进行竞标,无疑是一场军备竞赛。

0x2 我们的救援与挑战
0x2.1 救援方式
进行救援的基本思路非常直接。具体来说,我们需要监控已授权WETH给易受攻击合约的账户。当有任何WETH转移到账户时,我们可以通过利用易受攻击的AnySwap合约将其直接转移到我们的多重签名钱包。关键要求是:
- R1:有效定位向受害者账户转入代币的交易。我们将其称为转账交易。
- R2:正确地构建执行救援的交易。我们将其称为救援交易。
- R3:成功地抢跑攻击者(及任何其他第三方)发送的交易。我们将其称为攻击交易。
R1和R2对我们来说都不是障碍。具体来说,我们已经构建了一个内部系统来监控内存池,这使我们能够及时定位转账交易。同时,我们还开发了一个工具来自动构建交易。
然而,R3仍然是一个挑战。人们可能期望Flashbots能用于赢得竞争。然而,要达到目标并非易事。首先,攻击者也可能使用Flashbots。作为一个费用竞价系统,成功率可能取决于支付给矿工的费用。需要确定设置费用的策略。其次,由于竞争激烈,使用Flashbots可能不是一个好选择。因此,我们也通过普通交易使用内存池。为确保成功,需要考虑将交易放置在正确位置的策略。最后,我们还与其他白帽竞争,他们的行为在某些情况下似乎是可疑的。
0x2.2 我们参与的竞争
总的来说,我们尝试保护了171位潜在受害者,其中10位在我们将要执行救援之前通过撤销授权自行保护了自己。对于剩余的161位有效受害者,由于竞争,我们只成功救援了14位。失败案例总结如下表,涉及3个救援账户和16个攻击账户。
| 编号 | 账户 | 类型 | 受害者数量 | 损失金额(ETH) | 费用金额(ETH) | 平均费用百分比 |
|---|---|---|---|---|---|---|
| 1 | 0x14ca** | 救援账户 | 44 | 431.651020 | 286.891724 | 66.46% |
| 2 | 0x9a65** | 救援账户 | 7 | 11.321441 | 0.000000 | 0.00% |
| 3 | 0x6360** | 救援账户 | 3 | 3.300000 | 0.891000 | 27.00% |
| 4 | 0x48e9** | 攻击账户 | 35 | 301.681589 | 0.000000 | 0.00% |
| 5 | 0x5738** | 攻击账户 | 58 | 78.482472 | 58.851862 | 74.99% |
| 6 | 0x34b2** | 攻击账户 | 2 | 53.591712 | 17.685265 | 33.00% |
| 7 | 0xd374** | 攻击账户 | 6 | 23.658698 | 10.073638 | 42.58% |
| 8 | 0x4986** | 攻击账户 | 16 | 22.900105 | 0.000000 | 0.00% |
| 9 | 0x1fe7** | 攻击账户 | 6 | 12.057241 | 0.000000 | 0.00% |
| 10 | 0x1b45** | 攻击账户 | 5 | 4.402442 | 3.010013 | 68.37% |
| 11 | 0xbca4** | 攻击账户 | 1 | 2.784250 | 1.392125 | 50.00% |
| 12 | 0x98f5** | 攻击账户 | 8 | 2.339543 | 0.000000 | 0.00% |
| 13 | 0x455d** | 攻击账户 | 3 | 0.741817 | 0.175454 | 23.65% |
| 14 | 0xfa27** | 攻击账户 | 3 | 0.320288 | 0.032590 | 10.18% |
| 15 | 0x0a5b** | 攻击账户 | 1 | 0.286750 | 0.143375 | 50.00% |
| 16 | 0x3ec7** | 攻击账户 | 1 | 0.245000 | 0.049000 | 20.00% |
| 17 | 0xee7e** | 攻击账户 | 1 | 0.190000 | 0.096900 | 51.00% |
| 18 | 0x835d** | 攻击账户 | 3 | 0.024533 | 0.000000 | 0.00% |
| 19 | 0xb0ab** | 攻击账户 | 1 | 0.000618 | 0.000000 | 0.00% |
因此,确实有一些经验教训值得与社区分享。
0x3 我们学到的一些经验教训
0x3.1 如何确定支付给Flashbots矿工的费用?
总而言之,我们被12名竞争对手击败,其中包括2名救援账户和10名使用Flashbots的攻击账户。
我们设置矿工费用的策略相当保守。具体来说,我们的目标是以尽可能少的费用来保护受害者。因此,除非有成功的攻击交易已经设定了费用,否则我们不会主动使用/提高费用。例如,如果一个攻击者将费用设定为基金的10%,那么我们可能会使用11%来进行下一次救援,以与该攻击者竞争。然而,结果表明,该策略未能奏效,因为攻击者(甚至一些白帽)经常(如果不是总是)积极提高费用以击败他人,如下所示:
- 图5显示,攻击者0x5738**在区块14071986将费用百分比设定为70%。
- 图6显示,白帽0x14ca**在区块14072255将费用百分比设定为79%。
- 图7显示,白帽0x14ca**在区块14072385将费用百分比设定为80%。
- 图8显示,白帽0x9117**在区块14072417将费用百分比设定为81%。
- 图9显示,攻击者0x5738**在区块14073395将费用百分比设定为86%。





简而言之,这似乎是一个零和博弈,需要模拟不同参与者的行为才能获胜。
然而,在实践中,很难找到更好/最优策略,同时尽可能降低成本。
0x3.2 如何在内存池中找到正确的位置?
现在看来,救援似乎依赖于费用竞标Flashbots的军备竞赛。 然而,我们发现,由于与其他与救援和攻击无关的参与者之间的激烈竞争,使用Flashbots并非万能药。在这种情况下,即使是攻击交易设定的最高费用也不能保证赢得使用Flashbots的机会。
另一种选择是,一笔普通交易在内存池中占据正确的位置也有机会实现目标。正确的位置意味着救援/攻击交易应放置在转账交易之后,并且位置应非常接近转账交易(越近越好)。请注意,使用此策略,攻击者0x48e9**获得了312.014657 ETH,而无需向Flashbots矿工支付任何费用。
以下四张图展示了攻击者获得的最大的两个利润:
- 图10显示,一个受害者在区块14051020的第65个位置存入了50 ETH,图11显示,攻击者在同一区块的第66个位置获得了这50 ETH。
- 图12显示,一个受害者在区块14052155的第161个位置存入了200 ETH,图13显示,攻击者在同一区块的第164个位置获得了这200 ETH。




显然,这种复杂的策略非常有用且富有启发性,值得我们付出更多关注和努力去学习。
0x4 其他一些想法
0x4.1 白帽黑客还是攻击?
在识别白帽黑客时,它们可能不像人们预期的那样直接。
例如,地址0xfa27被Etherscan.io标记为Multichain Exploiter 4 (Whitehat)。实际上,最初它被标记为Multichain Exploiter 4。在攻击者和AnySwap项目进行了几轮谈判后,攻击者被说服归还了部分被盗资金。
- 在交易0x3c3d**中,AnySwap联系了攻击者:
首先,感谢您获得了weth。我当时并不知道这次攻击,直到weth从未到达我的钱包时才意识到情况。考虑到涉及的金额,您是否愿意接受50 ETH作为合理的报酬? 这是我的交易: 0x2db9a6a51604e2be8b2c3469773afb201f0b48a318fb7e5f5e49175e818df5ba 0xe50ed602bd916fc304d53c4fed236698b71691a95774ff0aeeb74b699c6227f7
- 在交易0xd360**中,攻击者回复道:
请通过发送一笔交易来验证。我将把剩余的258 ETH还给您。39 ETH已支付给矿工,因为有其他攻击者,所以我不得不支付这笔费用来保住资金。
- 在交易0x354f**中,AnySwap在收到资金后表示感谢:
已收到,感谢您的诚实。
显然,这位攻击者被“洗白”了,并且从攻击中还获得了一些利润。 类似的情况过去已经发生过几次,并且在社区中仍然存在争议,因为这种激励机制可能不公平。
0x4.2 白帽黑客之间的竞争?
有必要建立一个协调机制来减少/避免白帽之间的竞争。这种竞争不可避免地导致救援力量的浪费。 在此次救援中,有54位受害者(价值4.5亿ETH的资金)由另外三位白帽保护,而我们也试图进行救援。
白帽之间的竞争不仅浪费了救援力量,还增加了执行救援的成本。例如,如图7和图8所示,两笔不同白帽的救援交易花费的费用分别为80%和81%。
不幸的是,除非存在协调机制,否则白帽不会退让。否则,就不可能消除竞争。
0x4.3 如何进行更好的救援
一方面,为了赢得社区的信任,白帽最好提前公开其行动,而不泄露任何详细的敏感信息。 由于救援通常是一场反复的拉锯战,与一次性阻止特定攻击不同,有足够的时间进行公开。当然,关于漏洞的详细信息不应被泄露。
为了实现这一点,详细信息不会在第一时间披露,而是在完成救援后向社区披露,就像我们为AnySwap救援所做的一样。但是,包含白帽意图的文档的哈希可以与社区共享。
另一方面,社区可以做更多的事情来更有效、更高效地执行救援,包括但不限于:
- Flashbots/矿工可以为认证的白帽提供绿色通道。绿色通道可以提供高优先级来抢跑攻击者的交易,并避免白帽之间的竞争。
- 被攻击的项目应承担Flashbots/矿工的费用。
- 项目方可以为用户应用便捷快速的通知机制。
- 项目方可以在代码中应用紧急机制。
关于BlockSec
BlockSec是一家开创性的区块链安全公司,成立于2021年,由一群全球知名的安全专家组成。公司致力于提升新兴Web3世界的安全性和可用性,以促进其大规模采用。为此,BlockSec提供智能合约和EVM链安全审计服务,用于主动安全开发和威胁拦截的Phalcon平台,用于资金追踪和调查的MetaSleuth平台,以及用于Web3开发者高效浏览加密世界的MetaSuites扩展。
迄今为止,公司已服务超过300家知名客户,如MetaMask、Uniswap Foundation、Compound、Forta和PancakeSwap,并在两轮融资中获得了来自Matrix Partners、Vitalbridge Capital和Fenbushi Capital等知名投资者的数千万美元投资。
官方网站:https://blocksec.com/ 官方Twitter账号:https://twitter.com/BlockSecTeam



