[Ethernaut CTF] Dex Two

0xDave·2022년 10월 12일
0

Ethereum

목록 보기
45/112

소스코드


// 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);
  }
}

해결과제


This level will ask you to break DexTwo, 
a subtlely modified Dex contract from the previous level, in a different way.

You need to drain all balances of token1 and token2 from the DexTwo contract to succeed in this level.

You will still start with 10 tokens of token1 and 10 of token2. 
The DEX contract still starts with 100 of each token.

  Things that might help:

    How has the swap method been modified?
    Could you use a custom token contract in your attack?

token1,2 모두 탈취하기

해결과정


일단 swap에서 token1과 token2를 확인하던 require문이 하나 사라졌다. 힌트를 봤을 때 다른 토큰 컨트랙트를 사용해서 문제를 풀 수 있는 것 같다. 설마 이전에 복잡하게 생각했던 방법을 사용할 수 있는건가?

하지만 이번 문제는 이전 문제에서 토큰을 하나 추가해서 간단히 풀 수 있는 문제였다. 아래 컨트랙트를 디플로이해서 토큰을 하나 만들어주자.

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract EvilToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("EvilToken", "EVL") {
        _mint(msg.sender, initialSupply);
    }
}

이후 컨트랙트에 EVL 토큰을 100개 보내준다. 이렇게 되면 이전 문제와 마찬가지로 계속 스왑하면서 token1과 token2를 가져올 수 있다.

한 번 스왑한 후에 token2 100개를 가져오려면 EVL 토큰이 200개가 필요하다. (amount(필요한 양) * token2(현재 100개)) / EVL(현재 200개) = 가져오고 싶은 양 이라고 하면 가져오고 싶은 양이 100개 이므로 amount에는 EVL 토큰 200개가 필요한 것.

총 2번의 스왑으로 token1과 token2를 전부 탈취했다.


출처 및 참고자료


  1. Ethernaut Hacks Level 23: Dex Two
profile
Just BUIDL :)

0개의 댓글