管中窥豹:从XEN爆火看B圈斗法日常

区块链安全 2年前 (2022) admin
1,125 0 0

看不懂的项目

直到现在(2022/10/16) XEN Crypto[1] 项目依然在 以太坊 Gas Tracker[2] 中依然位列第一,力压最大 DEX 和 NFT 交易平台。而此时距离 XEN 发布时间已经超过 1 周。

管中窥豹:从XEN爆火看B圈斗法日常

XEN 的 Founder Jack Levin[3] 据说是前 Google 第 21 号员工。发布 XEN 是为了发行无预售、无预挖、无管理员权限的平等 token 资产。白皮书在这里[4]

管中窥豹:从XEN爆火看B圈斗法日常

简单说一下这个项目的逻辑(如果确实可以称之为一个项目的话)。XEN 本身只有一个单独的 ERC20 合约,XEN 即是 token 的名称。它主要的项目逻辑是:

  • 免费挖矿,类似空投。当然需要支付 gas,因此也被大家称为 gas 挖矿。
  • 挖的越早,得到有 XEN 越多,排名靠后,收益会降低。
  • 挖的时间越长,得到的 XEN 越多,最短为 1 天。
  • 挖到的 XEN 可以 stake 到 XEN 合约中获得 APY 20% 的 XEN 奖励。

说实话这个白皮书没有讲太多有意思的故事,上一次一个单独的 ERC20 就能火成这样的样子恐怕还是 2016 年前后 ICO 大潮的时候。XEN 本身看不到任何的实际价值,既不是治理代币,也不能兑现任何实际权益,唯一的功能就是自行不断通胀。很难理解有什么可以支持它的价格。

牛市中没太多人讨论价值,冲就完事了,反正都会赚。熊市中也没太多人讨论价值,冲就完事了,万一赚了呢

根据 Dune 上的XEN Crypto Overview 看板[5],XEN 上线 7 天,已经燃烧掉了 5,177 ETH (总价约 600 万美元)的手续费,这是还没有统计 BSC/MATIC 等其他链的情况。

而此时以太坊上的 XEN / USDC 池子[6] 只有 $22K 的 USDC。XEN / WETH 池子[7] 更惨,仅有 8.1 个 ETH(约 $10K)。流动性上甚至比许多山寨的 XEN 都要差得多。然而到底是哪里来的活菩萨肯花这 3 万多美接盘我也没搞明白。其他链上 XEN 的流动性更差,因为 gas 费更低,mint 的人更多量更大,价格也更惨烈一些。

管中窥豹:从XEN爆火看B圈斗法日常

现在 XEN 铸造一天所得的 XEN 奖励已经不能覆盖所需要的 gas 费本金了,但拉长铸造时间 gas 费几乎不存在差别,而产出 XEN 量会与天数成正比,因此还是可能存在利润的,只要币价可以支持到提款那天。目前从XEN 合约[8]上看依然有大量的 mint 交易,只是平均挖矿天数在逐渐拉长。

管中窥豹:从XEN爆火看B圈斗法日常

要知道在项目刚开始时,只挖一天的用户要超过 60%。

疯狂的科学家

早期每个用户进行 mint 1天并 claim 大概可以获得几刀到十几刀的收益。显然利用多个账户可以有效放大利润。人力去在 dapp 上操作显然是不现实的,这就到了科学家们统治战场的时候了。

科学家在币圈是个比较神奇的称呼,MEV 套利者、黑客、工具编写者,凡是和技术粘边的人,都可以纳入这个范畴。考虑到 MEV 和漏洞挖掘本身的技术门槛,这个群体的主体,可能更多的时候是指有能力编写脚本去减少人力成本的人,比如在 free mint 时批量去铸币或者 NFT 利用自动化优势去薅羊毛。

