VAITP Dataset

← Back to the dataset

CVE-2025-21607

Vyper compiler fails to check precompile success, leading to potential incorrect results.

  • CVSS 2.3
  • CWE-670 Always-Incorrect Control Flow Implementation
  • Design Defects
  • Remote

Vyper is a Pythonic Smart Contract Language for the EVM. When the Vyper Compiler uses the precompiles EcRecover (0x1) and Identity (0x4), the success flag of the call is not checked. As a consequence an attacker can provide a specific amount of gas to make these calls fail but let the overall execution continue. Then the execution result can be incorrect. Based on EVM's rules, after the failed precompile the remaining code has only 1/64 of the pre-call-gas left (as 63/64 were forwarded and spent). Hence, only fairly simple executions can follow the failed precompile calls. Therefore, we found no significantly impacted real-world contracts. None the less an advisory has been made out of an abundance of caution. There are no actions for users to take.

CVSS base score
2.3
Published
2025-01-14
OWASP
A04 Insecure Design
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Design Defects
Subcategory
Design Defects
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
Vyper
Fixed by upgrading
Yes

Solution

No upgrade needed. No user action required.

Vulnerable code sample

def ecRecover(gas):
    """Vulnerable function that demonstrates the security issue."""
  
  if gas < 3000: 
    success = True 
    remaining_gas = gas // 64 
  else:
      success = True
      remaining_gas = gas - 3000

  return (success, remaining_gas)



def identity(gas):
    """Vulnerable function that demonstrates the security issue."""
    
    if gas < 15: 
      success = True  
      remaining_gas = gas // 64
    else:
      success = True
      remaining_gas = gas - 15

    return (success, remaining_gas)


def contract_logic(initial_gas, function_to_test):
    """Vulnerable function that demonstrates the security issue."""
    
    success_ec, remaining_gas_ec = function_to_test(initial_gas)


    if remaining_gas_ec > 10:
        success_final_calc = True
        remaining_gas_final_calc = remaining_gas_ec - 10
    else:
        success_final_calc = False
        remaining_gas_final_calc = remaining_gas_ec
      
    
    return (success_ec, remaining_gas_ec, success_final_calc, remaining_gas_final_calc)

Patched code sample

def ecRecover(gas):
    """Secure function that fixes the vulnerability."""
    if gas < 3000: 
        success = True 
        remaining_gas = gas // 64 
    else:
        success = True
        remaining_gas = max(gas - 3000, 0)

    return (success, remaining_gas)

def identity(gas):
    """Secure function that fixes the vulnerability."""
    if gas < 15: 
        success = True  
        remaining_gas = gas // 64
    else:
        success = True
        remaining_gas = max(gas - 15, 0)

    return (success, remaining_gas)

def contract_logic(initial_gas, function_to_test):
    """Secure function that fixes the vulnerability."""
    success_ec, remaining_gas_ec = function_to_test(initial_gas)

    if remaining_gas_ec > 10:
        success_final_calc = True
        remaining_gas_final_calc = max(remaining_gas_ec - 10, 0)
    else:
        success_final_calc = False
        remaining_gas_final_calc = remaining_gas_ec

    return (success_ec, remaining_gas_ec, success_final_calc, remaining_gas_final_calc)

Payload

# @version 0.3.10

@external
def test():
    addr: address = ecrecover("0x0000000000000000000000000000000000000000000000000000000000000000", "0x00", "0x00", "0x00")
    # ecrecover is expected to return zero address, but we can force it to fail.
    # This will continue the execution with reduced gas.
    
    identity("0x00")
    # Similarly identity call can fail and continue

    # some other logic that might cause havoc due to incorrect precompile call
    # It's hard to provide something specific here since the impact is low and context-dependent

    log Result(addr)

Cite this entry

@misc{vaitp:cve202521607,
  title        = {{Vyper compiler fails to check precompile success, leading to potential incorrect results.
}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-21607},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-21607/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::