Token whale
pragma solidity ^0.4.21;
contract TokenWhaleChallenge {
address player;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
string public name = "Simple ERC20 Token";
string public symbol = "SET";
uint8 public decimals = 18;
function TokenWhaleChallenge(address _player) public {
player = _player;
totalSupply = 1000;
balanceOf[player] = 1000;
}
function isComplete() public view returns (bool) {
return balanceOf[player] >= 1000000;
}
event Transfer(address indexed from, address indexed to, uint256 value);
function _transfer(address to, uint256 value) internal {
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
}
function transfer(address to, uint256 value) public {
require(balanceOf[msg.sender] >= value);
require(balanceOf[to] + value >= balanceOf[to]);
_transfer(to, value);
}
event Approval(address indexed owner, address indexed spender, uint256 value);
function approve(address spender, uint256 value) public {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
}
function transferFrom(address from, address to, uint256 value) public {
require(balanceOf[from] >= value);
require(balanceOf[to] + value >= balanceOf[to]);
require(allowance[from][msg.sender] >= value);
allowance[from][msg.sender] -= value;
_transfer(to, value);
}
}
Goal
Find a way to accumulate at least 1,000,000 tokens to solve this challenge
Exploit
As per the CTF, we start our balance with 1000 tokens, and we need to find a way to acquire more tokens. First thing that comes to my mind to find a way to cause arithmetic overflow or underflow.
The transfer() function itself can not be expoloited, but there is an issue with the checks inside transferFrom().
To exploit the contract we need to:
- call transfer() from our account (player) and tansfer 1000 tokens to another address (let's say secondPlayer)
- call approve() from secondPlayer and approve the first player (our account) to be able to transfer the tokens on their behalf, the value for the approval can be set to 2000
- call transferFrom() from our account(player), and transfer 1000 tokens from the secondPlayer to another address (we can use secondPlayer again as the to address), ex. tansferFrom(secondPlayer, secondPlayer, 1000)
By calling transferFrom from our account, the internal _transfer function will be called, and this is the function that will underflow our balance of tokens.
balanceOf[msg.sender] -= value;
Remeber that in the first step we transferred our tokens and our balance is now 0, the code above will cause our balance to be equal 0 - 1000, and that is really big number.