Integer Overflow Exploit Generator
Sigvex exploit generator that validates unchecked arithmetic vulnerabilities by supplying maximum uint256 values and checking whether the execution reverts.
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
- OpenZeppelin SafeMath Documentation