以欺骗者角度分析蜜罐合约

  • A+
所属分类:安全闲碎

以欺骗者角度分析蜜罐合约

文章作者:pinging

1 前言

很久没有发布区块链的相关内容了,今天偶然间我点开了很久之前部署的一个蜜罐合约,借助比赛推出去之后发现收获了9个以太币hhhh(就是骗到的,当然这个是测试节点并不是真正的以太币)
以欺骗者角度分析蜜罐合约
效果还挺好的于是我就以欺骗者的角度来分析一下整个合约的代码,包括代码的设计思路以及上当的原因。最后我将该题目的wp放上以便爱好者学习。

2 题目分析


以欺骗者角度分析蜜罐合约
以欺骗者角度分析蜜罐合约
最初始部分是一个FatherOwned,该合约中有修饰函数onlyOwner用来判断调用合约方是否等于合约创建者。而该合约创建时会将owner赋初值。
之后为HoneyLock重头戏,该合约继承了标准代币合约StandardToken与父合约FatherOwned(这里为后面的蜜罐打下基础)。
该合约为一个简易的银行合约。HoneyLock函数的作用是初始化合约;之后为takeMoney。该函数使得新用户可以提取airDrop空投资金。
之后为lock()函数,用于判断存储时间是否大于一年。
为了让合约能修改owner,我们设置了useCode函数,该函数中需要传入一个猜测code,如果code == 设置好的guessCode,并且传入的msg.value大于初始的guessValue,那么就将owner设置为新的用户。
withdraw()中判断是否为owner,若为owner则可以将余额清零。
最后为CaptureTheFlag用于获取flag。

3 蜜罐分析


站在欺骗者角度,我们来分析一下本题目的技巧。为了达到蜜罐的作用,我们需要领用户自愿传入一定数量的代币。改题目中让用户参与合约的方法就是获取flag,而获取flag又需要满足两个条件: 
以欺骗者角度分析蜜罐合约
而要满足上述两个条件需要我们调用空投函数takeMoney(),而调用之后又需要满足合约余额为0 。正常的操作需要等1年才可以提取,所以很多人会瞄准useCode函数,而该函数需要猜测guessCode与guessValue,对于很多专业队员来说,这种链上的信息可以从区块链上直接获取到,所以会想当然的调用,并传入一定数量代币。然而事情并没有那么简单。
许多人会认为调用了useCode(uint256 code)函数之后会改变owner = msg.sender,而忽略的一个重要的地方是onlyOwner是继承来的,所以这里onlyOwner改变的是FatherOwned的owner,然而获取flag的要求是需要HoneyLock合约的owner被修改,但是我们并没有修改HoneyLockowner的地方,所以这个方法永远不可能成功。所有的钱就只是白白扔进去。
以欺骗者角度分析蜜罐合约
拿到合约后:
以欺骗者角度分析蜜罐合约
以欺骗者角度分析蜜罐合约
简单解读一下该合约,该用户若想获得flag,那么他必须满足两个条件:
以欺骗者角度分析蜜罐合约
第一个为记录值必须为true第二个为余额必须为0.
满足为true意味着要调用。
  以欺骗者角度分析蜜罐合约
而调用了这个合约之后用户的余额则变为1000.
所以我们如何将这1000的余额花掉?
我们在broke.sol函数中能发现转账函数:
  1. 以欺骗者角度分析蜜罐合约 但是该函数拥有修饰函数lock 以欺骗者角度分析蜜罐合约
而该函数使得该余额需要存储1年才能进行转账,所以该方法跳过。
第二个方法为使用withdraw函数:
以欺骗者角度分析蜜罐合约
而该函数只能onlyOwner调用。
modifier onlyOwner{ if (msg.sender != owner) revert(); _; }。
那么这里需要改变owner的身份。
这里是个坑点。
以欺骗者角度分析蜜罐合约
这个函数看似能改变,当调用了之后会发现owner并不会改变。这是由于继承机制的问题。(蜜罐的精髓)
所以真正的方案是什么?
我们这是需要逆向整个合约。
https://ethervm.io/decompile/ropsten/0xffd1e29ab7ea57836ce43c1089230fb29fd5b27e
既然上述的几种方法我们无法成功,那么我们需要通过逆向来查找是否有其他的函数了辅助我们进行。
其实在合约逆向后很容易能发现approve函数,而熟悉这个函数的用户都知道。ERC20中存在此类函数意味着拥有transferfrom类型的函数。
以欺骗者角度分析蜜罐合约
而我们并没有看到transferfrom函数。而逆向成为我们唯一的切入点,于是我们继续查看逆向合同:
以欺骗者角度分析蜜罐合约
这个函数中我们能看到需要传入四个参数,与地址相与操作意味着这是某个地址。
而if (arg2 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }代表ERC20中的授权参数,我们从approve中能够看出该存储位置为0x03,此函数中的变量存储位置同样为0x03,所以为同一个参数。
即类似于:require(_value <= allowed[_from][msg.sender]);
而:if (storage[0x02] + msg.sender != arg3) { revert(memory[0x00:0x00]); }代表了0x02位置的参数+msg.sender需要 == arg3参数。
而位置0x02的参数可以通过如下方法进行查询,即为53231323。
以欺骗者角度分析蜜罐合约
所以我们需要传入 53231323+uint(msg.sender)。
之后我们尝试上述方案。
首先使用账户调用takeMoney。
以欺骗者角度分析蜜罐合约
以欺骗者角度分析蜜罐合约
这个时候就满足require (takeRecord[msg.sender] == true);。
之后我们需要将钱转出。
所以我们在账户下调用approve函数:
传入参数:0x7083bcda08538cba3437d98ad01edfa73e2e2276,1000即赋予0x7083bcda08538cba3437d98ad01edfa73e2e2276代替账户转账1000的权利。
查看allowance得到:
以欺骗者角度分析蜜罐合约
即授权成功。之后我们进行转账操作,调用attack函数: 
以欺骗者角度分析蜜罐合约 
此时我们需要调整一下合约的gas值: 
以欺骗者角度分析蜜罐合约 
尽量调整大一些,防止gas不足。之后查看: 
以欺骗者角度分析蜜罐合约
 之后调用`CaptureTheFlag`,成功! 
以欺骗者角度分析蜜罐合约

结论

这是首次尝试使用蜜罐进行真实环境的尝试,在设计代码的时候是以欺骗者角度出发。为了让用户“上钩”我们必须给用户设计一个非常诱人的目标(这里是用CTF的flag),并非常明显的给用户一种提示,即需要满足什么条件。当用户拿到这个条件后会去代码中找达成条件的函数,而这些函数必须要非常明显并且易读。
以欺骗者角度分析蜜罐合约
对于熟悉攻击的人来说,这都是一些简单的技巧便能绕过。从而会让参与者觉得这个这个是合约的一个bug(内心窃喜?),当欢天喜地的去传入代币后会发现,完全不起作用??
目的达到,蜜罐便成功。
而对于防守方来说,要知己知彼才能做到万无一失,所以要了解攻击者的想法才能做到很好的防御,这篇文章愿能提供一些思路。

☆ END ☆

联系/合作/投稿邮箱:[email protected]


以欺骗者角度分析蜜罐合约

以欺骗者角度分析蜜罐合约
你点的每个赞,我都认真当成了喜欢

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: