题目描述

image.png

解题过程

题目源码

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.6.0;
  3. contract Token {
  4. mapping(address => uint) balances;
  5. uint public totalSupply;
  6. constructor(uint _initialSupply) public {
  7. balances[msg.sender] = totalSupply = _initialSupply;
  8. }
  9. function transfer(address _to, uint _value) public returns (bool) {
  10. require(balances[msg.sender] - _value >= 0);
  11. balances[msg.sender] -= _value;
  12. balances[_to] += _value;
  13. return true;
  14. }
  15. function balanceOf(address _owner) public view returns (uint balance) {
  16. return balances[_owner];
  17. }
  18. }

根据题目提示我们默认含有20代币,注意一下 transfer方法

  1. // 向某地址转移代币,且需要通过require的大于0的检测
  2. function transfer(address _to, uint _value) public returns (bool) {
  3. require(balances[msg.sender] - _value >= 0);
  4. balances[msg.sender] -= _value;
  5. balances[_to] += _value;
  6. return true;
  7. }

以太坊虚拟机(EVM)为整数指定固定大小的数据类型。这意味着一个整形变量只能表达一定范围的数字。
例如,uint8,只能存储[0,255]之间的数字,如果想存储256,那么就会上溢,从而将变量的值变为0。相对应的,如果从一个uint8类型的值为0的变量中减1,就会发生下溢,该变量会变成255。
所以我们需要通过下溢出来完成攻击
image.png
部署合约后可以看到默认20代币,产生下溢我们就可以向任意地址转移 21代币,发生下溢
image.png

  1. Overflow solidity 中非常常见, 你必须小心检查, 比如下面这样:
  2. if(a + c > a) {
  3. a = a + c;
  4. }
  5. 另一个简单的方法是使用 OpenZeppelin SafeMath 库, 它会自动检查所有数学运算的溢出, 可以像这样使用:
  6. a = a.add(c);
  7. 如果有溢出, 代码会自动恢复.