分享下我被盗25000的惨痛经历,供大家学习!

昨天早上想登录Ondo看看这个股票代币化的平台到底长啥样,顺便体验下功能,于是google搜索了ondo,名列第一的是这个钓鱼网站(后面观察了一下,当天应该是进行了集中的Google SEO,除了ondo, uniswap, sushiswap都有类似的盗币网站):

当时也没太注意区分,直接点进去了,然后就操作连接钱包,想看看功能。连接钱包后,metamask直接被唤起,由于网络原因,metamask经常有点卡,因此连续弹起时没太注意。而且没有意识到风险,下意识以为和平常的DAPP连接时要个签名是一样的,于是被其中一个盗币交易捡漏,签名执行了(老实说,平时也还算谨慎,不知道昨天的状态怎么回事,太难了)。被盗的交易哈希为:

后续回溯交易,进行反思时发现他一笔交易转走了我的OP、USDC两个币。而且没有Approve操作,感到非常奇怪。我印象中,转币前总是需要先进行Approve的,于是去请教了下Kahn老师,得到了Permit这个机制的线索。后面对这个机制进行了下学习,相关的原理可以看:什么是Permit2,我就不再这里赘述了。大致的效果是在EIP2612后,支持了一种新的玩法,可以通过传递一个permit签名,来将approve操作和转账交易放在一块儿执行,就不再需要先进行一次Approve操作了。

这里黑客就是利用了这个机制,调用了Uniswap V3部署在OP Mainnet上的Permit2合约, 完成了盗币行为。大致流程如图:


因为OP和USDC都是支持Permit机制的,之前在Uniswap进行过交易,应该是已经操作过了OP和USDC的Approve授权给了Uniswap Permit2合约。另一方面,Permit还支持批量操作,因此黑客在第一笔交易就直接转走了我所有的OP和USDC。不得不说,随着以太坊的不断完善,很多交互体验确实流畅了很多,因此我的整体被盗体验非常丝滑,就是我心在滴血 :smiling_face_with_tear:

弄清楚机制后,再结合反汇编之后的代码,一个盗币的合约差不多如下:

interface IPermit2 {
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    struct TokenPermissions {
        address token;
        uint256 amount;
    }

    struct PermitTransferFrom {
        TokenPermissions permitted;
        uint256 nonce;
        uint256 deadline;
    }

    struct PermitBatchTransferFrom {
        TokenPermissions[] permitted;
        uint256 nonce;
        uint256 deadline;
    }

    struct SignatureTransferDetails {
        address to;
        uint256 requestedAmount;
    }
}

    function vanilla(
        IPermit2.PermitBatchTransferFrom calldata permit,
        IPermit2.SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external {
        PERMIT2.permitTransferFrom(
            permit,
            transferDetails,
            owner,
            signature
        );
        
        emit EducationalHackCompleted(owner, permit.permitted.length, 0);
    }

在已经授权Uniswap Permit2合约的情况下,只需要调用vanilla()一次,即可完成OP和USDC代币的转移。

有了这个教训后,也是对自己的一些链上操作起了一个警醒。以前只严格区分了开发账号和自用账号,后续还是要对账号进行分层,得分为资金账号和交互账号,大的金额都放到资金账户去,平时就用交互账号。这样万一哪天又马大哈了,还能减少点损失。

顺便一提的是,后面kahn给我推荐了GoPlus插件,体验了一下,确实不错。如果有这个插件的话,可能就不会中招了!

最后,希望社区的小伙伴里都不再遇到类似事件 :sob:

3 Likes

后续我还研究了一下那些检测钓鱼网站的插件,结果没有一个可以成功检测,也不知道该说这个网站高明还是这些插件太菜了…

  1. SafeToOpen: 检测失败
  2. WOT: 一直阻塞进行评分,但是始终无法给出结果。
  3. ExVul: 拦截失败
2 Likes

很痛惜。。。。

我发现现在 Google 搜索已经没用了

我分享一下我遇见新网页的方法,

Defiliama 上找原始网页,或者推特上找官方网页进入,然后我会检查一下域名,如果不是常用域名以及拼写有错的,我也会马上关闭

https://defillama.com/

4 Likes

学到了,好思路!

太惨了,我现在 pc 端使用钱包的频率已经很低了,主要通过 rabby 钱包来交互,如果不在白名单内的应用会多次交叉验证

交叉验证确实是个好习惯 !

良心 Cooper

同情,我也被空投邮件钓鱼过一次,平时都很小心,那次就疏忽了。

1 Like