Dex Two
// 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);
}
}
Goal
Drain all balances of token1 and token2 from the DexTwo contract.
Exploit
The exploitable part of the code is located in the swap function, the issue is that we don't check if the from and to address are equal to token1 and token2 address correspondently.
To exploit the contract we need to:
- create new two new tokens (ex. TKN3, TKN4) that will be instances from SwappableTokenTwo
- both of these tokens should have total supply of 200 tokens, all of which will be minted to our account address
- we will approve the DexTwo contract to be able to spend 100 TKN3 tokens on our behalf
- we will transfer 100 TKN3 tokens to DexTwo contract
-
we will call swap() function from DexTwo contract with the following arguments:
- TKN3 address will be used as argument for from parameter
- TKN1 address will be used as argument for to parameter
- the amount that we will send will be 100 TKN3 tokens (100000000000000000000)
-
we will approve the DexTwo contract to be able to spend 100 TKN4 tokens on our behalf
- we will transfer 100 TKN4 tokens to DexTwo contract
- we will call swap() function from DexTwo contract with the following arguments:
- TKN4 address will be used as argument for from parameter
- TKN2 address will be used as argument for to parameter
- the amount that we will send will be 100 TKN4 tokens (100000000000000000000)