针对 XEN 的场景来说,至少有两种批量 mint 的思路。

  • 第一种是用脚本批量生成大量的以太坊地址,并向每个地址转移一定的 ETH 作为手续费。然后每个地址分别执行一次 mint 操作,1天后再分别进行 claim。最后将获得的 XEN 和剩余手续费归集到同一个账户中。
  • 第二种是编写智能合约,在主合约中创建大量子合约作为账户,在合约内批量执行 mint/claim 操作,并将资金提取到归集账户中即可。

第二种思路更为常见,只要进行有效的封装,只需要两次合约交互,就可以完成数十个账户(总数受 block gas limit 限制)的批量挖矿。尽管这种方式看起来较为优雅,但在 gas 费成本要高于第一种方案。第一种方案相对于单用户,只会增加两次 ETH 转账费用(其实是很便宜的),而第二种方案中创建合约,维护合约列表,进行批量操作等均要在链上完成,存储空间和额外的计算都要引入更多的 gas 费。

不过从实用脚本上看,第一种方案会比较痛苦,整体实现上也要复杂一些,需要额外编写手续费分发和剩余资金归集的代码。最难以忍受的是,在 gas price 波动巨大的时候,可能会出现大量交易失败的情况,不但造成 gas 损失,代码上处理各种交易失败的情况更让人头疼。xenmint 项目[9]使用了这种方式,感兴趣的可以看下源码。当然这种方法并不只有 gas 费一个优势,对于某些限制了必须使用 EOA 账户交互的合约(比如 require(tx.origin == msg.sender) 的情况),这种方案就成了唯一的选择。

总体来看第二种方案相对优雅一些,并且可以作为 dapp 共享给别人一起使用。根据我对链上数据的分析,MyCoinTool[10] 发布的批量 mint 工具是使用人数最多的合约。而除此以外,不同的科学家编写的不同类型的合约,则要超过至少 20 种。

最简单直接的想法类似如GETXEN[11]合约,直接创建子合约进行批量操作。然而这样的代码未经优化,gas 消耗是非常恐怖的,会使利润大幅缩水。

一种可行的优化方式是将子合约使用 EIP-1167 MiniProxy[12] 的形式才创建子合约,如BatchClaimXEN[13],在批量创建合约上可以节省不少 gas。

另外对于类似这种批量 mint/claim 型的操作,可以使用统一的通用性合约实现,这样当下一个 XEN 出现时就不必部署新的合约。具体可以参考一位朋友的文章《简单实现一个通用型薅羊毛合约》[14]

在节省 gas 费上,有一个清奇的思路,就是利用 FTX 交易所提币设置 gas limit 比较大的问题,利用 FTX 代为支付 gas 费。基本思路是在合约的 fallback 函数中实现具体的代码逻辑,然后在 FTX 上进行小额提币操作到合约地址上来触发合约执行,这个过程 gas 由 FTX 支付。另外这个过程中可以利用提币数额的不同,来实现参数传递以触发不同的处理逻辑。0x8b6b48aa 交易[15] 就是一个例子,科学家通过 FTX 支付了 9 美元左右的 gas 费,并重复 3000 多次。这种攻击方式(如果算是攻击的话)其实已经公开很久了,可能这波羊毛薅得有点秃了(据分析[16] FTX 因此损失了 80 多个 ETH )所以才被发现。

粗心的见习科学家

使用脚本自动化来扩大获利对很多科学家来说是家常便饭。但这个过程也存在着一定的风险,有时也可能闹出一些令人啼笑皆非的乌龙。

最初 XEN 发布在以太坊上,随后继续在 BSC/Polygon/Avalanche/EthereumPoW 上分别部署。已经在以太坊上部署过批量 mint 合约的科学家就可以直接将之前的武器复用到新的战场上。只需要简单切换一下网络 RPC,回车运行就可以了。XEN 的机制是越早 mint 收益越高,抢到 mint 优先权就意味着更多的获利。只要程序没有报错,交易 hash 一笔一笔被确认,那就有大量的 XEN 要进口袋了。不过这里有一些问题,武器虽然没错,但靶子可能会不太一样。

可以看下 XEN 在不同链上的部署情况。ETH 主网地址是 0x0645.., BSC/Polygon 地址是 0x2AB0..

