前言
熊市的投资生活总是很无聊的,不是亏就是血亏,但是在熊市里分析技术还是比较有趣的,因为这个时候黑客都会竭尽所能的用自己的方法去骗大家的钱,这个时候的案例反而是最有趣的,前两天收到同事的一个案例,初看上去像是一个授权钓鱼,仔细研究之后还发现别有一番风采,分析过后决定记录下,看看熊市钓鱼手法的最新操作 😀
技术分析
本次样本的交易哈希为 https://bscscan.com/tx/0x4d208e542cbcb0919cccb1d8209a18d8097c0b09e1ce2f600a8d041193abe670
,我们把它放到 etherscan
后,可以看到以下的结果
芜湖,这很明显就是一笔 approve
交易,立马联想到了 approve
钓鱼,然后又立马可以下结论是授权钓鱼攻击,好了,3s 不到完成分析,结束 😀 然而真的是这样吗?仔细一看,这个交易细节里头,似乎还有东西是 Chi Token
,还有我们再观察一下授权的是一个叫 DBT
的代币,查看这个代币的简介
芜湖,价值全都是0,总量也只有可怜的 3000 枚代币,总结下来就是搞了一波限量发行价格还全都是0,也就是说这波授权骗了用户一波不值钱的代币,但是回看 Chi Token
这个关注点,这个代币是 1 inch
发行的一个 Gas Token
,这个代币唯一的作用就是用来节省交易过程终的gas
,概括来说就是这个 gas token
它利用的是原理是以太坊的状态清理机制,在清理存储插槽(storage slot)和删除带有自毁操作码的合约(这些操作都可以删减全局状态树)时,会收到 gas
退款。这些操作都可以被认为具备负 gas 价格。 – 清理/自毁合约:- 24,000 gas – 清理/删除存储:-15,000 gas
当 EVM 执行这类操作时,gas 退款是通过一个独立的交易退款计量器来计算的。gas 退款只会在交易结束时提供。另外,最高 gas
退款量是该交易所消耗 gas
量的一半。具体的原理我放在文章的末尾,大家感兴趣的可以自行研究下。
回到我们的分析,既然用上了 Chi Token
,那么这个交易大概率是和 gas
问题相关的,为了探明交易的细节,我们把交易放到 Blocksec
的分析工具上看看
芜湖,通过分析交易,我们不难发现,在用户调用 approve
进行代币授权的过程中,DBT
代币在函数的内部还调用了 Chi Token
的 mint
函数,然后就是开始一系列的通过 create2
进行的合约创建操作。为了看明白这里发生了什么,我们看一下 Chi Token
这里的相关代码
function mint(uint256 value) public {
uint256 offset = totalMinted;
assembly {
mstore(0, 0x746d4946c0e9F43F4Dee607b0eF1fA1c3318585733ff6000526015600bf30000)
for {let i := div(value, 32)} i {i := sub(i, 1)} {
pop(create2(0, 0, 30, add(offset, 0))) pop(create2(0, 0, 30, add(offset, 1)))
pop(create2(0, 0, 30, add(offset, 2))) pop(create2(0, 0, 30, add(offset, 3)))
pop(create2(0, 0, 30, add(offset, 4))) pop(create2(0, 0, 30, add(offset, 5)))
pop(create2(0, 0, 30, add(offset, 6))) pop(create2(0, 0, 30, add(offset, 7)))
pop(create2(0, 0, 30, add(offset, 8))) pop(create2(0, 0, 30, add(offset, 9)))
pop(create2(0, 0, 30, add(offset, 10))) pop(create2(0, 0, 30, add(offset, 11)))
pop(create2(0, 0, 30, add(offset, 12))) pop(create2(0, 0, 30, add(offset, 13)))
pop(create2(0, 0, 30, add(offset, 14))) pop(create2(0, 0, 30, add(offset, 15)))
pop(create2(0, 0, 30, add(offset, 16))) pop(create2(0, 0, 30, add(offset, 17)))
pop(create2(0, 0, 30, add(offset, 18))) pop(create2(0, 0, 30, add(offset, 19)))
pop(create2(0, 0, 30, add(offset, 20))) pop(create2(0, 0, 30, add(offset, 21)))
pop(create2(0, 0, 30, add(offset, 22))) pop(create2(0, 0, 30, add(offset, 23)))
pop(create2(0, 0, 30, add(offset, 24))) pop(create2(0, 0, 30, add(offset, 25)))
pop(create2(0, 0, 30, add(offset, 26))) pop(create2(0, 0, 30, add(offset, 27)))
pop(create2(0, 0, 30, add(offset, 28))) pop(create2(0, 0, 30, add(offset, 29)))
pop(create2(0, 0, 30, add(offset, 30))) pop(create2(0, 0, 30, add(offset, 31)))
offset := add(offset, 32)
}
for {let i := and(value, 0x1F)} i {i := sub(i, 1)} {
pop(create2(0, 0, 30, offset))
offset := add(offset, 1)
}
}
_mint(msg.sender, value);
totalMinted = offset;
}
以上就是 mint
函数的相关实现,全部都是用 assembly
来实现的,这里我说一下大概的逻辑,mint
函数实际上是根据 value
参数来决定 #6 行中的循环次数,在这个循环里,会不停的通过 create2
来创建合约,然后合约创建完后会给调用该函数的用户分发 Chi Token
(L32)。回顾上文说的技术细节,在以太坊中,是可以通过合约创建来消耗一定量的 gas
的。那么既然 mint
操作代表的是存储的过程,那么一定有一个对应的释放的过程,通过查看 Chi Token
合约细节,这个函数叫 free
,顾名思义,就是用来释放创建合约消耗的 gas
的,我们一起来看下逻辑
function free(uint256 value) public returns (uint256) {
_burn(msg.sender, value);
_destroyChildren(value);
return value;
}
free
合约的操作很简单,就是根据 value
参数首先燃烧掉你的 Chi Token
代币,然后根据销毁的代币量来调用 _destoryChildren
来销毁对应在 mint
函数中创建的合约,_destoryChildren
的逻辑这里不展开,主要的功能就是销毁合约,再次回顾前面的知识点,由于以太坊的存储回收机制,在销毁合约的过程中,会返还一定量的gas
给到用户,也就是说,只要你持有 Chi Token
,那么你就可以不停的销毁合约来获取一定的 gas
补偿。我们再来回看最开始的钓鱼交易:
很明显这里只有一个 mint
操作,没有 free
操作,说明这里 gas
只是单方面的存储了起来,并没有被释放掉,而 mint
操作是在 approve
函数里调用的,根据 mint
函数的逻辑,这里 Chi Token
分发给的是 DBT
这个合约,而不是分发给调用 approve
的用户,但是由于这笔交易是用户发起调用的,在以太坊中,交易的 gas
费用都是由交易的发起者支付的,也就是说在这个过程中所有合约创建的 gas
都是用户支付的,但是这个过程中产生的 chi token
是归 DBT
合约的,在后续过程中,攻击者就可以从 DBT
这个合约中提取 chi token
来调用 free
操作来取回用户创建合约的 gas
总结
通过上面的分析,我们不难发现,这一次的钓鱼事件,初看上去很想是一起 approve
钓鱼事件,实际上这是一个通过 approve
操作伪装成 gas stealer
的钓鱼操作,其本质是通过以太坊的存储清理机制利用 1 inch
实施的一起钓鱼攻击,这种攻击隐匿性很高,用户难以察觉,因为根本没有任何代币或者 NFT
的授权,只是损失了比较多的 gas
,但 gas
本身也是链代币,也是很值钱的,所以本质上你是损失了很多的手续费。所以我们还是要警惕这种新型的钓鱼攻击,保护好自己的资产。
附录
-
Chi token 技术详解: https://learnblockchain.cn/article/1707
-
What is gas Token: https://gastoken.io/
原文始发于微信公众号(蛋蛋的区块链笔记):钓鱼这样才好玩 — 记一次有意思的钓鱼样本分析