一篇以前写的文,现在修改补充了些许,发了上来
以太坊中的智能合约蜜罐相对于互联网蜜罐的目的有着本质的区别:后者着重在于诱导攻击,然后做检测分析,来收集攻击手法与漏洞;而前者更像是一场赌博的骗局,利用种种方法,诱导目标转账进入合约,完成韭菜收割。但是这个蜜罐的名词也是挺恰当的,就也这么叫了。
有趣的是智能合约蜜罐其目标锁定在智能合约开发者,智能合约代码审计人员,略懂区块链技术的信息安全人员(emmmm)
通常而言智能合约蜜罐的欺骗性在于区块链漏洞,逻辑漏洞;又或是赌博合约。
此处介绍的是利用第三方组件导致的智能合约蜜罐。
蜜罐合约地址:0xcEA86636608BaCB632DfD1606A0dC1728b625387
我们可以通过Etherscan浏览器看到该合约的外部交易,内部调用,代码,代码abi等信息
先来看关键的智能合约代码
pragma solidity ^0.4.20; contract QUESTION { //玩家 输入答案字符串开始玩游戏 function Play(string _response) external payable { //需要该玩家地址不为智能合约地址 require(msg.sender == tx.origin); //如果答案的sha256哈希=答案hash 并且 传入的押金超过1ether if(responseHash == keccak256(_response) && msg.value>1 ether) { //给该玩家转账该智能合约所有的钱 msg.sender.transfer(this.balance); } } string public question; address questionSender; bytes32 responseHash; //开始游戏,传入题目和答案的字符串 function StartGame(string _question,string _response) public payable { //如果答案hash没有被赋值 if(responseHash==0x0) { //计算sha256赋值答案hash responseHash = keccak256(_response); //赋值题目字符串 question = _question; //赋值题目发送者的地址,为调用者的地址 questionSender = msg.sender; } } function StopGame() public payable { //需要调用者等于题目发送者 require(msg.sender==questionSender); //给调用者转账所有eth msg.sender.transfer(this.balance); } //更新一个新的问题,传入题目字符串,答案hash function NewQuestion(string _question, bytes32 _responseHash) public payable { //需要调用者等于题目发送者 require(msg.sender==questionSender); //更新题目 question = _question; //更新答案hash responseHash = _responseHash; } //该智能合约fallback函数可以接受钱 function() public payable{} }
很简单的一个合约,大致就是猜答案:hash符合就给所有钱
这是明显存在漏洞的智能合约:
也有一些奇怪的地方。
疑点1:
既然有函数知道用responseHash传入,也就代表着开发者应该是意识到了这个问题。这里是故意为之还是萌新犯蠢?
疑点2:
由于这是一个已经收网成功的蜜罐合约,我们队区块链浏览器上已经产生的交易进行分析,看看这个蜜罐钓鱼的过程。
Etherscan上的交易记录:0xCEA86636608BACB632DFD1606A0DC1728B625387
包括:4个外部交易,1个内部交易
在2020.02.14去看Etherscan,会发现交易不会直接帮我们解析出调用过程的参数,但是可以用下面介绍了另一种方法得到解析。
按照时间线排序分析
外部交易0xf9f25d... 0x8F1F6FEb78BA90ad003E1B7408caA164aD90830d地址创建合约(创建合约)
外部交易0x41365... 创建者使用交易调用合约函数Startgame(),带上1.03Ether(部署问题和答案,从结果来看,这应该是一个抛饵行为)
外部交易0xcb589e... 受害者使用交易调用合约函数Play(),没有value,即没有带钱(受害者试探,由于没有带钱是不会通过if判断的)
外部交易0x8486f4... 受害者使用交易调用合约函数Play(),带上1.05Ether的钱(受害者上钩,提交了1Ether以上的钱,讲道理按照逻辑这里应该获得合约返回的亲,但是并没有这笔交易)
内部调用0xb68f60... 合约把所有钱转账给了另一个合约0x4B2838d9326bD5126F0573D9b5c71C0626Ab28f2(创建者收网,暴露出了一个合约地址)
我们得到两个地址,我们给他取个别名:
钓鱼者:0x8F1F6FEb78BA90ad003E1B7408caA164aD90830d
还引出了一个奇怪的创建者收网用的智能合约:
钓鱼中间合约:0x4B2838d9326bD5126F0573D9b5c71C0626Ab28f2
总体流程总结如下:
尝试在Etherscan中查看0x4B2838d9326bD5126F0573D9b5c71C0626Ab28f2钓鱼中间合约
发现该智能合约源码不公开,由0x78d39cDf39e80498237BC330e752DaBd8f90AC2f(从转钱结果推断,取名为钓鱼者小号)进行创建,并且该地址对钓鱼中间合约进行了几次调用。就触发了0xcEA86636608BaCB632DfD1606A0dC1728b625387(钓鱼合约)给该中间智能合约转钱。
但是拥有钓鱼合约源码的我们可以知道,只有对合约的Stopgame()函数产生调用,该智能合约才会对外转钱。中间合约肯定调用了钓鱼合约,才导致钓鱼合约会给中间合约转钱
其中一定有一些我们通过区块链浏览器看不见的调用在发生。
而钓鱼合约运行不在我们预期之内,就是因为这些看不见的调用。
四处寻找有没有能显示这些预计之外的调用的区块链浏览器。
然后就找到了完全暴露的智能合约内部call调用etherchain:https://www.etherchain.org/account/cea86636608bacb632dfd1606a0dc1728b625387
一个合约创建,3个外部交易,4个call调用
在完整的调用分析前,重新理一下相关地址:
命名 | 地址 |
---|---|
钓鱼者 | 0x8F1F6FEb78BA90ad003E1B7408caA164aD90830d |
钓鱼者创建鱼钩智能合约 | 0xcEA86636608BaCB632DfD1606A0dC1728b625387 |
钓鱼者小号 | 0x78d39cDf39e80498237BC330e752DaBd8f90AC2f |
钓鱼中间智能合约 | 0x4B2838d9326bD5126F0573D9b5c71C0626Ab28f2 |
与原先Etherscan交易对比可知,与之前相比多了3个call调用,均是由中间智能合约发起。(交易调用时间顺序整体是下面的条目时间早,但是同一区块的条目,上面的时间早)
下面的两个call调用发生在部署智能合约之后,部署者调用StartGame之前,受害者输入答案之前。应该就是我们之前疏忽的关键调用。
这两个call调用都是属于一笔之前没有出现过的交易0x1754a4ecaecff5e6f3d6fd6384f80e00535fa50318de369b57fbb4dc2495defa中
在Etherscan中查看该笔交易,
由于这样看到的交易的input是钓鱼者小号调用钓鱼中间合约的input。我们想看到的两个call调用在虚拟机调用层面,才能查看。
查看Tools&Utilities -> Parity Trace 查看智能合约中函数调用栈的情况。(虽然Etherchain也有Parity Trace,但是Etherscan较为友好,Etherscan作为用的最多的区块链浏览器也是有原因的)
有三个调用栈:
第一个调用是钓鱼者小号0x78d39c...对于中间合约的调用。不管。
第二个调用:
第三个调用:
都对钓鱼合约进行了调用。
关注其中input数据到底调用了什么,利用ethereum-input-decoder解密
第二个调用:调用StartGame,传入问题,答案
第三个调用:调用NewQuestion,传入问题,答案hash
可以看出,其实真正的答案是先用了StartGame设定,再NewQuestion修改。
之后我们看到的钓鱼者的StartGame调用,由于if(responseHash==0x0)
验证不通过,不会对智能合约答案造成影响,只是一个烟雾弹。
同理看一下多出来的上面的那个call调用(在受害者上钩之后):
交易:https://etherscan.io/tx/0xb86f60ff9a075a30aa4008c1cd70ed15f424d141c4de5b3afbadd9d7a18f97b4
函数调用情况:https://etherscan.io/vmtrace?txhash=0xb86f60ff9a075a30aa4008c1cd70ed15f424d141c4de5b3afbadd9d7a18f97b4&type=parity
利用中间钓鱼合约完成收网。
真实交易时间线:
至此完美完成了一次智能合约蜜罐攻击,一次利用以太坊最流行区块链浏览器Ethersacn的缺陷,打一个信息差的蜜罐钓鱼
流程图如下:
再回过头去去看看似乎萌新弱鸡的代码,处处用心险恶
之前疑点1:
StartGame用于钓鱼,NewQuestion用于传递真实答案,即使被发现了Etherscan以太坊浏览器存在该不会完全显示call调用的问题也不能被破解。
之前疑点2:
如果这个地方也用require就会导致用于诱惑别人的钓鱼者StartGame调用报错失败,而引起别人怀疑。
那么假设我们不知道Etherscan有隐藏调用的情况,是否就肯定会上当受骗呢?
其实也不是的,因为智能合约的存储空间,我们也是可以读取的。我们可以直接读取智能合约中的变量值(不管是public还是不是public)从而意识到情况不对。
web3.eth.getStorageAt("0xcEA86636608BaCB632DfD1606A0dC1728b625387", 0, function(x,y){alert(y)});
0x00000000000000000000000000000000000000000000000000000000000000d1 //string的长度 string question 0x0000000000000000000000004b2838d9326bd5126f0573d9b5c71c0626ab28f2 //提问者的地址 address questionSender 0x684ff0e88cefc2b7ff23228e02c9a10cc9b5b2e67e12b259a9bca644e19d2b8f //答案hash bytes32 responseHash 0x0000000000000000000000000000000000000000000000000000000000000000
可以发现提问者地址不等于我们所知的调用StartGame钓鱼者的地址
答案hash也与我们的答案hash不符合
从2018年3月份至2018年10月份的都有,最长等待鱼儿上钩的时间有100天
游戏停止,骗币成功:https://etherscan.io/address/0xce6B1AFf0fE66da643D7A9A64d4747293628D667#code
游戏停止,骗币成功:https://etherscan.io/address/0xFf45211eBdfc7EBCC458E584bcEc4EAC19d6A624#code
游戏停止,骗币失败: https://etherscan.io/address/0x4bc53ead2ae82e0c723ee8e3d7bacfb1fafea1ce#code
游戏停止,骗币失败: https://etherscan.io/address/0x3B048ab84ddd61C2FfE89EDe66D68ef27661C0f2
游戏停止,骗币失败: https://etherscan.io/address/0x5ccfcDC1c88134993F48a898AE8E9E35853B2068#code
https://medium.com/quantstamp/exploiting-the-interface-of-etherscan-for-ethereum-attacks-17b72d2897e0
https://paper.seebug.org/671/