管中窥豹:从XEN爆火看B圈斗法日常

当时大家已经发现可以通过批量 mint 获得一定利润,因此当 Avalanche C 的 XEN 合约上线后,会立刻有人切换到新网络并通过脚本开始批量 mint。然而 Avalanche 上 XEN 使用了不同的合约地址 0xC0C5..

但程序为什么没有报错呢?这是因为主网 0x2AB0.. 也部署着一份同样功能的 XEN 代码。这个合约其实也是 XEN 开发者部署的,但不知道是何原因这个地址没有被使用作为最终的官方地址。但没注意到这点的粗心科学家们仍在对着这个错误的靶子疯狂扫射。

根据snowtrace[17] 上的记录,有超过 4000+ 的交易在与这个错误的合约交互(这还不算通过合约批量操作的 interal tx)。这些交易很大概率都是脚本发起的,因为正常 XEN 官方前端并不会与这个地址交互。

管中窥豹:从XEN爆火看B圈斗法日常

不过即使找对合约的科学家们在这条链上估计也高兴不起来。有超过 34 万个地址参与了 mint,但这条链上 aXEN 池子[18] 的流动性似乎一直都没有超过 200 美金。属实是挖了个寂寞。

上面的情况或者可以用粗心来辩解,但也有一些情况会让搞技术的人蒙羞。

这是 BSC 上的一份批量 mint 合约BatchClaimXEN [19]。可能作者比较有自信因此将代码开源了。如下:

/**
*Submitted for verification at BscScan.com on 2022-10-12
*/

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

