我们监测到 Bybit Cold Wallet 发起⼀笔⼤额转账,转出 401,346 ETH 价值约 1.5 BillionUSD 。经过多⽅确认,确定这是⼀起针对 Bybit 的攻击。
<!--StartFragment-->
2025年2⽉21⽇晚,我们监测到⼀笔涉及 Bybit 交易所的重⼤安全事件。当晚 02:16 UTC ,我们监测到 Bybit Cold Wallet 发起⼀笔⼤额转账:
https\://etherscan.io/tx/0xb61413c495fdad6114a7aa863a00b2e3c28945979a10885b12b30316ea9f072c ,
转出 401,346 ETH 价值约 1.5 BillionUSD 。经过多⽅确认,确定这是⼀起针对 Bybit 的攻击。
<!--EndFragment-->
<!--StartFragment-->
经过多⽅的分析和调查,整个事件的⼤致流程已经比较清晰。攻击者⾸先利⽤钓⻥的⼿段,攻击了 Safe{Wallet} 和核⼼开发者,窃取到了 AWS 的 CloudFront 或者 S3 的 Access Key 。随后,利⽤ Access Key 向 Safe{Wallet} 的前端注入恶意代码。恰好,受影响时, Safe{Walllet} 被 archive.org 的 Wayback Machine 收录。具 Wayback Machine 显⽰, Safe{Wallet} 的前端代码在 2025年2⽉19⽇17:28:33 已经被注入恶意代码。
<!--EndFragment-->
<!--StartFragment-->
恶意代码出现在前端 js ⽂件 _app-52c9031bfa03da47.js 中,
<!--EndFragment-->
<!--StartFragment-->
我们讲其中恶意代码的核⼼逻辑取出,如下
<!--EndFragment-->
<!--StartFragment-->
整个逻辑比较简单,当 sd.getAddress() 即Safe钱包的地址在["0x1db92e2eebc8e0c075a02bea49a2935bcd2dfcf4","0x19c6876e978d9f128147439ac4cd9ea2582cd141"] 中,
则将 to 地址设置为: 0x96221423681a6d52e184d440a8efcebb105c7242 ,
data设置为: 0xa9059cbb000000000000000000000000bdd077f651ebe7f7b3ce16fe5f2b025be29695160000000000000000000000000000000000000000000000000000000000000000 ,然后调⽤ executeTransaction 签名交易。
<!--EndFragment-->
<!--StartFragment-->
其中,恶意的 js 代码中: 0x1db92e2eebc8e0c075a02bea49a2935bcd2dfcf4 ,
这个钱包地址就是 Bybit 失窃的多签冷钱包地址。也就是说,攻击者的恶意代码是针对 Bybit 的冷钱包进⾏定向攻击,这样做的好处是尽量减少被发现的⼏率。防⽌其他⽤户注意到。其中 to 地址也就是 Safe 钱包即将调⽤的合约地址为 0x96221423681a6d52e184d440a8efcebb105c7242 ,这个智能合约未开源,经过反编译发现合约功能很简单,仅仅实现了⼀个 transfer 函数,函数的功能是将合约的 slot0 改为 recipient 地址。
<!--EndFragment-->
<!--StartFragment-->
se.data.data 就是调⽤上述合约的 calldata ,其中前四个字节为 0xa9059cbb ,就是 transfer(address recipient, uint256value) 的签名。后续的参数分别为 recipient 为: 0xbdd077f651ebe7f7b3ce16fe5f2b025be2969516 ,value 为 0 。因为, Bybit 钱包使⽤的 Safe{Wallet} 版本为 1.1.1 ,改版本的钱包执⾏合约调⽤的逻辑为 MultiSig Address-[Signature]->Wallet MasterCopy Proxy-[Delegatecall]->Safe Wallet Logic Contract-[Delegatecall | calldata]->TargetContract Address ,所以实际上修改的是 Wallet MasterCopy Proxy 的 slot0 ,也就是正式接管了该钱包。
恶意代码中,其中的另⼀个地址: 0x19c6876e978d9f128147439ac4cd9ea2582cd141,是攻击者⽤来测试攻击流程的测试地址。也就是说,改恶意代码会劫持两个钱包,钱包1为Bybit的 Cold Wallet ,钱包2为攻击者的测试钱包。由于 Bybit Cold Wallet 的代码版本为 1.1.1 ,所以攻击者同样创建了⼀个 1.1.1 版本的Safe钱包,创建的交易为:https\://etherscan.io/tx/0x8df9884dd022f900ea7ebcbd0d47356137e3dcc8032e36d8706bed86f158f7c8 ,由于Safe的App已经⽆法创建老版本的钱包,攻击者是如何创建的呢?
我们看到,攻击者是调⽤ Proxy Factory 1.1.1 的 createProxyWithNonce 来创建的在 Ethereum mainnet 上测试钱包
<!--EndFragment-->
<!--StartFragment-->
于是,我们在 Sepolia 测试链上同样利⽤ createProxyWithNonce 创建了⼀个 Safe 钱包, version 为 1.1.1 。我们的创建交易为https\://sepolia.etherscan.io/tx/0xa7ce1ce4cb5ccb65eb9a35842ebde8f9a161dd561bce80528dbb7362efc33ff0 ,创建的钱包地址为:0xfdfd440e9d920a8a577eb3d586547986b889ad56。
但是,我们创建的钱包地址却⽆法导入到Safe的app中。
<!--EndFragment-->
<!--StartFragment-->
我们可以看到Safe创建Multisig wallet的代码如下:
<!--EndFragment-->
<!--StartFragment-->
由于 Safe 创建 Wallet 使⽤create2来创建 proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData),salt) ,所以只需要保证 MasterCopy , initializer , saltNonce ⼀直,就可以创建相同地址的钱包。于是,我们利⽤和攻击者同样的参数,在 Sepolia 测试链上创建了与攻击者在 Ethereum mainnet 上相同的地址0x19c6876e978d9f128147439ac4cd9ea2582cd141 ,创建交易为https\://sepolia.etherscan.io/tx/0x3ec6ef599d1df75c9f255f4c1a0edaed8ef992a4fd6ea90af50a05ca962c9e69 ,但是该地址仍⽆法导入 Safe 的 app 中。
<!--EndFragment-->
<!--StartFragment-->
⽽攻击者在 Ethereum mainnet 上创建的地址即可成功导入到 Safe app 中,
<!--EndFragment-->
<!--StartFragment-->
随后,我们在官⽅的⽂档中发现,Safe钱包在L1上使⽤trace-base来索引创建好的钱包,https\://github.com/safe-global/safedocs/blob/f487ace5e0221437277bd9d56fb340794dda424c/pages/advanced/cli-guides/recovery-safe-deployment.mdx#L42
<!--EndFragment-->
<!--StartFragment-->
理论上,所有通过Factory创建的钱包都可以使⽤Safe app,因为攻击者篡改了Safe app的前端代码,导致只有使⽤Safe app才会收到攻击。但是,我们通过Factory 1.1.1创建的Safe钱包始终⽆法被索引,所以也⽆法测试。我们也不知道为何攻击者创建的钱包可以被Safe索引。
<!--EndFragment-->
<!--StartFragment-->
由于Bybit使⽤的Safe{Wallet}版本为1.1.1比较老,⽬前最新的版本1.4.0对于本次攻击的攻击⼿法(利⽤修改slot0来接管钱包)已经失效了
<!--EndFragment-->
<!--StartFragment-->
我们可以看到,此处已经由 delegatecall 变为了 call ,也就意味着只能修改被调⽤合约本⾝的 slot ,⽆法修改 proxy 的slot 。但是,后续还需防范类似的攻击⼿段,攻击者可能直接构建转移资产的交易,因此,我们建议在签名交易前,先使⽤ Safe 内置的Tenderly 对交易进⾏模拟后,确认⽆误再进⾏签名。
<!--EndFragment-->
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!