Integer Overflow Exploit Generator
Overview
The integer overflow exploit generator validates findings from the integer_overflow detector by executing the target contract with 2^256 - 1 (the maximum 256-bit unsigned integer) as the primary parameter and observing whether the execution reverts. Contracts compiled with Solidity >=0.8.0 or using the SafeMath library should revert on overflow; contracts compiled with earlier Solidity versions that use unchecked arithmetic will silently wrap around.
Integer overflow bugs were the dominant vulnerability class before Solidity 0.8.0 introduced automatic revert-on-overflow. Historical examples include the BECToken (BatchOverflow) exploit (2018), where an overflow in token quantity allowed attackers to mint tokens worth billions of dollars in seconds.
Note: Exploit generation in Sigvex is for vulnerability validation purposes only.
Attack Scenario
Overflow (addition):
- A token contract stores
uint256 balance. The contract uses Solidity<0.8.0without SafeMath. - The attacker calls
transfer(address, uint256)withamount = type(uint256).max. - The contract computes
balances[recipient] += amount. With the attacker’s tiny existing balance, this wraps around to a small value, but the arithmetic preceding it can bypass balance checks. - Impact: The attacker transfers more tokens than they own, or sets another account’s balance to an arbitrary value.
Underflow (subtraction):
- A contract tracks user deposits with
uint256 balances. - The attacker calls
withdraw(amount)withamountslightly greater than their balance. - Without an underflow check,
balances[msg.sender] -= amountwraps from 0 totype(uint256).max. - The attacker effectively has an infinite token balance.
Exploit Mechanics
The generator constructs the following execution:
- Calldata construction: The
transfer(address,uint256)selector (0xa9059cbb) is used unless the finding provides a specific function location. - Overflow parameter:
2^256 - 1(the maximumuint256value) is ABI-encoded and appended to the calldata. This is the value most likely to trigger a wrap-around. - Execution: The contract bytecode is executed with the constructed calldata.
- Verdict:
- Reverted → SafeMath or Solidity
>=0.8.0protection is in place (not exploitable at this level). - Completed without revert → overflow protection is absent (confidence 0.75).
- VM error containing “overflow” string → protection detected.
- Reverted → SafeMath or Solidity
Separately, the generator tests an underflow scenario by tracking that "underflow_test" = "0 - 1" is recorded in the inputs map.
The generated PoC targets the older Solidity version explicitly:
pragma solidity ^0.7.6; // Older version without checked arithmetic
interface IVulnerableToken {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract OverflowAttacker {
// Trigger addition overflow: send max uint256 to inflate recipient balance
function exploitAdditionOverflow(address token, address victim) external {
IVulnerableToken t = IVulnerableToken(token);
// With Solidity <0.8.0: balances[victim] + type(uint256).max wraps to near-zero
// depending on existing balance — demonstrates the silent wrap
t.transfer(victim, type(uint256).max);
}
// Trigger underflow: withdraw more than balance, wrapping to uint256.max
function exploitUnderflow(address token) external {
IVulnerableToken t = IVulnerableToken(token);
uint256 myBalance = t.balanceOf(address(this));
// If contract does not check balance >= amount, this wraps to max
t.transfer(address(this), myBalance + 1);
// After: balanceOf(this) == type(uint256).max (overflow of uint256)
}
}
Remediation
- Detector: Integer Overflow Detector
- Remediation Guide: Integer Overflow Remediation
Upgrade to Solidity >=0.8.0, which reverts on overflow/underflow by default:
// VULNERABLE (Solidity <0.8.0 without SafeMath)
function transfer(uint256 amount) external {
balances[msg.sender] -= amount; // silently wraps if amount > balance
}
// SECURE (Solidity >=0.8.0 — automatic checked arithmetic)
function transfer(uint256 amount) external {
balances[msg.sender] -= amount; // reverts if amount > balance
}
// SECURE (Solidity <0.8.0 with SafeMath)
using SafeMath for uint256;
function transfer(uint256 amount) external {
balances[msg.sender] = balances[msg.sender].sub(amount); // reverts on underflow
}
For performance-critical sections in Solidity >=0.8.0, use unchecked{} blocks only when the arithmetic is provably safe with explicit precondition checks.
References
- SWC-101: Integer Overflow and Underflow
- BECToken BatchOverflow Exploit (April 2018): Unlimited token minting via multiplication overflow
- Solidity 0.8.0 Release Notes: Checked Arithmetic