# 智能合约安全初探之撸爆小学生

By [Hackit](https://paragraph.com/@hackbot) · 2022-02-09

---

**一、前言**

春节假期的最后一天，网上突然出现了这样一条消息：RND 创立者、12岁小学生黄正，在网上发布了智能合约创建和部署的视频教程。别的暂且不谈，但这 RND 是什么呢？原来是小学生在以太坊上发布的一个 ERC20 代币 (0x1c7E83f8C581a967940DBfa7984744646AE46b29) ,合约中制定了一系列空投规则：每个地址都可以领取，领取的越早给的代币越多。这引得网友们争相领取，跑得快的也收益颇丰。这么来看的话，每个钱包都可以领取，如果自己搞100个钱包，则可以领取100次，岂不美哉？但考虑到需要分发以太币及领取空投的GAS费，叠加到一起也不是小数目。然而小学生百密一疏，合约中有一处可以利用的地方，最终导致“科学家”在链上生成众多智能合约，实现了RND代币的“批量领取”。本文基于RND代码及 Robot DAO 群友 GCC (Github地址 ：[github.com/GGCCCC](http://github.com/GGCCCC) )的代码，对本次事件进行复现。

**二、RND 代币合约概览**

这是一个基于ERC20标准的代币，完整代码可到etherscan上自行查阅。领取空投的具体实现如下(token.sol)：

![](https://storage.googleapis.com/papyrus_images/f77fb894aff191e6f1d9620edab051d799dbcfe640e30562d9c65bd1e066565d.png)

可以看到，代码中规定：如果一个地址在360天之内、且之前未曾领取过空投，则按照`return_claim_number()` 计算出来的数额给予RND代币空投。

看起来似乎没什么问题，但是小学生忽略了一个方面：外部控制账户（EOA，也就是俗称的“钱包”）与智能合约账户（Contract）都能够参与空投的领取。在有些场景，这算不上什么大问题；而在前段时间 GameFi 火热的时候，各种盲盒、抽卡层出不穷，这时就体现出差异了：用户使用自己钱包发起交易去抽卡，这笔交易广播出去之后，用户就无法再干预了，只能等待抽卡结果；而如果用智能合约发起交易，就可在合约里面加上逻辑判断，如果抽卡结果不是自己想要的，就可以直接revert，除了损失一点gas费之外没有任何支出，直到抽出自己想要的结果。这样的攻击案例层出不穷，也对游戏生态造成了很大破坏。有一种解决方案是加上 `msg.sender == tx.origin` 的判断，规定项目合约的调用者必须是交易最初发起者，这样就较好地防止了用合约来重复抽卡的行为。

言归正传，既然RND的代码中没有限制智能合约，那么我们就部署一个合约来批量撸一波空投吧！（本次测试目标为笔者部署在Rinkeby测试网的RND [rinkeby.etherscan.io/token/0xcb33f7fb101e377a4b0e19fd647f391fad14d0b5](http://rinkeby.etherscan.io/token/0xcb33f7fb101e377a4b0e19fd647f391fad14d0b5)，并非针对主网RND项目。批量领取合约参考了 GCC 的代码：[github.com/GGCCCC/airdrop\_multi\_claim](http://github.com/GGCCCC/airdrop_multi_claim) ）

**三、部署Exploit合约进行批量领取**

开发工具我们选用 Hardhat 。首先要安装 Node.js 。然后打开新的终端，输入以下命令来配置Hardhat 环境：

`mkdir rndExploit`

`cd rndExploit`

`npm init --yes`

`npm install --save-dev hardhat`

安装完毕后，在 rndExploit 目录中运行：

`npx hardhat`

用键盘选择 Create a basic sample project ，然后一路按Enter：

![](https://storage.googleapis.com/papyrus_images/9bd8ed6fd8f2bc019fa02cbae03ffb3247871133b055734b64aa456fa67a24a7.png)

合约不复杂，实现了claim和transfer。但本次操作的重点在于`new`这个关键字，我们要用它来创建很多新合约，然后让这些新合约批量领取空投后再转移给我们。我们直接在Harhat的sample代码中修改(./contracts/Greeter.sol)。代码如下：

![](https://storage.googleapis.com/papyrus_images/38a7128e1f7132d73dcf2d13f8f70144514775d4c6de4981ccdc64f95936ba83.png)

然后稍加修改./scripts/sample-script.js。我们把合约部署和调用写到同一个脚本中，连部署带调用，方便 。这里我们向合约传入参数 10 ，意为创建10个子合约来进行空投的领取:

![](https://storage.googleapis.com/papyrus_images/b3e83a4da5e32e399ee90690876c269c7f78ccf845919915eec8290dded0a5b7.png)

最后对 ./hardhat.config.js 进行设置 ，以部署到Rinkeby测试网：

![](https://storage.googleapis.com/papyrus_images/3973f9af435c034c8818893b25eef58b5d1752cbe2239100957b613d1de807f3.png)

一切设置完毕后，在 rndExploit 目录中运行

`npx hardhat run scripts/sample-script.js --network rinkeby`

稍等片刻，命令行中显示

“Compiling 1 file with 0.8.7 Solidity compilation finished successfully Greeter deployed to: 0x0074bdB3da306F9051f172E7646d1dF2959b4443”

程序部署完毕，我们查看链上记录：

[rinkeby.etherscan.io/tx/0x343a4e09f91acb5b8178471cfa7f7295765ad135f43acdd2d6e1896f4c3ed222](http://rinkeby.etherscan.io/tx/0x343a4e09f91acb5b8178471cfa7f7295765ad135f43acdd2d6e1896f4c3ed222)

可以发现已经成功Claim了10次空投。

**声明：本文代码均作为演示之用，不对安全性做出承诺。**

HardHat工程源码见：

[

GitHub - 0xNezha/mutiClaimRND: 批量生成合约并Claim空投代币
-----------------------------------------------

批量生成合约并Claim空投代币. Contribute to 0xNezha/mutiClaimRND development by creating an account on GitHub.

https://github.com

![](https://storage.googleapis.com/papyrus_images/208151a9a0eec0e892aca8a811f61118ad00e824521e98bf119b86d7fdabd087.png)

](https://github.com/0xNezha/mutiClaimRND)

---

*Originally published on [Hackit](https://paragraph.com/@hackbot/9l7BOPjjLwunKBmrlbWk)*
