Skip to content

Faulty Permit Implementation

id: LS13H
title: Faulty Permit Implementation 
baseSeverity: H
category: signature-verification
language: solidity
blockchain: [ethereum]
impact: Attacker bypasses approval flow and spends tokens without consent
status: draft
complexity: high
attack_vector: external
mitigation_difficulty: medium
versions: [">=0.6.0", "<0.8.21"]
cwe: CWE-347
swc: SWC-122

πŸ“ Description

  • Faulty permit implementations occur when ERC-2612-style permit() functions are implemented incorrectly, particularly in:
  • Signature digest construction (EIP-712),
  • nonce tracking,domainSeparator usage,Or validation logic.
  • When permit() fails to properly validate the signed data or incorrectly handles replays or approvals, it enables:
  • Unauthorized token approvals or Replay attacks, letting attackers spend tokens repeatedly.

🚨 Vulnerable Code

contract BrokenPermitToken {
    mapping(address => uint256) public nonces;

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v, bytes32 r, bytes32 s
    ) external {
        // ❌ Missing deadline check
        bytes32 digest = keccak256(abi.encodePacked(owner, spender, value, nonces[owner]));
        address signer = ecrecover(digest, v, r, s);
        require(signer == owner, "Invalid signature");

        nonces[owner]++; // βœ… Used but digest was incorrectly formed

        // Assume this sets allowance (omitted for brevity)
    }
}

πŸ§ͺ Exploit Scenario

Step-by-step exploit process:

  1. Attacker sees a valid signature from a user that includes owner, spender, value, nonce.
  2. Since no deadline is enforced, signature never expires.
  3. Attacker reuses the same signature on multiple chains or contracts where nonces[owner] hasn't changed.
  4. The faulty digest allows permit() to succeed again and again, allowing unlimited token approvals.

Assumptions:

  • Permit digest is non-EIP-712 conformant or missing deadline.
  • nonces[owner] is not enforced across domains or replay-safe contexts.

βœ… Fixed Code

import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";

contract SafePermitToken is ERC20Permit {
    constructor(string memory name) ERC20(name, "SPT") ERC20Permit(name) {}

    // Uses OpenZeppelin's validated implementation of EIP-2612
}

🧭 Contextual Severity

- context: "Permit used for core token approval or paymaster integration"
  severity: H
  reasoning: "Replay or spoofing can lead to full user balance drain"
- context: "Permit optional and fallback to approval model exists"
  severity: M
  reasoning: "Impact is lower due to alternatives"
- context: "Permit protected with strong nonce and domain validation"
  severity: I
  reasoning: "Secure implementation; no known vector"

πŸ›‘οΈ Prevention

Primary Defenses

  • Use OpenZeppelin’s ERC20Permit or draft-ERC20Permit contracts.
  • Always enforce:
  • require(block.timestamp <= deadline, "Permit expired");
  • EIP-712-compliant domain separator
  • Unique nonce per signer

Additional Safeguards

  • Use DOMAIN_SEPARATOR() with EIP712 helper contract to avoid inconsistencies.
  • Validate all permit() calls using test vectors and tooling like eth-permit or ethers.utils._TypedDataEncoder.

Detection Methods

  • Slither: incorrect-ecrecover-digest, missing-deadline, signature-replay detectors.
  • Manual inspection of digest construction and nonce handling.
  • Unit tests using replayed signatures and expired deadlines.

πŸ•°οΈ Historical Exploits

  • Name: Permit Signature Replay Attack
  • Date: 2025-03
  • Loss: Significant risk of unauthorized token transfers across chains
  • Post-mortem: Link to post-mortem

πŸ“š Further Reading


βœ… Vulnerability Report

id: LS13H
title: Faulty Permit Implementation 
severity: H
score:
impact: 5         
exploitability: 4 
reachability: 4   
complexity: 3     
detectability: 4  
finalScore: 4.3

πŸ“„ Justifications & Analysis

  • Impact: Attackers can grant themselves approvals, withdraw tokens, or abuse liquidity.
  • Exploitability: Simple replay or signature injection possible with faulty digest.
  • Reachability: Widespread due to adoption of EIP-2612.
  • Complexity: Moderate β€” attacker needs to craft or reuse a valid signature.
  • Detectability: Very detectable with audits, test vectors, and OpenZeppelin comparison.