以太坊钓鱼合约:因为了解,所以上当

以太坊钓鱼合约:因为了解,所以上当
众所周知以太坊是一个区块链平台以多种多样的智能合约闻名四方同时相信很多人也听到过或者研究过由于智能合约中的漏洞导致加密货币被攻击者窃取的事件对一些攻击手法都有所了解像Reentrancy这种漏洞更是已经烂熟于心了。首先整个社区的安全意识都有提高这是非常可喜的然而道高一尺魔高一丈以太坊上还出现了一些利用人们的安全嗅觉来进行钓鱼活动的合约也就是说部署一个看上去有漏洞的合约然后里面放上价值不菲的数字货币吸引别人来攻击。而攻击者要想攻击成功需要先转入一定的数字货币作为种子。由于这一类钓鱼合约是看上去有漏洞而实际上并没有漏洞因此攻击者不但没法攻击还损失了自己转入的种子货币。钓鱼合约的特征这些合约通常有这样几个特征含有漏洞钓鱼钓鱼没有鱼钩哪钓到鱼钓鱼合约一定会包含一个比较明显的漏洞这种漏洞是非常易于利用的如果稍微研究过一点以太坊智能合约安全或者具有对常见的漏洞又了解的人甚至可以一眼就看出合约中存在的这种漏洞。合约具有一定价值有了鱼钩当然要上饵。钓鱼合约中通常会有1-2个以太币Ether用作鱼饵并且结合合约的逻辑这些鱼饵包含了一些背后的故事比如说是用户的存款或者游戏的奖金之类。在Etherscan上开源如果在Etherscan上查不到合约的源代码一般人是不会通过仔细研究合约的bytecode来理解合约有哪些功能的。为了吸引别人看自己合约的源代码钓鱼合约通常会在Etherscan上公开合约源代码。源代码比较短为了让看合约的人快速理解并找到漏洞钓鱼合约的代码长度一般不会太长经常只有几十行代码。发现过程笔者之前是找(shang)到(dang)过一些钓鱼合约的因此对这种合约格外敏感。这次发现钓鱼合约的起因也就是我在看Etherscan的时候没事看了一眼Verified contracts就是这发现有个合约里面居然有15个以太币而且交易数量只有两笔 合约地址为0x2a752d08F2EAD90C1CF7572d8A00C00E7b1913Cf不过目前已经有人上当钓鱼者收杆了这里放一个一样的合约展示一下我当时看到这个合约的场景吧。直觉告诉我这是一个钓鱼合约于是我立马点进去看了一下代码果然和我想象的一样。不过这里有意思的是我误解了这个合约的钓鱼方式两次。。下面讲讲对这个合约的分析以及我的误解。合约代码直接上钓鱼合约的代码。可能有些读者对Solidity智能合约编写语言不太熟悉我就简单讲讲这个代码如果读者可以自己阅读代码可直接跳到下一节。首先是合约的构造函数constructor这种函数会且仅会在合约被部署的时候被执行对于这个合约而言constructor的作用是将一个byte32数组中的每个成员在合约中admin这个mapping中的对应位置置位。置位有啥用呢这里看modifier isAdmin()这个片段它是一种修饰器修饰器名字叫isAdmin只要函数后面带了isAdmin这个片段就会被编译器加到函数最前面去。这个合约里面isAdmin规定其修饰的函数只能由特定的用户调用如果调用者的地址的keccak256哈希值在admin中对应的位置不是True调用就会失败。然后看主要的功能函数。在此之前我先讲一下这个合约是想要伪装成一个解谜游戏合约的admin会将问题答案对应的哈希值和问题放到合约中如果游戏参与者提交的答案哈希值与正确答案哈希值相同就可以拿到奖金。以此为背景去理解这个合约的主要功能函数Try 这个函数是游戏参与者在猜谜游戏中提交答案的途径参与者每次竞猜除了要提供自己的答案外还要向这个合约中转入至少1以太币这也是该合约获利的方式。Start仅admin可用接收两个参数一个是新的问题_question另一个是该问题的回答_response并且如果之前没有设置合约中回答的哈希值这个函数会将这个值设置为参数中_response的哈希值Stop仅admin可用终止游戏将合约中的以太币转走。New仅admin可用设置新的问题和答案哈希值。合约漏洞可以看到合约中有个非常诡异的函数Start它的第二个参数其实就是问题的答案由于以太坊的历史交易都是公开的如果通过这个函数去设置游戏中的问题那么该问题的答案也会被所有人看到。如果说这是开发者对以太坊不熟悉、不了解New函数中设置新问题和回答的方法却是正确的做法所以可以很明显地看出这里是合约开发者故意留下这个漏洞作为鱼钩进行钓鱼的这个合约在被部署后其部署者也确实调用了Start方法并设置了15个Ether的奖金作为鱼饵。刚开始我没有细看交易我想的是既然admin可以随时更改问题和答案会不会持续监听链上交易如果发现有人发出了提交正确答案的交易就用更高的gas price抢在正确答案被提交前修改问题和答案使得游戏参与者白费功夫类似的情形我们也见到过不过不适用于这里。首先这个合约给出的鱼饵太大了万一失手可能得不偿失而且现在矿池提供了暗池服务这样钓鱼风险还是很大的。所以我觉得合约中还有其他玄机。第二层合约部署者其实不是adminEtherscan提供了查看交易改变的世界状态的功能其实仔细看该合约部署者发出的这笔交易改变的状态可以发现合约并没有任何一个值被这个交易改变了。这就很耐人寻味因为合约部署时只设置了admin这个mapping的值没有改变responseHash也就是答案的哈希值。如果部署者是admin这笔交易应当会将question和responseHash都设置为对应的值。而我们回过头来看合约代码观察这里可以发现admin这个mapping里面的key其实是地址哈希值这是不是为了隐藏信息呢通过这样的设计我们没办法通过constructor的参数知晓部署者是否为admin误以为部署者必然是合约的admin从而被他调用Start的行为误导。如果是这样的话由于实际上这次调用并没有修改responseHash所以如果有人以合约部署者一定是admin的前提去预估合约的执行结果就会上钩。正当我以为找到了正解的时候这里还有个问题让我无法将整个流程梳理清楚在modifier中是使用了require来验证交易发送者是不是admin的不管按照旧版Solidity还是新版中的定义如果条件不满足合约应当会revert这笔交易也就是说这里甚至不应该继续执行下去Etherscan也应该给出reverted的信息这笔交易附带的以太币也应该退还给发送者。看来这个思路不对。第三层内部交易。最后我想看看与这个合约相关的有哪些内部交易由合约调用合约。由于Etherscan对内部交易显示不全我并没有能在Etherscan上找到答案因此我用了其他的工具发现确实有一笔内部交易与这个合约相关再追查这笔内部交易改变的状态发现在调用Start方法之前有人已经通过哈希为0x0d974ab8723e15b1e5d2d2766aca443e494c03fe9bbbff8eaf18a26c416a8f77的交易调用另一个合约改写了responseHash所以调用Start方法的这笔交易并没有改变合约中的任何变量。所以说由于Etherscan不能显示这样的内部交易导致我们没能一开始就找出问题的真正原因。继续追查通过内部交易改写钓鱼合约状态的那个合约地址为0xf4e96e2d3b4e27853b5eabc5b0ddcc664f2b1ed1已经有407笔交易了看来这个人是老渔夫了。受害者这里是刚刚提到的钓鱼合约可以看到它已经成功地钓到了一只肥鱼了。截至到本文发布以太币价格是1270美刀怎么又涨了这么多也就是说受害者损失2个以太币金额达到了2500美刀左右看着就肉痛。总结人为财死鸟为食亡。15个以太币虽然价值很高看起来很诱人但是这些钓鱼合约其实都是设计好的不可能让你真正吃到这香喷喷的鱼饵的所以如果你也遇到一个看起来有漏洞、只需要动动手指就能攻击的合约一定一定要多想想顺带一提如果想持续关注这一类钓鱼合约或者你想到了反钓鱼的方法可以在Etherscan上查看0xf4e96e2d3b4e27853b5eabc5b0ddcc664f2b1ed1这个地址的交易看它最新的交易修改了哪个合约中的变量比如这笔交易:再点击State可以看到三个地址再点击最上面的这个地址就可以看到最新的钓鱼合约了。