2022 年7 月 10 一个热的NFT项目 TheSaudis 开启了 freemint 活动(白名单用户可以免费铸造 NFT)。而就在 mint 活动结束后,一个名字 RIGHTBLOCK 用户在市场上大量出售 NFT,项目方发现后,迅速锁定用户并更改合同,以便在用户手中获得大量用户 NFT 他们承诺将这些转移回来 NFT 回馈社区用户。
那么为什么项目方可以将该用户手里的 NFT 转移呢?经过我们的分析,我们发现该怎么办? NFT 采用了项目合同 EIP-2535 该协议也称为钻石协议,项目方利用该协议重写合同的功能来实现这些功能 NFT 转移。接下来,慢雾安全团队将介绍钻石协议(EIP-2535)的细节。
智能合约突破 24kb 最大限大限制,合同更新功能更方便。
要理解钻石协议,首先需要知道几个相关的概念定义:
-
钻石可以理解为代理合同(Proxy),也是与用户互动的主合同
-
就像真正的钻石有不同的侧面一样,钻石合同也有不同的表面。钻石合同的每个功能需要调用的合同对应于一个截面,因此也可以理解为实现合同 (Implementation)
-
钻石协议标准扩展了一种叫做钻石切割的功能,其主要功能是从钻石中增加、更换或删除切面和功能,可以理解为合同的升级 (Upgrade)
-
钻石协议标准中的放大镜功能主要是返回关于切面的信息和钻石存在的功能,这些信息是保存在钻石合约内部的存储结构——DiamondStorage 中
整个钻石模型类似下图:
钻石合用钻石标准规范创建钻石合同,该合同可以使用任何数量的其他截面合同代码,如使用当前合同的代码。
钻石合同中的不同函数功能需要调用不同截面合同的代码来实现,钻石合同中的函数功能可以通过钻石切割功能进行修改(添加、更换或删除)。
这不同于市场上大多数使用代理合同和实现交互和升级合同的方式。
该函数将首先调用 LibDiamond 库的 enforceIsContractOwner 判断调用者是否是合同的函数 owner,如果是 owner 调用会调用 LibDiamond 库的 diamondCut 实现钻石合约功能更新的函数。
跟进这个函数,我们发现钻石切割会根据传入的不同而不同 action 判断添加、替换或删除功能,然后跟进项目方调用函数的交易。
我们发现新的切面合同已经引入 0x70d8ccaf6b50b051ab1e8fa238626163e45a8b03(未开源),传入 action 设置为 1 应该是调用 replaceFunctions 实现替换功能。
从 replaceFunctions 在函数中,可以分析函数将首先为传输的地址添加一个截面,然后从存储中循环读取每个函数选择器对应的旧截面,并将这些函数的截面添加到传输的新截面地址中。
至此可得知 The Saudis 项目方利用钻石切割函数重写转账功能 RIGHTBLOCK 手中的 NFT 转回自己的账户。