昨天早上想登录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。不得不说,随着以太坊的不断完善,很多交互体验确实流畅了很多,因此我的整体被盗体验非常丝滑,就是我心在滴血
弄清楚机制后,再结合反汇编之后的代码,一个盗币的合约差不多如下:
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插件,体验了一下,确实不错。如果有这个插件的话,可能就不会中招了!
最后,希望社区的小伙伴里都不再遇到类似事件 ![]()



