Skip to main content
Sigvex

ERC20 Violations Exploit Generator

Sigvex exploit generator that validates ERC20 standard violations by simulating non-standard token behaviors including missing return values, fee-on-transfer tokens, and rebase tokens.

ERC20 Violations Exploit Generator

Overview

The ERC20 violations exploit generator validates findings from the erc20_violation, token_standard, and fee_on_transfer detectors by analyzing how the contract’s token integration logic handles non-standard ERC20 token behaviors. The generator classifies the specific violation type from the finding description and produces a targeted proof-of-concept for each.

ERC20 non-compliance affects a large subset of production tokens. USDT (a major stablecoin issuer) omits return values from transfer and transferFrom. SAFEMOON and many DeFi tokens take a percentage fee on each transfer. AMPL and stETH are rebase tokens whose balances change without explicit transfers. Each of these deviations causes integration failures or accounting exploits in protocols that assume standard ERC20 behavior.

Note: Exploit generation in Sigvex is for vulnerability validation purposes only.

Attack Scenario

Missing return value (USDT-style):

  1. The target protocol calls token.transferFrom(msg.sender, address(this), amount) and wraps it in require(...).
  2. The integration is deployed with USDT, which has no return value.
  3. The require statement receives empty returndata and interprets it as false — the deposit reverts.
  4. Alternatively, if the protocol omits the require, the transfer may silently fail and the user is credited tokens they did not actually send.

Fee-on-transfer token:

  1. A lending protocol records balances[msg.sender] += amount after a deposit call.
  2. The token deducts a 2% fee during transferFrom. The protocol receives 980 tokens but records 1000.
  3. Later withdrawals drain 1000 tokens from the pool, creating a 20-token shortfall that accumulates per deposit.
  4. Eventually, the last depositors cannot withdraw because the pool is insolvent.

Rebase token:

  1. A protocol tracks shares using a balance snapshot at deposit time.
  2. After a positive rebase (stETH yield accrual or AMPL supply expansion), all token balances increase proportionally.
  3. The protocol’s stored depositedAmount[user] no longer reflects actual current value.
  4. Users can withdraw more than they deposited, or the accounting becomes permanently inconsistent.

Exploit Mechanics

The generator checks the finding’s detector and description to classify the violation, then simulates a concrete token interaction:

Violation type Trigger condition Simulated impact
missing_return_value Description contains “return” or “bool” Silent transfer failure, false credit
fee_on_transfer Description contains “fee” or “transfer” 2% fee: 1000 deposited, 980 received, 20-token shortfall
rebase_token Description contains “rebase” Balance drift after supply adjustment
pausable_token Description contains “pause” All transfers blocked by owner
non_standard_decimals Description contains “decimal” Calculation mismatch
general_erc20_violation Other Integration assumption failure

The fee-on-transfer simulation computes:

transfer_amount = 1000 tokens (1000 * 1e18)
fee_amount = transfer_amount * 2 / 100 = 20 tokens
actual_received = 980 tokens

Estimated gas: 75,000. The confidence level defaults to the finding’s own confidence since the generator relies on bytecode-level detection rather than live VM execution.

The generated PoC includes:

  • NonStandardToken: No return value on transfer/transferFrom
  • FeeOnTransferToken: 2% fee deducted on every transfer
  • RebaseToken: Balance changes via rebase(newTotalSupply)
  • SecureTokenIntegration: Safe deposit using balanceAfter - balanceBefore accounting
// SECURE: Check actual received amount
function depositFeeToken(IERC20 token, uint256 amount) external {
    uint256 balanceBefore = token.balanceOf(address(this));
    safeTransferFrom(token, msg.sender, address(this), amount);
    uint256 balanceAfter = token.balanceOf(address(this));
    uint256 actualReceived = balanceAfter - balanceBefore;
    // Credit actual amount received, not specified amount
    deposits[msg.sender] += actualReceived;
}

Remediation

Use OpenZeppelin’s SafeERC20 library for all token operations, and measure actual received amounts:

import "@audited/token/ERC20/utils/SafeERC20.sol";

contract SecureVault {
    using SafeERC20 for IERC20;

    // SafeERC20 handles USDT-style missing return values
    function deposit(IERC20 token, uint256 amount) external {
        uint256 balanceBefore = token.balanceOf(address(this));
        token.safeTransferFrom(msg.sender, address(this), amount);
        uint256 actualReceived = token.balanceOf(address(this)) - balanceBefore;
        // Credit actual amount to handle fee-on-transfer tokens
        deposits[msg.sender] += actualReceived;
    }
}

Known non-standard tokens that require special handling:

  • USDT, BNB, OMG: No return value from transfer/transferFrom
  • SAFEMOON, REFLECT, many DeFi tokens: Fee-on-transfer
  • AMPL, stETH, aTokens: Rebasing or interest-bearing balances
  • USDC, many stablecoins: Pausable transfers

References