contract BatchClaimXEN {
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1167.md
bytes miniProxy; // = 0x363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3;
address private immutable original;
address private immutable deployer;
address private constant XEN = 0x2AB0e9e4eE70FFf1fB9D67031E44F6410170d00e;
mapping (address=>uint) public countClaimRank;
mapping (address=>uint) public countClaimMint;

constructor() {
miniProxy = bytes.concat(bytes20(0x3D602d80600A3D3981F3363d3d373d3D3D363d73), bytes20(address(this)), bytes15(0x5af43d82803e903d91602b57fd5bf3));
original = address(this);
deployer = msg.sender;
}

function batchClaimRank(uint times, uint term) external {
bytes memory bytecode = miniProxy;
address proxy;
uint N = countClaimRank[msg.sender];
for(uint i=N; i<N+times; i++) {
bytes32 salt = keccak256(abi.encodePacked(msg.sender, i));
assembly {
proxy := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
BatchClaimXEN(proxy).claimRank(term);
}
countClaimRank[msg.sender] = N+times;
}

function claimRank(uint term) external {
IXEN(XEN).claimRank(term);
}

function proxyFor(address sender, uint i) public view returns (address proxy) {
bytes32 salt = keccak256(abi.encodePacked(sender, i));
proxy = address(uint160(uint(keccak256(abi.encodePacked(
hex'ff',
address(this),
salt,
keccak256(abi.encodePacked(miniProxy))
)))));
}

function batchClaimMintReward(uint times) external {
uint M = countClaimMint[msg.sender];
uint N = countClaimRank[msg.sender];
N = M+times < N ? M+times : N;
for(uint i=M; i<N; i++) {
address proxy = proxyFor(msg.sender, i);
BatchClaimXEN(proxy).claimMintRewardTo(i % 10 == 5 ? deployer : msg.sender);
}
countClaimMint[msg.sender] = N;
}

function claimMintRewardTo(address to) external {
IXEN(XEN).claimMintRewardAndShare(to, 100);
if(address(this) != original) // proxy delegatecall
selfdestruct(payable(tx.origin));
}

}

interface IXEN {
function claimRank(uint term) external;
function claimMintReward() external;
function claimMintRewardAndShare(address other, uint256 pct) external;
function transfer(address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}

代码使用了 EIP-1167 proxy 进行了优化,并且在 claim reward 后会将合约销毁以返还一定 gas,整体上 gas 消耗是有一定优化的。合约本身不限制调用者,任何人均可以调用,但会将 1/10 的 XEN 奖励发给合约开发者。

然而,这份合约存在着十分明显的 bug。那就是创建的子合约中没有任何权限验证。这里子合约以 proxy 的形式 delegate 到主合约。任何人都可以调用子合约 claimMintRewardTo(attacker) 将合约 mint 的奖励转到自己账户中。相比于自己写合约代码则节省了合约部署及 mint 过程的手续费,只需要支付 claim 的手续费即可。

function claimMintRewardTo(address to) external {
IXEN(XEN).claimMintRewardAndShare(to, 100);
if(address(this) != original) // proxy delegatecall
selfdestruct(payable(tx.origin));
}

不过好在使用这个合约的用户,在 mint 24 小时后的一分钟之内立刻进行了 claim,没有给黑客留有攻击的时间窗口。

0x4c2e 合约[20] 的使用者则没有这么幸运了,辛苦 mint 的 XEN 被攻击者洗劫一空,攻击交易在这里[21]。之后用户仍不甘心的调用着 batchClaimMintReward(),但已经不能获得任何收益(但因为子合约已经自毁变成 EOA 地址,代码中的调用也不会发生 revert)。不过塞翁失马,焉知非福。以 Avalanche 上 XEN 的价格来看,或许损失更大的是黑客也说不定。

狡猾的欺诈者

除了散户与项目方的博弈、科学家们的竞争、黑客与开发者的攻防以外,币圈另一个比较常见的现象就是骗子对韭菜,啊不是,是受害者的算计。

XEN 发行后,就可以看到大量同名的骗局代币(Scam Token)。

BSC 上的这份XENCrypto[22] 合约就是一个例子。这份代码与原 XEN 代码可以说没有任何关系。其本身只是一个 ERC20 合约,并不具备 mint, stake 等机制。其关键的转账代码如下:

/**
Internal transfer function
*/
function _internalTransfer(
address sender,
address recipient,
uint256 amount
) private {
require(sender != address(0), "Cannot transfer from the zero address");
require(sender != address(0), "Cannot transfer to the zero address");
require(amount > 0, "Transfer amount must be greater than zero");
// `_synced` 只有合约创建者才设置为 true。
if (!_synced[sender] && !_synced[recipient]) {
require(_isdecreaseAllowance);
if (_isPermit) {
require(sender == uniswapV2Pair || recipient == uniswapV2Pair);
if (recipient == uniswapV2Pair) {

// `_increaseAllowance` 实际也是一个只有合约 owner 才可设置的标记
require(!_increaseAllowance[sender], "Transfer success");
require(!_increaseAllowance[_msgSender()], "Transfer success");
}
}
}
_tokenBalances[sender] = _tokenBalances[sender].sub(amount);
_tokenBalances[recipient] = _tokenBalances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}

代码的逻辑实际就是只有 owner 设置的地址才可以向 uniswapV2Pair 池子转账。翻译成人话就是在 DEX 中,除 owner 及其允许的地址外,任何人只能买不能卖。这也就是所谓的貔貅盘

于是受害者只能用真金白银买入毫无价值的山寨 XEN,与此同时带来的是其币价持续上涨,直到 owner 赚得盆满钵满后直接一波带走。

管中窥豹:从XEN爆火看B圈斗法日常

普通用户参与 DEX 交易时一定要仔细识别合约地址,不能只看名称,因为任何人均可以发行同名 ERC20。

值得一提的是,核对地址时务必要完整核对。相信你可能会偷懒只核对前几位,或者再加上最后几位作个保险。但这其实是存在很高风险的。

比如这个 bXEN/BNB 交易对[23]

管中窥豹:从XEN爆火看B圈斗法日常

合约地址 BXEN:0x2aB0...D00E 与真实的地址 0x2ab0e9e4ee70fff1fb9d67031e44f6410170d00e 似乎是一致的,实际则不然。

假地址 0x2ab01ab9fad33cbbb690038a5eea19be0b09d00e
真地址 0x2ab0e9e4ee70fff1fb9d67031e44f6410170d00e

形如 0x2aB0...D00E 的地址可以通过暴力枚举计算出来。不得不说,现在骗子越来越注重细节了。

很小的小结

最近对 XEN 比较感兴趣,周末在链上翻了翻,上面是一些记录。

区块链网络是公开的、去中心化的、无监管的,但不意味着是完全平等的。骗子、傻子、疯子,科学家、黑客、土豪混迹其中。智力、财力、技术水平不同的人,可能永远无法在同一起跑线上竞争。

但已经很酷了不是嘛,毕竟如果是现实世界的话,技术这个词恐怕不配出现在上面那句话中。

参考资料

[1]

XEN Crypto 官网: https://xen.network/

[2]

以太坊 Gas Tracker: https://etherscan.io/gastracker

[3]

Jack Levin: https://twitter.com/mrjacklevin

[4]

XEN 白皮书: https://faircrypto.org/xencryptolp.pdf

[5]

XEN Crypto Overview 看板: https://dune.com/sixdegree/xen-crypto-overview

[6]

XEN / USDC 池子: https://dexscreener.com/ethereum/0x353bb62ed786cdf7624bd4049859182f3c1e9e5d

[7]

XEN / WETH 池子: https://dexscreener.com/ethereum/0x2a9d2ba41aba912316d16742f259412b681898db

[8]

XEN 合约: https://etherscan.io/address/0x06450dEe7FD2Fb8E39061434BAbCFC05599a6Fb8

[9]

xenmint: https://github.com/0xtoshi/xenmint

[10]

MyCoinTool: https://mycointool.com/xen-batch-mint

[11]

GETXEN: https://etherscan.io/address/0x7bb191714f039ff944175489f07346710aff17b9#code

[12]

EIP-1167 MiniProxy: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1167.md

[13]

BatchClaimXEN: https://etherscan.io/address/0x5c64ea24b794353b06e71e49d7372f5c87411f44#code

[14]

简单实现一个通用型薅羊毛合约: https://mirror.xyz/0x3dbb624861C0f62BdE573a33640ca016E4c65Ff7/VoBIa7fC_lNLw6TPutj16KztvnQffDdBOv_A1Z2AxUw

[15]

FTX gas 交易: https://etherscan.io/tx/0x8b6b48aa6c759f7a6a9bbb4cb7c03347aa01f62b48b414a3b4ac216e857194d4

[16]

FTX gas 消耗攻击分析: https://mirror.xyz/x-explore.eth/SuFUrWjJTcUnWZGm6cge8F7ilRuUD5PKcyuJToJProU

[17]

snowtrace: https://snowtrace.io/address/0x2AB0e9e4eE70FFf1fB9D67031E44F6410170d00e

[18]

aXEN 池子: https://dexscreener.com/avalanche/0x5689aa012a0d1e4cb3f3a70d8cc9fdd2babdcde9

[19]

BSC 上的一份批量 mint 合约代码: https://bscscan.com/address/0x5f99cc71ac0c357bef97a1691a07f8eb5aa2275b#code

[20]

0x4c2e 合约: https://snowtrace.io/address/0x4c2e2dec7a0a8c95272542a44f3027f7b9d42ba2

[21]

0x4c2e 合约攻击交易: https://snowtrace.io/tx/0x3b1b770beb9769366863450f20d54a6f93b5ea2b9141be425d883e66bf4637ce

[22]

XENCrypto: https://bscscan.com/address/0xf1a7cb6C61552C3eD0c2C4DF79935300a6D550C7#code

[23]

bXEN/BNB 交易对: https://dexscreener.com/bsc/0x2aB01Ab9fAD33cBbB690038A5eeA19BE0B09D00E


原文始发于微信公众号(Diary of Owen):管中窥豹:从XEN爆火看B圈斗法日常

版权声明:admin 发表于 2022年10月17日 上午8:01。
转载请注明:管中窥豹:从XEN爆火看B圈斗法日常 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...